Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
M
Magisk
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
Magisk
Commits
8647ba47
Commit
8647ba47
authored
Jul 09, 2020
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove more RxJava
parent
4631077c
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
349 additions
and
395 deletions
+349
-395
build.gradle.kts
app/build.gradle.kts
+3
-2
GeneralReceiver.kt
...rc/main/java/com/topjohnwu/magisk/core/GeneralReceiver.kt
+8
-2
UpdateCheckService.kt
...main/java/com/topjohnwu/magisk/core/UpdateCheckService.kt
+14
-12
BaseDao.kt
...c/main/java/com/topjohnwu/magisk/core/magiskdb/BaseDao.kt
+1
-17
PolicyDao.kt
...main/java/com/topjohnwu/magisk/core/magiskdb/PolicyDao.kt
+21
-18
Query.kt
...src/main/java/com/topjohnwu/magisk/core/magiskdb/Query.kt
+24
-0
SettingsDao.kt
...in/java/com/topjohnwu/magisk/core/magiskdb/SettingsDao.kt
+8
-6
StringDao.kt
...main/java/com/topjohnwu/magisk/core/magiskdb/StringDao.kt
+8
-6
SuCallbackHandler.kt
...in/java/com/topjohnwu/magisk/core/su/SuCallbackHandler.kt
+5
-2
SuRequestHandler.kt
...ain/java/com/topjohnwu/magisk/core/su/SuRequestHandler.kt
+2
-2
SuLogDao.kt
.../main/java/com/topjohnwu/magisk/data/database/SuLogDao.kt
+12
-11
GithubServices.kt
.../java/com/topjohnwu/magisk/data/network/GithubServices.kt
+4
-4
DBConfig.kt
...ain/java/com/topjohnwu/magisk/data/repository/DBConfig.kt
+25
-15
LogRepository.kt
...ava/com/topjohnwu/magisk/data/repository/LogRepository.kt
+9
-11
MagiskRepository.kt
.../com/topjohnwu/magisk/data/repository/MagiskRepository.kt
+37
-34
ApplicationModule.kt
...rc/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt
+0
-2
XSU.kt
app/src/main/java/com/topjohnwu/magisk/extensions/XSU.kt
+4
-0
HideTarget.kt
...main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt
+8
-4
BaseViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt
+7
-82
FlashViewModel.kt
...main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
+16
-17
HideViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
+15
-13
HomeViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
+39
-38
LogViewModel.kt
...src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt
+29
-42
ModuleViewModel.kt
...in/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
+1
-1
SuperuserViewModel.kt
...a/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt
+48
-53
build.gradle.kts
build.gradle.kts
+1
-1
No files found.
app/build.gradle.kts
View file @
8647ba47
...
...
@@ -126,7 +126,7 @@ dependencies {
val
vRoom
=
"2.2.5"
implementation
(
"androidx.room:room-runtime:${vRoom}"
)
implementation
(
"androidx.room:room-
rxjava2
:${vRoom}"
)
implementation
(
"androidx.room:room-
ktx
:${vRoom}"
)
kapt
(
"androidx.room:room-compiler:${vRoom}"
)
implementation
(
"androidx.navigation:navigation-fragment-ktx:${Deps.vNav}"
)
...
...
@@ -139,9 +139,10 @@ dependencies {
implementation
(
"androidx.preference:preference:1.1.1"
)
implementation
(
"androidx.recyclerview:recyclerview:1.1.0"
)
implementation
(
"androidx.fragment:fragment-ktx:1.2.5"
)
implementation
(
"androidx.work:work-runtime:2.3.4"
)
implementation
(
"androidx.work:work-runtime
-ktx
:2.3.4"
)
implementation
(
"androidx.transition:transition:1.3.1"
)
implementation
(
"androidx.multidex:multidex:2.0.1"
)
implementation
(
"androidx.core:core-ktx:1.3.0"
)
implementation
(
"androidx.localbroadcastmanager:localbroadcastmanager:1.0.0"
)
implementation
(
"com.google.android.material:material:1.2.0-beta01"
)
}
app/src/main/java/com/topjohnwu/magisk/core/GeneralReceiver.kt
View file @
8647ba47
...
...
@@ -12,6 +12,8 @@ import com.topjohnwu.magisk.extensions.reboot
import
com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
org.koin.core.inject
open
class
GeneralReceiver
:
BaseReceiver
()
{
...
...
@@ -25,6 +27,10 @@ open class GeneralReceiver : BaseReceiver() {
override
fun
onReceive
(
context
:
ContextWrapper
,
intent
:
Intent
?)
{
intent
?:
return
fun
rmPolicy
(
pkg
:
String
)
=
GlobalScope
.
launch
{
policyDB
.
delete
(
pkg
)
}
when
(
intent
.
action
?:
return
)
{
Intent
.
ACTION_REBOOT
->
{
SuCallbackHandler
(
context
,
intent
.
getStringExtra
(
"action"
),
intent
.
extras
)
...
...
@@ -32,11 +38,11 @@ open class GeneralReceiver : BaseReceiver() {
Intent
.
ACTION_PACKAGE_REPLACED
->
{
// This will only work pre-O
if
(
Config
.
suReAuth
)
policyDB
.
delete
(
getPkg
(
intent
)).
blockingGet
(
)
rmPolicy
(
getPkg
(
intent
)
)
}
Intent
.
ACTION_PACKAGE_FULLY_REMOVED
->
{
val
pkg
=
getPkg
(
intent
)
policyDB
.
delete
(
pkg
).
blockingGet
(
)
rmPolicy
(
pkg
)
Shell
.
su
(
"magiskhide --rm $pkg"
).
submit
()
}
Intent
.
ACTION_LOCALE_CHANGED
->
Shortcuts
.
setup
(
context
)
...
...
app/src/main/java/com/topjohnwu/magisk/core/UpdateCheckService.kt
View file @
8647ba47
package
com.topjohnwu.magisk.core
import
android.content.Context
import
androidx.work.Worker
import
androidx.work.
Coroutine
Worker
import
androidx.work.WorkerParameters
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.data.repository.MagiskRepository
import
com.topjohnwu.magisk.extensions.inject
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
org.koin.core.KoinComponent
import
org.koin.core.inject
class
UpdateCheckService
(
context
:
Context
,
workerParams
:
WorkerParameters
)
:
Worker
(
context
,
workerParams
)
{
:
CoroutineWorker
(
context
,
workerParams
),
KoinComponent
{
private
val
magiskRepo
:
MagiskRepository
by
inject
()
override
fun
doWork
():
Result
{
override
suspend
fun
doWork
():
Result
{
// Make sure shell initializer was ran
withContext
(
Dispatchers
.
IO
)
{
Shell
.
getShell
()
return
runCatching
{
magiskRepo
.
fetchUpdate
().
blockingGet
()
if
(
BuildConfig
.
VERSION_CODE
<
Info
.
remote
.
app
.
versionCode
)
}
return
magiskRepo
.
fetchUpdate
()
?.
let
{
if
(
BuildConfig
.
VERSION_CODE
<
it
.
app
.
versionCode
)
Notifications
.
managerUpdate
(
applicationContext
)
else
if
(
Info
.
env
.
isActive
&&
Info
.
env
.
magiskVersionCode
<
Info
.
remote
.
magisk
.
versionCode
)
else
if
(
Info
.
env
.
isActive
&&
Info
.
env
.
magiskVersionCode
<
it
.
magisk
.
versionCode
)
Notifications
.
magiskUpdate
(
applicationContext
)
Result
.
success
()
}.
getOrElse
{
Result
.
failure
()
}
}
?:
Result
.
failure
()
}
}
app/src/main/java/com/topjohnwu/magisk/core/magiskdb/BaseDao.kt
View file @
8647ba47
package
com.topjohnwu.magisk.core.magiskdb
import
androidx.annotation.StringDef
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Single
abstract
class
BaseDao
{
...
...
@@ -20,25 +18,11 @@ abstract class BaseDao {
@TableStrict
abstract
val
table
:
String
inline
fun
<
reified
Builder
:
Query
.
Builder
>
q
uery
(
builder
:
Builder
.()
->
Unit
=
{})
=
inline
fun
<
reified
Builder
:
Query
.
Builder
>
buildQ
uery
(
builder
:
Builder
.()
->
Unit
=
{})
=
Builder
::
class
.
java
.
newInstance
()
.
apply
{
table
=
this
@BaseDao
.
table
}
.
apply
(
builder
)
.
toString
()
.
let
{
Query
(
it
)
}
.
query
()
}
fun
Query
.
query
()
=
query
.
su
()
private
fun
String
.
suRaw
()
=
Single
.
fromCallable
{
Shell
.
su
(
this
).
exec
().
out
}
private
fun
String
.
su
()
=
suRaw
().
map
{
it
.
toMap
()
}
private
fun
List
<
String
>.
toMap
()
=
map
{
it
.
split
(
Regex
(
"\\|"
))
}
.
map
{
it
.
toMapInternal
()
}
private
fun
List
<
String
>.
toMapInternal
()
=
map
{
it
.
split
(
"="
,
limit
=
2
)
}
.
filter
{
it
.
size
==
2
}
.
map
{
Pair
(
it
[
0
],
it
[
1
])
}
.
toMap
()
app/src/main/java/com/topjohnwu/magisk/core/magiskdb/PolicyDao.kt
View file @
8647ba47
...
...
@@ -7,6 +7,8 @@ import com.topjohnwu.magisk.core.model.MagiskPolicy
import
com.topjohnwu.magisk.core.model.toMap
import
com.topjohnwu.magisk.core.model.toPolicy
import
com.topjohnwu.magisk.extensions.now
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
timber.log.Timber
import
java.util.concurrent.TimeUnit
...
...
@@ -17,55 +19,56 @@ class PolicyDao(
override
val
table
:
String
=
Table
.
POLICY
fun
deleteOutdated
(
nowSeconds
:
Long
=
TimeUnit
.
MILLISECONDS
.
toSeconds
(
now
)
)
=
query
<
Delete
>
{
suspend
fun
deleteOutdated
()
=
buildQuery
<
Delete
>
{
condition
{
greaterThan
(
"until"
,
"0"
)
and
{
lessThan
(
"until"
,
nowSeconds
.
toString
())
lessThan
(
"until"
,
TimeUnit
.
MILLISECONDS
.
toSeconds
(
now
)
.
toString
())
}
or
{
lessThan
(
"until"
,
"0"
)
}
}
}.
ignoreElemen
t
()
}.
commi
t
()
fun
delete
(
packageName
:
String
)
=
q
uery
<
Delete
>
{
suspend
fun
delete
(
packageName
:
String
)
=
buildQ
uery
<
Delete
>
{
condition
{
equals
(
"package_name"
,
packageName
)
}
}.
ignoreElemen
t
()
}.
commi
t
()
fun
delete
(
uid
:
Int
)
=
q
uery
<
Delete
>
{
suspend
fun
delete
(
uid
:
Int
)
=
buildQ
uery
<
Delete
>
{
condition
{
equals
(
"uid"
,
uid
)
}
}.
ignoreElemen
t
()
}.
commi
t
()
fun
fetch
(
uid
:
Int
)
=
q
uery
<
Select
>
{
suspend
fun
fetch
(
uid
:
Int
)
=
buildQ
uery
<
Select
>
{
condition
{
equals
(
"uid"
,
uid
)
}
}.
map
{
it
.
first
().
toPolicySafe
()
}
}.
query
().
first
().
toPolicyOrNull
()
fun
update
(
policy
:
MagiskPolicy
)
=
q
uery
<
Replace
>
{
suspend
fun
update
(
policy
:
MagiskPolicy
)
=
buildQ
uery
<
Replace
>
{
values
(
policy
.
toMap
())
}.
ignoreElemen
t
()
}.
commi
t
()
fun
fetchAll
()
=
q
uery
<
Select
>
{
suspend
fun
<
R
:
Any
>
fetchAll
(
mapper
:
(
MagiskPolicy
)
->
R
)
=
buildQ
uery
<
Select
>
{
condition
{
equals
(
"uid/100000"
,
Const
.
USER_ID
)
}
}.
map
{
it
.
mapNotNull
{
it
.
toPolicySafe
()
}
}
}.
query
{
it
.
toPolicyOrNull
()
?.
let
(
mapper
)
}
private
fun
Map
<
String
,
String
>.
toPolicy
Safe
():
MagiskPolicy
?
{
private
fun
Map
<
String
,
String
>.
toPolicy
OrNull
():
MagiskPolicy
?
{
return
runCatching
{
toPolicy
(
context
.
packageManager
)
}.
getOrElse
{
Timber
.
e
(
it
)
if
(
it
is
PackageManager
.
NameNotFoundException
)
{
val
uid
=
getOrElse
(
"uid"
)
{
null
}
?:
return
null
delete
(
uid
).
subscribe
()
GlobalScope
.
launch
{
delete
(
uid
.
toInt
())
}
}
null
}
...
...
app/src/main/java/com/topjohnwu/magisk/core/magiskdb/Query.kt
View file @
8647ba47
package
com.topjohnwu.magisk.core.magiskdb
import
androidx.annotation.StringDef
import
com.topjohnwu.magisk.extensions.await
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.async
import
kotlinx.coroutines.awaitAll
import
kotlinx.coroutines.withContext
class
Query
(
private
val
_query
:
String
)
{
val
query
get
()
=
"magisk --sqlite '$_query'"
...
...
@@ -9,6 +15,24 @@ class Query(private val _query: String) {
val
requestType
:
String
var
table
:
String
}
suspend
inline
fun
<
R
:
Any
>
query
(
crossinline
mapper
:
(
Map
<
String
,
String
>)
->
R
?):
List
<
R
>
=
withContext
(
Dispatchers
.
Default
)
{
Shell
.
su
(
query
).
await
().
out
.
map
{
line
->
async
{
line
.
split
(
"\\|"
.
toRegex
())
.
map
{
it
.
split
(
"="
,
limit
=
2
)
}
.
filter
{
it
.
size
==
2
}
.
map
{
it
[
0
]
to
it
[
1
]
}
.
toMap
()
.
let
(
mapper
)
}
}.
awaitAll
().
filterNotNull
()
}
suspend
inline
fun
query
()
=
query
{
it
}
suspend
inline
fun
commit
()
=
Shell
.
su
(
query
).
to
(
null
).
await
()
}
class
Delete
:
Query
.
Builder
{
...
...
app/src/main/java/com/topjohnwu/magisk/core/magiskdb/SettingsDao.kt
View file @
8647ba47
...
...
@@ -4,17 +4,19 @@ class SettingsDao : BaseDao() {
override
val
table
=
Table
.
SETTINGS
fun
delete
(
key
:
String
)
=
q
uery
<
Delete
>
{
suspend
fun
delete
(
key
:
String
)
=
buildQ
uery
<
Delete
>
{
condition
{
equals
(
"key"
,
key
)
}
}.
ignoreElemen
t
()
}.
commi
t
()
fun
put
(
key
:
String
,
value
:
Int
)
=
q
uery
<
Replace
>
{
suspend
fun
put
(
key
:
String
,
value
:
Int
)
=
buildQ
uery
<
Replace
>
{
values
(
"key"
to
key
,
"value"
to
value
)
}.
ignoreElemen
t
()
}.
commi
t
()
fun
fetch
(
key
:
String
,
default
:
Int
=
-
1
)
=
q
uery
<
Select
>
{
suspend
fun
fetch
(
key
:
String
,
default
:
Int
=
-
1
)
=
buildQ
uery
<
Select
>
{
fields
(
"value"
)
condition
{
equals
(
"key"
,
key
)
}
}.
map
{
it
.
firstOrNull
()
?.
values
?.
firstOrNull
()
?.
toIntOrNull
()
?:
default
}
}.
query
{
it
[
"value"
]
?.
toIntOrNull
()
}.
firstOrNull
()
?:
default
}
app/src/main/java/com/topjohnwu/magisk/core/magiskdb/StringDao.kt
View file @
8647ba47
...
...
@@ -4,17 +4,19 @@ class StringDao : BaseDao() {
override
val
table
=
Table
.
STRINGS
fun
delete
(
key
:
String
)
=
q
uery
<
Delete
>
{
suspend
fun
delete
(
key
:
String
)
=
buildQ
uery
<
Delete
>
{
condition
{
equals
(
"key"
,
key
)
}
}.
ignoreElemen
t
()
}.
commi
t
()
fun
put
(
key
:
String
,
value
:
String
)
=
q
uery
<
Replace
>
{
suspend
fun
put
(
key
:
String
,
value
:
String
)
=
buildQ
uery
<
Replace
>
{
values
(
"key"
to
key
,
"value"
to
value
)
}.
ignoreElemen
t
()
}.
commi
t
()
fun
fetch
(
key
:
String
,
default
:
String
=
""
)
=
q
uery
<
Select
>
{
suspend
fun
fetch
(
key
:
String
,
default
:
String
=
""
)
=
buildQ
uery
<
Select
>
{
fields
(
"value"
)
condition
{
equals
(
"key"
,
key
)
}
}.
map
{
it
.
firstOrNull
()
?.
values
?.
firstOrNull
()
?:
default
}
}.
query
{
it
[
"value"
]
}.
firstOrNull
()
?:
default
}
app/src/main/java/com/topjohnwu/magisk/core/su/SuCallbackHandler.kt
View file @
8647ba47
...
...
@@ -19,10 +19,11 @@ import com.topjohnwu.magisk.data.repository.LogRepository
import
com.topjohnwu.magisk.extensions.get
import
com.topjohnwu.magisk.extensions.startActivity
import
com.topjohnwu.magisk.extensions.startActivityWithRoot
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.model.entity.toLog
import
com.topjohnwu.magisk.ui.surequest.SuRequestActivity
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
timber.log.Timber
object
SuCallbackHandler
:
ProviderCallHandler
{
...
...
@@ -111,7 +112,9 @@ object SuCallbackHandler : ProviderCallHandler {
)
val
logRepo
=
get
<
LogRepository
>()
logRepo
.
insert
(
log
).
subscribeK
(
onError
=
{
Timber
.
e
(
it
)
})
GlobalScope
.
launch
{
logRepo
.
insert
(
log
)
}
}
private
fun
handleNotify
(
context
:
Context
,
data
:
Bundle
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/core/su/SuRequestHandler.kt
View file @
8647ba47
...
...
@@ -111,13 +111,13 @@ abstract class SuRequestHandler(
}
catch
(
e
:
IOException
)
{
Timber
.
e
(
e
)
}
finally
{
if
(
until
>=
0
)
policyDB
.
update
(
policy
).
blockingAwait
()
runCatching
{
input
.
close
()
output
.
close
()
socket
.
close
()
}
if
(
until
>=
0
)
policyDB
.
update
(
policy
)
}
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/data/database/SuLogDao.kt
View file @
8647ba47
...
...
@@ -2,8 +2,8 @@ package com.topjohnwu.magisk.data.database
import
androidx.room.*
import
com.topjohnwu.magisk.model.entity.MagiskLog
import
io.reactivex.Completable
import
io.reactivex.Single
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
java.util.*
@Database
(
version
=
1
,
entities
=
[
MagiskLog
::
class
],
exportSchema
=
false
)
...
...
@@ -18,19 +18,20 @@ abstract class SuLogDao(private val db: SuLogDatabase) {
private
val
twoWeeksAgo
=
Calendar
.
getInstance
().
apply
{
add
(
Calendar
.
WEEK_OF_YEAR
,
-
2
)
}.
timeInMillis
fun
deleteAll
()
=
Completable
.
fromAction
{
db
.
clearAllTables
()
}
suspend
fun
deleteAll
()
=
withContext
(
Dispatchers
.
IO
)
{
db
.
clearAllTables
()
}
fun
fetchAll
()
=
deleteOutdated
().
andThen
(
fetch
())
suspend
fun
fetchAll
():
MutableList
<
MagiskLog
>
{
deleteOutdated
()
return
fetch
()
}
@Query
(
"SELECT * FROM logs ORDER BY time DESC"
)
protected
abstract
fun
fetch
():
Single
<
MutableList
<
MagiskLog
>>
@Insert
abstract
fun
insert
(
log
:
MagiskLog
):
Completable
protected
abstract
suspend
fun
fetch
():
MutableList
<
MagiskLog
>
@Query
(
"DELETE FROM logs WHERE time < :timeout"
)
protected
abstract
fun
deleteOutdated
(
timeout
:
Long
=
twoWeeksAgo
):
Completable
protected
abstract
suspend
fun
deleteOutdated
(
timeout
:
Long
=
twoWeeksAgo
)
@Insert
abstract
suspend
fun
insert
(
log
:
MagiskLog
)
}
app/src/main/java/com/topjohnwu/magisk/data/network/GithubServices.kt
View file @
8647ba47
...
...
@@ -13,16 +13,16 @@ interface GithubRawServices {
//region topjohnwu/magisk_files
@GET
(
"$MAGISK_FILES/master/stable.json"
)
fun
fetchStableUpdate
():
Single
<
UpdateInfo
>
suspend
fun
fetchStableUpdate
():
UpdateInfo
@GET
(
"$MAGISK_FILES/master/beta.json"
)
fun
fetchBetaUpdate
():
Single
<
UpdateInfo
>
suspend
fun
fetchBetaUpdate
():
UpdateInfo
@GET
(
"$MAGISK_FILES/canary/debug.json"
)
fun
fetchCanaryUpdate
():
Single
<
UpdateInfo
>
suspend
fun
fetchCanaryUpdate
():
UpdateInfo
@GET
fun
fetchCustomUpdate
(
@Url
url
:
String
):
Single
<
UpdateInfo
>
suspend
fun
fetchCustomUpdate
(
@Url
url
:
String
):
UpdateInfo
@GET
(
"$MAGISK_FILES/{$REVISION}/snet.jar"
)
@Streaming
...
...
app/src/main/java/com/topjohnwu/magisk/data/repository/DBConfig.kt
View file @
8647ba47
...
...
@@ -2,7 +2,9 @@ package com.topjohnwu.magisk.data.repository
import
com.topjohnwu.magisk.core.magiskdb.SettingsDao
import
com.topjohnwu.magisk.core.magiskdb.StringDao
import
io.reactivex.schedulers.Schedulers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.runBlocking
import
kotlin.properties.ReadWriteProperty
import
kotlin.reflect.KProperty
...
...
@@ -38,17 +40,19 @@ class DBSettingsValue(
@Synchronized
override
fun
getValue
(
thisRef
:
DBConfig
,
property
:
KProperty
<
*
>):
Int
{
if
(
value
==
null
)
value
=
thisRef
.
settingsDao
.
fetch
(
name
,
default
).
blockingGet
()
return
value
!!
value
=
runBlocking
{
thisRef
.
settingsDao
.
fetch
(
name
,
default
)
}
return
value
as
Int
}
override
fun
setValue
(
thisRef
:
DBConfig
,
property
:
KProperty
<
*
>,
value
:
Int
)
{
synchronized
(
this
)
{
this
.
value
=
value
}
GlobalScope
.
launch
{
thisRef
.
settingsDao
.
put
(
name
,
value
)
.
subscribeOn
(
Schedulers
.
io
())
.
subscribe
()
}
}
}
...
...
@@ -77,7 +81,9 @@ class DBStringsValue(
@Synchronized
override
fun
getValue
(
thisRef
:
DBConfig
,
property
:
KProperty
<
*
>):
String
{
if
(
value
==
null
)
value
=
thisRef
.
stringDao
.
fetch
(
name
,
default
).
blockingGet
()
value
=
runBlocking
{
thisRef
.
stringDao
.
fetch
(
name
,
default
)
}
return
value
!!
}
...
...
@@ -87,19 +93,23 @@ class DBStringsValue(
}
if
(
value
.
isEmpty
())
{
if
(
sync
)
{
thisRef
.
stringDao
.
delete
(
name
).
blockingAwait
()
runBlocking
{
thisRef
.
stringDao
.
delete
(
name
)
}
}
else
{
GlobalScope
.
launch
{
thisRef
.
stringDao
.
delete
(
name
)
.
subscribeOn
(
Schedulers
.
io
())
.
subscribe
()
}
}
}
else
{
if
(
sync
)
{
thisRef
.
stringDao
.
put
(
name
,
value
).
blockingAwait
()
runBlocking
{
thisRef
.
stringDao
.
put
(
name
,
value
)
}
}
else
{
GlobalScope
.
launch
{
thisRef
.
stringDao
.
put
(
name
,
value
)
.
subscribeOn
(
Schedulers
.
io
())
.
subscribe
()
}
}
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt
View file @
8647ba47
...
...
@@ -2,19 +2,18 @@ package com.topjohnwu.magisk.data.repository
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.data.database.SuLogDao
import
com.topjohnwu.magisk.extensions.await
import
com.topjohnwu.magisk.model.entity.MagiskLog
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Completable
import
io.reactivex.Single
class
LogRepository
(
private
val
logDao
:
SuLogDao
)
{
fun
fetch
Logs
()
=
logDao
.
fetchAll
()
suspend
fun
fetchSu
Logs
()
=
logDao
.
fetchAll
()
fun
fetchMagiskLogs
()
=
Single
.
fromCallable
{
suspend
fun
fetchMagiskLogs
():
String
{
val
list
=
object
:
AbstractMutableList
<
String
>()
{
val
buf
=
StringBuilder
()
override
val
size
get
()
=
0
...
...
@@ -28,16 +27,15 @@ class LogRepository(
}
}
}
Shell
.
su
(
"cat ${Const.MAGISK_LOG}"
).
to
(
list
).
exec
()
list
.
buf
.
toString
()
Shell
.
su
(
"cat ${Const.MAGISK_LOG}"
).
to
(
list
).
await
()
return
list
.
buf
.
toString
()
}
fun
clearLogs
()
=
logDao
.
deleteAll
()
suspend
fun
clearLogs
()
=
logDao
.
deleteAll
()
fun
clearMagiskLogs
()
=
Completable
.
fromAction
{
Shell
.
su
(
"echo -n > ${Const.MAGISK_LOG}"
).
exec
()
}
fun
clearMagiskLogs
(
cb
:
(
Shell
.
Result
)
->
Unit
)
=
Shell
.
su
(
"echo -n > ${Const.MAGISK_LOG}"
).
submit
(
cb
)
fun
insert
(
log
:
MagiskLog
)
=
logDao
.
insert
(
log
)
suspend
fun
insert
(
log
:
MagiskLog
)
=
logDao
.
insert
(
log
)
}
app/src/main/java/com/topjohnwu/magisk/data/repository/MagiskRepository.kt
View file @
8647ba47
...
...
@@ -4,13 +4,16 @@ import android.content.pm.PackageManager
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.extensions.await
import
com.topjohnwu.magisk.extensions.getLabel
import
com.topjohnwu.magisk.extensions.packageName
import
com.topjohnwu.magisk.extensions.toSingle
import
com.topjohnwu.magisk.model.entity.HideAppInfo
import
com.topjohnwu.magisk.model.entity.HideTarget
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Single
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
java.io.IOException
class
MagiskRepository
(
private
val
apiRaw
:
GithubRawServices
,
...
...
@@ -19,40 +22,40 @@ class MagiskRepository(
fun
fetchSafetynet
()
=
apiRaw
.
fetchSafetynet
()
fun
fetchUpdate
()
=
when
(
Config
.
updateChannel
)
{
suspend
fun
fetchUpdate
()
=
try
{
var
info
=
when
(
Config
.
updateChannel
)
{
Config
.
Value
.
DEFAULT_CHANNEL
,
Config
.
Value
.
STABLE_CHANNEL
->
apiRaw
.
fetchStableUpdate
()
Config
.
Value
.
BETA_CHANNEL
->
apiRaw
.
fetchBetaUpdate
()
Config
.
Value
.
CANARY_CHANNEL
->
apiRaw
.
fetchCanaryUpdate
()
Config
.
Value
.
CUSTOM_CHANNEL
->
apiRaw
.
fetchCustomUpdate
(
Config
.
customChannelUrl
)
else
->
throw
IllegalArgumentException
()
}.
flatMap
{
// If remote version is lower than current installed, try switching to beta
if
(
it
.
magisk
.
versionCode
<
Info
.
env
.
magiskVersionCode
&&
Config
.
updateChannel
==
Config
.
Value
.
DEFAULT_CHANNEL
)
{
}
if
(
info
.
magisk
.
versionCode
<
Info
.
env
.
magiskVersionCode
&&
Config
.
updateChannel
==
Config
.
Value
.
DEFAULT_CHANNEL
)
{
Config
.
updateChannel
=
Config
.
Value
.
BETA_CHANNEL
apiRaw
.
fetchBetaUpdate
()
}
else
{
Single
.
just
(
it
)
info
=
apiRaw
.
fetchBetaUpdate
()
}
Info
.
remote
=
info
info
}
catch
(
e
:
IOException
)
{
Timber
.
e
(
e
)
null
}
}.
doOnSuccess
{
Info
.
remote
=
it
}
fun
fetchApps
()
=
Single
.
fromCallable
{
packageManager
.
getInstalledApplications
(
0
)
}
.
flattenAsFlowable
{
it
}
.
filter
{
it
.
enabled
&&
!
blacklist
.
contains
(
it
.
packageName
)
}
.
map
{
suspend
fun
fetchApps
()
=
withContext
(
Dispatchers
.
Default
)
{
packageManager
.
getInstalledApplications
(
0
).
filter
{
it
.
enabled
&&
!
blacklist
.
contains
(
it
.
packageName
)
}.
map
{
val
label
=
it
.
getLabel
(
packageManager
)
val
icon
=
it
.
loadIcon
(
packageManager
)
HideAppInfo
(
it
,
label
,
icon
)
}.
filter
{
it
.
processes
.
isNotEmpty
()
}
}
.
filter
{
it
.
processes
.
isNotEmpty
()
}
.
toList
()
fun
fetchHideTargets
()
=
Shell
.
su
(
"magiskhide --ls"
).
toSingle
()
.
map
{
it
.
exec
().
out
}
.
flattenAsFlowable
{
it
}
.
map
{
HideTarget
(
it
)
}
.
toList
()
suspend
fun
fetchHideTargets
()
=
Shell
.
su
(
"\"magiskhide --ls\""
).
await
().
out
.
map
{
HideTarget
(
it
)
}
fun
toggleHide
(
isEnabled
:
Boolean
,
packageName
:
String
,
process
:
String
)
=
Shell
.
su
(
"magiskhide --${isEnabled.state} $packageName $process"
).
submit
()
...
...
app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt
View file @
8647ba47
...
...
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.di
import
android.content.Context
import
android.os.Build
import
androidx.localbroadcastmanager.content.LocalBroadcastManager
import
androidx.preference.PreferenceManager
import
com.topjohnwu.magisk.core.ResMgr
import
com.topjohnwu.magisk.utils.RxBus
...
...
@@ -19,7 +18,6 @@ val applicationModule = module {
factory
(
Protected
)
{
createDEContext
(
get
())
}
single
(
SUTimeout
)
{
get
<
Context
>(
Protected
).
getSharedPreferences
(
"su_timeout"
,
0
)
}
single
{
PreferenceManager
.
getDefaultSharedPreferences
(
get
<
Context
>(
Protected
))
}
single
{
LocalBroadcastManager
.
getInstance
(
get
())
}
}
private
fun
createDEContext
(
context
:
Context
):
Context
{
...
...
app/src/main/java/com/topjohnwu/magisk/extensions/XSU.kt
View file @
8647ba47
...
...
@@ -4,6 +4,8 @@ import com.topjohnwu.magisk.core.Info
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.io.SuFileInputStream
import
com.topjohnwu.superuser.io.SuFileOutputStream
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
java.io.File
fun
reboot
(
reason
:
String
=
if
(
Info
.
recovery
)
"recovery"
else
""
)
{
...
...
@@ -14,3 +16,5 @@ fun File.suOutputStream() = SuFileOutputStream(this)
fun
File
.
suInputStream
()
=
SuFileInputStream
(
this
)
val
hasRoot
get
()
=
Shell
.
rootAccess
()
suspend
fun
Shell
.
Job
.
await
()
=
withContext
(
Dispatchers
.
IO
)
{
exec
()
}
app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt
View file @
8647ba47
...
...
@@ -2,9 +2,13 @@ package com.topjohnwu.magisk.model.entity
class
HideTarget
(
line
:
String
)
{
private
val
split
=
line
.
split
(
Regex
(
"\\|"
),
2
)
val
packageName
=
split
[
0
]
val
process
=
split
.
getOrElse
(
1
)
{
packageName
}
val
packageName
:
String
val
process
:
String
init
{
val
split
=
line
.
split
(
Regex
(
"\\|"
),
2
)
packageName
=
split
[
0
]
process
=
split
.
getOrElse
(
1
)
{
packageName
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt
View file @
8647ba47
...
...
@@ -4,6 +4,7 @@ import android.Manifest
import
androidx.annotation.CallSuper
import
androidx.core.graphics.Insets
import
androidx.databinding.Bindable
import
androidx.databinding.Observable
import
androidx.databinding.ObservableField
import
androidx.databinding.PropertyChangeRegistry
import
androidx.lifecycle.LiveData
...
...
@@ -13,23 +14,18 @@ import androidx.navigation.NavDirections
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.base.BaseActivity
import
com.topjohnwu.magisk.extensions.doOnSubscribeUi
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.model.events.*
import
com.topjohnwu.magisk.model.navigation.NavigationWrapper
import
com.topjohnwu.magisk.model.observer.Observer
import
io.reactivex.*
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.disposables.Disposable
import
io.reactivex.subjects.PublishSubject
import
kotlinx.coroutines.Job
import
org.koin.core.KoinComponent
import
androidx.databinding.Observable
as
BindingObservable
abstract
class
BaseViewModel
(
initialState
:
State
=
State
.
LOADING
,
val
useRx
:
Boolean
=
true
)
:
ViewModel
(),
BindingObservable
,
KoinComponent
{
initialState
:
State
=
State
.
LOADING
)
:
ViewModel
(),
Observable
,
KoinComponent
{
enum
class
State
{
LOADED
,
LOADING
,
LOADING_FAILED
...
...
@@ -53,8 +49,8 @@ abstract class BaseViewModel(
private
val
_viewEvents
=
MutableLiveData
<
ViewEvent
>()
private
var
runningTask
:
Disposable
?
=
null
private
var
runningJob
:
Job
?
=
null
private
val
refreshCallback
=
object
:
androidx
.
databinding
.
Observable
.
OnPropertyChangedCallback
()
{
override
fun
onPropertyChanged
(
sender
:
androidx
.
databinding
.
Observable
?,
propertyId
:
Int
)
{
private
val
refreshCallback
=
object
:
Observable
.
OnPropertyChangedCallback
()
{
override
fun
onPropertyChanged
(
sender
:
Observable
?,
propertyId
:
Int
)
{
requestRefresh
()
}
}
...
...
@@ -66,13 +62,6 @@ abstract class BaseViewModel(
/** This should probably never be called manually, it's called manually via delegate. */
@Synchronized
fun
requestRefresh
()
{
if
(
useRx
)
{
if
(
runningTask
?.
isDisposed
?.
not
()
==
true
)
{
return
}
runningTask
=
rxRefresh
()
return
}
if
(
runningJob
?.
isActive
==
true
)
{
return
}
...
...
@@ -100,11 +89,6 @@ abstract class BaseViewModel(
ViewActionEvent
(
action
).
publish
()
}
fun
withPermissions
(
vararg
permissions
:
String
):
Observable
<
Boolean
>
{
val
subject
=
PublishSubject
.
create
<
Boolean
>()
return
subject
.
doOnSubscribeUi
{
RxPermissionEvent
(
permissions
.
toList
(),
subject
).
publish
()
}
}
fun
withPermissions
(
vararg
permissions
:
String
,
callback
:
(
Boolean
)
->
Unit
)
{
PermissionEvent
(
permissions
.
toList
(),
callback
).
publish
()
}
...
...
@@ -137,7 +121,7 @@ abstract class BaseViewModel(
private
var
callbacks
:
PropertyChangeRegistry
?
=
null
@Synchronized
override
fun
addOnPropertyChangedCallback
(
callback
:
Binding
Observable
.
OnPropertyChangedCallback
)
{
override
fun
addOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
)
{
if
(
callbacks
==
null
)
{
callbacks
=
PropertyChangeRegistry
()
}
...
...
@@ -145,7 +129,7 @@ abstract class BaseViewModel(
}
@Synchronized
override
fun
removeOnPropertyChangedCallback
(
callback
:
Binding
Observable
.
OnPropertyChangedCallback
)
{
override
fun
removeOnPropertyChangedCallback
(
callback
:
Observable
.
OnPropertyChangedCallback
)
{
callbacks
?.
remove
(
callback
)
}
...
...
@@ -168,63 +152,4 @@ abstract class BaseViewModel(
callbacks
?.
notifyCallbacks
(
this
,
fieldId
,
null
)
}
//region Rx
protected
fun
<
T
>
Observable
<
T
>.
applyViewModel
(
viewModel
:
BaseViewModel
,
allowFinishing
:
Boolean
=
true
)
=
doOnSubscribe
{
viewModel
.
state
=
State
.
LOADING
}
.
doOnError
{
viewModel
.
state
=
State
.
LOADING_FAILED
}
.
doOnNext
{
if
(
allowFinishing
)
viewModel
.
state
=
State
.
LOADED
}
protected
fun
<
T
>
Single
<
T
>.
applyViewModel
(
viewModel
:
BaseViewModel
,
allowFinishing
:
Boolean
=
true
)
=
doOnSubscribe
{
viewModel
.
state
=
State
.
LOADING
}
.
doOnError
{
viewModel
.
state
=
State
.
LOADING_FAILED
}
.
doOnSuccess
{
if
(
allowFinishing
)
viewModel
.
state
=
State
.
LOADED
}
protected
fun
<
T
>
Maybe
<
T
>.
applyViewModel
(
viewModel
:
BaseViewModel
,
allowFinishing
:
Boolean
=
true
)
=
doOnSubscribe
{
viewModel
.
state
=
State
.
LOADING
}
.
doOnError
{
viewModel
.
state
=
State
.
LOADING_FAILED
}
.
doOnComplete
{
if
(
allowFinishing
)
viewModel
.
state
=
State
.
LOADED
}
.
doOnSuccess
{
if
(
allowFinishing
)
viewModel
.
state
=
State
.
LOADED
}
protected
fun
<
T
>
Flowable
<
T
>.
applyViewModel
(
viewModel
:
BaseViewModel
,
allowFinishing
:
Boolean
=
true
)
=
doOnSubscribe
{
viewModel
.
state
=
State
.
LOADING
}
.
doOnError
{
viewModel
.
state
=
State
.
LOADING_FAILED
}
.
doOnNext
{
if
(
allowFinishing
)
viewModel
.
state
=
State
.
LOADED
}
protected
fun
Completable
.
applyViewModel
(
viewModel
:
BaseViewModel
,
allowFinishing
:
Boolean
=
true
)
=
doOnSubscribe
{
viewModel
.
state
=
State
.
LOADING
}
.
doOnError
{
viewModel
.
state
=
State
.
LOADING_FAILED
}
.
doOnComplete
{
if
(
allowFinishing
)
viewModel
.
state
=
State
.
LOADED
}
//endregion
}
app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
View file @
8647ba47
package
com.topjohnwu.magisk.ui.flash
import
android.Manifest
import
android.content.res.Resources
import
android.net.Uri
import
android.view.MenuItem
...
...
@@ -21,7 +20,9 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
java.io.File
import
java.util.*
...
...
@@ -96,25 +97,23 @@ class FlashViewModel(
return
true
}
private
fun
savePressed
()
=
withPermissions
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
)
.
map
{
now
}
.
map
{
it
.
toTime
(
timeFormatStandard
)
}
.
map
{
Const
.
MAGISK_INSTALL_LOG_FILENAME
.
format
(
it
)
}
.
map
{
File
(
Config
.
downloadDirectory
,
it
)
}
.
map
{
file
->
private
fun
savePressed
()
=
withExternalRW
{
if
(!
it
)
return
@withExternalRW
viewModelScope
.
launch
{
val
name
=
Const
.
MAGISK_INSTALL_LOG_FILENAME
.
format
(
now
.
toTime
(
timeFormatStandard
))
val
file
=
File
(
Config
.
downloadDirectory
,
name
)
withContext
(
Dispatchers
.
IO
)
{
file
.
bufferedWriter
().
use
{
writer
->
logItems
.
forEach
{
writer
.
write
(
it
)
writer
.
newLine
()
}
}
file
.
path
}
.
subscribeK
{
SnackbarEvent
(
it
).
publish
()
}
.
add
()
SnackbarEvent
(
file
.
path
).
publish
()
}
}
fun
restartPressed
()
=
reboot
()
}
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
View file @
8647ba47
...
...
@@ -3,10 +3,10 @@ package com.topjohnwu.magisk.ui.hide
import
android.content.pm.ApplicationInfo
import
androidx.databinding.Bindable
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.core.utils.currentLocale
import
com.topjohnwu.magisk.data.repository.MagiskRepository
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.model.entity.HideAppInfo
import
com.topjohnwu.magisk.model.entity.HideTarget
...
...
@@ -18,6 +18,9 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.Queryable
import
com.topjohnwu.magisk.ui.base.filterableListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
class
HideViewModel
(
private
val
magiskRepo
:
MagiskRepository
...
...
@@ -50,18 +53,17 @@ class HideViewModel(
val
isFilterExpanded
=
ObservableField
(
false
)
override
fun
rxRefresh
()
=
magiskRepo
.
fetchApps
()
.
map
{
it
to
magiskRepo
.
fetchHideTargets
().
blockingGet
()
}
.
map
{
pair
->
pair
.
first
.
map
{
mergeAppTargets
(
it
,
pair
.
second
)
}
}
.
flattenAsFlowable
{
it
}
.
map
{
HideItem
(
it
)
}
.
toList
()
.
map
{
it
.
sort
()
}
.
map
{
it
to
items
.
calculateDiff
(
it
)
}
.
applyViewModel
(
this
)
.
subscribeK
{
items
.
update
(
it
.
first
,
it
.
second
)
override
fun
refresh
()
=
viewModelScope
.
launch
{
state
=
State
.
LOADING
val
apps
=
magiskRepo
.
fetchApps
()
val
hides
=
magiskRepo
.
fetchHideTargets
()
val
(
hidden
,
diff
)
=
withContext
(
Dispatchers
.
Default
)
{
val
hidden
=
apps
.
map
{
mergeAppTargets
(
it
,
hides
)
}.
map
{
HideItem
(
it
)
}.
sort
()
hidden
to
items
.
calculateDiff
(
hidden
)
}
items
.
update
(
hidden
,
diff
)
submitQuery
()
state
=
State
.
LOADED
}
// ---
...
...
app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
View file @
8647ba47
package
com.topjohnwu.magisk.ui.home
import
android.Manifest
import
android.os.Build
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Config
...
...
@@ -11,9 +11,11 @@ import com.topjohnwu.magisk.core.base.BaseActivity
import
com.topjohnwu.magisk.core.download.RemoteFileService
import
com.topjohnwu.magisk.core.model.MagiskJson
import
com.topjohnwu.magisk.core.model.ManagerJson
import
com.topjohnwu.magisk.core.model.UpdateInfo
import
com.topjohnwu.magisk.data.repository.MagiskRepository
import
com.topjohnwu.magisk.extensions.*
import
com.topjohnwu.magisk.extensions.await
import
com.topjohnwu.magisk.extensions.packageName
import
com.topjohnwu.magisk.extensions.res
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Manager
import
com.topjohnwu.magisk.model.entity.recycler.DeveloperItem
import
com.topjohnwu.magisk.model.entity.recycler.HomeItem
...
...
@@ -26,6 +28,7 @@ import com.topjohnwu.magisk.model.events.dialog.UninstallDialog
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.launch
import
me.tatarka.bindingcollectionadapter2.BR
import
kotlin.math.roundToInt
...
...
@@ -72,11 +75,8 @@ class HomeViewModel(
}
}
override
fun
rxRefresh
()
=
repoMagisk
.
fetchUpdate
()
.
onErrorReturn
{
null
}
.
subscribeK
{
it
?.
updateUI
()
}
private
fun
UpdateInfo
.
updateUI
()
{
override
fun
refresh
()
=
viewModelScope
.
launch
{
repoMagisk
.
fetchUpdate
()
?.
apply
{
stateMagisk
.
value
=
when
{
!
Info
.
env
.
isActive
->
MagiskState
.
NOT_INSTALLED
magisk
.
isObsolete
->
MagiskState
.
OBSOLETE
...
...
@@ -89,11 +89,16 @@ class HomeViewModel(
else
->
MagiskState
.
UP_TO_DATE
}
stateMagiskRemoteVersion
.
value
=
"${magisk.version} (${magisk.versionCode})"
stateManagerRemoteVersion
.
value
=
"${app.version} (${app.versionCode}) (${stub.versionCode})"
stateMagiskRemoteVersion
.
value
=
"${magisk.version} (${magisk.versionCode})"
stateManagerRemoteVersion
.
value
=
"${app.version} (${app.versionCode}) (${stub.versionCode})"
launch
{
ensureEnv
()
}
}
}
val
showTest
=
false
...
...
@@ -109,19 +114,18 @@ class HomeViewModel(
fun
onManagerPressed
()
=
ManagerInstallDialog
().
publish
()
fun
onMagiskPressed
()
=
withPermissions
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
).
map
{
check
(
it
);
it
}
.
subscribeK
{
HomeFragmentDirections
.
actionHomeFragmentToInstallFragment
().
publish
()
}
.
add
()
fun
onMagiskPressed
()
=
withExternalRW
{
if
(
it
)
{
HomeFragmentDirections
.
actionHomeFragmentToInstallFragment
().
publish
()
}
}
fun
hideNotice
()
{
Config
.
safetyNotice
=
false
isNoticeVisible
.
value
=
false
}
private
fun
ensureEnv
()
{
private
suspend
fun
ensureEnv
()
{
val
invalidStates
=
listOf
(
MagiskState
.
NOT_INSTALLED
,
MagiskState
.
LOADING
...
...
@@ -138,11 +142,8 @@ class HomeViewModel(
return
}
Shell
.
su
(
"env_check"
)
.
toSingle
()
.
map
{
it
.
exec
()
}
.
filter
{
!
it
.
isSuccess
}
.
subscribeK
{
val
result
=
Shell
.
su
(
"env_check"
).
await
()
if
(!
result
.
isSuccess
)
{
shownDialog
=
true
EnvFixDialog
().
publish
()
}
...
...
@@ -150,9 +151,9 @@ class HomeViewModel(
private
val
MagiskJson
.
isObsolete
get
()
=
Info
.
env
.
isActive
&&
Info
.
env
.
magiskVersionCode
<
versionCode
val
ManagerJson
.
isUpdateChannelCorrect
private
val
ManagerJson
.
isUpdateChannelCorrect
get
()
=
versionCode
>
0
val
ManagerJson
.
isObsolete
private
val
ManagerJson
.
isObsolete
get
()
=
BuildConfig
.
VERSION_CODE
<
versionCode
}
app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt
View file @
8647ba47
package
com.topjohnwu.magisk.ui.log
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.data.repository.LogRepository
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.model.entity.recycler.LogItem
import
com.topjohnwu.magisk.model.entity.recycler.TextItem
...
...
@@ -15,12 +15,10 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Completable
import
io.reactivex.android.schedulers.AndroidSchedulers
import
io.reactivex.disposables.Disposable
import
io.reactivex.schedulers.Schedulers
import
kotlinx.coroutines.*
import
timber.log.Timber
import
java.io.File
import
java.io.IOException
import
java.util.*
class
LogViewModel
(
...
...
@@ -43,29 +41,22 @@ class LogViewModel(
val
consoleText
=
ObservableField
(
" "
)
override
fun
rxRefresh
():
Disposable
{
val
logs
=
repo
.
fetchLogs
()
.
map
{
it
.
map
{
LogItem
(
it
)
}
}
.
observeOn
(
Schedulers
.
computation
())
.
map
{
it
to
items
.
calculateDiff
(
it
)
}
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
doOnSuccess
{
override
fun
refresh
()
=
viewModelScope
.
launch
{
consoleText
.
value
=
repo
.
fetchMagiskLogs
()
val
deferred
=
withContext
(
Dispatchers
.
Default
)
{
async
{
val
suLogs
=
repo
.
fetchSuLogs
().
map
{
LogItem
(
it
)
}
suLogs
to
items
.
calculateDiff
(
suLogs
)
}
}
delay
(
500
)
val
(
suLogs
,
diff
)
=
deferred
.
await
()
items
.
firstOrNull
()
?.
isTop
=
false
items
.
lastOrNull
()
?.
isBottom
=
false
items
.
update
(
it
.
first
,
it
.
second
)
items
.
update
(
suLogs
,
diff
)
items
.
firstOrNull
()
?.
isTop
=
true
items
.
lastOrNull
()
?.
isBottom
=
true
}
.
ignoreElement
()
val
console
=
repo
.
fetchMagiskLogs
()
.
doOnSuccess
{
consoleText
.
value
=
it
}
.
ignoreElement
()
return
Completable
.
merge
(
listOf
(
logs
,
console
)).
subscribeK
()
}
fun
saveMagiskLog
()
{
val
now
=
Calendar
.
getInstance
()
...
...
@@ -76,10 +67,10 @@ class LogViewModel(
)
val
logFile
=
File
(
Config
.
downloadDirectory
,
filename
)
runCatching
{
try
{
logFile
.
createNewFile
()
}
.
onFailure
{
Timber
.
e
(
it
)
}
catch
(
e
:
IOException
)
{
Timber
.
e
(
e
)
return
}
...
...
@@ -88,18 +79,14 @@ class LogViewModel(
}
}
fun
clearMagiskLog
()
=
repo
.
clearMagiskLogs
()
.
subscribeK
{
fun
clearMagiskLog
()
=
repo
.
clearMagiskLogs
{
SnackbarEvent
(
R
.
string
.
logs_cleared
).
publish
()
requestRefresh
()
}
.
add
()
fun
clearLog
()
=
repo
.
clearLogs
()
.
subscribeK
{
fun
clearLog
()
=
viewModelScope
.
launch
{
repo
.
clearLogs
()
SnackbarEvent
(
R
.
string
.
logs_cleared
).
publish
()
requestRefresh
()
}
.
add
()
}
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
View file @
8647ba47
...
...
@@ -47,7 +47,7 @@ class ModuleViewModel(
private
val
repoName
:
RepoByNameDao
,
private
val
repoUpdated
:
RepoByUpdatedDao
,
private
val
repoUpdater
:
RepoUpdater
)
:
BaseViewModel
(
useRx
=
false
),
Queryable
{
)
:
BaseViewModel
(),
Queryable
{
override
val
queryDelay
=
1000L
private
var
queryJob
:
Job
?
=
null
...
...
app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt
View file @
8647ba47
...
...
@@ -3,6 +3,7 @@ package com.topjohnwu.magisk.ui.superuser
import
android.content.pm.PackageManager
import
android.content.res.Resources
import
androidx.databinding.ObservableArrayList
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.magiskdb.PolicyDao
...
...
@@ -10,8 +11,6 @@ import com.topjohnwu.magisk.core.model.MagiskPolicy
import
com.topjohnwu.magisk.core.utils.BiometricHelper
import
com.topjohnwu.magisk.core.utils.currentLocale
import
com.topjohnwu.magisk.databinding.ComparableRvItem
import
com.topjohnwu.magisk.extensions.applySchedulers
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.toggle
import
com.topjohnwu.magisk.model.entity.recycler.PolicyItem
import
com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem
...
...
@@ -24,7 +23,9 @@ import com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.adapterOf
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
io.reactivex.Single
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
class
SuperuserViewModel
(
...
...
@@ -53,26 +54,22 @@ class SuperuserViewModel(
// ---
override
fun
rxRefresh
()
=
db
.
fetchAll
()
.
flattenAsFlowable
{
it
}
.
parallel
()
.
map
{
PolicyItem
(
it
,
it
.
applicationInfo
.
loadIcon
(
packageManager
))
}
.
sequential
()
.
sorted
{
o1
,
o2
->
compareBy
<
PolicyItem
>(
override
fun
refresh
()
=
viewModelScope
.
launch
{
state
=
State
.
LOADING
val
(
policies
,
diff
)
=
withContext
(
Dispatchers
.
Default
)
{
val
policies
=
db
.
fetchAll
{
PolicyItem
(
it
,
it
.
applicationInfo
.
loadIcon
(
packageManager
))
}.
sortedWith
(
compareBy
(
{
it
.
item
.
appName
.
toLowerCase
(
currentLocale
)
},
{
it
.
item
.
packageName
}
).
compare
(
o1
,
o2
)
}
.
toList
()
.
map
{
it
to
itemsPolicies
.
calculateDiff
(
it
)
}
.
applySchedulers
()
.
applyViewModel
(
this
)
.
subscribeK
{
itemsPolicies
.
update
(
it
.
first
,
it
.
second
)
))
policies
to
itemsPolicies
.
calculateDiff
(
policies
)
}
itemsPolicies
.
update
(
policies
,
diff
)
if
(
itemsPolicies
.
isNotEmpty
())
{
itemsHelpers
.
remove
(
itemNoData
)
}
state
=
State
.
LOADED
}
// ---
...
...
@@ -91,14 +88,13 @@ class SuperuserViewModel(
SuperuserFragmentDirections
.
actionSuperuserFragmentToHideFragment
().
publish
()
fun
deletePressed
(
item
:
PolicyItem
)
{
fun
updateState
()
=
deletePolicy
(
item
.
item
)
.
subscribeK
{
fun
updateState
()
=
viewModelScope
.
launch
{
db
.
delete
(
item
.
item
.
uid
)
itemsPolicies
.
removeAll
{
it
.
genericItemSameAs
(
item
)
}
if
(
itemsPolicies
.
isEmpty
()
&&
itemsHelpers
.
isEmpty
())
{
itemsHelpers
.
add
(
itemNoData
)
}
}
.
add
()
if
(
BiometricHelper
.
isEnabled
)
{
BiometricDialog
{
...
...
@@ -114,34 +110,37 @@ class SuperuserViewModel(
//---
fun
updatePolicy
(
it
:
PolicyUpdateEvent
)
=
when
(
it
)
{
is
PolicyUpdateEvent
.
Notification
->
updatePolicy
(
it
.
item
).
map
{
fun
updatePolicy
(
it
:
PolicyUpdateEvent
)
=
viewModelScope
.
launch
{
val
snackStr
=
when
(
it
)
{
is
PolicyUpdateEvent
.
Notification
->
{
updatePolicy
(
it
.
item
)
when
{
it
.
notification
->
R
.
string
.
su_snack_notif_on
it
.
item
.
notification
->
R
.
string
.
su_snack_notif_on
else
->
R
.
string
.
su_snack_notif_off
}
to
it
.
appName
}
is
PolicyUpdateEvent
.
Log
->
updatePolicy
(
it
.
item
).
map
{
}
is
PolicyUpdateEvent
.
Log
->
{
updatePolicy
(
it
.
item
)
when
{
it
.
logging
->
R
.
string
.
su_snack_log_on
it
.
item
.
logging
->
R
.
string
.
su_snack_log_on
else
->
R
.
string
.
su_snack_log_off
}
to
it
.
appName
}
}.
map
{
resources
.
getString
(
it
.
first
,
it
.
second
)
}
.
subscribeK
{
SnackbarEvent
(
it
).
publish
()
}
.
add
()
}
}
SnackbarEvent
(
resources
.
getString
(
snackStr
,
it
.
item
.
appName
)).
publish
()
}
fun
togglePolicy
(
item
:
PolicyItem
,
enable
:
Boolean
)
{
fun
updateState
()
{
val
policy
=
if
(
enable
)
MagiskPolicy
.
ALLOW
else
MagiskPolicy
.
DENY
val
app
=
item
.
item
.
copy
(
policy
=
policy
)
viewModelScope
.
launch
{
updatePolicy
(
app
)
.
map
{
it
.
policy
==
MagiskPolicy
.
ALLOW
}
.
map
{
if
(
it
)
R
.
string
.
su_snack_grant
else
R
.
string
.
su_snack_deny
}
.
map
{
resources
.
getString
(
it
).
format
(
item
.
item
.
appName
)
}
.
subscribeK
{
SnackbarEvent
(
it
).
publish
()
}
.
add
()
val
res
=
if
(
app
.
policy
==
MagiskPolicy
.
ALLOW
)
R
.
string
.
su_snack_grant
else
R
.
string
.
su_snack_deny
SnackbarEvent
(
resources
.
getString
(
res
).
format
(
item
.
item
.
appName
))
}
}
if
(
BiometricHelper
.
isEnabled
)
{
...
...
@@ -156,10 +155,6 @@ class SuperuserViewModel(
//---
private
fun
updatePolicy
(
policy
:
MagiskPolicy
)
=
db
.
update
(
policy
).
andThen
(
Single
.
just
(
policy
))
private
fun
deletePolicy
(
policy
:
MagiskPolicy
)
=
db
.
delete
(
policy
.
uid
).
andThen
(
Single
.
just
(
policy
))
private
suspend
fun
updatePolicy
(
policy
:
MagiskPolicy
)
=
db
.
update
(
policy
)
}
build.gradle.kts
View file @
8647ba47
...
...
@@ -60,7 +60,7 @@ subprojects {
plugins
.
hasPlugin
(
"com.android.application"
))
{
android
.
apply
{
compileSdkVersion
(
30
)
buildToolsVersion
=
"30.0.
0
"
buildToolsVersion
=
"30.0.
1
"
defaultConfig
{
if
(
minSdkVersion
==
null
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment