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
6348d0a6
Commit
6348d0a6
authored
Jul 10, 2020
by
topjohnwu
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove more code using RxJava
parent
f7a650b9
Show whitespace changes
Inline
Side-by-side
Showing
31 changed files
with
300 additions
and
420 deletions
+300
-420
DownloadService.kt
...ava/com/topjohnwu/magisk/core/download/DownloadService.kt
+21
-34
ManagerUpgrade.kt
...java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt
+3
-3
NotificationService.kt
...com/topjohnwu/magisk/core/download/NotificationService.kt
+10
-0
RemoteFileService.kt
...a/com/topjohnwu/magisk/core/download/RemoteFileService.kt
+24
-31
Repo.kt
.../main/java/com/topjohnwu/magisk/core/model/module/Repo.kt
+1
-1
MagiskInstaller.kt
...n/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt
+12
-10
Locales.kt
app/src/main/java/com/topjohnwu/magisk/core/utils/Locales.kt
+8
-6
PatchAPK.kt
...src/main/java/com/topjohnwu/magisk/core/utils/PatchAPK.kt
+13
-9
RepoDao.kt
...c/main/java/com/topjohnwu/magisk/data/database/RepoDao.kt
+3
-1
GithubServices.kt
.../java/com/topjohnwu/magisk/data/network/GithubServices.kt
+5
-9
MagiskRepository.kt
.../com/topjohnwu/magisk/data/repository/MagiskRepository.kt
+0
-2
StringRepository.kt
.../com/topjohnwu/magisk/data/repository/StringRepository.kt
+3
-2
AdaptersGeneric.kt
.../java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt
+16
-6
NetworkingModule.kt
...src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt
+2
-0
ViewModelsModule.kt
...src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt
+1
-1
RxJava.kt
app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt
+2
-151
XList.kt
app/src/main/java/com/topjohnwu/magisk/extensions/XList.kt
+13
-9
XSU.kt
app/src/main/java/com/topjohnwu/magisk/extensions/XSU.kt
+0
-8
ViewEvents.kt
...main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt
+65
-56
ManagerInstallDialog.kt
...johnwu/magisk/model/events/dialog/ManagerInstallDialog.kt
+8
-1
BaseViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt
+6
-0
FlashViewModel.kt
...main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
+1
-1
InstallFragment.kt
...n/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt
+4
-0
InstallViewModel.kt
.../java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt
+7
-12
SafetynetViewModel.kt
...a/com/topjohnwu/magisk/ui/safetynet/SafetynetViewModel.kt
+1
-1
SettingsItems.kt
...in/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt
+11
-8
SettingsViewModel.kt
...ava/com/topjohnwu/magisk/ui/settings/SettingsViewModel.kt
+10
-15
DataBindingAdapters.kt
...in/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt
+7
-9
MagiskDialog.kt
app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt
+1
-1
MarkDownWindow.kt
...src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt
+40
-32
fragment_install_md2.xml
app/src/main/res/layout/fragment_install_md2.xml
+2
-1
No files found.
app/src/main/java/com/topjohnwu/magisk/core/download/DownloadService.kt
View file @
6348d0a6
...
@@ -13,16 +13,12 @@ import com.topjohnwu.magisk.core.tasks.EnvFixTask
...
@@ -13,16 +13,12 @@ import com.topjohnwu.magisk.core.tasks.EnvFixTask
import
com.topjohnwu.magisk.extensions.chooser
import
com.topjohnwu.magisk.extensions.chooser
import
com.topjohnwu.magisk.extensions.exists
import
com.topjohnwu.magisk.extensions.exists
import
com.topjohnwu.magisk.extensions.provide
import
com.topjohnwu.magisk.extensions.provide
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.model.entity.internal.Configuration.*
import
com.topjohnwu.magisk.model.entity.internal.Configuration.*
import
com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
import
com.topjohnwu.magisk.model.entity.internal.Configuration.Flash.Secondary
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.*
import
com.topjohnwu.magisk.ui.flash.FlashFragment
import
com.topjohnwu.magisk.ui.flash.FlashFragment
import
com.topjohnwu.magisk.utils.APKInstall
import
com.topjohnwu.magisk.utils.APKInstall
import
io.reactivex.Completable
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
org.koin.core.get
import
org.koin.core.get
import
java.io.File
import
java.io.File
import
kotlin.random.Random.Default.nextInt
import
kotlin.random.Random.Default.nextInt
...
@@ -37,20 +33,20 @@ open class DownloadService : RemoteFileService() {
...
@@ -37,20 +33,20 @@ open class DownloadService : RemoteFileService() {
.
getMimeTypeFromExtension
(
extension
)
.
getMimeTypeFromExtension
(
extension
)
?:
"resource/folder"
?:
"resource/folder"
override
fun
onFinished
(
subject
:
DownloadSubject
,
id
:
Int
)
=
when
(
subject
)
{
override
suspend
fun
onFinished
(
subject
:
DownloadSubject
,
id
:
Int
)
=
when
(
subject
)
{
is
Magisk
->
onFinished
Internal
(
subject
,
id
)
is
Magisk
->
onFinished
(
subject
,
id
)
is
Module
->
onFinished
Internal
(
subject
,
id
)
is
Module
->
onFinished
(
subject
,
id
)
is
Manager
->
onFinished
Internal
(
subject
,
id
)
is
Manager
->
onFinished
(
subject
,
id
)
}
}
private
fun
onFinishedInternal
(
private
suspend
fun
onFinished
(
subject
:
Magisk
,
subject
:
Magisk
,
id
:
Int
id
:
Int
)
=
when
(
val
conf
=
subject
.
configuration
)
{
)
=
when
(
val
conf
=
subject
.
configuration
)
{
Uninstall
->
FlashFragment
.
uninstall
(
subject
.
file
,
id
)
Uninstall
->
FlashFragment
.
uninstall
(
subject
.
file
,
id
)
EnvFix
->
{
EnvFix
->
{
remove
(
id
)
remove
(
id
)
GlobalScope
.
launch
{
EnvFixTask
(
subject
.
file
).
exec
()
}
EnvFixTask
(
subject
.
file
).
exec
()
Unit
Unit
}
}
is
Patch
->
FlashFragment
.
patch
(
subject
.
file
,
conf
.
fileUri
,
id
)
is
Patch
->
FlashFragment
.
patch
(
subject
.
file
,
conf
.
fileUri
,
id
)
...
@@ -58,7 +54,7 @@ open class DownloadService : RemoteFileService() {
...
@@ -58,7 +54,7 @@ open class DownloadService : RemoteFileService() {
else
->
Unit
else
->
Unit
}
}
private
fun
onFinished
Internal
(
private
fun
onFinished
(
subject
:
Module
,
subject
:
Module
,
id
:
Int
id
:
Int
)
=
when
(
subject
.
configuration
)
{
)
=
when
(
subject
.
configuration
)
{
...
@@ -66,33 +62,30 @@ open class DownloadService : RemoteFileService() {
...
@@ -66,33 +62,30 @@ open class DownloadService : RemoteFileService() {
else
->
Unit
else
->
Unit
}
}
private
fun
onFinishedInternal
(
private
suspend
fun
onFinished
(
subject
:
Manager
,
subject
:
Manager
,
id
:
Int
id
:
Int
)
{
)
{
Completable
.
fromAction
{
handleAPK
(
subject
)
handleAPK
(
subject
)
}.
subscribeK
{
remove
(
id
)
remove
(
id
)
when
(
subject
.
configuration
)
{
when
(
subject
.
configuration
)
{
is
APK
.
Upgrade
->
APKInstall
.
install
(
this
,
subject
.
file
)
is
APK
.
Upgrade
->
APKInstall
.
install
(
this
,
subject
.
file
)
is
APK
.
Restore
->
Unit
is
APK
.
Restore
->
Unit
}
}
}
}
}
// ---
// ---
override
fun
Notification
.
Builder
.
addActions
(
subject
:
DownloadSubject
)
override
fun
Notification
.
Builder
.
addActions
(
subject
:
DownloadSubject
)
=
when
(
subject
)
{
=
when
(
subject
)
{
is
Magisk
->
addActions
Internal
(
subject
)
is
Magisk
->
addActions
(
subject
)
is
Module
->
addActions
Internal
(
subject
)
is
Module
->
addActions
(
subject
)
is
Manager
->
addActions
Internal
(
subject
)
is
Manager
->
addActions
(
subject
)
}
}
private
fun
Notification
.
Builder
.
addActions
Internal
(
subject
:
Magisk
)
private
fun
Notification
.
Builder
.
addActions
(
subject
:
Magisk
)
=
when
(
val
conf
=
subject
.
configuration
)
{
=
when
(
val
conf
=
subject
.
configuration
)
{
Download
->
this
.
apply
{
Download
->
apply
{
fileIntent
(
subject
.
file
.
parentFile
!!
)
fileIntent
(
subject
.
file
.
parentFile
!!
)
.
takeIf
{
it
.
exists
(
get
())
}
.
takeIf
{
it
.
exists
(
get
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_parent
,
it
.
chooser
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_parent
,
it
.
chooser
())
}
...
@@ -101,18 +94,12 @@ open class DownloadService : RemoteFileService() {
...
@@ -101,18 +94,12 @@ open class DownloadService : RemoteFileService() {
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_self
,
it
.
chooser
())
}
?.
let
{
addAction
(
0
,
R
.
string
.
download_open_self
,
it
.
chooser
())
}
}
}
Uninstall
->
setContentIntent
(
FlashFragment
.
uninstallIntent
(
context
,
subject
.
file
))
Uninstall
->
setContentIntent
(
FlashFragment
.
uninstallIntent
(
context
,
subject
.
file
))
is
Flash
->
setContentIntent
(
is
Flash
->
setContentIntent
(
FlashFragment
.
flashIntent
(
context
,
subject
.
file
,
conf
is
Secondary
))
FlashFragment
.
flashIntent
(
context
,
subject
.
file
,
conf
is
Secondary
)
)
is
Patch
->
setContentIntent
(
FlashFragment
.
patchIntent
(
context
,
subject
.
file
,
conf
.
fileUri
))
is
Patch
->
setContentIntent
(
FlashFragment
.
patchIntent
(
context
,
subject
.
file
,
conf
.
fileUri
))
else
->
this
else
->
this
}
}
private
fun
Notification
.
Builder
.
addActions
Internal
(
subject
:
Module
)
private
fun
Notification
.
Builder
.
addActions
(
subject
:
Module
)
=
when
(
subject
.
configuration
)
{
=
when
(
subject
.
configuration
)
{
Download
->
this
.
apply
{
Download
->
this
.
apply
{
fileIntent
(
subject
.
file
.
parentFile
!!
)
fileIntent
(
subject
.
file
.
parentFile
!!
)
...
@@ -126,7 +113,7 @@ open class DownloadService : RemoteFileService() {
...
@@ -126,7 +113,7 @@ open class DownloadService : RemoteFileService() {
else
->
this
else
->
this
}
}
private
fun
Notification
.
Builder
.
addActions
Internal
(
subject
:
Manager
)
private
fun
Notification
.
Builder
.
addActions
(
subject
:
Manager
)
=
when
(
subject
.
configuration
)
{
=
when
(
subject
.
configuration
)
{
APK
.
Upgrade
->
setContentIntent
(
APKInstall
.
installIntent
(
context
,
subject
.
file
))
APK
.
Upgrade
->
setContentIntent
(
APKInstall
.
installIntent
(
context
,
subject
.
file
))
else
->
this
else
->
this
...
...
app/src/main/java/com/topjohnwu/magisk/core/download/ManagerUpgrade.kt
View file @
6348d0a6
...
@@ -32,14 +32,14 @@ private fun RemoteFileService.patch(apk: File, id: Int) {
...
@@ -32,14 +32,14 @@ private fun RemoteFileService.patch(apk: File, id: Int) {
patched
.
renameTo
(
apk
)
patched
.
renameTo
(
apk
)
}
}
private
fun
RemoteFileService
.
upgrade
(
apk
:
File
,
id
:
Int
)
{
private
suspend
fun
RemoteFileService
.
upgrade
(
apk
:
File
,
id
:
Int
)
{
if
(
isRunningAsStub
)
{
if
(
isRunningAsStub
)
{
// Move to upgrade location
// Move to upgrade location
apk
.
copyTo
(
DynAPK
.
update
(
this
),
overwrite
=
true
)
apk
.
copyTo
(
DynAPK
.
update
(
this
),
overwrite
=
true
)
apk
.
delete
()
apk
.
delete
()
if
(
Info
.
stub
!!
.
version
<
Info
.
remote
.
stub
.
versionCode
)
{
if
(
Info
.
stub
!!
.
version
<
Info
.
remote
.
stub
.
versionCode
)
{
// We also want to upgrade stub
// We also want to upgrade stub
service
.
fetchFile
(
Info
.
remote
.
stub
.
link
).
b
lockingGet
().
b
yteStream
().
use
{
service
.
fetchFile
(
Info
.
remote
.
stub
.
link
).
byteStream
().
use
{
it
.
writeTo
(
apk
)
it
.
writeTo
(
apk
)
}
}
patch
(
apk
,
id
)
patch
(
apk
,
id
)
...
@@ -65,7 +65,7 @@ private fun RemoteFileService.restore(apk: File, id: Int) {
...
@@ -65,7 +65,7 @@ private fun RemoteFileService.restore(apk: File, id: Int) {
Shell
.
su
(
"pm install $apk && pm uninstall $packageName"
).
exec
()
Shell
.
su
(
"pm install $apk && pm uninstall $packageName"
).
exec
()
}
}
fun
RemoteFileService
.
handleAPK
(
subject
:
DownloadSubject
.
Manager
)
=
suspend
fun
RemoteFileService
.
handleAPK
(
subject
:
DownloadSubject
.
Manager
)
=
when
(
subject
.
configuration
)
{
when
(
subject
.
configuration
)
{
is
Upgrade
->
upgrade
(
subject
.
file
,
subject
.
hashCode
())
is
Upgrade
->
upgrade
(
subject
.
file
,
subject
.
hashCode
())
is
Restore
->
restore
(
subject
.
file
,
subject
.
hashCode
())
is
Restore
->
restore
(
subject
.
file
,
subject
.
hashCode
())
...
...
app/src/main/java/com/topjohnwu/magisk/core/download/NotificationService.kt
View file @
6348d0a6
...
@@ -5,6 +5,9 @@ import android.content.Intent
...
@@ -5,6 +5,9 @@ import android.content.Intent
import
android.os.IBinder
import
android.os.IBinder
import
com.topjohnwu.magisk.core.base.BaseService
import
com.topjohnwu.magisk.core.base.BaseService
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.core.view.Notifications
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.cancel
import
org.koin.core.KoinComponent
import
org.koin.core.KoinComponent
import
java.util.*
import
java.util.*
import
kotlin.collections.HashMap
import
kotlin.collections.HashMap
...
@@ -16,12 +19,19 @@ abstract class NotificationService : BaseService(), KoinComponent {
...
@@ -16,12 +19,19 @@ abstract class NotificationService : BaseService(), KoinComponent {
private
val
notifications
=
Collections
.
synchronizedMap
(
HashMap
<
Int
,
Notification
.
Builder
>())
private
val
notifications
=
Collections
.
synchronizedMap
(
HashMap
<
Int
,
Notification
.
Builder
>())
val
coroutineScope
=
CoroutineScope
(
Dispatchers
.
IO
)
override
fun
onTaskRemoved
(
rootIntent
:
Intent
?)
{
override
fun
onTaskRemoved
(
rootIntent
:
Intent
?)
{
super
.
onTaskRemoved
(
rootIntent
)
super
.
onTaskRemoved
(
rootIntent
)
notifications
.
forEach
{
cancel
(
it
.
key
)
}
notifications
.
forEach
{
cancel
(
it
.
key
)
}
notifications
.
clear
()
notifications
.
clear
()
}
}
override
fun
onDestroy
()
{
super
.
onDestroy
()
coroutineScope
.
cancel
()
}
abstract
fun
createNotification
():
Notification
.
Builder
abstract
fun
createNotification
():
Notification
.
Builder
// --
// --
...
...
app/src/main/java/com/topjohnwu/magisk/core/download/RemoteFileService.kt
View file @
6348d0a6
...
@@ -9,17 +9,17 @@ import com.topjohnwu.magisk.core.ForegroundTracker
...
@@ -9,17 +9,17 @@ import com.topjohnwu.magisk.core.ForegroundTracker
import
com.topjohnwu.magisk.core.utils.ProgressInputStream
import
com.topjohnwu.magisk.core.utils.ProgressInputStream
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Magisk
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Magisk
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Module
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Module
import
com.topjohnwu.superuser.ShellUtils
import
com.topjohnwu.superuser.ShellUtils
import
io.reactivex.Completable
import
kotlinx.coroutines.launch
import
okhttp3.ResponseBody
import
okhttp3.ResponseBody
import
org.koin.android.ext.android.inject
import
org.koin.android.ext.android.inject
import
org.koin.core.KoinComponent
import
org.koin.core.KoinComponent
import
timber.log.Timber
import
timber.log.Timber
import
java.io.IOException
import
java.io.InputStream
import
java.io.InputStream
abstract
class
RemoteFileService
:
NotificationService
()
{
abstract
class
RemoteFileService
:
NotificationService
()
{
...
@@ -29,7 +29,14 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -29,7 +29,14 @@ abstract class RemoteFileService : NotificationService() {
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
override
fun
onStartCommand
(
intent
:
Intent
?,
flags
:
Int
,
startId
:
Int
):
Int
{
intent
?.
getParcelableExtra
<
DownloadSubject
>(
ARG_URL
)
?.
let
{
intent
?.
getParcelableExtra
<
DownloadSubject
>(
ARG_URL
)
?.
let
{
update
(
it
.
hashCode
())
update
(
it
.
hashCode
())
coroutineScope
.
launch
{
try
{
start
(
it
)
start
(
it
)
}
catch
(
e
:
IOException
)
{
Timber
.
e
(
e
)
failNotify
(
it
)
}
}
}
}
return
START_REDELIVER_INTENT
return
START_REDELIVER_INTENT
}
}
...
@@ -38,37 +45,24 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -38,37 +45,24 @@ abstract class RemoteFileService : NotificationService() {
// ---
// ---
private
fun
start
(
subject
:
DownloadSubject
)
=
checkExisting
(
subject
)
private
suspend
fun
start
(
subject
:
DownloadSubject
)
{
.
onErrorResumeNext
{
download
(
subject
)
}
if
(
subject
!
is
Magisk
||
.
subscribeK
(
onError
=
{
!
subject
.
file
.
exists
()
||
Timber
.
e
(
it
)
!
ShellUtils
.
checkSum
(
"MD5"
,
subject
.
file
,
subject
.
magisk
.
md5
))
{
failNotify
(
subject
)
val
stream
=
service
.
fetchFile
(
subject
.
url
).
toProgressStream
(
subject
)
})
{
when
(
subject
)
{
is
Module
->
stream
.
toModule
(
subject
.
file
,
service
.
fetchInstaller
().
byteStream
())
else
->
stream
.
writeTo
(
subject
.
file
)
}
}
val
newId
=
finishNotify
(
subject
)
val
newId
=
finishNotify
(
subject
)
if
(
ForegroundTracker
.
hasForeground
)
{
if
(
ForegroundTracker
.
hasForeground
)
{
onFinished
(
subject
,
newId
)
onFinished
(
subject
,
newId
)
}
}
}
}
private
fun
checkExisting
(
subject
:
DownloadSubject
)
=
Completable
.
fromAction
{
check
(
subject
is
Magisk
)
{
"Download cache is disabled"
}
check
(
subject
.
file
.
exists
()
&&
ShellUtils
.
checkSum
(
"MD5"
,
subject
.
file
,
subject
.
magisk
.
md5
))
{
"The given file does not match checksum"
}
}
private
fun
download
(
subject
:
DownloadSubject
)
=
service
.
fetchFile
(
subject
.
url
)
.
map
{
it
.
toProgressStream
(
subject
)
}
.
flatMapCompletable
{
stream
->
when
(
subject
)
{
is
Module
->
service
.
fetchInstaller
()
.
doOnSuccess
{
stream
.
toModule
(
subject
.
file
,
it
.
byteStream
())
}
.
ignoreElement
()
else
->
Completable
.
fromAction
{
stream
.
writeTo
(
subject
.
file
)
}
}
}
private
fun
ResponseBody
.
toProgressStream
(
subject
:
DownloadSubject
):
InputStream
{
private
fun
ResponseBody
.
toProgressStream
(
subject
:
DownloadSubject
):
InputStream
{
val
maxRaw
=
contentLength
()
val
maxRaw
=
contentLength
()
val
max
=
maxRaw
/
1
_000_000f
val
max
=
maxRaw
/
1
_000_000f
...
@@ -112,8 +106,7 @@ abstract class RemoteFileService : NotificationService() {
...
@@ -112,8 +106,7 @@ abstract class RemoteFileService : NotificationService() {
// ---
// ---
@Throws
(
Throwable
::
class
)
protected
abstract
suspend
fun
onFinished
(
subject
:
DownloadSubject
,
id
:
Int
)
protected
abstract
fun
onFinished
(
subject
:
DownloadSubject
,
id
:
Int
)
protected
abstract
fun
Notification
.
Builder
.
addActions
(
subject
:
DownloadSubject
)
protected
abstract
fun
Notification
.
Builder
.
addActions
(
subject
:
DownloadSubject
)
:
Notification
.
Builder
:
Notification
.
Builder
...
...
app/src/main/java/com/topjohnwu/magisk/core/model/module/Repo.kt
View file @
6348d0a6
...
@@ -31,7 +31,7 @@ data class Repo(
...
@@ -31,7 +31,7 @@ data class Repo(
val
downloadFilename
:
String
get
()
=
"$name-$version($versionCode).zip"
.
legalFilename
()
val
downloadFilename
:
String
get
()
=
"$name-$version($versionCode).zip"
.
legalFilename
()
val
readme
get
()
=
stringRepo
.
getReadme
(
this
)
suspend
fun
readme
()
=
stringRepo
.
getReadme
(
this
)
val
zipUrl
:
String
get
()
=
Const
.
Url
.
ZIP_URL
.
format
(
id
)
val
zipUrl
:
String
get
()
=
Const
.
Url
.
ZIP_URL
.
format
(
id
)
...
...
app/src/main/java/com/topjohnwu/magisk/core/tasks/MagiskInstaller.kt
View file @
6348d0a6
...
@@ -15,7 +15,9 @@ import com.topjohnwu.magisk.core.Info
...
@@ -15,7 +15,9 @@ import com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.di.Protected
import
com.topjohnwu.magisk.di.Protected
import
com.topjohnwu.magisk.extensions.*
import
com.topjohnwu.magisk.extensions.readUri
import
com.topjohnwu.magisk.extensions.reboot
import
com.topjohnwu.magisk.extensions.withStreams
import
com.topjohnwu.magisk.model.events.dialog.EnvFixDialog
import
com.topjohnwu.magisk.model.events.dialog.EnvFixDialog
import
com.topjohnwu.signing.SignBoot
import
com.topjohnwu.signing.SignBoot
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
...
@@ -322,7 +324,7 @@ abstract class MagiskInstallImpl : KoinComponent {
...
@@ -322,7 +324,7 @@ abstract class MagiskInstallImpl : KoinComponent {
tarOut
=
null
tarOut
=
null
it
it
}
?:
destFile
.
outputStream
()
}
?:
destFile
.
outputStream
()
patched
.
suInputStream
(
).
use
{
it
.
copyTo
(
os
);
os
.
close
()
}
SuFileInputStream
(
patched
).
use
{
it
.
copyTo
(
os
);
os
.
close
()
}
}
catch
(
e
:
IOException
)
{
}
catch
(
e
:
IOException
)
{
console
.
add
(
"! Failed to output to $destFile"
)
console
.
add
(
"! Failed to output to $destFile"
)
Timber
.
e
(
e
)
Timber
.
e
(
e
)
...
@@ -338,10 +340,10 @@ abstract class MagiskInstallImpl : KoinComponent {
...
@@ -338,10 +340,10 @@ abstract class MagiskInstallImpl : KoinComponent {
return
true
return
true
}
}
private
fun
postOTA
():
Boolean
{
private
suspend
fun
postOTA
():
Boolean
{
val
bootctl
=
SuFile
(
"/data/adb/bootctl"
)
val
bootctl
=
SuFile
(
"/data/adb/bootctl"
)
try
{
try
{
withStreams
(
service
.
fetchBootctl
().
b
lockingGet
().
byteStream
(),
bootctl
.
suOutputStream
(
))
{
withStreams
(
service
.
fetchBootctl
().
b
yteStream
(),
SuFileOutputStream
(
bootctl
))
{
input
,
out
->
input
.
copyTo
(
out
)
input
,
out
->
input
.
copyTo
(
out
)
}
}
}
catch
(
e
:
IOException
)
{
}
catch
(
e
:
IOException
)
{
...
@@ -368,7 +370,7 @@ abstract class MagiskInstallImpl : KoinComponent {
...
@@ -368,7 +370,7 @@ abstract class MagiskInstallImpl : KoinComponent {
protected
fun
direct
()
=
findImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
protected
fun
direct
()
=
findImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
protected
fun
secondSlot
()
=
protected
suspend
fun
secondSlot
()
=
findSecondaryImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
&&
postOTA
()
findSecondaryImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
&&
postOTA
()
protected
fun
fixEnv
(
zip
:
File
):
Boolean
{
protected
fun
fixEnv
(
zip
:
File
):
Boolean
{
...
@@ -379,7 +381,7 @@ abstract class MagiskInstallImpl : KoinComponent {
...
@@ -379,7 +381,7 @@ abstract class MagiskInstallImpl : KoinComponent {
}
}
@WorkerThread
@WorkerThread
protected
abstract
fun
operations
():
Boolean
protected
abstract
suspend
fun
operations
():
Boolean
open
suspend
fun
exec
()
=
withContext
(
Dispatchers
.
IO
)
{
operations
()
}
open
suspend
fun
exec
()
=
withContext
(
Dispatchers
.
IO
)
{
operations
()
}
}
}
...
@@ -407,7 +409,7 @@ sealed class MagiskInstaller(
...
@@ -407,7 +409,7 @@ sealed class MagiskInstaller(
console
:
MutableList
<
String
>,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>
logs
:
MutableList
<
String
>
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
override
fun
operations
()
=
doPatchFile
(
uri
)
override
suspend
fun
operations
()
=
doPatchFile
(
uri
)
}
}
class
SecondSlot
(
class
SecondSlot
(
...
@@ -415,7 +417,7 @@ sealed class MagiskInstaller(
...
@@ -415,7 +417,7 @@ sealed class MagiskInstaller(
console
:
MutableList
<
String
>,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>
logs
:
MutableList
<
String
>
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
override
fun
operations
()
=
secondSlot
()
override
suspend
fun
operations
()
=
secondSlot
()
}
}
class
Direct
(
class
Direct
(
...
@@ -423,7 +425,7 @@ sealed class MagiskInstaller(
...
@@ -423,7 +425,7 @@ sealed class MagiskInstaller(
console
:
MutableList
<
String
>,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>
logs
:
MutableList
<
String
>
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
)
:
MagiskInstaller
(
file
,
console
,
logs
)
{
override
fun
operations
()
=
direct
()
override
suspend
fun
operations
()
=
direct
()
}
}
}
}
...
@@ -431,7 +433,7 @@ sealed class MagiskInstaller(
...
@@ -431,7 +433,7 @@ sealed class MagiskInstaller(
class
EnvFixTask
(
class
EnvFixTask
(
private
val
zip
:
File
private
val
zip
:
File
)
:
MagiskInstallImpl
()
{
)
:
MagiskInstallImpl
()
{
override
fun
operations
()
=
fixEnv
(
zip
)
override
suspend
fun
operations
()
=
fixEnv
(
zip
)
override
suspend
fun
exec
():
Boolean
{
override
suspend
fun
exec
():
Boolean
{
val
success
=
super
.
exec
()
val
success
=
super
.
exec
()
...
...
app/src/main/java/com/topjohnwu/magisk/core/utils/Locales.kt
View file @
6348d0a6
...
@@ -13,7 +13,8 @@ import com.topjohnwu.magisk.core.ResMgr
...
@@ -13,7 +13,8 @@ import com.topjohnwu.magisk.core.ResMgr
import
com.topjohnwu.magisk.core.addAssetPath
import
com.topjohnwu.magisk.core.addAssetPath
import
com.topjohnwu.magisk.extensions.langTagToLocale
import
com.topjohnwu.magisk.extensions.langTagToLocale
import
com.topjohnwu.magisk.extensions.toLangTag
import
com.topjohnwu.magisk.extensions.toLangTag
import
io.reactivex.Single
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
java.util.*
import
java.util.*
import
kotlin.Comparator
import
kotlin.Comparator
import
kotlin.collections.ArrayList
import
kotlin.collections.ArrayList
...
@@ -23,7 +24,10 @@ var currentLocale: Locale = Locale.getDefault()
...
@@ -23,7 +24,10 @@ var currentLocale: Locale = Locale.getDefault()
@SuppressLint
(
"ConstantLocale"
)
@SuppressLint
(
"ConstantLocale"
)
val
defaultLocale
:
Locale
=
Locale
.
getDefault
()
val
defaultLocale
:
Locale
=
Locale
.
getDefault
()
val
availableLocales
=
Single
.
fromCallable
{
private
var
cachedLocales
:
Pair
<
Array
<
String
>,
Array
<
String
>>?
=
null
suspend
fun
availableLocales
()
=
cachedLocales
?:
withContext
(
Dispatchers
.
Default
)
{
val
compareId
=
R
.
string
.
app_changelog
val
compareId
=
R
.
string
.
app_changelog
// Create a completely new resource to prevent cross talk over app's configs
// Create a completely new resource to prevent cross talk over app's configs
...
@@ -56,8 +60,6 @@ val availableLocales = Single.fromCallable {
...
@@ -56,8 +60,6 @@ val availableLocales = Single.fromCallable {
res
.
updateConfiguration
(
config
,
metrics
)
res
.
updateConfiguration
(
config
,
metrics
)
val
defName
=
res
.
getString
(
R
.
string
.
system_default
)
val
defName
=
res
.
getString
(
R
.
string
.
system_default
)
Pair
(
locales
,
defName
)
}.
map
{
(
locales
,
defName
)
->
val
names
=
ArrayList
<
String
>(
locales
.
size
+
1
)
val
names
=
ArrayList
<
String
>(
locales
.
size
+
1
)
val
values
=
ArrayList
<
String
>(
locales
.
size
+
1
)
val
values
=
ArrayList
<
String
>(
locales
.
size
+
1
)
...
@@ -69,8 +71,8 @@ val availableLocales = Single.fromCallable {
...
@@ -69,8 +71,8 @@ val availableLocales = Single.fromCallable {
values
.
add
(
locale
.
toLangTag
())
values
.
add
(
locale
.
toLangTag
())
}
}
Pair
(
names
.
toTypedArray
(),
values
.
toTypedArray
())
(
names
.
toTypedArray
()
to
values
.
toTypedArray
()).
also
{
cachedLocales
=
it
}
}
.
cache
()
!!
}
fun
Resources
.
updateConfig
(
config
:
Configuration
=
configuration
)
{
fun
Resources
.
updateConfig
(
config
:
Configuration
=
configuration
)
{
config
.
setLocale
(
currentLocale
)
config
.
setLocale
(
currentLocale
)
...
...
app/src/main/java/com/topjohnwu/magisk/core/utils/PatchAPK.kt
View file @
6348d0a6
...
@@ -11,15 +11,18 @@ import com.topjohnwu.magisk.core.isRunningAsStub
...
@@ -11,15 +11,18 @@ import com.topjohnwu.magisk.core.isRunningAsStub
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.core.view.Notifications
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.extensions.get
import
com.topjohnwu.magisk.extensions.get
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.signing.JarMap
import
com.topjohnwu.signing.JarMap
import
com.topjohnwu.signing.SignAPK
import
com.topjohnwu.signing.SignAPK
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Single
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
timber.log.Timber
import
java.io.File
import
java.io.File
import
java.io.FileOutputStream
import
java.io.FileOutputStream
import
java.io.IOException
import
java.nio.ByteBuffer
import
java.nio.ByteBuffer
import
java.nio.ByteOrder
import
java.nio.ByteOrder
import
java.security.SecureRandom
import
java.security.SecureRandom
...
@@ -102,16 +105,16 @@ object PatchAPK {
...
@@ -102,16 +105,16 @@ object PatchAPK {
return
true
return
true
}
}
private
fun
patchAndHide
(
context
:
Context
,
label
:
String
):
Boolean
{
private
suspend
fun
patchAndHide
(
context
:
Context
,
label
:
String
):
Boolean
{
val
dlStub
=
!
isRunningAsStub
&&
SDK_INT
>=
28
&&
Const
.
Version
.
atLeast_20_2
()
val
dlStub
=
!
isRunningAsStub
&&
SDK_INT
>=
28
&&
Const
.
Version
.
atLeast_20_2
()
val
src
=
if
(
dlStub
)
{
val
src
=
if
(
dlStub
)
{
val
stub
=
File
(
context
.
cacheDir
,
"stub.apk"
)
val
stub
=
File
(
context
.
cacheDir
,
"stub.apk"
)
val
svc
=
get
<
GithubRawServices
>()
val
svc
=
get
<
GithubRawServices
>()
try
{
try
{
svc
.
fetchFile
(
Info
.
remote
.
stub
.
link
).
b
lockingGet
().
b
yteStream
().
use
{
svc
.
fetchFile
(
Info
.
remote
.
stub
.
link
).
byteStream
().
use
{
it
.
writeTo
(
stub
)
it
.
writeTo
(
stub
)
}
}
}
catch
(
e
:
Exception
)
{
}
catch
(
e
:
IO
Exception
)
{
Timber
.
e
(
e
)
Timber
.
e
(
e
)
return
false
return
false
}
}
...
@@ -143,10 +146,11 @@ object PatchAPK {
...
@@ -143,10 +146,11 @@ object PatchAPK {
fun
hideManager
(
context
:
Context
,
label
:
String
)
{
fun
hideManager
(
context
:
Context
,
label
:
String
)
{
val
progress
=
Notifications
.
progress
(
context
,
context
.
getString
(
R
.
string
.
hide_manager_title
))
val
progress
=
Notifications
.
progress
(
context
,
context
.
getString
(
R
.
string
.
hide_manager_title
))
Notifications
.
mgr
.
notify
(
Const
.
ID
.
HIDE_MANAGER_NOTIFICATION_ID
,
progress
.
build
())
Notifications
.
mgr
.
notify
(
Const
.
ID
.
HIDE_MANAGER_NOTIFICATION_ID
,
progress
.
build
())
Single
.
fromCallable
{
GlobalScope
.
launch
{
val
result
=
withContext
(
Dispatchers
.
IO
)
{
patchAndHide
(
context
,
label
)
patchAndHide
(
context
,
label
)
}.
subscribeK
{
}
if
(!
i
t
)
if
(!
resul
t
)
Utils
.
toast
(
R
.
string
.
hide_manager_fail_toast
,
Toast
.
LENGTH_LONG
)
Utils
.
toast
(
R
.
string
.
hide_manager_fail_toast
,
Toast
.
LENGTH_LONG
)
Notifications
.
mgr
.
cancel
(
Const
.
ID
.
HIDE_MANAGER_NOTIFICATION_ID
)
Notifications
.
mgr
.
cancel
(
Const
.
ID
.
HIDE_MANAGER_NOTIFICATION_ID
)
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/data/database/RepoDao.kt
View file @
6348d0a6
...
@@ -3,6 +3,8 @@ package com.topjohnwu.magisk.data.database
...
@@ -3,6 +3,8 @@ package com.topjohnwu.magisk.data.database
import
androidx.room.*
import
androidx.room.*
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.Config
import
com.topjohnwu.magisk.core.model.module.Repo
import
com.topjohnwu.magisk.core.model.module.Repo
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
@Database
(
version
=
6
,
entities
=
[
Repo
::
class
,
RepoEtag
::
class
],
exportSchema
=
false
)
@Database
(
version
=
6
,
entities
=
[
Repo
::
class
,
RepoEtag
::
class
],
exportSchema
=
false
)
abstract
class
RepoDatabase
:
RoomDatabase
()
{
abstract
class
RepoDatabase
:
RoomDatabase
()
{
...
@@ -26,7 +28,7 @@ abstract class RepoDao(private val db: RepoDatabase) {
...
@@ -26,7 +28,7 @@ abstract class RepoDao(private val db: RepoDatabase) {
set
(
value
)
=
addEtagRaw
(
RepoEtag
(
0
,
value
))
set
(
value
)
=
addEtagRaw
(
RepoEtag
(
0
,
value
))
get
()
=
etagRaw
()
?.
key
.
orEmpty
()
get
()
=
etagRaw
()
?.
key
.
orEmpty
()
fun
clear
()
=
db
.
clearAllTables
()
suspend
fun
clear
()
=
withContext
(
Dispatchers
.
IO
)
{
db
.
clearAllTables
()
}
@Query
(
"SELECT * FROM repos ORDER BY last_update DESC"
)
@Query
(
"SELECT * FROM repos ORDER BY last_update DESC"
)
protected
abstract
fun
getReposDateOrder
():
List
<
Repo
>
protected
abstract
fun
getReposDateOrder
():
List
<
Repo
>
...
...
app/src/main/java/com/topjohnwu/magisk/data/network/GithubServices.kt
View file @
6348d0a6
...
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.data.network
...
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.data.network
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.model.UpdateInfo
import
com.topjohnwu.magisk.core.model.UpdateInfo
import
com.topjohnwu.magisk.core.tasks.GithubRepoInfo
import
com.topjohnwu.magisk.core.tasks.GithubRepoInfo
import
io.reactivex.Single
import
okhttp3.ResponseBody
import
okhttp3.ResponseBody
import
retrofit2.Response
import
retrofit2.Response
import
retrofit2.http.*
import
retrofit2.http.*
...
@@ -26,18 +25,15 @@ interface GithubRawServices {
...
@@ -26,18 +25,15 @@ interface GithubRawServices {
@GET
(
"$MAGISK_FILES/{$REVISION}/snet.jar"
)
@GET
(
"$MAGISK_FILES/{$REVISION}/snet.jar"
)
@Streaming
@Streaming
fun
fetchSafetynet
(
@Path
(
REVISION
)
revision
:
String
=
Const
.
SNET_REVISION
):
Single
<
ResponseBody
>
suspend
fun
fetchSafetynet
(
@Path
(
REVISION
)
revision
:
String
=
Const
.
SNET_REVISION
):
ResponseBody
@GET
(
"$MAGISK_FILES/{$REVISION}/bootctl"
)
@GET
(
"$MAGISK_FILES/{$REVISION}/bootctl"
)
@Streaming
@Streaming
fun
fetchBootctl
(
@Path
(
REVISION
)
revision
:
String
=
Const
.
BOOTCTL_REVISION
):
Single
<
ResponseBody
>
suspend
fun
fetchBootctl
(
@Path
(
REVISION
)
revision
:
String
=
Const
.
BOOTCTL_REVISION
):
ResponseBody
@GET
(
"$MAGISK_MASTER/scripts/module_installer.sh"
)
@GET
(
"$MAGISK_MASTER/scripts/module_installer.sh"
)
@Streaming
@Streaming
fun
fetchInstaller
():
Single
<
ResponseBody
>
suspend
fun
fetchInstaller
():
ResponseBody
@GET
(
"$MAGISK_MODULES/{$MODULE}/master/{$FILE}"
)
fun
fetchModuleInfo
(
@Path
(
MODULE
)
id
:
String
,
@Path
(
FILE
)
file
:
String
):
Single
<
String
>
@GET
(
"$MAGISK_MODULES/{$MODULE}/master/{$FILE}"
)
@GET
(
"$MAGISK_MODULES/{$MODULE}/master/{$FILE}"
)
suspend
fun
fetchModuleFile
(
@Path
(
MODULE
)
id
:
String
,
@Path
(
FILE
)
file
:
String
):
String
suspend
fun
fetchModuleFile
(
@Path
(
MODULE
)
id
:
String
,
@Path
(
FILE
)
file
:
String
):
String
...
@@ -50,10 +46,10 @@ interface GithubRawServices {
...
@@ -50,10 +46,10 @@ interface GithubRawServices {
* */
* */
@GET
@GET
@Streaming
@Streaming
fun
fetchFile
(
@Url
url
:
String
):
Single
<
ResponseBody
>
suspend
fun
fetchFile
(
@Url
url
:
String
):
ResponseBody
@GET
@GET
fun
fetchString
(
@Url
url
:
String
):
Single
<
String
>
suspend
fun
fetchString
(
@Url
url
:
String
):
String
companion
object
{
companion
object
{
...
...
app/src/main/java/com/topjohnwu/magisk/data/repository/MagiskRepository.kt
View file @
6348d0a6
...
@@ -20,8 +20,6 @@ class MagiskRepository(
...
@@ -20,8 +20,6 @@ class MagiskRepository(
private
val
packageManager
:
PackageManager
private
val
packageManager
:
PackageManager
)
{
)
{
fun
fetchSafetynet
()
=
apiRaw
.
fetchSafetynet
()
suspend
fun
fetchUpdate
()
=
try
{
suspend
fun
fetchUpdate
()
=
try
{
var
info
=
when
(
Config
.
updateChannel
)
{
var
info
=
when
(
Config
.
updateChannel
)
{
Config
.
Value
.
DEFAULT_CHANNEL
,
Config
.
Value
.
STABLE_CHANNEL
->
apiRaw
.
fetchStableUpdate
()
Config
.
Value
.
DEFAULT_CHANNEL
,
Config
.
Value
.
STABLE_CHANNEL
->
apiRaw
.
fetchStableUpdate
()
...
...
app/src/main/java/com/topjohnwu/magisk/data/repository/StringRepository.kt
View file @
6348d0a6
...
@@ -7,9 +7,10 @@ class StringRepository(
...
@@ -7,9 +7,10 @@ class StringRepository(
private
val
api
:
GithubRawServices
private
val
api
:
GithubRawServices
)
{
)
{
fun
getString
(
url
:
String
)
=
api
.
fetchString
(
url
)
suspend
fun
getString
(
url
:
String
)
=
api
.
fetchString
(
url
)
suspend
fun
getMetadata
(
repo
:
Repo
)
=
api
.
fetchModuleFile
(
repo
.
id
,
"module.prop"
)
suspend
fun
getMetadata
(
repo
:
Repo
)
=
api
.
fetchModuleFile
(
repo
.
id
,
"module.prop"
)
fun
getReadme
(
repo
:
Repo
)
=
api
.
fetchModuleInfo
(
repo
.
id
,
"README.md"
)
suspend
fun
getReadme
(
repo
:
Repo
)
=
api
.
fetchModuleFile
(
repo
.
id
,
"README.md"
)
}
}
app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt
View file @
6348d0a6
...
@@ -7,8 +7,11 @@ import androidx.core.view.isGone
...
@@ -7,8 +7,11 @@ import androidx.core.view.isGone
import
androidx.core.view.isInvisible
import
androidx.core.view.isInvisible
import
androidx.core.widget.TextViewCompat
import
androidx.core.widget.TextViewCompat
import
androidx.databinding.BindingAdapter
import
androidx.databinding.BindingAdapter
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.get
import
io.reactivex.Single
import
io.noties.markwon.Markwon
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
@BindingAdapter
(
"gone"
)
@BindingAdapter
(
"gone"
)
fun
setGone
(
view
:
View
,
gone
:
Boolean
)
{
fun
setGone
(
view
:
View
,
gone
:
Boolean
)
{
...
@@ -32,9 +35,16 @@ fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
...
@@ -32,9 +35,16 @@ fun setInvisibleUnless(view: View, invisibleUnless: Boolean) {
@BindingAdapter
(
"precomputedText"
)
@BindingAdapter
(
"precomputedText"
)
fun
setPrecomputedText
(
tv
:
TextView
,
text
:
CharSequence
)
{
fun
setPrecomputedText
(
tv
:
TextView
,
text
:
CharSequence
)
{
Single
.
fromCallable
{
GlobalScope
.
launch
(
Dispatchers
.
Default
)
{
PrecomputedTextCompat
.
create
(
text
,
TextViewCompat
.
getTextMetricsParams
(
tv
))
val
pre
=
PrecomputedTextCompat
.
create
(
text
,
TextViewCompat
.
getTextMetricsParams
(
tv
))
}.
subscribeK
{
tv
.
post
{
TextViewCompat
.
setPrecomputedText
(
tv
,
it
);
TextViewCompat
.
setPrecomputedText
(
tv
,
pre
);
}
}
}
}
@BindingAdapter
(
"markdownText"
)
fun
setMarkdownText
(
tv
:
TextView
,
text
:
CharSequence
)
{
val
markwon
=
get
<
Markwon
>()
markwon
.
setMarkdown
(
tv
,
text
.
toString
())
}
}
app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt
View file @
6348d0a6
...
@@ -7,6 +7,7 @@ import com.topjohnwu.magisk.data.network.GithubApiServices
...
@@ -7,6 +7,7 @@ import com.topjohnwu.magisk.data.network.GithubApiServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.data.network.GithubRawServices
import
com.topjohnwu.magisk.net.Networking
import
com.topjohnwu.magisk.net.Networking
import
com.topjohnwu.magisk.net.NoSSLv3SocketFactory
import
com.topjohnwu.magisk.net.NoSSLv3SocketFactory
import
com.topjohnwu.magisk.view.PrecomputedTextSetter
import
io.noties.markwon.Markwon
import
io.noties.markwon.Markwon
import
io.noties.markwon.html.HtmlPlugin
import
io.noties.markwon.html.HtmlPlugin
import
io.noties.markwon.image.ImagesPlugin
import
io.noties.markwon.image.ImagesPlugin
...
@@ -93,6 +94,7 @@ inline fun <reified T> createApiService(retrofitBuilder: Retrofit.Builder, baseU
...
@@ -93,6 +94,7 @@ inline fun <reified T> createApiService(retrofitBuilder: Retrofit.Builder, baseU
fun
createMarkwon
(
context
:
Context
,
okHttpClient
:
OkHttpClient
):
Markwon
{
fun
createMarkwon
(
context
:
Context
,
okHttpClient
:
OkHttpClient
):
Markwon
{
return
Markwon
.
builder
(
context
)
return
Markwon
.
builder
(
context
)
.
textSetter
(
PrecomputedTextSetter
())
.
usePlugin
(
HtmlPlugin
.
create
())
.
usePlugin
(
HtmlPlugin
.
create
())
.
usePlugin
(
ImagesPlugin
.
create
{
.
usePlugin
(
ImagesPlugin
.
create
{
it
.
addSchemeHandler
(
OkHttpNetworkSchemeHandler
.
create
(
okHttpClient
))
it
.
addSchemeHandler
(
OkHttpNetworkSchemeHandler
.
create
(
okHttpClient
))
...
...
app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt
View file @
6348d0a6
...
@@ -25,7 +25,7 @@ val viewModelModules = module {
...
@@ -25,7 +25,7 @@ val viewModelModules = module {
viewModel
{
SettingsViewModel
(
get
())
}
viewModel
{
SettingsViewModel
(
get
())
}
viewModel
{
SuperuserViewModel
(
get
(),
get
(),
get
())
}
viewModel
{
SuperuserViewModel
(
get
(),
get
(),
get
())
}
viewModel
{
ThemeViewModel
()
}
viewModel
{
ThemeViewModel
()
}
viewModel
{
InstallViewModel
(
get
()
,
get
()
)
}
viewModel
{
InstallViewModel
(
get
())
}
viewModel
{
MainViewModel
()
}
viewModel
{
MainViewModel
()
}
// Legacy
// Legacy
...
...
app/src/main/java/com/topjohnwu/magisk/extensions/RxJava.kt
View file @
6348d0a6
package
com.topjohnwu.magisk.extensions
package
com.topjohnwu.magisk.extensions
import
androidx.databinding.ObservableField
import
io.reactivex.Observable
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
io.reactivex.Scheduler
import
io.reactivex.*
import
io.reactivex.android.schedulers.AndroidSchedulers
import
io.reactivex.android.schedulers.AndroidSchedulers
import
io.reactivex.disposables.Disposables
import
io.reactivex.functions.BiFunction
import
io.reactivex.schedulers.Schedulers
import
io.reactivex.schedulers.Schedulers
import
androidx.databinding.Observable
as
BindingObservable
fun
<
T
>
Observable
<
T
>.
applySchedulers
(
fun
<
T
>
Observable
<
T
>.
applySchedulers
(
subscribeOn
:
Scheduler
=
Schedulers
.
io
(),
subscribeOn
:
Scheduler
=
Schedulers
.
io
(),
observeOn
:
Scheduler
=
AndroidSchedulers
.
mainThread
()
observeOn
:
Scheduler
=
AndroidSchedulers
.
mainThread
()
):
Observable
<
T
>
=
this
.
subscribeOn
(
subscribeOn
).
observeOn
(
observeOn
)
):
Observable
<
T
>
=
this
.
subscribeOn
(
subscribeOn
).
observeOn
(
observeOn
)
fun
<
T
>
Flowable
<
T
>.
applySchedulers
(
subscribeOn
:
Scheduler
=
Schedulers
.
io
(),
observeOn
:
Scheduler
=
AndroidSchedulers
.
mainThread
()
):
Flowable
<
T
>
=
this
.
subscribeOn
(
subscribeOn
).
observeOn
(
observeOn
)
fun
<
T
>
Single
<
T
>.
applySchedulers
(
subscribeOn
:
Scheduler
=
Schedulers
.
io
(),
observeOn
:
Scheduler
=
AndroidSchedulers
.
mainThread
()
):
Single
<
T
>
=
this
.
subscribeOn
(
subscribeOn
).
observeOn
(
observeOn
)
fun
<
T
>
Maybe
<
T
>.
applySchedulers
(
subscribeOn
:
Scheduler
=
Schedulers
.
io
(),
observeOn
:
Scheduler
=
AndroidSchedulers
.
mainThread
()
):
Maybe
<
T
>
=
this
.
subscribeOn
(
subscribeOn
).
observeOn
(
observeOn
)
fun
Completable
.
applySchedulers
(
subscribeOn
:
Scheduler
=
Schedulers
.
io
(),
observeOn
:
Scheduler
=
AndroidSchedulers
.
mainThread
()
):
Completable
=
this
.
subscribeOn
(
subscribeOn
).
observeOn
(
observeOn
)
/*=== ALIASES FOR OBSERVABLES ===*/
/*=== ALIASES FOR OBSERVABLES ===*/
typealias
OnCompleteListener
=
()
->
Unit
typealias
OnCompleteListener
=
()
->
Unit
...
@@ -49,128 +25,3 @@ fun <T> Observable<T>.subscribeK(
...
@@ -49,128 +25,3 @@ fun <T> Observable<T>.subscribeK(
)
=
applySchedulers
()
)
=
applySchedulers
()
.
subscribe
(
onNext
,
onError
,
onComplete
)
.
subscribe
(
onNext
,
onError
,
onComplete
)
fun
<
T
>
Single
<
T
>.
subscribeK
(
onError
:
OnErrorListener
=
{
it
.
printStackTrace
()
},
onNext
:
OnSuccessListener
<
T
>
=
{}
)
=
applySchedulers
()
.
subscribe
(
onNext
,
onError
)
fun
<
T
>
Maybe
<
T
>.
subscribeK
(
onError
:
OnErrorListener
=
{
it
.
printStackTrace
()
},
onComplete
:
OnCompleteListener
=
{},
onSuccess
:
OnSuccessListener
<
T
>
=
{}
)
=
applySchedulers
()
.
subscribe
(
onSuccess
,
onError
,
onComplete
)
fun
<
T
>
Flowable
<
T
>.
subscribeK
(
onError
:
OnErrorListener
=
{
it
.
printStackTrace
()
},
onComplete
:
OnCompleteListener
=
{},
onNext
:
OnSuccessListener
<
T
>
=
{}
)
=
applySchedulers
()
.
subscribe
(
onNext
,
onError
,
onComplete
)
fun
Completable
.
subscribeK
(
onError
:
OnErrorListener
=
{
it
.
printStackTrace
()
},
onComplete
:
OnCompleteListener
=
{}
)
=
applySchedulers
()
.
subscribe
(
onComplete
,
onError
)
fun
<
T
>
Observable
<
T
>.
doOnSubscribeUi
(
body
:
()
->
Unit
)
=
doOnSubscribe
{
UiThreadHandler
.
run
{
body
()
}
}
fun
<
T
>
Single
<
T
>.
doOnSubscribeUi
(
body
:
()
->
Unit
)
=
doOnSubscribe
{
UiThreadHandler
.
run
{
body
()
}
}
fun
<
T
>
Maybe
<
T
>.
doOnSubscribeUi
(
body
:
()
->
Unit
)
=
doOnSubscribe
{
UiThreadHandler
.
run
{
body
()
}
}
fun
<
T
>
Flowable
<
T
>.
doOnSubscribeUi
(
body
:
()
->
Unit
)
=
doOnSubscribe
{
UiThreadHandler
.
run
{
body
()
}
}
fun
Completable
.
doOnSubscribeUi
(
body
:
()
->
Unit
)
=
doOnSubscribe
{
UiThreadHandler
.
run
{
body
()
}
}
fun
<
T
>
Observable
<
T
>.
doOnErrorUi
(
body
:
(
Throwable
)
->
Unit
)
=
doOnError
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Single
<
T
>.
doOnErrorUi
(
body
:
(
Throwable
)
->
Unit
)
=
doOnError
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Maybe
<
T
>.
doOnErrorUi
(
body
:
(
Throwable
)
->
Unit
)
=
doOnError
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Flowable
<
T
>.
doOnErrorUi
(
body
:
(
Throwable
)
->
Unit
)
=
doOnError
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
Completable
.
doOnErrorUi
(
body
:
(
Throwable
)
->
Unit
)
=
doOnError
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Observable
<
T
>.
doOnNextUi
(
body
:
(
T
)
->
Unit
)
=
doOnNext
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Flowable
<
T
>.
doOnNextUi
(
body
:
(
T
)
->
Unit
)
=
doOnNext
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Single
<
T
>.
doOnSuccessUi
(
body
:
(
T
)
->
Unit
)
=
doOnSuccess
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Maybe
<
T
>.
doOnSuccessUi
(
body
:
(
T
)
->
Unit
)
=
doOnSuccess
{
UiThreadHandler
.
run
{
body
(
it
)
}
}
fun
<
T
>
Maybe
<
T
>.
doOnCompleteUi
(
body
:
()
->
Unit
)
=
doOnComplete
{
UiThreadHandler
.
run
{
body
()
}
}
fun
Completable
.
doOnCompleteUi
(
body
:
()
->
Unit
)
=
doOnComplete
{
UiThreadHandler
.
run
{
body
()
}
}
fun
<
T
,
R
>
Observable
<
List
<
T
>>.
mapList
(
transformer
:
(
T
)
->
R
)
=
flatMapIterable
{
it
}
.
map
(
transformer
)
.
toList
()
fun
<
T
,
R
>
Single
<
List
<
T
>>.
mapList
(
transformer
:
(
T
)
->
R
)
=
flattenAsFlowable
{
it
}
.
map
(
transformer
)
.
toList
()
fun
<
T
,
R
>
Maybe
<
List
<
T
>>.
mapList
(
transformer
:
(
T
)
->
R
)
=
flattenAsFlowable
{
it
}
.
map
(
transformer
)
.
toList
()
fun
<
T
,
R
>
Flowable
<
List
<
T
>>.
mapList
(
transformer
:
(
T
)
->
R
)
=
flatMapIterable
{
it
}
.
map
(
transformer
)
.
toList
()
fun
<
T
>
ObservableField
<
T
>.
toObservable
():
Observable
<
T
>
{
val
observableField
=
this
return
Observable
.
create
{
emitter
->
observableField
.
get
()
?.
let
{
emitter
.
onNext
(
it
)
}
val
callback
=
object
:
BindingObservable
.
OnPropertyChangedCallback
()
{
override
fun
onPropertyChanged
(
sender
:
BindingObservable
?,
propertyId
:
Int
)
{
observableField
.
get
()
?.
let
{
emitter
.
onNext
(
it
)
}
}
}
observableField
.
addOnPropertyChangedCallback
(
callback
)
emitter
.
setDisposable
(
Disposables
.
fromAction
{
observableField
.
removeOnPropertyChangedCallback
(
callback
)
})
}
}
fun
<
T
:
Any
>
T
.
toSingle
()
=
Single
.
just
(
this
)
inline
fun
<
T1
,
T2
,
R
>
zip
(
t1
:
Single
<
T1
>,
t2
:
Single
<
T2
>,
crossinline
zipper
:
(
T1
,
T2
)
->
R
)
=
Single
.
zip
(
t1
,
t2
,
BiFunction
<
T1
,
T2
,
R
>
{
rt1
,
rt2
->
zipper
(
rt1
,
rt2
)
})
app/src/main/java/com/topjohnwu/magisk/extensions/XList.kt
View file @
6348d0a6
...
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.extensions
...
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.extensions
import
androidx.collection.SparseArrayCompat
import
androidx.collection.SparseArrayCompat
import
androidx.databinding.ObservableList
import
androidx.databinding.ObservableList
import
com.topjohnwu.magisk.utils.DiffObservableList
import
com.topjohnwu.magisk.utils.DiffObservableList
import
io.reactivex.disposables.Disposable
import
kotlinx.coroutines.*
fun
<
T
>
MutableList
<
T
>.
update
(
newList
:
List
<
T
>)
{
fun
<
T
>
MutableList
<
T
>.
update
(
newList
:
List
<
T
>)
{
clear
()
clear
()
...
@@ -26,6 +26,7 @@ fun List<String>.toShellCmd(): String {
...
@@ -26,6 +26,7 @@ fun List<String>.toShellCmd(): String {
fun
<
T1
,
T2
>
ObservableList
<
T1
>.
sendUpdatesTo
(
fun
<
T1
,
T2
>
ObservableList
<
T1
>.
sendUpdatesTo
(
target
:
DiffObservableList
<
T2
>,
target
:
DiffObservableList
<
T2
>,
scope
:
CoroutineScope
,
mapper
:
(
List
<
T1
>)
->
List
<
T2
>
mapper
:
(
List
<
T1
>)
->
List
<
T2
>
)
=
addOnListChangedCallback
(
object
:
)
=
addOnListChangedCallback
(
object
:
ObservableList
.
OnListChangedCallback
<
ObservableList
<
T1
>>()
{
ObservableList
.
OnListChangedCallback
<
ObservableList
<
T1
>>()
{
...
@@ -49,14 +50,17 @@ fun <T1, T2> ObservableList<T1>.sendUpdatesTo(
...
@@ -49,14 +50,17 @@ fun <T1, T2> ObservableList<T1>.sendUpdatesTo(
updateAsync
(
sender
?:
return
)
updateAsync
(
sender
?:
return
)
}
}
private
var
updater
:
Disposable
?
=
null
private
var
updater
:
Job
?
=
null
private
fun
updateAsync
(
sender
:
List
<
T1
>)
{
private
fun
updateAsync
(
sender
:
List
<
T1
>)
{
updater
?.
dispose
()
updater
?.
cancel
()
updater
=
sender
.
toSingle
()
updater
=
scope
.
launch
{
.
map
{
mapper
(
it
)
}
val
(
list
,
diff
)
=
withContext
(
Dispatchers
.
Default
)
{
.
map
{
it
to
target
.
calculateDiff
(
it
)
}
val
list
=
mapper
(
sender
)
.
subscribeK
{
target
.
update
(
it
.
first
,
it
.
second
)
}
list
to
target
.
calculateDiff
(
list
)
}
target
.
update
(
list
,
diff
)
}
}
}
})
})
...
...
app/src/main/java/com/topjohnwu/magisk/extensions/XSU.kt
View file @
6348d0a6
...
@@ -2,19 +2,11 @@ package com.topjohnwu.magisk.extensions
...
@@ -2,19 +2,11 @@ package com.topjohnwu.magisk.extensions
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.io.SuFileInputStream
import
com.topjohnwu.superuser.io.SuFileOutputStream
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.withContext
import
kotlinx.coroutines.withContext
import
java.io.File
fun
reboot
(
reason
:
String
=
if
(
Info
.
recovery
)
"recovery"
else
""
)
{
fun
reboot
(
reason
:
String
=
if
(
Info
.
recovery
)
"recovery"
else
""
)
{
Shell
.
su
(
"/system/bin/svc power reboot $reason || /system/bin/reboot $reason"
).
submit
()
Shell
.
su
(
"/system/bin/svc power reboot $reason || /system/bin/reboot $reason"
).
submit
()
}
}
fun
File
.
suOutputStream
()
=
SuFileOutputStream
(
this
)
fun
File
.
suInputStream
()
=
SuFileInputStream
(
this
)
val
hasRoot
get
()
=
Shell
.
rootAccess
()
suspend
fun
Shell
.
Job
.
await
()
=
withContext
(
Dispatchers
.
IO
)
{
exec
()
}
suspend
fun
Shell
.
Job
.
await
()
=
withContext
(
Dispatchers
.
IO
)
{
exec
()
}
app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt
View file @
6348d0a6
...
@@ -8,23 +8,22 @@ import com.topjohnwu.magisk.core.Const
...
@@ -8,23 +8,22 @@ import com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.base.BaseActivity
import
com.topjohnwu.magisk.core.base.BaseActivity
import
com.topjohnwu.magisk.core.model.module.Repo
import
com.topjohnwu.magisk.core.model.module.Repo
import
com.topjohnwu.magisk.core.utils.SafetyNetHelper
import
com.topjohnwu.magisk.core.utils.SafetyNetHelper
import
com.topjohnwu.magisk.data.
repository.MagiskRepository
import
com.topjohnwu.magisk.data.
network.GithubRawServices
import
com.topjohnwu.magisk.extensions.DynamicClassLoader
import
com.topjohnwu.magisk.extensions.DynamicClassLoader
import
com.topjohnwu.magisk.extensions.OnErrorListener
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.extensions.writeTo
import
com.topjohnwu.magisk.ui.safetynet.SafetyNetResult
import
com.topjohnwu.magisk.ui.safetynet.SafetyNetResult
import
com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.magisk.view.MarkDownWindow
import
com.topjohnwu.magisk.view.MarkDownWindow
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
dalvik.system.DexFile
import
dalvik.system.DexFile
import
io.reactivex.Completable
import
io.reactivex.subjects.PublishSubject
import
io.reactivex.subjects.PublishSubject
import
kotlinx.coroutines.*
import
org.json.JSONObject
import
org.json.JSONObject
import
org.koin.core.KoinComponent
import
org.koin.core.KoinComponent
import
org.koin.core.inject
import
org.koin.core.inject
import
timber.log.Timber
import
timber.log.Timber
import
java.io.File
import
java.io.File
import
java.io.IOException
import
java.lang.reflect.InvocationHandler
import
java.lang.reflect.InvocationHandler
/**
/**
...
@@ -38,11 +37,15 @@ abstract class ViewEvent {
...
@@ -38,11 +37,15 @@ abstract class ViewEvent {
var
handled
=
false
var
handled
=
false
}
}
abstract
class
ViewEventsWithScope
:
ViewEvent
()
{
lateinit
var
scope
:
CoroutineScope
}
class
CheckSafetyNetEvent
(
class
CheckSafetyNetEvent
(
private
val
callback
:
(
SafetyNetResult
)
->
Unit
=
{}
private
val
callback
:
(
SafetyNetResult
)
->
Unit
=
{}
)
:
ViewEvent
(),
ContextExecutor
,
KoinComponent
,
SafetyNetHelper
.
Callback
{
)
:
ViewEvent
sWithScope
(),
ContextExecutor
,
KoinComponent
,
SafetyNetHelper
.
Callback
{
private
val
magiskRepo
by
inject
<
MagiskRepository
>()
private
val
svc
by
inject
<
GithubRawServices
>()
private
lateinit
var
apk
:
File
private
lateinit
var
apk
:
File
private
lateinit
var
dex
:
File
private
lateinit
var
dex
:
File
...
@@ -51,16 +54,21 @@ class CheckSafetyNetEvent(
...
@@ -51,16 +54,21 @@ class CheckSafetyNetEvent(
apk
=
File
(
"${context.filesDir.parent}/snet"
,
"snet.jar"
)
apk
=
File
(
"${context.filesDir.parent}/snet"
,
"snet.jar"
)
dex
=
File
(
apk
.
parent
,
"snet.dex"
)
dex
=
File
(
apk
.
parent
,
"snet.dex"
)
scope
.
launch
{
attest
(
context
)
{
attest
(
context
)
{
// Download and retry
// Download and retry
withContext
(
Dispatchers
.
IO
)
{
Shell
.
sh
(
"rm -rf "
+
apk
.
parent
).
exec
()
Shell
.
sh
(
"rm -rf "
+
apk
.
parent
).
exec
()
apk
.
parentFile
?.
mkdir
()
apk
.
parentFile
?.
mkdir
()
}
download
(
context
,
true
)
download
(
context
,
true
)
}
}
}
}
}
private
fun
attest
(
context
:
Context
,
onError
:
OnErrorListener
)
{
private
suspend
fun
attest
(
context
:
Context
,
onError
:
suspend
(
Exception
)
->
Unit
)
{
Completable
.
fromAction
{
try
{
val
helper
=
withContext
(
Dispatchers
.
IO
)
{
val
loader
=
DynamicClassLoader
(
apk
)
val
loader
=
DynamicClassLoader
(
apk
)
val
dex
=
DexFile
.
loadDex
(
apk
.
path
,
dex
.
path
,
0
)
val
dex
=
DexFile
.
loadDex
(
apk
.
path
,
dex
.
path
,
0
)
...
@@ -79,23 +87,39 @@ class CheckSafetyNetEvent(
...
@@ -79,23 +87,39 @@ class CheckSafetyNetEvent(
val
helper
=
helperClass
val
helper
=
helperClass
.
getMethod
(
"get"
,
Class
::
class
.
java
,
Context
::
class
.
java
,
Any
::
class
.
java
)
.
getMethod
(
"get"
,
Class
::
class
.
java
,
Context
::
class
.
java
,
Any
::
class
.
java
)
.
invoke
(
null
,
SafetyNetHelper
::
class
.
java
,
context
,
this
)
as
SafetyNetHelper
.
invoke
(
null
,
SafetyNetHelper
::
class
.
java
,
context
,
this
@CheckSafetyNetEvent
)
as
SafetyNetHelper
if
(
helper
.
version
<
Const
.
SNET_EXT_VER
)
if
(
helper
.
version
<
Const
.
SNET_EXT_VER
)
throw
Exception
()
throw
Exception
()
helper
}
helper
.
attest
()
helper
.
attest
()
}.
subscribeK
(
onError
=
onError
)
}
catch
(
e
:
Exception
)
{
if
(
e
is
CancellationException
)
throw
e
onError
(
e
)
}
}
}
@Suppress
(
"SameParameterValue"
)
@Suppress
(
"SameParameterValue"
)
private
fun
download
(
context
:
Context
,
askUser
:
Boolean
)
{
private
fun
download
(
context
:
Context
,
askUser
:
Boolean
)
{
fun
downloadInternal
()
=
magiskRepo
.
fetchSafetynet
()
fun
downloadInternal
()
=
scope
.
launch
{
.
map
{
it
.
byteStream
().
writeTo
(
apk
)
}
val
abort
:
suspend
(
Exception
)
->
Unit
=
{
.
subscribeK
{
attest
(
context
)
{
Timber
.
e
(
it
)
Timber
.
e
(
it
)
callback
(
SafetyNetResult
())
callback
(
SafetyNetResult
())
}
}
}
try
{
withContext
(
Dispatchers
.
IO
)
{
svc
.
fetchSafetynet
().
byteStream
().
writeTo
(
apk
)
}
attest
(
context
,
abort
)
}
catch
(
e
:
IOException
)
{
if
(
e
is
CancellationException
)
throw
e
abort
(
e
)
}
}
if
(!
askUser
)
{
if
(!
askUser
)
{
downloadInternal
()
downloadInternal
()
...
@@ -126,25 +150,10 @@ class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), Activi
...
@@ -126,25 +150,10 @@ class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), Activi
override
fun
invoke
(
activity
:
BaseActivity
)
=
activity
.
run
(
action
)
override
fun
invoke
(
activity
:
BaseActivity
)
=
activity
.
run
(
action
)
}
}
class
OpenChangelogEvent
(
val
item
:
Repo
)
:
ViewEvent
(),
ContextExecutor
{
class
OpenChangelogEvent
(
val
item
:
Repo
)
:
ViewEvent
sWithScope
(),
ContextExecutor
{
override
fun
invoke
(
context
:
Context
)
{
override
fun
invoke
(
context
:
Context
)
{
MarkDownWindow
.
show
(
context
,
null
,
item
.
readme
)
scope
.
launch
{
}
MarkDownWindow
.
show
(
context
,
null
,
item
::
readme
)
}
class
RxPermissionEvent
(
val
permissions
:
List
<
String
>,
val
callback
:
PublishSubject
<
Boolean
>
)
:
ViewEvent
(),
ActivityExecutor
{
override
fun
invoke
(
activity
:
BaseActivity
)
=
activity
.
withPermissions
(*
permissions
.
toTypedArray
())
{
onSuccess
{
callback
.
onNext
(
true
)
}
onFailure
{
callback
.
onNext
(
false
)
callback
.
onError
(
SecurityException
(
"User refused permissions"
))
}
}
}
}
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/model/events/dialog/ManagerInstallDialog.kt
View file @
6348d0a6
...
@@ -8,6 +8,9 @@ import com.topjohnwu.magisk.model.entity.internal.Configuration
...
@@ -8,6 +8,9 @@ import com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.magisk.view.MagiskDialog
import
com.topjohnwu.magisk.view.MarkDownWindow
import
com.topjohnwu.magisk.view.MarkDownWindow
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
class
ManagerInstallDialog
:
DialogEvent
()
{
class
ManagerInstallDialog
:
DialogEvent
()
{
...
@@ -28,7 +31,11 @@ class ManagerInstallDialog : DialogEvent() {
...
@@ -28,7 +31,11 @@ class ManagerInstallDialog : DialogEvent() {
if
(
Info
.
remote
.
app
.
note
.
isEmpty
())
return
if
(
Info
.
remote
.
app
.
note
.
isEmpty
())
return
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
titleRes
=
R
.
string
.
app_changelog
titleRes
=
R
.
string
.
app_changelog
onClick
{
MarkDownWindow
.
show
(
context
,
null
,
Info
.
remote
.
app
.
note
)
}
onClick
{
GlobalScope
.
launch
(
Dispatchers
.
Main
.
immediate
)
{
MarkDownWindow
.
show
(
context
,
null
,
Info
.
remote
.
app
.
note
)
}
}
}
}
}
}
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt
View file @
6348d0a6
...
@@ -10,6 +10,7 @@ import androidx.databinding.PropertyChangeRegistry
...
@@ -10,6 +10,7 @@ import androidx.databinding.PropertyChangeRegistry
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.MutableLiveData
import
androidx.lifecycle.MutableLiveData
import
androidx.lifecycle.ViewModel
import
androidx.lifecycle.ViewModel
import
androidx.lifecycle.viewModelScope
import
androidx.navigation.NavDirections
import
androidx.navigation.NavDirections
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.Info
...
@@ -96,6 +97,11 @@ abstract class BaseViewModel(
...
@@ -96,6 +97,11 @@ abstract class BaseViewModel(
_viewEvents
.
postValue
(
this
)
_viewEvents
.
postValue
(
this
)
}
}
fun
<
Event
:
ViewEventsWithScope
>
Event
.
publish
()
{
scope
=
viewModelScope
_viewEvents
.
postValue
(
this
)
}
fun
Int
.
publish
()
{
fun
Int
.
publish
()
{
_viewEvents
.
postValue
(
SimpleViewEvent
(
this
))
_viewEvents
.
postValue
(
SimpleViewEvent
(
this
))
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
View file @
6348d0a6
...
@@ -42,7 +42,7 @@ class FlashViewModel(
...
@@ -42,7 +42,7 @@ class FlashViewModel(
private
val
logItems
=
Collections
.
synchronizedList
(
mutableListOf
<
String
>())
private
val
logItems
=
Collections
.
synchronizedList
(
mutableListOf
<
String
>())
init
{
init
{
outItems
.
sendUpdatesTo
(
items
)
{
it
.
map
{
ConsoleItem
(
it
)
}
}
outItems
.
sendUpdatesTo
(
items
,
viewModelScope
)
{
it
.
map
{
ConsoleItem
(
it
)
}
}
outItems
.
copyNewInputInto
(
logItems
)
outItems
.
copyNewInputInto
(
logItems
)
args
.
dismissId
.
takeIf
{
it
!=
-
1
}
?.
also
{
args
.
dismissId
.
takeIf
{
it
!=
-
1
}
?.
also
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/install/InstallFragment.kt
View file @
6348d0a6
package
com.topjohnwu.magisk.ui.install
package
com.topjohnwu.magisk.ui.install
import
android.content.Intent
import
android.content.Intent
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import
com.topjohnwu.magisk.databinding.FragmentInstallMd2Binding
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.extensions.value
...
@@ -21,6 +22,9 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
...
@@ -21,6 +22,9 @@ class InstallFragment : BaseUIFragment<InstallViewModel, FragmentInstallMd2Bindi
override
fun
onStart
()
{
override
fun
onStart
()
{
super
.
onStart
()
super
.
onStart
()
requireActivity
().
setTitle
(
R
.
string
.
install
)
requireActivity
().
setTitle
(
R
.
string
.
install
)
// Allow markwon to run in viewmodel scope
binding
.
releaseNotes
.
tag
=
viewModel
.
viewModelScope
}
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/install/InstallViewModel.kt
View file @
6348d0a6
package
com.topjohnwu.magisk.ui.install
package
com.topjohnwu.magisk.ui.install
import
android.net.Uri
import
android.net.Uri
import
android.text.SpannableString
import
android.text.Spanned
import
android.widget.Toast
import
android.widget.Toast
import
androidx.databinding.ObservableField
import
androidx.databinding.ObservableField
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.Info
import
com.topjohnwu.magisk.core.download.DownloadService
import
com.topjohnwu.magisk.core.download.DownloadService
...
@@ -12,7 +11,6 @@ import com.topjohnwu.magisk.core.download.RemoteFileService
...
@@ -12,7 +11,6 @@ import com.topjohnwu.magisk.core.download.RemoteFileService
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
import
com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
...
@@ -20,13 +18,12 @@ import com.topjohnwu.magisk.model.events.RequestFileEvent
...
@@ -20,13 +18,12 @@ import com.topjohnwu.magisk.model.events.RequestFileEvent
import
com.topjohnwu.magisk.model.events.dialog.SecondSlotWarningDialog
import
com.topjohnwu.magisk.model.events.dialog.SecondSlotWarningDialog
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
io.noties.markwon.Markwon
import
kotlinx.coroutines.launch
import
org.koin.core.get
import
org.koin.core.get
import
kotlin.math.roundToInt
import
kotlin.math.roundToInt
class
InstallViewModel
(
class
InstallViewModel
(
stringRepo
:
StringRepository
,
stringRepo
:
StringRepository
markwon
:
Markwon
)
:
BaseViewModel
(
State
.
LOADED
)
{
)
:
BaseViewModel
(
State
.
LOADED
)
{
val
isRooted
get
()
=
Shell
.
rootAccess
()
val
isRooted
get
()
=
Shell
.
rootAccess
()
...
@@ -35,8 +32,8 @@ class InstallViewModel(
...
@@ -35,8 +32,8 @@ class InstallViewModel(
val
step
=
ObservableField
(
0
)
val
step
=
ObservableField
(
0
)
val
method
=
ObservableField
(-
1
)
val
method
=
ObservableField
(-
1
)
val
progress
=
ObservableField
(
0
)
val
progress
=
ObservableField
(
0
)
val
data
=
ObservableField
<
Uri
?>(
null
)
val
data
=
ObservableField
(
null
as
Uri
?
)
val
notes
=
ObservableField
<
Spanned
>(
SpannableString
(
""
)
)
val
notes
=
ObservableField
(
""
)
init
{
init
{
RemoteFileService
.
reset
()
RemoteFileService
.
reset
()
...
@@ -50,10 +47,8 @@ class InstallViewModel(
...
@@ -50,10 +47,8 @@ class InstallViewModel(
state
=
State
.
LOADED
state
=
State
.
LOADED
}
}
}
}
stringRepo
.
getString
(
Info
.
remote
.
magisk
.
note
).
map
{
viewModelScope
.
launch
{
markwon
.
toMarkdown
(
it
)
notes
.
value
=
stringRepo
.
getString
(
Info
.
remote
.
magisk
.
note
)
}.
subscribeK
{
notes
.
value
=
it
}
}
method
.
addOnPropertyChangedCallback
{
method
.
addOnPropertyChangedCallback
{
when
(
it
!!
)
{
when
(
it
!!
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/safetynet/SafetynetViewModel.kt
View file @
6348d0a6
...
@@ -50,7 +50,7 @@ class SafetynetViewModel : BaseViewModel() {
...
@@ -50,7 +50,7 @@ class SafetynetViewModel : BaseViewModel() {
private
fun
attest
()
{
private
fun
attest
()
{
currentState
=
LOADING
currentState
=
LOADING
CheckSafetyNetEvent
{
CheckSafetyNetEvent
()
{
resolveResponse
(
it
)
resolveResponse
(
it
)
}.
publish
()
}.
publish
()
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt
View file @
6348d0a6
...
@@ -16,10 +16,11 @@ import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding
...
@@ -16,10 +16,11 @@ import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding
import
com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
import
com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding
import
com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
import
com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding
import
com.topjohnwu.magisk.extensions.get
import
com.topjohnwu.magisk.extensions.get
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.model.entity.recycler.SettingsItem
import
com.topjohnwu.magisk.model.entity.recycler.SettingsItem
import
com.topjohnwu.magisk.utils.asTransitive
import
com.topjohnwu.magisk.utils.asTransitive
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.launch
import
java.io.File
import
java.io.File
// --- Customization
// --- Customization
...
@@ -38,8 +39,9 @@ object Language : SettingsItem.Selector() {
...
@@ -38,8 +39,9 @@ object Language : SettingsItem.Selector() {
override
var
entries
=
emptyArray
<
String
>()
override
var
entries
=
emptyArray
<
String
>()
override
var
entryValues
=
emptyArray
<
String
>()
override
var
entryValues
=
emptyArray
<
String
>()
init
{
suspend
fun
loadLanguages
(
scope
:
CoroutineScope
)
{
availableLocales
.
subscribeK
{
(
names
,
values
)
->
scope
.
launch
{
availableLocales
().
let
{
(
names
,
values
)
->
entries
=
names
entries
=
names
entryValues
=
values
entryValues
=
values
val
selectedLocale
=
currentLocale
.
getDisplayName
(
currentLocale
)
val
selectedLocale
=
currentLocale
.
getDisplayName
(
currentLocale
)
...
@@ -47,6 +49,7 @@ object Language : SettingsItem.Selector() {
...
@@ -47,6 +49,7 @@ object Language : SettingsItem.Selector() {
notifyChange
(
BR
.
selectedEntry
)
notifyChange
(
BR
.
selectedEntry
)
}
}
}
}
}
}
}
object
Theme
:
SettingsItem
.
Blank
()
{
object
Theme
:
SettingsItem
.
Blank
()
{
...
...
app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsViewModel.kt
View file @
6348d0a6
package
com.topjohnwu.magisk.ui.settings
package
com.topjohnwu.magisk.ui.settings
import
android.Manifest
import
android.os.Build
import
android.os.Build
import
android.view.View
import
android.view.View
import
android.widget.Toast
import
android.widget.Toast
import
androidx.lifecycle.viewModelScope
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.core.Const
import
com.topjohnwu.magisk.core.Const
...
@@ -12,21 +12,18 @@ import com.topjohnwu.magisk.core.download.DownloadService
...
@@ -12,21 +12,18 @@ import com.topjohnwu.magisk.core.download.DownloadService
import
com.topjohnwu.magisk.core.utils.PatchAPK
import
com.topjohnwu.magisk.core.utils.PatchAPK
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.core.utils.Utils
import
com.topjohnwu.magisk.data.database.RepoDao
import
com.topjohnwu.magisk.data.database.RepoDao
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.extensions.value
import
com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.Configuration
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import
com.topjohnwu.magisk.model.entity.recycler.SettingsItem
import
com.topjohnwu.magisk.model.entity.recycler.SettingsItem
import
com.topjohnwu.magisk.model.events.RecreateEvent
import
com.topjohnwu.magisk.model.events.RecreateEvent
import
com.topjohnwu.magisk.model.events.RxPermissionEvent
import
com.topjohnwu.magisk.model.events.dialog.BiometricDialog
import
com.topjohnwu.magisk.model.events.dialog.BiometricDialog
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.BaseViewModel
import
com.topjohnwu.magisk.ui.base.adapterOf
import
com.topjohnwu.magisk.ui.base.adapterOf
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.diffListOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.magisk.ui.base.itemBindingOf
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Completable
import
kotlinx.coroutines.launch
import
io.reactivex.subjects.PublishSubject
import
org.koin.core.get
import
org.koin.core.get
class
SettingsViewModel
(
class
SettingsViewModel
(
...
@@ -48,6 +45,9 @@ class SettingsViewModel(
...
@@ -48,6 +45,9 @@ class SettingsViewModel(
// making theming a pain in the ass. Just forget about it
// making theming a pain in the ass. Just forget about it
list
.
remove
(
Theme
)
list
.
remove
(
Theme
)
}
}
viewModelScope
.
launch
{
Language
.
loadLanguages
(
this
)
}
// Manager
// Manager
list
.
addAll
(
listOf
(
list
.
addAll
(
listOf
(
...
@@ -125,8 +125,10 @@ class SettingsViewModel(
...
@@ -125,8 +125,10 @@ class SettingsViewModel(
}
}
private
fun
clearRepoCache
()
{
private
fun
clearRepoCache
()
{
Completable
.
fromAction
{
repositoryDao
.
clear
()
}
viewModelScope
.
launch
{
.
subscribeK
{
Utils
.
toast
(
R
.
string
.
repo_cache_cleared
,
Toast
.
LENGTH_SHORT
)
}
repositoryDao
.
clear
()
Utils
.
toast
(
R
.
string
.
repo_cache_cleared
,
Toast
.
LENGTH_SHORT
)
}
}
}
private
fun
createHosts
()
{
private
fun
createHosts
()
{
...
@@ -136,14 +138,7 @@ class SettingsViewModel(
...
@@ -136,14 +138,7 @@ class SettingsViewModel(
}
}
private
fun
requireRWPermission
()
{
private
fun
requireRWPermission
()
{
val
callback
=
PublishSubject
.
create
<
Boolean
>()
withExternalRW
{
if
(!
it
)
requireRWPermission
()
}
callback
.
subscribeK
{
if
(!
it
)
requireRWPermission
()
}
RxPermissionEvent
(
listOf
(
Manifest
.
permission
.
READ_EXTERNAL_STORAGE
,
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
),
callback
).
publish
()
}
}
private
fun
updateManager
(
hide
:
Boolean
)
{
private
fun
updateManager
(
hide
:
Boolean
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt
View file @
6348d0a6
...
@@ -22,11 +22,8 @@ import com.google.android.material.chip.Chip
...
@@ -22,11 +22,8 @@ import com.google.android.material.chip.Chip
import
com.google.android.material.textfield.TextInputLayout
import
com.google.android.material.textfield.TextInputLayout
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.extensions.replaceRandomWithSpecial
import
com.topjohnwu.magisk.extensions.replaceRandomWithSpecial
import
com.topjohnwu.magisk.extensions.subscribeK
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
com.topjohnwu.superuser.internal.UiThreadHandler
import
io.reactivex.Observable
import
kotlinx.coroutines.*
import
io.reactivex.disposables.Disposable
import
java.util.concurrent.TimeUnit
import
kotlin.math.roundToInt
import
kotlin.math.roundToInt
...
@@ -42,14 +39,15 @@ fun setImageResource(view: AppCompatImageView, @DrawableRes resId: Int) {
...
@@ -42,14 +39,15 @@ fun setImageResource(view: AppCompatImageView, @DrawableRes resId: Int) {
@BindingAdapter
(
"movieBehavior"
,
"movieBehaviorText"
)
@BindingAdapter
(
"movieBehavior"
,
"movieBehaviorText"
)
fun
setMovieBehavior
(
view
:
TextView
,
isMovieBehavior
:
Boolean
,
text
:
String
)
{
fun
setMovieBehavior
(
view
:
TextView
,
isMovieBehavior
:
Boolean
,
text
:
String
)
{
(
view
.
tag
as
?
Disposable
)
?.
dispose
()
(
view
.
tag
as
?
Job
)
?.
cancel
()
view
.
tag
=
null
if
(
isMovieBehavior
)
{
if
(
isMovieBehavior
)
{
v
al
observer
=
Observable
v
iew
.
tag
=
GlobalScope
.
launch
(
Dispatchers
.
Main
.
immediate
)
{
.
interval
(
150
,
TimeUnit
.
MILLISECONDS
)
while
(
true
)
{
.
subscribeK
{
delay
(
150
)
view
.
text
=
text
.
replaceRandomWithSpecial
()
view
.
text
=
text
.
replaceRandomWithSpecial
()
}
}
view
.
tag
=
observer
}
}
else
{
}
else
{
view
.
text
=
text
view
.
text
=
text
}
}
...
...
app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt
View file @
6348d0a6
...
@@ -28,7 +28,7 @@ import com.topjohnwu.magisk.ui.base.itemBindingOf
...
@@ -28,7 +28,7 @@ import com.topjohnwu.magisk.ui.base.itemBindingOf
import
me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters
import
me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters
import
me.tatarka.bindingcollectionadapter2.ItemBinding
import
me.tatarka.bindingcollectionadapter2.ItemBinding
class
MagiskDialog
@JvmOverloads
constructor
(
class
MagiskDialog
(
context
:
Context
,
theme
:
Int
=
0
context
:
Context
,
theme
:
Int
=
0
)
:
AppCompatDialog
(
context
,
theme
)
{
)
:
AppCompatDialog
(
context
,
theme
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.kt
View file @
6348d0a6
package
com.topjohnwu.magisk.view
package
com.topjohnwu.magisk.view
import
android.content.Context
import
android.content.Context
import
android.text.Spanned
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.widget.TextView
import
android.widget.TextView
import
androidx.core.text.PrecomputedTextCompat
import
androidx.core.widget.TextViewCompat
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.data.repository.StringRepository
import
com.topjohnwu.magisk.extensions.subscribeK
import
io.noties.markwon.Markwon
import
io.noties.markwon.Markwon
import
io.reactivex.Completable
import
kotlinx.coroutines.*
import
io.reactivex.Single
import
org.koin.core.KoinComponent
import
org.koin.core.KoinComponent
import
org.koin.core.inject
import
org.koin.core.inject
import
timber.log.Timber
import
timber.log.Timber
import
java.io.InputStream
import
kotlin.coroutines.coroutineContext
import
java.util.*
class
PrecomputedTextSetter
:
Markwon
.
TextSetter
{
override
fun
setText
(
tv
:
TextView
,
text
:
Spanned
,
bufferType
:
TextView
.
BufferType
,
onComplete
:
Runnable
)
{
val
scope
=
tv
.
tag
as
?
CoroutineScope
?:
GlobalScope
scope
.
launch
(
Dispatchers
.
Default
)
{
val
pre
=
PrecomputedTextCompat
.
create
(
text
,
TextViewCompat
.
getTextMetricsParams
(
tv
))
tv
.
post
{
TextViewCompat
.
setPrecomputedText
(
tv
,
pre
)
onComplete
.
run
()
}
}
}
}
object
MarkDownWindow
:
KoinComponent
{
object
MarkDownWindow
:
KoinComponent
{
private
val
stringR
epo
:
StringRepository
by
inject
()
private
val
r
epo
:
StringRepository
by
inject
()
private
val
markwon
:
Markwon
by
inject
()
private
val
markwon
:
Markwon
by
inject
()
fun
show
(
activity
:
Context
,
title
:
String
?,
url
:
String
)
{
suspend
fun
show
(
activity
:
Context
,
title
:
String
?,
url
:
String
)
{
show
(
activity
,
title
,
stringRepo
.
getString
(
url
))
show
(
activity
,
title
)
{
}
repo
.
getString
(
url
)
fun
show
(
activity
:
Context
,
title
:
String
?,
input
:
InputStream
)
{
Single
.
just
(
Scanner
(
input
,
"UTF-8"
).
apply
{
useDelimiter
(
"\\A"
)
})
.
map
{
it
.
next
()
}
.
also
{
show
(
activity
,
title
,
it
)
}
}
}
}
fun
show
(
activity
:
Context
,
title
:
String
?,
content
:
Single
<
String
>
)
{
suspend
fun
show
(
activity
:
Context
,
title
:
String
?,
input
:
suspend
()
->
String
)
{
val
mdRes
=
R
.
layout
.
markdown_window_md2
val
mdRes
=
R
.
layout
.
markdown_window_md2
val
mv
=
LayoutInflater
.
from
(
activity
).
inflate
(
mdRes
,
null
)
val
mv
=
LayoutInflater
.
from
(
activity
).
inflate
(
mdRes
,
null
)
val
tv
=
mv
.
findViewById
<
TextView
>(
R
.
id
.
md_txt
)
val
tv
=
mv
.
findViewById
<
TextView
>(
R
.
id
.
md_txt
)
tv
.
tag
=
CoroutineScope
(
coroutineContext
)
content
.
map
{
try
{
markwon
.
setMarkdown
(
tv
,
it
)
markwon
.
setMarkdown
(
tv
,
input
())
}.
ignoreElement
().
onErrorResumeNext
{
}
catch
(
e
:
Exception
)
{
// Nothing we can actually do other than show error message
if
(
e
is
CancellationException
)
Timber
.
e
(
it
)
throw
e
Timber
.
e
(
e
)
tv
.
setText
(
R
.
string
.
download_file_error
)
tv
.
setText
(
R
.
string
.
download_file_error
)
Completable
.
complete
()
}
}.
subscribeK
{
MagiskDialog
(
activity
)
MagiskDialog
(
activity
)
.
applyTitle
(
title
?:
""
)
.
applyTitle
(
title
?:
""
)
.
applyView
(
mv
)
.
applyView
(
mv
)
...
@@ -52,7 +62,5 @@ object MarkDownWindow : KoinComponent {
...
@@ -52,7 +62,5 @@ object MarkDownWindow : KoinComponent {
titleRes
=
android
.
R
.
string
.
cancel
titleRes
=
android
.
R
.
string
.
cancel
}
}
.
reveal
()
.
reveal
()
return
@subscribeK
}
}
}
}
}
app/src/main/res/layout/fragment_install_md2.xml
View file @
6348d0a6
...
@@ -228,11 +228,12 @@
...
@@ -228,11 +228,12 @@
app:strokeWidth=
"@{viewModel.step != 0 ? 0f : @dimen/l_125}"
>
app:strokeWidth=
"@{viewModel.step != 0 ? 0f : @dimen/l_125}"
>
<TextView
<TextView
android:id=
"@+id/release_notes"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_margin=
"15dp"
android:layout_margin=
"15dp"
android:textAppearance=
"@style/AppearanceFoundation.Caption"
android:textAppearance=
"@style/AppearanceFoundation.Caption"
android:t
ext=
"@{viewModel.notes}"
/>
markdownT
ext=
"@{viewModel.notes}"
/>
</com.google.android.material.card.MaterialCardView>
</com.google.android.material.card.MaterialCardView>
...
...
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