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
2813d203
Commit
2813d203
authored
May 02, 2019
by
topjohnwu
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'WIP'
parents
4040a024
db218407
Show whitespace changes
Inline
Side-by-side
Showing
180 changed files
with
6898 additions
and
4990 deletions
+6898
-4990
build.gradle
app/build.gradle
+25
-11
proguard-rules.pro
app/proguard-rules.pro
+5
-0
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+4
-4
App.java
app/src/main/java/com/topjohnwu/magisk/App.java
+0
-101
App.kt
app/src/main/java/com/topjohnwu/magisk/App.kt
+129
-0
Config.java
app/src/main/java/com/topjohnwu/magisk/Config.java
+24
-24
Const.java
app/src/main/java/com/topjohnwu/magisk/Const.java
+3
-0
ApplicationModule.kt
...rc/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt
+20
-0
DatabaseModule.kt
app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt
+12
-0
MiscModule.kt
app/src/main/java/com/topjohnwu/magisk/di/MiscModule.kt
+10
-0
Modules.kt
app/src/main/java/com/topjohnwu/magisk/di/Modules.kt
+10
-0
NamedInjection.kt
app/src/main/java/com/topjohnwu/magisk/di/NamedInjection.kt
+5
-0
NetworkingModule.kt
...src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt
+6
-0
RepositoryModule.kt
...src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt
+6
-0
ViewModelsModule.kt
...src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt
+30
-0
ApplicationAdapter.java
...m/topjohnwu/magisk/model/adapters/ApplicationAdapter.java
+0
-427
ModulesAdapter.java
...a/com/topjohnwu/magisk/model/adapters/ModulesAdapter.java
+0
-127
PolicyAdapter.java
...va/com/topjohnwu/magisk/model/adapters/PolicyAdapter.java
+0
-172
ReposAdapter.java
...ava/com/topjohnwu/magisk/model/adapters/ReposAdapter.java
+0
-246
SectionedAdapter.java
...com/topjohnwu/magisk/model/adapters/SectionedAdapter.java
+0
-96
StringListAdapter.java
...om/topjohnwu/magisk/model/adapters/StringListAdapter.java
+0
-103
SuLogAdapter.java
...ava/com/topjohnwu/magisk/model/adapters/SuLogAdapter.java
+0
-144
TabFragmentAdapter.java
...m/topjohnwu/magisk/model/adapters/TabFragmentAdapter.java
+0
-41
DownloadModuleService.java
...opjohnwu/magisk/model/download/DownloadModuleService.java
+2
-2
BaseModule.java
...in/java/com/topjohnwu/magisk/model/entity/BaseModule.java
+2
-2
HideAppInfo.kt
...ain/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt
+16
-0
HideTarget.kt
...main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt
+10
-0
Policy.java
...c/main/java/com/topjohnwu/magisk/model/entity/Policy.java
+2
-2
ConsoleRvItem.kt
...m/topjohnwu/magisk/model/entity/recycler/ConsoleRvItem.kt
+11
-0
HideRvItem.kt
.../com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt
+92
-0
LogRvItem.kt
...a/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt
+75
-0
ModuleRvItem.kt
...om/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt
+66
-0
PolicyRvItem.kt
...om/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt
+47
-0
SectionRvItem.kt
...m/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt
+11
-0
SpinnerRvItem.kt
...m/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt
+13
-0
IndeterminateState.kt
...topjohnwu/magisk/model/entity/state/IndeterminateState.kt
+5
-0
RxEvents.kt
...c/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt
+16
-0
ViewEvents.kt
...main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt
+40
-0
FlashResultListener.kt
...a/com/topjohnwu/magisk/model/flash/FlashResultListener.kt
+7
-0
Flashing.kt
...rc/main/java/com/topjohnwu/magisk/model/flash/Flashing.kt
+62
-0
Patching.kt
...rc/main/java/com/topjohnwu/magisk/model/flash/Patching.kt
+51
-0
MagiskNavigationEvent.kt
...opjohnwu/magisk/model/navigation/MagiskNavigationEvent.kt
+84
-0
Navigation.kt
.../java/com/topjohnwu/magisk/model/navigation/Navigation.kt
+57
-0
Navigator.kt
...n/java/com/topjohnwu/magisk/model/navigation/Navigator.kt
+13
-0
Observer.kt
...main/java/com/topjohnwu/magisk/model/observer/Observer.kt
+31
-0
PermissionRequestBuilder.kt
...hnwu/magisk/model/permissions/PermissionRequestBuilder.kt
+40
-0
GeneralReceiver.java
.../com/topjohnwu/magisk/model/receiver/GeneralReceiver.java
+2
-2
DelegateWorker.java
...ava/com/topjohnwu/magisk/model/worker/DelegateWorker.java
+6
-6
UpdateRepos.java
...src/main/java/com/topjohnwu/magisk/tasks/UpdateRepos.java
+9
-9
MainActivity.java
app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.java
+0
-194
MainActivity.kt
app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
+104
-0
MainViewModel.kt
app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt
+27
-0
SplashActivity.java
...src/main/java/com/topjohnwu/magisk/ui/SplashActivity.java
+0
-97
SplashActivity.kt
app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt
+87
-0
ActivityResultListener.kt
...va/com/topjohnwu/magisk/ui/base/ActivityResultListener.kt
+7
-0
BaseActivity.java
.../main/java/com/topjohnwu/magisk/ui/base/BaseActivity.java
+0
-159
BaseFragment.java
.../main/java/com/topjohnwu/magisk/ui/base/BaseFragment.java
+0
-58
BasePreferenceFragment.java
.../com/topjohnwu/magisk/ui/base/BasePreferenceFragment.java
+7
-7
IBaseLeanback.kt
...c/main/java/com/topjohnwu/magisk/ui/base/IBaseLeanback.kt
+11
-0
MagiskActivity.kt
.../main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt
+191
-0
MagiskFragment.kt
.../main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt
+52
-0
MagiskLeanbackActivity.kt
...va/com/topjohnwu/magisk/ui/base/MagiskLeanbackActivity.kt
+64
-0
MagiskViewModel.kt
...main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt
+30
-0
FlashActivity.java
...ain/java/com/topjohnwu/magisk/ui/flash/FlashActivity.java
+0
-274
FlashActivity.kt
.../main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt
+24
-0
FlashViewModel.kt
...main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
+104
-0
HideViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
+123
-0
MagiskHideFragment.java
...java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.java
+0
-101
MagiskHideFragment.kt
...n/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt
+64
-0
HomeViewModel.kt
...c/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
+232
-0
MagiskFragment.java
...ain/java/com/topjohnwu/magisk/ui/home/MagiskFragment.java
+0
-333
MagiskFragment.kt
.../main/java/com/topjohnwu/magisk/ui/home/MagiskFragment.kt
+98
-0
MagiskItem.kt
app/src/main/java/com/topjohnwu/magisk/ui/home/MagiskItem.kt
+6
-0
MagiskState.kt
...src/main/java/com/topjohnwu/magisk/ui/home/MagiskState.kt
+6
-0
SafetyNetState.kt
.../main/java/com/topjohnwu/magisk/ui/home/SafetyNetState.kt
+5
-0
LogFragment.java
...rc/main/java/com/topjohnwu/magisk/ui/log/LogFragment.java
+0
-47
LogFragment.kt
app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt
+53
-0
LogViewModel.kt
...src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt
+122
-0
MagiskLogFragment.java
...n/java/com/topjohnwu/magisk/ui/log/MagiskLogFragment.java
+0
-157
SuLogFragment.java
.../main/java/com/topjohnwu/magisk/ui/log/SuLogFragment.java
+0
-80
ModuleViewModel.kt
...in/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
+141
-0
ModulesFragment.java
.../java/com/topjohnwu/magisk/ui/module/ModulesFragment.java
+0
-142
ModulesFragment.kt
...in/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt
+115
-0
ReposFragment.java
...in/java/com/topjohnwu/magisk/ui/module/ReposFragment.java
+0
-102
ReposFragment.kt
...main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt
+104
-0
SettingsFragment.java
...va/com/topjohnwu/magisk/ui/settings/SettingsFragment.java
+21
-14
SuperuserFragment.java
.../com/topjohnwu/magisk/ui/superuser/SuperuserFragment.java
+0
-64
SuperuserFragment.kt
...va/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt
+25
-0
SuperuserViewModel.kt
...a/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt
+139
-0
SuRequestActivity.java
.../com/topjohnwu/magisk/ui/surequest/SuRequestActivity.java
+0
-287
SuRequestActivity.kt
...va/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt
+67
-0
SuRequestViewModel.kt
...a/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt
+251
-0
_SuRequestActivity.kt
...a/com/topjohnwu/magisk/ui/surequest/_SuRequestActivity.kt
+166
-0
_SuRequestViewModel.kt
.../com/topjohnwu/magisk/ui/surequest/_SuRequestViewModel.kt
+112
-0
DataBindingAdapters.kt
...in/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt
+144
-0
PatchAPK.java
app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java
+2
-2
SuLogger.java
app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.java
+2
-2
Utils.java
app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
+6
-6
XAndroid.kt
app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
+50
-0
XBinding.kt
app/src/main/java/com/topjohnwu/magisk/utils/XBinding.kt
+8
-0
XKoin.kt
app/src/main/java/com/topjohnwu/magisk/utils/XKoin.kt
+20
-0
XList.kt
app/src/main/java/com/topjohnwu/magisk/utils/XList.kt
+49
-0
XRx.kt
app/src/main/java/com/topjohnwu/magisk/utils/XRx.kt
+9
-0
XString.kt
app/src/main/java/com/topjohnwu/magisk/utils/XString.kt
+11
-0
XTime.kt
app/src/main/java/com/topjohnwu/magisk/utils/XTime.kt
+18
-0
Annotations.kt
...in/java/com/topjohnwu/magisk/utils/feature/Annotations.kt
+16
-0
ArrowExpandable.java
.../main/java/com/topjohnwu/magisk/view/ArrowExpandable.java
+0
-42
MagiskDialog.kt
app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt
+155
-0
MarkDownWindow.java
...c/main/java/com/topjohnwu/magisk/view/MarkDownWindow.java
+6
-7
Notifications.java
...rc/main/java/com/topjohnwu/magisk/view/Notifications.java
+4
-4
ProgressNotification.java
.../java/com/topjohnwu/magisk/view/ProgressNotification.java
+2
-2
SafetyNet.java
app/src/main/java/com/topjohnwu/magisk/view/SafetyNet.java
+38
-39
Shortcuts.java
app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.java
+2
-2
SnackbarMaker.java
...rc/main/java/com/topjohnwu/magisk/view/SnackbarMaker.java
+2
-2
UpdateCardHolder.java
...main/java/com/topjohnwu/magisk/view/UpdateCardHolder.java
+0
-59
CustomAlertDialog.java
.../com/topjohnwu/magisk/view/dialogs/CustomAlertDialog.java
+8
-9
EnvFixDialog.java
.../java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java
+2
-2
FingerprintAuthDialog.java
.../topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.java
+25
-7
InstallMethodDialog.java
...om/topjohnwu/magisk/view/dialogs/InstallMethodDialog.java
+8
-8
MagiskInstallDialog.java
...om/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.java
+3
-2
UninstallDialog.java
...va/com/topjohnwu/magisk/view/dialogs/UninstallDialog.java
+2
-2
ic_back.xml
app/src/main/res/drawable/ic_back.xml
+10
-0
ic_checked.xml
app/src/main/res/drawable/ic_checked.xml
+11
-0
ic_indeterminate.xml
app/src/main/res/drawable/ic_indeterminate.xml
+10
-0
ic_menu.xml
app/src/main/res/drawable/ic_menu.xml
+10
-0
ic_restart.xml
app/src/main/res/drawable/ic_restart.xml
+10
-0
ic_unchecked.xml
app/src/main/res/drawable/ic_unchecked.xml
+10
-0
activity_flash.xml
app/src/main/res/layout/activity_flash.xml
+142
-53
activity_main.xml
app/src/main/res/layout/activity_main.xml
+29
-26
activity_main_content.xml
app/src/main/res/layout/activity_main_content.xml
+46
-0
activity_request.xml
app/src/main/res/layout/activity_request.xml
+132
-101
activity_su_request.xml
app/src/main/res/layout/activity_su_request.xml
+17
-0
alert_dialog.xml
app/src/main/res/layout/alert_dialog.xml
+14
-10
dialog_magisk_base.xml
app/src/main/res/layout/dialog_magisk_base.xml
+276
-0
fragment_log.xml
app/src/main/res/layout/fragment_log.xml
+33
-20
fragment_magisk.xml
app/src/main/res/layout/fragment_magisk.xml
+240
-221
fragment_magisk_hide.xml
app/src/main/res/layout/fragment_magisk_hide.xml
+23
-12
fragment_modules.xml
app/src/main/res/layout/fragment_modules.xml
+41
-24
fragment_repos.xml
app/src/main/res/layout/fragment_repos.xml
+40
-18
fragment_superuser.xml
app/src/main/res/layout/fragment_superuser.xml
+49
-22
include_update_card.xml
app/src/main/res/layout/include_update_card.xml
+154
-0
item_console.xml
app/src/main/res/layout/item_console.xml
+26
-0
item_hide_app.xml
app/src/main/res/layout/item_hide_app.xml
+124
-0
item_hide_process.xml
app/src/main/res/layout/item_hide_process.xml
+51
-0
item_module.xml
app/src/main/res/layout/item_module.xml
+145
-0
item_page_log.xml
app/src/main/res/layout/item_page_log.xml
+37
-0
item_page_magisk_log.xml
app/src/main/res/layout/item_page_magisk_log.xml
+39
-0
item_policy.xml
app/src/main/res/layout/item_policy.xml
+211
-0
item_repo.xml
app/src/main/res/layout/item_repo.xml
+126
-0
item_section.xml
app/src/main/res/layout/item_section.xml
+33
-0
item_spinner.xml
app/src/main/res/layout/item_spinner.xml
+26
-0
item_superuser_log.xml
app/src/main/res/layout/item_superuser_log.xml
+84
-0
item_superuser_log_entry.xml
app/src/main/res/layout/item_superuser_log_entry.xml
+51
-44
list_item_console.xml
app/src/main/res/layout/list_item_console.xml
+0
-7
list_item_hide_app.xml
app/src/main/res/layout/list_item_hide_app.xml
+0
-100
list_item_module.xml
app/src/main/res/layout/list_item_module.xml
+0
-117
list_item_policy.xml
app/src/main/res/layout/list_item_policy.xml
+0
-216
list_item_repo.xml
app/src/main/res/layout/list_item_repo.xml
+0
-120
list_item_sulog_group.xml
app/src/main/res/layout/list_item_sulog_group.xml
+0
-28
toolbar.xml
app/src/main/res/layout/toolbar.xml
+4
-3
update_card.xml
app/src/main/res/layout/update_card.xml
+0
-3
drawer.xml
app/src/main/res/menu/drawer.xml
+6
-6
colors.xml
app/src/main/res/values-night/colors.xml
+18
-0
styles_new.xml
app/src/main/res/values-night/styles_new.xml
+17
-0
colors.xml
app/src/main/res/values/colors.xml
+27
-0
dimens.xml
app/src/main/res/values/dimens.xml
+17
-0
strings.xml
app/src/main/res/values/strings.xml
+3
-1
styles_new.xml
app/src/main/res/values/styles_new.xml
+59
-0
view_appearances.xml
app/src/main/res/values/view_appearances.xml
+45
-0
view_button_styles.xml
app/src/main/res/values/view_button_styles.xml
+60
-0
view_card_styles.xml
app/src/main/res/values/view_card_styles.xml
+20
-0
view_dialog_styles.xml
app/src/main/res/values/view_dialog_styles.xml
+11
-0
view_divider_styles.xml
app/src/main/res/values/view_divider_styles.xml
+18
-0
view_image_styles.xml
app/src/main/res/values/view_image_styles.xml
+27
-0
view_input_styles.xml
app/src/main/res/values/view_input_styles.xml
+15
-0
view_progress_styles.xml
app/src/main/res/values/view_progress_styles.xml
+10
-0
view_styles.xml
app/src/main/res/values/view_styles.xml
+6
-0
view_text_styles.xml
app/src/main/res/values/view_text_styles.xml
+68
-0
build.gradle
build.gradle
+6
-4
gradle-wrapper.properties
gradle/wrapper/gradle-wrapper.properties
+2
-2
No files found.
app/build.gradle
View file @
2813d203
apply
plugin:
'com.android.application'
apply
plugin:
'kotlin-android-extensions'
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android-extensions'
apply
plugin:
'kotlin-kapt'
kapt
{
...
...
@@ -16,6 +16,7 @@ android {
defaultConfig
{
applicationId
'com.topjohnwu.magisk'
vectorDrawables
.
useSupportLibrary
=
true
multiDexEnabled
true
versionName
configProps
[
'appVersion'
]
versionCode
configProps
[
'appVersionCode'
]
as
Integer
javaCompileOptions
{
...
...
@@ -30,6 +31,10 @@ android {
proguardFiles
getDefaultProguardFile
(
'proguard-android-optimize.txt'
),
'proguard-rules.pro'
}
}
dataBinding
{
enabled
=
true
}
}
dependencies
{
...
...
@@ -37,25 +42,19 @@ dependencies {
implementation
project
(
':net'
)
implementation
project
(
':shared'
)
implementation
project
(
':signing'
)
implementation
'com.github.topjohnwu:jtar:1.0.0'
implementation
'net.sourceforge.streamsupport:android-retrostreams:1.7.0'
implementation
'com.github.sevar83:indeterminate-checkbox:1.0.5'
implementation
'com.jakewharton.timber:timber:4.7.1'
implementation
'com.github.skoumalcz:teanity:0.3.3'
implementation
'com.ncapdevi:frag-nav:3.2.0'
def
markwonVersion
=
'3.0.0'
implementation
"ru.noties.markwon:core:${markwonVersion}"
implementation
"ru.noties.markwon:html:${markwonVersion}"
implementation
"ru.noties.markwon:image-svg:${markwonVersion}"
def
androidXVersion
=
"1.0.0"
implementation
'androidx.constraintlayout:constraintlayout:1.1.3'
implementation
'androidx.appcompat:appcompat:1.0.2'
implementation
"androidx.preference:preference:${androidXVersion}"
implementation
"androidx.recyclerview:recyclerview:${androidXVersion}"
implementation
"androidx.cardview:cardview:${androidXVersion}"
implementation
"com.google.android.material:material:${androidXVersion}"
implementation
'androidx.work:work-runtime:2.0.1'
implementation
'androidx.transition:transition:1.1.0-beta01'
def
libsuVersion
=
'2.5.0'
implementation
"com.github.topjohnwu.libsu:core:${libsuVersion}"
implementation
"com.github.topjohnwu.libsu:io:${libsuVersion}"
...
...
@@ -63,4 +62,19 @@ dependencies {
def
butterKnifeVersion
=
'10.1.0'
implementation
"com.jakewharton:butterknife-runtime:${butterKnifeVersion}"
kapt
"com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
def
koin
=
"2.0.0-rc-2"
implementation
"org.koin:koin-core:${koin}"
implementation
"org.koin:koin-android:${koin}"
implementation
"org.koin:koin-androidx-viewmodel:${koin}"
implementation
'androidx.constraintlayout:constraintlayout:1.1.3'
implementation
'androidx.appcompat:appcompat:1.0.2'
implementation
'androidx.preference:preference:1.0.0'
implementation
'androidx.recyclerview:recyclerview:1.1.0-alpha04'
implementation
'androidx.cardview:cardview:1.0.0'
implementation
'com.google.android.material:material:1.1.0-alpha05'
implementation
'androidx.work:work-runtime:2.0.1'
implementation
'androidx.transition:transition:1.1.0-beta01'
implementation
'androidx.multidex:multidex:2.0.1'
}
app/proguard-rules.pro
View file @
2813d203
...
...
@@ -29,6 +29,11 @@
void
onResponse
(
int
);
}
# Keep all fragment constructors
-
keepclassmembers
class
*
extends
androidx
.
fragment
.
app
.
Fragment
{
public
<
init
>
(...);
}
# DelegateWorker
-
keep
,
allowobfuscation
class
*
extends
com
.
topjohnwu
.
magisk
.
model
.
worker
.
DelegateWorker
...
...
app/src/main/AndroidManifest.xml
View file @
2813d203
...
...
@@ -11,7 +11,7 @@
<application
android:allowBackup=
"true"
android:name=
"a.e"
android:theme=
"@style/
App
Theme"
android:theme=
"@style/
Magisk
Theme"
android:usesCleartextTraffic=
"true"
tools:ignore=
"UnusedAttribute,GoogleAppIndexingWarning"
>
...
...
@@ -35,7 +35,7 @@
android:name=
"a.f"
android:configChanges=
"keyboardHidden|orientation|screenSize"
android:screenOrientation=
"nosensor"
android:theme=
"@style/
AppTheme.NoDrawer
"
/>
android:theme=
"@style/
MagiskTheme.Flashing
"
/>
<!-- Superuser -->
...
...
@@ -44,7 +44,7 @@
android:exported=
"false"
android:directBootAware=
"true"
android:excludeFromRecents=
"true"
android:theme=
"@style/
SuRequest
"
/>
android:theme=
"@style/
MagiskTheme.SU
"
/>
<!-- Receiver -->
...
...
app/src/main/java/com/topjohnwu/magisk/App.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
;
import
android.app.Activity
;
import
android.app.Application
;
import
android.content.Context
;
import
android.content.SharedPreferences
;
import
android.content.res.Configuration
;
import
android.os.AsyncTask
;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.preference.PreferenceManager
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.appcompat.app.AppCompatDelegate
;
import
com.topjohnwu.magisk.data.database.MagiskDB
;
import
com.topjohnwu.magisk.data.database.RepoDatabaseHelper
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.utils.LocaleManager
;
import
com.topjohnwu.magisk.utils.RootUtils
;
import
com.topjohnwu.net.Networking
;
import
com.topjohnwu.superuser.Shell
;
import
java.util.concurrent.ThreadPoolExecutor
;
public
class
App
extends
Application
implements
Application
.
ActivityLifecycleCallbacks
{
public
static
App
self
;
public
static
Context
deContext
;
public
static
ThreadPoolExecutor
THREAD_POOL
;
// Global resources
public
SharedPreferences
prefs
;
public
MagiskDB
mDB
;
public
RepoDatabaseHelper
repoDB
;
private
volatile
BaseActivity
foreground
;
static
{
AppCompatDelegate
.
setCompatVectorFromResourcesEnabled
(
true
);
Shell
.
Config
.
setFlags
(
Shell
.
FLAG_MOUNT_MASTER
|
Shell
.
FLAG_USE_MAGISK_BUSYBOX
);
Shell
.
Config
.
verboseLogging
(
BuildConfig
.
DEBUG
);
Shell
.
Config
.
addInitializers
(
RootUtils
.
class
);
Shell
.
Config
.
setTimeout
(
2
);
THREAD_POOL
=
(
ThreadPoolExecutor
)
AsyncTask
.
THREAD_POOL_EXECUTOR
;
}
@Override
protected
void
attachBaseContext
(
Context
base
)
{
super
.
attachBaseContext
(
base
);
self
=
this
;
deContext
=
base
;
registerActivityLifecycleCallbacks
(
this
);
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
{
deContext
=
base
.
createDeviceProtectedStorageContext
();
deContext
.
moveSharedPreferencesFrom
(
base
,
PreferenceManager
.
getDefaultSharedPreferencesName
(
base
));
}
prefs
=
PreferenceManager
.
getDefaultSharedPreferences
(
deContext
);
mDB
=
new
MagiskDB
(
base
);
Networking
.
init
(
base
);
LocaleManager
.
setLocale
(
this
);
}
@Override
public
void
onConfigurationChanged
(
@NonNull
Configuration
newConfig
)
{
super
.
onConfigurationChanged
(
newConfig
);
LocaleManager
.
setLocale
(
this
);
}
public
static
BaseActivity
foreground
()
{
return
self
.
foreground
;
}
@Override
public
void
onActivityCreated
(
@NonNull
Activity
activity
,
@Nullable
Bundle
bundle
)
{}
@Override
public
void
onActivityStarted
(
@NonNull
Activity
activity
)
{}
@Override
public
synchronized
void
onActivityResumed
(
@NonNull
Activity
activity
)
{
foreground
=
(
BaseActivity
)
activity
;
}
@Override
public
synchronized
void
onActivityPaused
(
@NonNull
Activity
activity
)
{
foreground
=
null
;
}
@Override
public
void
onActivityStopped
(
@NonNull
Activity
activity
)
{}
@Override
public
void
onActivitySaveInstanceState
(
@NonNull
Activity
activity
,
@NonNull
Bundle
bundle
)
{}
@Override
public
void
onActivityDestroyed
(
@NonNull
Activity
activity
)
{}
}
app/src/main/java/com/topjohnwu/magisk/App.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk
import
android.annotation.SuppressLint
import
android.app.Activity
import
android.app.Application
import
android.content.Context
import
android.content.SharedPreferences
import
android.content.res.Configuration
import
android.os.AsyncTask
import
android.os.Build
import
android.os.Bundle
import
androidx.appcompat.app.AppCompatDelegate
import
androidx.multidex.MultiDex
import
com.topjohnwu.magisk.data.database.MagiskDB
import
com.topjohnwu.magisk.data.database.RepoDatabaseHelper
import
com.topjohnwu.magisk.di.koinModules
import
com.topjohnwu.magisk.utils.LocaleManager
import
com.topjohnwu.magisk.utils.RootUtils
import
com.topjohnwu.magisk.utils.inject
import
com.topjohnwu.net.Networking
import
com.topjohnwu.superuser.Shell
import
org.koin.android.ext.android.inject
import
org.koin.android.ext.koin.androidContext
import
org.koin.core.context.startKoin
import
timber.log.Timber
import
java.util.concurrent.ThreadPoolExecutor
open
class
App
:
Application
(),
Application
.
ActivityLifecycleCallbacks
{
lateinit
var
protectedContext
:
Context
@Deprecated
(
"Use dependency injection"
)
val
prefs
:
SharedPreferences
by
inject
()
@Deprecated
(
"Use dependency injection"
)
val
DB
:
MagiskDB
by
inject
()
@Deprecated
(
"Use dependency injection"
)
val
repoDB
:
RepoDatabaseHelper
by
inject
()
@Volatile
private
var
foreground
:
Activity
?
=
null
override
fun
attachBaseContext
(
base
:
Context
)
{
super
.
attachBaseContext
(
base
)
MultiDex
.
install
(
base
)
Timber
.
plant
(
Timber
.
DebugTree
())
startKoin
{
androidContext
(
this
@App
)
modules
(
koinModules
)
}
protectedContext
=
baseContext
self
=
this
deContext
=
base
if
(
Build
.
VERSION
.
SDK_INT
>=
24
)
{
protectedContext
=
base
.
createDeviceProtectedStorageContext
()
deContext
=
protectedContext
deContext
.
moveSharedPreferencesFrom
(
base
,
base
.
defaultPrefsName
)
}
registerActivityLifecycleCallbacks
(
this
)
Networking
.
init
(
base
)
LocaleManager
.
setLocale
(
this
)
}
override
fun
onConfigurationChanged
(
newConfig
:
Configuration
)
{
super
.
onConfigurationChanged
(
newConfig
)
LocaleManager
.
setLocale
(
this
)
}
//region ActivityLifecycleCallbacks
override
fun
onActivityCreated
(
activity
:
Activity
,
bundle
:
Bundle
?)
{}
override
fun
onActivityStarted
(
activity
:
Activity
)
{}
@Synchronized
override
fun
onActivityResumed
(
activity
:
Activity
)
{
foreground
=
activity
}
@Synchronized
override
fun
onActivityPaused
(
activity
:
Activity
)
{
foreground
=
null
}
override
fun
onActivityStopped
(
activity
:
Activity
)
{}
override
fun
onActivitySaveInstanceState
(
activity
:
Activity
,
bundle
:
Bundle
)
{}
override
fun
onActivityDestroyed
(
activity
:
Activity
)
{}
//endregion
private
val
Context
.
defaultPrefsName
get
()
=
"${packageName}_preferences"
companion
object
{
@SuppressLint
(
"StaticFieldLeak"
)
@Deprecated
(
"Use dependency injection"
)
@JvmStatic
lateinit
var
self
:
App
@SuppressLint
(
"StaticFieldLeak"
)
@Deprecated
(
"Use dependency injection; replace with protectedContext"
)
@JvmStatic
lateinit
var
deContext
:
Context
@Deprecated
(
"Use Rx or similar"
)
@JvmField
var
THREAD_POOL
:
ThreadPoolExecutor
init
{
AppCompatDelegate
.
setCompatVectorFromResourcesEnabled
(
true
)
Shell
.
Config
.
setFlags
(
Shell
.
FLAG_MOUNT_MASTER
or
Shell
.
FLAG_USE_MAGISK_BUSYBOX
)
Shell
.
Config
.
verboseLogging
(
BuildConfig
.
DEBUG
)
Shell
.
Config
.
addInitializers
(
RootUtils
::
class
.
java
)
Shell
.
Config
.
setTimeout
(
2
)
THREAD_POOL
=
AsyncTask
.
THREAD_POOL_EXECUTOR
as
ThreadPoolExecutor
}
@Deprecated
(
""
)
@JvmStatic
fun
foreground
():
Activity
?
{
val
app
:
App
by
inject
()
return
app
.
foreground
}
}
}
app/src/main/java/com/topjohnwu/magisk/Config.java
View file @
2813d203
...
...
@@ -3,8 +3,6 @@ package com.topjohnwu.magisk;
import
android.content.SharedPreferences
;
import
android.util.Xml
;
import
androidx.collection.ArrayMap
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.ShellUtils
;
...
...
@@ -17,6 +15,8 @@ import org.xmlpull.v1.XmlPullParserException;
import
java.io.File
;
import
java.io.IOException
;
import
androidx.collection.ArrayMap
;
public
class
Config
{
// Current status
...
...
@@ -109,14 +109,14 @@ public class Config {
public
static
void
export
()
{
// Flush prefs to disk
App
app
=
App
.
self
;
app
.
prefs
.
edit
().
commit
();
app
.
getPrefs
()
.
edit
().
commit
();
File
xml
=
new
File
(
App
.
deContext
.
getFilesDir
().
getParent
()
+
"/shared_prefs"
,
app
.
getPackageName
()
+
"_preferences.xml"
);
Shell
.
su
(
Utils
.
fmt
(
"cat %s > /data/adb/%s"
,
xml
,
Const
.
MANAGER_CONFIGS
)).
exec
();
}
public
static
void
initialize
()
{
SharedPreferences
pref
=
App
.
self
.
prefs
;
SharedPreferences
pref
=
App
.
self
.
getPrefs
()
;
SharedPreferences
.
Editor
editor
=
pref
.
edit
();
File
config
=
SuFile
.
open
(
"/data/adb"
,
Const
.
MANAGER_CONFIGS
);
if
(
config
.
exists
())
{
...
...
@@ -238,19 +238,19 @@ public class Config {
App
app
=
App
.
self
;
switch
(
getConfigType
(
key
))
{
case
PREF_INT:
return
(
T
)
(
Integer
)
app
.
prefs
.
getInt
(
key
,
getDef
(
key
));
return
(
T
)
(
Integer
)
app
.
getPrefs
()
.
getInt
(
key
,
getDef
(
key
));
case
PREF_STR_INT:
return
(
T
)
(
Integer
)
Utils
.
getPrefsInt
(
app
.
prefs
,
key
,
getDef
(
key
));
return
(
T
)
(
Integer
)
Utils
.
getPrefsInt
(
app
.
getPrefs
()
,
key
,
getDef
(
key
));
case
PREF_BOOL:
return
(
T
)
(
Boolean
)
app
.
prefs
.
getBoolean
(
key
,
getDef
(
key
));
return
(
T
)
(
Boolean
)
app
.
getPrefs
()
.
getBoolean
(
key
,
getDef
(
key
));
case
PREF_STR:
return
(
T
)
app
.
prefs
.
getString
(
key
,
getDef
(
key
));
return
(
T
)
app
.
getPrefs
()
.
getString
(
key
,
getDef
(
key
));
case
DB_INT:
return
(
T
)
(
Integer
)
app
.
mDB
.
getSettings
(
key
,
getDef
(
key
));
return
(
T
)
(
Integer
)
app
.
getDB
()
.
getSettings
(
key
,
getDef
(
key
));
case
DB_BOOL:
return
(
T
)
(
Boolean
)
(
app
.
mDB
.
getSettings
(
key
,
getDef
(
key
)
?
1
:
0
)
!=
0
);
return
(
T
)
(
Boolean
)
(
app
.
getDB
()
.
getSettings
(
key
,
getDef
(
key
)
?
1
:
0
)
!=
0
);
case
DB_STR:
return
(
T
)
app
.
mDB
.
getStrings
(
key
,
getDef
(
key
));
return
(
T
)
app
.
getDB
()
.
getStrings
(
key
,
getDef
(
key
));
}
/* Will never get here (IllegalArgumentException in getConfigType) */
return
null
;
...
...
@@ -260,25 +260,25 @@ public class Config {
App
app
=
App
.
self
;
switch
(
getConfigType
(
key
))
{
case
PREF_INT:
app
.
prefs
.
edit
().
putInt
(
key
,
(
int
)
val
).
apply
();
app
.
getPrefs
()
.
edit
().
putInt
(
key
,
(
int
)
val
).
apply
();
break
;
case
PREF_STR_INT:
app
.
prefs
.
edit
().
putString
(
key
,
String
.
valueOf
(
val
)).
apply
();
app
.
getPrefs
()
.
edit
().
putString
(
key
,
String
.
valueOf
(
val
)).
apply
();
break
;
case
PREF_BOOL:
app
.
prefs
.
edit
().
putBoolean
(
key
,
(
boolean
)
val
).
apply
();
app
.
getPrefs
()
.
edit
().
putBoolean
(
key
,
(
boolean
)
val
).
apply
();
break
;
case
PREF_STR:
app
.
prefs
.
edit
().
putString
(
key
,
(
String
)
val
).
apply
();
app
.
getPrefs
()
.
edit
().
putString
(
key
,
(
String
)
val
).
apply
();
break
;
case
DB_INT:
app
.
mDB
.
setSettings
(
key
,
(
int
)
val
);
app
.
getDB
()
.
setSettings
(
key
,
(
int
)
val
);
break
;
case
DB_BOOL:
app
.
mDB
.
setSettings
(
key
,
(
boolean
)
val
?
1
:
0
);
app
.
getDB
()
.
setSettings
(
key
,
(
boolean
)
val
?
1
:
0
);
break
;
case
DB_STR:
app
.
mDB
.
setStrings
(
key
,
(
String
)
val
);
app
.
getDB
()
.
setStrings
(
key
,
(
String
)
val
);
break
;
}
}
...
...
@@ -290,14 +290,14 @@ public class Config {
case
PREF_STR_INT:
case
PREF_BOOL:
case
PREF_STR:
app
.
prefs
.
edit
().
remove
(
key
).
apply
();
app
.
getPrefs
()
.
edit
().
remove
(
key
).
apply
();
break
;
case
DB_BOOL:
case
DB_INT:
app
.
mDB
.
rmSettings
(
key
);
app
.
getDB
()
.
rmSettings
(
key
);
break
;
case
DB_STR:
app
.
mDB
.
setStrings
(
key
,
null
);
app
.
getDB
()
.
setStrings
(
key
,
null
);
break
;
}
}
...
...
@@ -365,13 +365,13 @@ public class Config {
switch
(
type
)
{
case
DB_INT:
editor
.
putString
(
key
,
String
.
valueOf
(
app
.
mDB
.
getSettings
(
key
,
(
Integer
)
defs
.
get
(
key
))));
app
.
getDB
()
.
getSettings
(
key
,
(
Integer
)
defs
.
get
(
key
))));
continue
;
case
DB_STR:
editor
.
putString
(
key
,
app
.
mDB
.
getStrings
(
key
,
(
String
)
defs
.
get
(
key
)));
editor
.
putString
(
key
,
app
.
getDB
()
.
getStrings
(
key
,
(
String
)
defs
.
get
(
key
)));
continue
;
case
DB_BOOL:
int
bs
=
app
.
mDB
.
getSettings
(
key
,
-
1
);
int
bs
=
app
.
getDB
()
.
getSettings
(
key
,
-
1
);
editor
.
putBoolean
(
key
,
bs
<
0
?
(
Boolean
)
defs
.
get
(
key
)
:
bs
!=
0
);
continue
;
}
...
...
app/src/main/java/com/topjohnwu/magisk/Const.java
View file @
2813d203
...
...
@@ -35,6 +35,9 @@ public class Const {
public
static
final
int
USER_ID
=
Process
.
myUid
()
/
100000
;
// Generic
public
static
final
String
MAGISK_INSTALL_LOG_FILENAME
=
"magisk_install_log_%s.log"
;
public
static
final
class
MAGISK_VER
{
public
static
final
int
MIN_SUPPORT
=
18000
;
}
...
...
app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
android.content.Context
import
androidx.preference.PreferenceManager
import
com.skoumal.teanity.rxbus.RxBus
import
com.topjohnwu.magisk.App
import
org.koin.dsl.module
val
applicationModule
=
module
{
single
{
RxBus
()
}
single
{
get
<
Context
>().
resources
}
single
{
get
<
Context
>()
as
App
}
single
{
get
<
Context
>().
packageManager
}
single
(
SUTimeout
)
{
get
<
App
>().
protectedContext
.
getSharedPreferences
(
"su_timeout"
,
0
)
}
single
{
PreferenceManager
.
getDefaultSharedPreferences
(
get
<
App
>().
protectedContext
)
}
}
app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
com.topjohnwu.magisk.App
import
com.topjohnwu.magisk.data.database.MagiskDB
import
com.topjohnwu.magisk.data.database.RepoDatabaseHelper
import
org.koin.dsl.module
val
databaseModule
=
module
{
single
{
MagiskDB
(
get
<
App
>().
protectedContext
)
}
single
{
RepoDatabaseHelper
(
get
())
}
}
app/src/main/java/com/topjohnwu/magisk/di/MiscModule.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
org.koin.dsl.module
val
miscModule
=
module
{
// define miscs here
}
app/src/main/java/com/topjohnwu/magisk/di/Modules.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
val
koinModules
=
listOf
(
applicationModule
,
networkingModule
,
databaseModule
,
repositoryModule
,
viewModelModules
,
miscModule
)
app/src/main/java/com/topjohnwu/magisk/di/NamedInjection.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
org.koin.core.qualifier.named
val
SUTimeout
=
named
(
"su_timeout"
)
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/di/NetworkingModule.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
org.koin.dsl.module
val
networkingModule
=
module
{}
app/src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
org.koin.dsl.module
val
repositoryModule
=
module
{}
app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.di
import
android.content.Intent
import
android.net.Uri
import
com.topjohnwu.magisk.ui.MainViewModel
import
com.topjohnwu.magisk.ui.flash.FlashViewModel
import
com.topjohnwu.magisk.ui.hide.HideViewModel
import
com.topjohnwu.magisk.ui.home.HomeViewModel
import
com.topjohnwu.magisk.ui.log.LogViewModel
import
com.topjohnwu.magisk.ui.module.ModuleViewModel
import
com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
import
com.topjohnwu.magisk.ui.surequest.SuRequestViewModel
import
com.topjohnwu.magisk.ui.surequest._SuRequestViewModel
import
org.koin.androidx.viewmodel.dsl.viewModel
import
org.koin.dsl.module
val
viewModelModules
=
module
{
viewModel
{
MainViewModel
()
}
viewModel
{
HomeViewModel
(
get
(),
get
())
}
viewModel
{
SuperuserViewModel
(
get
(),
get
(),
get
(),
get
())
}
viewModel
{
HideViewModel
(
get
(),
get
())
}
viewModel
{
ModuleViewModel
(
get
(),
get
())
}
viewModel
{
LogViewModel
(
get
(),
get
())
}
viewModel
{
(
action
:
String
,
uri
:
Uri
?)
->
FlashViewModel
(
action
,
uri
,
get
())
}
viewModel
{
(
intent
:
Intent
,
action
:
String
?)
->
_SuRequestViewModel
(
intent
,
action
.
orEmpty
(),
get
(),
get
())
}
viewModel
{
SuRequestViewModel
(
get
(),
get
(),
get
(
SUTimeout
),
get
())
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/ApplicationAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.content.Context
;
import
android.content.pm.ApplicationInfo
;
import
android.content.pm.ComponentInfo
;
import
android.content.pm.PackageInfo
;
import
android.content.pm.PackageManager
;
import
android.os.AsyncTask
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.CheckBox
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.WorkerThread
;
import
androidx.collection.ArraySet
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.buildware.widget.indeterm.IndeterminateCheckBox
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.Event
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.magisk.view.ArrowExpandable
;
import
com.topjohnwu.magisk.view.Expandable
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.internal.UiThreadHandler
;
import
java.util.Collections
;
import
java.util.Comparator
;
import
java.util.List
;
import
java.util.Set
;
import
butterknife.BindView
;
import
java9.util.Comparators
;
import
java9.util.Lists
;
import
java9.util.Objects
;
import
java9.util.Sets
;
import
java9.util.stream.Collectors
;
import
java9.util.stream.Stream
;
import
java9.util.stream.StreamSupport
;
public
class
ApplicationAdapter
extends
SectionedAdapter
<
ApplicationAdapter
.
AppViewHolder
,
ApplicationAdapter
.
ProcessViewHolder
>
{
private
static
final
String
SAFETYNET_PROCESS
=
"com.google.android.gms.unstable"
;
private
static
final
String
GMS_PACKAGE
=
"com.google.android.gms"
;
private
static
boolean
old_hide
=
false
;
/* A list of apps that should not be shown as hide-able */
private
static
final
List
<
String
>
HIDE_BLACKLIST
=
Lists
.
of
(
App
.
self
.
getPackageName
(),
"android"
,
"com.android.chrome"
,
"com.chrome.beta"
,
"com.chrome.dev"
,
"com.chrome.canary"
,
"com.android.webview"
,
"com.google.android.webview"
);
private
static
final
List
<
String
>
DEFAULT_HIDELIST
=
Lists
.
of
(
SAFETYNET_PROCESS
);
private
static
int
BOTTOM_MARGIN
=
-
1
;
private
List
<
HideAppInfo
>
fullList
,
showList
;
private
List
<
HideTarget
>
hideList
;
private
PackageManager
pm
;
private
boolean
showSystem
;
public
ApplicationAdapter
(
Context
context
)
{
fullList
=
showList
=
Collections
.
emptyList
();
hideList
=
Collections
.
emptyList
();
pm
=
context
.
getPackageManager
();
showSystem
=
Config
.
get
(
Config
.
Key
.
SHOW_SYSTEM_APP
);
AsyncTask
.
SERIAL_EXECUTOR
.
execute
(
this
::
loadApps
);
}
private
static
ViewGroup
.
MarginLayoutParams
getMargins
(
RecyclerView
.
ViewHolder
vh
)
{
return
(
ViewGroup
.
MarginLayoutParams
)
vh
.
itemView
.
getLayoutParams
();
}
@Override
public
int
getSectionCount
()
{
return
showList
.
size
();
}
@Override
public
int
getItemCount
(
int
section
)
{
HideAppInfo
app
=
showList
.
get
(
section
);
return
app
.
expanded
?
app
.
processList
.
size
()
:
0
;
}
@Override
public
AppViewHolder
onCreateSectionViewHolder
(
ViewGroup
parent
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_hide_app
,
parent
,
false
);
AppViewHolder
vh
=
new
AppViewHolder
(
v
);
if
(
BOTTOM_MARGIN
<
0
)
BOTTOM_MARGIN
=
getMargins
(
vh
).
bottomMargin
;
return
vh
;
}
@Override
public
ProcessViewHolder
onCreateItemViewHolder
(
ViewGroup
parent
,
int
viewType
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_hide_process
,
parent
,
false
);
return
new
ProcessViewHolder
(
v
);
}
@Override
public
void
onBindSectionViewHolder
(
AppViewHolder
holder
,
int
section
)
{
HideAppInfo
app
=
showList
.
get
(
section
);
holder
.
app_name
.
setText
(
app
.
name
);
holder
.
app_icon
.
setImageDrawable
(
app
.
info
.
loadIcon
(
pm
));
holder
.
package_name
.
setText
(
app
.
info
.
packageName
);
holder
.
checkBox
.
setOnStateChangedListener
(
null
);
holder
.
checkBox
.
setState
(
app
.
getState
());
holder
.
ex
.
setExpanded
(
app
.
expanded
);
int
index
=
getItemPosition
(
section
,
0
);
holder
.
checkBox
.
setOnStateChangedListener
((
IndeterminateCheckBox
box
,
@Nullable
Boolean
status
)
->
{
if
(
status
!=
null
)
{
setHide
(
status
,
app
);
if
(
app
.
expanded
)
notifyItemRangeChanged
(
index
,
app
.
processList
.
size
());
}
});
if
(
app
.
processList
.
size
()
>
1
)
{
holder
.
arrow
.
setVisibility
(
View
.
VISIBLE
);
holder
.
trigger
.
setOnClickListener
((
v
)
->
{
if
(
app
.
expanded
)
{
app
.
expanded
=
false
;
notifyItemRangeRemoved
(
index
,
app
.
processList
.
size
());
holder
.
ex
.
collapse
();
}
else
{
app
.
expanded
=
true
;
notifyItemRangeInserted
(
index
,
app
.
processList
.
size
());
holder
.
ex
.
expand
();
}
});
}
else
{
holder
.
arrow
.
setVisibility
(
View
.
GONE
);
holder
.
trigger
.
setOnClickListener
(
null
);
}
}
@Override
public
void
onBindItemViewHolder
(
ProcessViewHolder
holder
,
int
section
,
int
position
)
{
HideAppInfo
app
=
showList
.
get
(
section
);
HideProcessInfo
target
=
app
.
processList
.
get
(
position
);
holder
.
process
.
setText
(
target
.
name
);
holder
.
checkbox
.
setOnCheckedChangeListener
(
null
);
holder
.
checkbox
.
setChecked
(
target
.
hidden
);
holder
.
checkbox
.
setOnCheckedChangeListener
((
v
,
checked
)
->
{
setHide
(
checked
,
app
,
target
);
notifyItemChanged
(
getSectionPosition
(
section
));
});
getMargins
(
holder
).
bottomMargin
=
position
==
app
.
processList
.
size
()
-
1
?
BOTTOM_MARGIN
:
0
;
}
public
void
filter
(
String
constraint
)
{
AsyncTask
.
SERIAL_EXECUTOR
.
execute
(()
->
{
Stream
<
HideAppInfo
>
s
=
StreamSupport
.
stream
(
fullList
)
.
filter
(
this
::
systemFilter
)
.
filter
(
t
->
nameFilter
(
t
,
constraint
));
UiThreadHandler
.
run
(()
->
{
showList
=
s
.
collect
(
Collectors
.
toList
());
notifyDataSetChanged
();
});
});
}
public
void
setShowSystem
(
boolean
b
)
{
showSystem
=
b
;
}
public
void
refresh
()
{
AsyncTask
.
SERIAL_EXECUTOR
.
execute
(
this
::
loadApps
);
}
private
void
setHide
(
boolean
add
,
HideAppInfo
app
)
{
if
(
add
)
{
StreamSupport
.
stream
(
app
.
processList
).
forEach
(
p
->
setHide
(
true
,
app
,
p
));
}
else
{
if
(
StreamSupport
.
stream
(
app
.
processList
)
.
anyMatch
(
p
->
p
.
name
.
equals
(
SAFETYNET_PROCESS
)))
{
StreamSupport
.
stream
(
app
.
processList
).
forEach
(
p
->
setHide
(
false
,
app
,
p
));
}
else
{
// Quick removal
Shell
.
su
(
"magiskhide --rm "
+
app
.
info
.
packageName
).
submit
();
StreamSupport
.
stream
(
app
.
processList
).
forEach
(
p
->
p
.
hidden
=
false
);
}
}
}
private
void
setHide
(
boolean
add
,
HideAppInfo
app
,
HideProcessInfo
process
)
{
// Don't remove SafetyNet
if
(!
add
&&
DEFAULT_HIDELIST
.
contains
(
process
.
name
))
return
;
Shell
.
su
(
Utils
.
fmt
(
"magiskhide --%s %s %s"
,
add
?
"add"
:
"rm"
,
app
.
info
.
packageName
,
process
.
name
)).
submit
();
process
.
hidden
=
add
;
}
private
void
addProcesses
(
Set
<
String
>
set
,
ComponentInfo
[]
infos
)
{
if
(
infos
!=
null
)
for
(
ComponentInfo
info
:
infos
)
set
.
add
(
info
.
processName
);
}
private
PackageInfo
getPackageInfo
(
String
pkg
)
{
// Try super hard to get as much info as possible
try
{
return
pm
.
getPackageInfo
(
pkg
,
PackageManager
.
GET_ACTIVITIES
|
PackageManager
.
GET_SERVICES
|
PackageManager
.
GET_RECEIVERS
|
PackageManager
.
GET_PROVIDERS
);
}
catch
(
Exception
e1
)
{
try
{
PackageInfo
info
=
pm
.
getPackageInfo
(
pkg
,
PackageManager
.
GET_ACTIVITIES
);
info
.
services
=
pm
.
getPackageInfo
(
pkg
,
PackageManager
.
GET_SERVICES
).
services
;
info
.
receivers
=
pm
.
getPackageInfo
(
pkg
,
PackageManager
.
GET_RECEIVERS
).
receivers
;
info
.
providers
=
pm
.
getPackageInfo
(
pkg
,
PackageManager
.
GET_PROVIDERS
).
providers
;
return
info
;
}
catch
(
Exception
e2
)
{
return
null
;
}
}
}
@WorkerThread
private
void
loadApps
()
{
hideList
=
StreamSupport
.
stream
(
Shell
.
su
(
"magiskhide --ls"
).
exec
().
getOut
())
.
map
(
HideTarget:
:
new
)
.
collect
(
Collectors
.
toList
());
fullList
=
StreamSupport
.
stream
(
pm
.
getInstalledApplications
(
0
))
.
filter
(
info
->
!
HIDE_BLACKLIST
.
contains
(
info
.
packageName
)
&&
info
.
enabled
)
.
map
(
info
->
{
if
(
old_hide
)
{
return
new
HideAppInfo
(
info
,
Sets
.
of
(
info
.
packageName
));
}
else
{
Set
<
String
>
set
=
new
ArraySet
<>();
PackageInfo
pkg
=
getPackageInfo
(
info
.
packageName
);
if
(
pkg
!=
null
)
{
addProcesses
(
set
,
pkg
.
activities
);
addProcesses
(
set
,
pkg
.
services
);
addProcesses
(
set
,
pkg
.
receivers
);
addProcesses
(
set
,
pkg
.
providers
);
}
if
(
set
.
isEmpty
())
return
null
;
return
new
HideAppInfo
(
info
,
set
);
}
}).
filter
(
Objects:
:
nonNull
).
sorted
()
.
collect
(
Collectors
.
toList
());
Event
.
trigger
(
false
,
Event
.
MAGISK_HIDE_DONE
);
}
// True if not system app or user already hidden it
private
boolean
systemFilter
(
HideAppInfo
target
)
{
return
showSystem
||
target
.
haveHidden
()
||
(
target
.
info
.
flags
&
ApplicationInfo
.
FLAG_SYSTEM
)
==
0
;
}
private
boolean
contains
(
String
s
,
String
filter
)
{
return
s
.
toLowerCase
().
contains
(
filter
);
}
private
boolean
nameFilter
(
HideAppInfo
target
,
String
filter
)
{
if
(
filter
==
null
||
filter
.
isEmpty
())
return
true
;
filter
=
filter
.
toLowerCase
();
if
(
contains
(
target
.
name
,
filter
))
return
true
;
for
(
HideProcessInfo
p
:
target
.
processList
)
{
if
(
contains
(
p
.
name
,
filter
))
return
true
;
}
return
contains
(
target
.
info
.
packageName
,
filter
);
}
class
HideAppInfo
implements
Comparable
<
HideAppInfo
>
{
String
name
;
ApplicationInfo
info
;
List
<
HideProcessInfo
>
processList
;
boolean
expanded
;
IndeterminateCheckBox
.
OnStateChangedListener
listener
;
HideAppInfo
(
ApplicationInfo
appInfo
,
Set
<
String
>
set
)
{
info
=
appInfo
;
name
=
Utils
.
getAppLabel
(
info
,
pm
);
expanded
=
false
;
processList
=
StreamSupport
.
stream
(
set
)
.
map
(
process
->
new
HideProcessInfo
(
info
.
packageName
,
process
))
.
sorted
().
collect
(
Collectors
.
toList
());
listener
=
(
IndeterminateCheckBox
box
,
@Nullable
Boolean
status
)
->
{
if
(
status
!=
null
)
{
for
(
HideProcessInfo
p
:
processList
)
{
String
cmd
=
Utils
.
fmt
(
"magiskhide --%s %s %s"
,
status
?
"add"
:
"rm"
,
info
.
packageName
,
p
.
name
);
Shell
.
su
(
cmd
).
submit
();
p
.
hidden
=
status
;
}
}
};
}
@Override
public
int
compareTo
(
HideAppInfo
o
)
{
Comparator
<
HideAppInfo
>
c
;
c
=
Comparators
.
comparing
(
HideAppInfo:
:
haveHidden
);
c
=
Comparators
.
reversed
(
c
);
c
=
Comparators
.
thenComparing
(
c
,
t
->
t
.
name
,
String:
:
compareToIgnoreCase
);
c
=
Comparators
.
thenComparing
(
c
,
t
->
t
.
info
.
packageName
);
return
c
.
compare
(
this
,
o
);
}
Boolean
getState
()
{
boolean
all
=
true
;
boolean
hidden
=
false
;
for
(
HideProcessInfo
p
:
processList
)
{
if
(!
p
.
hidden
)
all
=
false
;
else
hidden
=
true
;
}
if
(
all
)
return
true
;
return
hidden
?
null
:
false
;
}
boolean
haveHidden
()
{
Boolean
c
=
getState
();
return
c
==
null
?
true
:
c
;
}
}
class
HideProcessInfo
implements
Comparable
<
HideProcessInfo
>
{
String
name
;
boolean
hidden
;
HideProcessInfo
(
String
pkg
,
String
process
)
{
this
.
name
=
process
;
for
(
HideTarget
t
:
hideList
)
{
if
(
t
.
pkg
.
equals
(
pkg
)
&&
t
.
process
.
equals
(
process
))
{
hidden
=
true
;
break
;
}
}
}
@Override
public
int
compareTo
(
HideProcessInfo
o
)
{
Comparator
<
HideProcessInfo
>
c
;
c
=
Comparators
.
comparing
((
HideProcessInfo
t
)
->
t
.
hidden
);
c
=
Comparators
.
reversed
(
c
);
c
=
Comparators
.
thenComparing
(
c
,
t
->
t
.
name
);
return
c
.
compare
(
this
,
o
);
}
}
class
HideTarget
{
String
pkg
;
String
process
;
HideTarget
(
String
line
)
{
String
[]
split
=
line
.
split
(
"\\|"
,
2
);
pkg
=
split
[
0
];
if
(
split
.
length
==
2
)
{
process
=
split
[
1
];
}
else
{
// Backwards compatibility
old_hide
=
true
;
process
=
pkg
;
}
}
}
class
AppViewHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
app_icon
)
ImageView
app_icon
;
@BindView
(
R
.
id
.
app_name
)
TextView
app_name
;
@BindView
(
R
.
id
.
package_name
)
TextView
package_name
;
@BindView
(
R
.
id
.
checkbox
)
IndeterminateCheckBox
checkBox
;
@BindView
(
R
.
id
.
trigger
)
View
trigger
;
@BindView
(
R
.
id
.
arrow
)
ImageView
arrow
;
Expandable
ex
;
AppViewHolder
(
@NonNull
View
itemView
)
{
super
(
itemView
);
new
ApplicationAdapter$AppViewHolder_ViewBinding
(
this
,
itemView
);
ex
=
new
ArrowExpandable
(
new
Expandable
()
{
@Override
protected
void
onExpand
()
{
getMargins
(
AppViewHolder
.
this
).
bottomMargin
=
0
;
}
@Override
protected
void
onCollapse
()
{
getMargins
(
AppViewHolder
.
this
).
bottomMargin
=
BOTTOM_MARGIN
;
}
},
arrow
);
}
}
class
ProcessViewHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
process
)
TextView
process
;
@BindView
(
R
.
id
.
checkbox
)
CheckBox
checkbox
;
ProcessViewHolder
(
@NonNull
View
itemView
)
{
super
(
itemView
);
new
ApplicationAdapter$ProcessViewHolder_ViewBinding
(
this
,
itemView
);
}
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/ModulesAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.content.Context
;
import
android.text.TextUtils
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.CheckBox
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
androidx.annotation.NonNull
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.google.android.material.snackbar.Snackbar
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.entity.Module
;
import
com.topjohnwu.magisk.view.SnackbarMaker
;
import
com.topjohnwu.superuser.Shell
;
import
java.util.List
;
import
butterknife.BindView
;
public
class
ModulesAdapter
extends
RecyclerView
.
Adapter
<
ModulesAdapter
.
ViewHolder
>
{
private
final
List
<
Module
>
mList
;
public
ModulesAdapter
(
List
<
Module
>
list
)
{
mList
=
list
;
}
@NonNull
@Override
public
ViewHolder
onCreateViewHolder
(
ViewGroup
parent
,
int
viewType
)
{
View
view
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_module
,
parent
,
false
);
return
new
ViewHolder
(
view
);
}
@Override
public
void
onBindViewHolder
(
final
ViewHolder
holder
,
int
position
)
{
Context
context
=
holder
.
itemView
.
getContext
();
final
Module
module
=
mList
.
get
(
position
);
String
version
=
module
.
getVersion
();
String
author
=
module
.
getAuthor
();
String
description
=
module
.
getDescription
();
String
noInfo
=
context
.
getString
(
R
.
string
.
no_info_provided
);
holder
.
title
.
setText
(
module
.
getName
());
holder
.
versionName
.
setText
(
TextUtils
.
isEmpty
(
version
)
?
noInfo
:
version
);
holder
.
author
.
setText
(
TextUtils
.
isEmpty
(
author
)
?
noInfo
:
context
.
getString
(
R
.
string
.
author
,
author
));
holder
.
description
.
setText
(
TextUtils
.
isEmpty
(
description
)
?
noInfo
:
description
);
holder
.
checkBox
.
setOnCheckedChangeListener
(
null
);
holder
.
checkBox
.
setChecked
(
module
.
isEnabled
());
holder
.
checkBox
.
setOnCheckedChangeListener
((
v
,
isChecked
)
->
{
int
snack
;
if
(
isChecked
)
{
module
.
removeDisableFile
();
snack
=
R
.
string
.
disable_file_removed
;
}
else
{
module
.
createDisableFile
();
snack
=
R
.
string
.
disable_file_created
;
}
SnackbarMaker
.
make
(
holder
.
itemView
,
snack
,
Snackbar
.
LENGTH_SHORT
).
show
();
});
holder
.
delete
.
setOnClickListener
(
v
->
{
boolean
removed
=
module
.
willBeRemoved
();
int
snack
;
if
(
removed
)
{
module
.
deleteRemoveFile
();
snack
=
R
.
string
.
remove_file_deleted
;
}
else
{
module
.
createRemoveFile
();
snack
=
R
.
string
.
remove_file_created
;
}
SnackbarMaker
.
make
(
holder
.
itemView
,
snack
,
Snackbar
.
LENGTH_SHORT
).
show
();
updateDeleteButton
(
holder
,
module
);
});
if
(
module
.
isUpdated
())
{
holder
.
notice
.
setVisibility
(
View
.
VISIBLE
);
holder
.
notice
.
setText
(
R
.
string
.
update_file_created
);
holder
.
delete
.
setEnabled
(
false
);
}
else
{
updateDeleteButton
(
holder
,
module
);
}
}
private
void
updateDeleteButton
(
ViewHolder
holder
,
Module
module
)
{
holder
.
notice
.
setVisibility
(
module
.
willBeRemoved
()
?
View
.
VISIBLE
:
View
.
GONE
);
if
(
module
.
willBeRemoved
())
{
holder
.
delete
.
setImageResource
(
R
.
drawable
.
ic_undelete
);
}
else
{
holder
.
delete
.
setImageResource
(
R
.
drawable
.
ic_delete
);
}
}
@Override
public
int
getItemCount
()
{
return
mList
.
size
();
}
static
class
ViewHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
title
)
TextView
title
;
@BindView
(
R
.
id
.
version_name
)
TextView
versionName
;
@BindView
(
R
.
id
.
description
)
TextView
description
;
@BindView
(
R
.
id
.
notice
)
TextView
notice
;
@BindView
(
R
.
id
.
checkbox
)
CheckBox
checkBox
;
@BindView
(
R
.
id
.
author
)
TextView
author
;
@BindView
(
R
.
id
.
delete
)
ImageView
delete
;
ViewHolder
(
View
itemView
)
{
super
(
itemView
);
new
ModulesAdapter$ViewHolder_ViewBinding
(
this
,
itemView
);
if
(!
Shell
.
rootAccess
())
{
checkBox
.
setEnabled
(
false
);
delete
.
setEnabled
(
false
);
}
}
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/PolicyAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.app.Activity
;
import
android.content.DialogInterface
;
import
android.content.pm.PackageManager
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
androidx.annotation.NonNull
;
import
androidx.appcompat.widget.SwitchCompat
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.google.android.material.snackbar.Snackbar
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.data.database.MagiskDB
;
import
com.topjohnwu.magisk.model.entity.Policy
;
import
com.topjohnwu.magisk.utils.FingerprintHelper
;
import
com.topjohnwu.magisk.view.ArrowExpandable
;
import
com.topjohnwu.magisk.view.Expandable
;
import
com.topjohnwu.magisk.view.ExpandableViewHolder
;
import
com.topjohnwu.magisk.view.SnackbarMaker
;
import
com.topjohnwu.magisk.view.dialogs.CustomAlertDialog
;
import
com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
;
import
java.util.List
;
import
butterknife.BindView
;
public
class
PolicyAdapter
extends
RecyclerView
.
Adapter
<
PolicyAdapter
.
ViewHolder
>
{
private
List
<
Policy
>
policyList
;
private
MagiskDB
dbHelper
;
private
PackageManager
pm
;
private
boolean
[]
expandList
;
public
PolicyAdapter
(
List
<
Policy
>
list
,
MagiskDB
db
,
PackageManager
pm
)
{
policyList
=
list
;
expandList
=
new
boolean
[
policyList
.
size
()];
dbHelper
=
db
;
this
.
pm
=
pm
;
}
@NonNull
@Override
public
ViewHolder
onCreateViewHolder
(
@NonNull
ViewGroup
parent
,
int
viewType
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_policy
,
parent
,
false
);
return
new
ViewHolder
(
v
);
}
@Override
public
void
onBindViewHolder
(
@NonNull
ViewHolder
holder
,
int
position
)
{
Policy
policy
=
policyList
.
get
(
position
);
holder
.
settings
.
setExpanded
(
expandList
[
position
]);
holder
.
trigger
.
setOnClickListener
(
view
->
{
if
(
holder
.
settings
.
isExpanded
())
{
holder
.
settings
.
collapse
();
expandList
[
position
]
=
false
;
}
else
{
holder
.
settings
.
expand
();
expandList
[
position
]
=
true
;
}
});
holder
.
appName
.
setText
(
policy
.
appName
);
holder
.
packageName
.
setText
(
policy
.
packageName
);
holder
.
appIcon
.
setImageDrawable
(
policy
.
info
.
loadIcon
(
pm
));
holder
.
notificationSwitch
.
setOnCheckedChangeListener
(
null
);
holder
.
loggingSwitch
.
setOnCheckedChangeListener
(
null
);
holder
.
masterSwitch
.
setChecked
(
policy
.
policy
==
Policy
.
ALLOW
);
holder
.
notificationSwitch
.
setChecked
(
policy
.
notification
);
holder
.
loggingSwitch
.
setChecked
(
policy
.
logging
);
holder
.
masterSwitch
.
setOnClickListener
(
v
->
{
boolean
isChecked
=
holder
.
masterSwitch
.
isChecked
();
Runnable
r
=
()
->
{
if
((
isChecked
&&
policy
.
policy
==
Policy
.
DENY
)
||
(!
isChecked
&&
policy
.
policy
==
Policy
.
ALLOW
))
{
policy
.
policy
=
isChecked
?
Policy
.
ALLOW
:
Policy
.
DENY
;
String
message
=
v
.
getContext
().
getString
(
isChecked
?
R
.
string
.
su_snack_grant
:
R
.
string
.
su_snack_deny
,
policy
.
appName
);
SnackbarMaker
.
make
(
holder
.
itemView
,
message
,
Snackbar
.
LENGTH_SHORT
).
show
();
dbHelper
.
updatePolicy
(
policy
);
}
};
if
(
FingerprintHelper
.
useFingerprint
())
{
holder
.
masterSwitch
.
setChecked
(!
isChecked
);
new
FingerprintAuthDialog
((
Activity
)
v
.
getContext
(),
()
->
{
holder
.
masterSwitch
.
setChecked
(
isChecked
);
r
.
run
();
}).
show
();
}
else
{
r
.
run
();
}
});
holder
.
notificationSwitch
.
setOnCheckedChangeListener
((
v
,
isChecked
)
->
{
if
((
isChecked
&&
!
policy
.
notification
)
||
(!
isChecked
&&
policy
.
notification
))
{
policy
.
notification
=
isChecked
;
String
message
=
v
.
getContext
().
getString
(
isChecked
?
R
.
string
.
su_snack_notif_on
:
R
.
string
.
su_snack_notif_off
,
policy
.
appName
);
SnackbarMaker
.
make
(
holder
.
itemView
,
message
,
Snackbar
.
LENGTH_SHORT
).
show
();
dbHelper
.
updatePolicy
(
policy
);
}
});
holder
.
loggingSwitch
.
setOnCheckedChangeListener
((
v
,
isChecked
)
->
{
if
((
isChecked
&&
!
policy
.
logging
)
||
(!
isChecked
&&
policy
.
logging
))
{
policy
.
logging
=
isChecked
;
String
message
=
v
.
getContext
().
getString
(
isChecked
?
R
.
string
.
su_snack_log_on
:
R
.
string
.
su_snack_log_off
,
policy
.
appName
);
SnackbarMaker
.
make
(
holder
.
itemView
,
message
,
Snackbar
.
LENGTH_SHORT
).
show
();
dbHelper
.
updatePolicy
(
policy
);
}
});
holder
.
delete
.
setOnClickListener
(
v
->
{
DialogInterface
.
OnClickListener
l
=
(
dialog
,
which
)
->
{
policyList
.
remove
(
position
);
notifyItemRemoved
(
position
);
notifyItemRangeChanged
(
position
,
policyList
.
size
());
SnackbarMaker
.
make
(
holder
.
itemView
,
v
.
getContext
().
getString
(
R
.
string
.
su_snack_revoke
,
policy
.
appName
),
Snackbar
.
LENGTH_SHORT
).
show
();
dbHelper
.
deletePolicy
(
policy
);
};
if
(
FingerprintHelper
.
useFingerprint
())
{
new
FingerprintAuthDialog
((
Activity
)
v
.
getContext
(),
()
->
l
.
onClick
(
null
,
0
)).
show
();
}
else
{
new
CustomAlertDialog
((
Activity
)
v
.
getContext
())
.
setTitle
(
R
.
string
.
su_revoke_title
)
.
setMessage
(
v
.
getContext
().
getString
(
R
.
string
.
su_revoke_msg
,
policy
.
appName
))
.
setPositiveButton
(
R
.
string
.
yes
,
l
)
.
setNegativeButton
(
R
.
string
.
no_thanks
,
null
)
.
setCancelable
(
true
)
.
show
();
}
});
}
@Override
public
int
getItemCount
()
{
return
policyList
.
size
();
}
static
class
ViewHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
app_name
)
TextView
appName
;
@BindView
(
R
.
id
.
package_name
)
TextView
packageName
;
@BindView
(
R
.
id
.
app_icon
)
ImageView
appIcon
;
@BindView
(
R
.
id
.
master_switch
)
SwitchCompat
masterSwitch
;
@BindView
(
R
.
id
.
notification_switch
)
SwitchCompat
notificationSwitch
;
@BindView
(
R
.
id
.
logging_switch
)
SwitchCompat
loggingSwitch
;
@BindView
(
R
.
id
.
expand_layout
)
ViewGroup
expandLayout
;
@BindView
(
R
.
id
.
arrow
)
ImageView
arrow
;
@BindView
(
R
.
id
.
trigger
)
View
trigger
;
@BindView
(
R
.
id
.
delete
)
ImageView
delete
;
@BindView
(
R
.
id
.
more_info
)
ImageView
moreInfo
;
Expandable
settings
;
public
ViewHolder
(
View
itemView
)
{
super
(
itemView
);
new
PolicyAdapter$ViewHolder_ViewBinding
(
this
,
itemView
);
settings
=
new
ArrowExpandable
(
new
ExpandableViewHolder
(
expandLayout
),
arrow
);
}
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/ReposAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.database.Cursor
;
import
android.os.Build
;
import
android.text.TextUtils
;
import
android.util.Pair
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.ImageView
;
import
android.widget.SearchView
;
import
android.widget.TextView
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.data.database.RepoDatabaseHelper
;
import
com.topjohnwu.magisk.model.download.DownloadModuleService
;
import
com.topjohnwu.magisk.model.entity.Module
;
import
com.topjohnwu.magisk.model.entity.Repo
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.utils.Event
;
import
com.topjohnwu.magisk.view.MarkDownWindow
;
import
com.topjohnwu.magisk.view.dialogs.CustomAlertDialog
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Map
;
import
butterknife.BindView
;
import
java9.util.stream.StreamSupport
;
public
class
ReposAdapter
extends
SectionedAdapter
<
ReposAdapter
.
SectionHolder
,
ReposAdapter
.
RepoHolder
>
implements
Event
.
AutoListener
,
SearchView
.
OnQueryTextListener
{
private
static
final
int
UPDATES
=
0
;
private
static
final
int
INSTALLED
=
1
;
private
static
final
int
OTHERS
=
2
;
private
Map
<
String
,
Module
>
moduleMap
;
private
RepoDatabaseHelper
repoDB
;
private
List
<
Pair
<
Integer
,
List
<
Repo
>>>
repoPairs
;
private
List
<
Repo
>
fullList
;
private
SearchView
mSearch
;
public
ReposAdapter
()
{
repoDB
=
App
.
self
.
repoDB
;
moduleMap
=
Collections
.
emptyMap
();
fullList
=
Collections
.
emptyList
();
repoPairs
=
new
ArrayList
<>();
}
@Override
public
int
getSectionCount
()
{
return
repoPairs
.
size
();
}
@Override
public
int
getItemCount
(
int
section
)
{
return
repoPairs
.
get
(
section
).
second
.
size
();
}
@Override
public
SectionHolder
onCreateSectionViewHolder
(
ViewGroup
parent
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
section
,
parent
,
false
);
return
new
SectionHolder
(
v
);
}
@Override
public
RepoHolder
onCreateItemViewHolder
(
ViewGroup
parent
,
int
viewType
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_repo
,
parent
,
false
);
return
new
RepoHolder
(
v
);
}
@Override
public
void
onBindSectionViewHolder
(
SectionHolder
holder
,
int
section
)
{
switch
(
repoPairs
.
get
(
section
).
first
)
{
case
UPDATES:
holder
.
sectionText
.
setText
(
R
.
string
.
update_available
);
break
;
case
INSTALLED:
holder
.
sectionText
.
setText
(
R
.
string
.
installed
);
break
;
case
OTHERS:
holder
.
sectionText
.
setText
(
R
.
string
.
not_installed
);
break
;
}
}
@Override
public
void
onBindItemViewHolder
(
RepoHolder
holder
,
int
section
,
int
position
)
{
Repo
repo
=
repoPairs
.
get
(
section
).
second
.
get
(
position
);
Context
context
=
holder
.
itemView
.
getContext
();
String
name
=
repo
.
getName
();
String
version
=
repo
.
getVersion
();
String
author
=
repo
.
getAuthor
();
String
description
=
repo
.
getDescription
();
String
noInfo
=
context
.
getString
(
R
.
string
.
no_info_provided
);
holder
.
title
.
setText
(
TextUtils
.
isEmpty
(
name
)
?
noInfo
:
name
);
holder
.
versionName
.
setText
(
TextUtils
.
isEmpty
(
version
)
?
noInfo
:
version
);
holder
.
author
.
setText
(
TextUtils
.
isEmpty
(
author
)
?
noInfo
:
context
.
getString
(
R
.
string
.
author
,
author
));
holder
.
description
.
setText
(
TextUtils
.
isEmpty
(
description
)
?
noInfo
:
description
);
holder
.
updateTime
.
setText
(
context
.
getString
(
R
.
string
.
updated_on
,
repo
.
getLastUpdateString
()));
holder
.
infoLayout
.
setOnClickListener
(
v
->
MarkDownWindow
.
show
((
BaseActivity
)
context
,
null
,
repo
.
getDetailUrl
()));
holder
.
downloadImage
.
setOnClickListener
(
v
->
{
new
CustomAlertDialog
((
BaseActivity
)
context
)
.
setTitle
(
context
.
getString
(
R
.
string
.
repo_install_title
,
repo
.
getName
()))
.
setMessage
(
context
.
getString
(
R
.
string
.
repo_install_msg
,
repo
.
getDownloadFilename
()))
.
setCancelable
(
true
)
.
setPositiveButton
(
R
.
string
.
install
,
(
d
,
i
)
->
startDownload
((
BaseActivity
)
context
,
repo
,
true
))
.
setNeutralButton
(
R
.
string
.
download
,
(
d
,
i
)
->
startDownload
((
BaseActivity
)
context
,
repo
,
false
))
.
setNegativeButton
(
R
.
string
.
no_thanks
,
null
)
.
show
();
});
}
private
void
startDownload
(
BaseActivity
activity
,
Repo
repo
,
Boolean
install
)
{
activity
.
runWithExternalRW
(()
->
{
Intent
intent
=
new
Intent
(
activity
,
ClassMap
.
get
(
DownloadModuleService
.
class
))
.
putExtra
(
"repo"
,
repo
).
putExtra
(
"install"
,
install
);
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
O
)
{
activity
.
startForegroundService
(
intent
);
}
else
{
activity
.
startService
(
intent
);
}
});
}
private
void
updateLists
()
{
if
(
mSearch
!=
null
)
onQueryTextChange
(
mSearch
.
getQuery
().
toString
());
else
onQueryTextChange
(
""
);
}
private
static
boolean
noCaseContain
(
String
a
,
String
b
)
{
return
a
.
toLowerCase
().
contains
(
b
.
toLowerCase
());
}
public
void
setSearchView
(
SearchView
view
)
{
mSearch
=
view
;
mSearch
.
setOnQueryTextListener
(
this
);
}
public
void
notifyDBChanged
(
boolean
refresh
)
{
try
(
Cursor
c
=
repoDB
.
getRepoCursor
())
{
fullList
=
new
ArrayList
<>(
c
.
getCount
());
while
(
c
.
moveToNext
())
fullList
.
add
(
new
Repo
(
c
));
}
if
(
refresh
)
updateLists
();
}
@Override
public
void
onEvent
(
int
event
)
{
moduleMap
=
Event
.
getResult
(
event
);
updateLists
();
}
@Override
public
int
[]
getListeningEvents
()
{
return
new
int
[]
{
Event
.
MODULE_LOAD_DONE
};
}
@Override
public
boolean
onQueryTextSubmit
(
String
query
)
{
return
false
;
}
@Override
public
boolean
onQueryTextChange
(
String
s
)
{
List
<
Repo
>
updates
=
new
ArrayList
<>();
List
<
Repo
>
installed
=
new
ArrayList
<>();
List
<
Repo
>
others
=
new
ArrayList
<>();
StreamSupport
.
stream
(
fullList
)
.
filter
(
repo
->
noCaseContain
(
repo
.
getName
(),
s
)
||
noCaseContain
(
repo
.
getAuthor
(),
s
)
||
noCaseContain
(
repo
.
getDescription
(),
s
))
.
forEach
(
repo
->
{
Module
module
=
moduleMap
.
get
(
repo
.
getId
());
if
(
module
!=
null
)
{
if
(
repo
.
getVersionCode
()
>
module
.
getVersionCode
())
{
// Updates
updates
.
add
(
repo
);
}
else
{
installed
.
add
(
repo
);
}
}
else
{
others
.
add
(
repo
);
}
});
repoPairs
.
clear
();
if
(!
updates
.
isEmpty
())
repoPairs
.
add
(
new
Pair
<>(
UPDATES
,
updates
));
if
(!
installed
.
isEmpty
())
repoPairs
.
add
(
new
Pair
<>(
INSTALLED
,
installed
));
if
(!
others
.
isEmpty
())
repoPairs
.
add
(
new
Pair
<>(
OTHERS
,
others
));
notifyDataSetChanged
();
return
false
;
}
static
class
SectionHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
section_text
)
TextView
sectionText
;
SectionHolder
(
View
itemView
)
{
super
(
itemView
);
new
ReposAdapter$SectionHolder_ViewBinding
(
this
,
itemView
);
}
}
static
class
RepoHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
title
)
TextView
title
;
@BindView
(
R
.
id
.
version_name
)
TextView
versionName
;
@BindView
(
R
.
id
.
description
)
TextView
description
;
@BindView
(
R
.
id
.
author
)
TextView
author
;
@BindView
(
R
.
id
.
info_layout
)
View
infoLayout
;
@BindView
(
R
.
id
.
download
)
ImageView
downloadImage
;
@BindView
(
R
.
id
.
update_time
)
TextView
updateTime
;
RepoHolder
(
View
itemView
)
{
super
(
itemView
);
new
ReposAdapter$RepoHolder_ViewBinding
(
this
,
itemView
);
}
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/SectionedAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.view.ViewGroup
;
import
androidx.annotation.NonNull
;
import
androidx.recyclerview.widget.RecyclerView
;
public
abstract
class
SectionedAdapter
<
S
extends
RecyclerView
.
ViewHolder
,
C
extends
RecyclerView
.
ViewHolder
>
extends
RecyclerView
.
Adapter
<
RecyclerView
.
ViewHolder
>
{
private
static
final
int
SECTION_TYPE
=
Integer
.
MIN_VALUE
;
@NonNull
@Override
final
public
RecyclerView
.
ViewHolder
onCreateViewHolder
(
@NonNull
ViewGroup
parent
,
int
viewType
)
{
if
(
viewType
==
SECTION_TYPE
)
return
onCreateSectionViewHolder
(
parent
);
return
onCreateItemViewHolder
(
parent
,
viewType
);
}
@Override
@SuppressWarnings
(
"unchecked"
)
final
public
void
onBindViewHolder
(
@NonNull
RecyclerView
.
ViewHolder
holder
,
int
position
)
{
PositionInfo
info
=
getPositionInfo
(
position
);
if
(
info
.
position
==
-
1
)
onBindSectionViewHolder
((
S
)
holder
,
info
.
section
);
else
onBindItemViewHolder
((
C
)
holder
,
info
.
section
,
info
.
position
);
}
@Override
final
public
int
getItemCount
()
{
int
size
,
sec
;
size
=
sec
=
getSectionCount
();
for
(
int
i
=
0
;
i
<
sec
;
++
i
){
size
+=
getItemCount
(
i
);
}
return
size
;
}
@Override
final
public
int
getItemViewType
(
int
position
)
{
PositionInfo
info
=
getPositionInfo
(
position
);
if
(
info
.
position
==
-
1
)
return
SECTION_TYPE
;
else
return
getItemViewType
(
info
.
section
,
info
.
position
);
}
public
int
getItemViewType
(
int
section
,
int
position
)
{
return
0
;
}
protected
int
getSectionPosition
(
int
section
)
{
return
getItemPosition
(
section
,
-
1
);
}
protected
int
getItemPosition
(
int
section
,
int
position
)
{
int
realPosition
=
0
;
// Previous sections
for
(
int
i
=
0
;
i
<
section
;
++
i
)
{
realPosition
+=
getItemCount
(
i
)
+
1
;
}
// Current section
realPosition
+=
position
+
1
;
return
realPosition
;
}
private
PositionInfo
getPositionInfo
(
int
position
)
{
int
section
=
0
;
while
(
true
)
{
if
(
position
==
0
)
return
new
PositionInfo
(
section
,
-
1
);
position
-=
1
;
if
(
position
<
getItemCount
(
section
))
return
new
PositionInfo
(
section
,
position
);
position
-=
getItemCount
(
section
++);
}
}
private
static
class
PositionInfo
{
int
section
;
int
position
;
PositionInfo
(
int
section
,
int
position
)
{
this
.
section
=
section
;
this
.
position
=
position
;
}
}
public
abstract
int
getSectionCount
();
public
abstract
int
getItemCount
(
int
section
);
public
abstract
S
onCreateSectionViewHolder
(
ViewGroup
parent
);
public
abstract
C
onCreateItemViewHolder
(
ViewGroup
parent
,
int
viewType
);
public
abstract
void
onBindSectionViewHolder
(
S
holder
,
int
section
);
public
abstract
void
onBindItemViewHolder
(
C
holder
,
int
section
,
int
position
);
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/StringListAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.app.Activity
;
import
android.util.DisplayMetrics
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.TextView
;
import
androidx.annotation.IdRes
;
import
androidx.annotation.LayoutRes
;
import
androidx.annotation.NonNull
;
import
androidx.recyclerview.widget.RecyclerView
;
import
java.util.List
;
public
abstract
class
StringListAdapter
<
VH
extends
StringListAdapter
.
ViewHolder
>
extends
RecyclerView
.
Adapter
<
VH
>
{
private
RecyclerView
rv
;
private
boolean
dynamic
;
private
int
screenWidth
;
private
int
txtWidth
=
-
1
;
private
int
padding
;
protected
List
<
String
>
mList
;
public
StringListAdapter
(
List
<
String
>
list
)
{
this
(
list
,
false
);
}
public
StringListAdapter
(
List
<
String
>
list
,
boolean
isDynamic
)
{
mList
=
list
;
dynamic
=
isDynamic
;
}
@NonNull
@Override
public
final
VH
onCreateViewHolder
(
@NonNull
ViewGroup
parent
,
int
viewType
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
itemLayoutRes
(),
parent
,
false
);
VH
vh
=
createViewHolder
(
v
);
if
(
txtWidth
<
0
)
onUpdateTextWidth
(
vh
);
return
vh
;
}
@Override
public
void
onBindViewHolder
(
@NonNull
VH
holder
,
int
position
)
{
holder
.
txt
.
setText
(
mList
.
get
(
position
));
holder
.
txt
.
getLayoutParams
().
width
=
txtWidth
;
if
(
dynamic
)
onUpdateTextWidth
(
holder
);
}
protected
void
onUpdateTextWidth
(
VH
vh
)
{
if
(
txtWidth
<
0
)
{
txtWidth
=
screenWidth
-
padding
;
}
else
{
vh
.
txt
.
measure
(
0
,
0
);
int
width
=
vh
.
txt
.
getMeasuredWidth
();
if
(
width
>
txtWidth
)
{
txtWidth
=
width
;
vh
.
txt
.
getLayoutParams
().
width
=
txtWidth
;
}
}
if
(
rv
.
getWidth
()
!=
txtWidth
+
padding
)
rv
.
requestLayout
();
}
@Override
public
void
onAttachedToRecyclerView
(
@NonNull
RecyclerView
rv
)
{
DisplayMetrics
displayMetrics
=
new
DisplayMetrics
();
((
Activity
)
rv
.
getContext
()).
getWindowManager
()
.
getDefaultDisplay
().
getMetrics
(
displayMetrics
);
screenWidth
=
displayMetrics
.
widthPixels
;
padding
=
rv
.
getPaddingStart
()
+
rv
.
getPaddingEnd
();
this
.
rv
=
rv
;
}
@Override
public
final
int
getItemCount
()
{
return
mList
.
size
();
}
@LayoutRes
protected
abstract
int
itemLayoutRes
();
@NonNull
public
abstract
VH
createViewHolder
(
@NonNull
View
v
);
public
static
abstract
class
ViewHolder
extends
RecyclerView
.
ViewHolder
{
public
TextView
txt
;
public
ViewHolder
(
@NonNull
View
itemView
)
{
super
(
itemView
);
txt
=
itemView
.
findViewById
(
textViewResId
());
}
@IdRes
protected
abstract
int
textViewResId
();
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/SuLogAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
android.content.Context
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.view.animation.Animation
;
import
android.view.animation.RotateAnimation
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.data.database.MagiskDB
;
import
com.topjohnwu.magisk.model.entity.SuLogEntry
;
import
com.topjohnwu.magisk.view.ExpandableViewHolder
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
butterknife.BindView
;
public
class
SuLogAdapter
extends
SectionedAdapter
<
SuLogAdapter
.
SectionHolder
,
SuLogAdapter
.
LogViewHolder
>
{
private
List
<
List
<
SuLogEntry
>>
logEntries
;
private
Set
<
Integer
>
itemExpanded
,
sectionExpanded
;
private
MagiskDB
suDB
;
public
SuLogAdapter
(
MagiskDB
db
)
{
suDB
=
db
;
logEntries
=
Collections
.
emptyList
();
sectionExpanded
=
new
HashSet
<>();
itemExpanded
=
new
HashSet
<>();
}
@Override
public
int
getSectionCount
()
{
return
logEntries
.
size
();
}
@Override
public
int
getItemCount
(
int
section
)
{
return
sectionExpanded
.
contains
(
section
)
?
logEntries
.
get
(
section
).
size
()
:
0
;
}
@Override
public
SectionHolder
onCreateSectionViewHolder
(
ViewGroup
parent
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_sulog_group
,
parent
,
false
);
return
new
SectionHolder
(
v
);
}
@Override
public
LogViewHolder
onCreateItemViewHolder
(
ViewGroup
parent
,
int
viewType
)
{
View
v
=
LayoutInflater
.
from
(
parent
.
getContext
()).
inflate
(
R
.
layout
.
list_item_sulog
,
parent
,
false
);
return
new
LogViewHolder
(
v
);
}
@Override
public
void
onBindSectionViewHolder
(
SectionHolder
holder
,
int
section
)
{
SuLogEntry
entry
=
logEntries
.
get
(
section
).
get
(
0
);
holder
.
arrow
.
setRotation
(
sectionExpanded
.
contains
(
section
)
?
180
:
0
);
holder
.
itemView
.
setOnClickListener
(
v
->
{
RotateAnimation
rotate
;
if
(
sectionExpanded
.
contains
(
section
))
{
holder
.
arrow
.
setRotation
(
0
);
rotate
=
new
RotateAnimation
(
180
,
0
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
);
sectionExpanded
.
remove
(
section
);
notifyItemRangeRemoved
(
getItemPosition
(
section
,
0
),
logEntries
.
get
(
section
).
size
());
}
else
{
rotate
=
new
RotateAnimation
(
0
,
180
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
);
sectionExpanded
.
add
(
section
);
notifyItemRangeInserted
(
getItemPosition
(
section
,
0
),
logEntries
.
get
(
section
).
size
());
}
rotate
.
setDuration
(
300
);
rotate
.
setFillAfter
(
true
);
holder
.
arrow
.
setAnimation
(
rotate
);
});
holder
.
date
.
setText
(
entry
.
getDateString
());
}
@Override
public
void
onBindItemViewHolder
(
LogViewHolder
holder
,
int
section
,
int
position
)
{
SuLogEntry
entry
=
logEntries
.
get
(
section
).
get
(
position
);
int
realIdx
=
getItemPosition
(
section
,
position
);
holder
.
expandable
.
setExpanded
(
itemExpanded
.
contains
(
realIdx
));
holder
.
itemView
.
setOnClickListener
(
view
->
{
if
(
holder
.
expandable
.
isExpanded
())
{
holder
.
expandable
.
collapse
();
itemExpanded
.
remove
(
realIdx
);
}
else
{
holder
.
expandable
.
expand
();
itemExpanded
.
add
(
realIdx
);
}
});
Context
context
=
holder
.
itemView
.
getContext
();
holder
.
appName
.
setText
(
entry
.
appName
);
holder
.
action
.
setText
(
entry
.
action
?
R
.
string
.
grant
:
R
.
string
.
deny
);
holder
.
pid
.
setText
(
context
.
getString
(
R
.
string
.
pid
,
entry
.
fromPid
));
holder
.
uid
.
setText
(
context
.
getString
(
R
.
string
.
target_uid
,
entry
.
toUid
));
holder
.
command
.
setText
(
context
.
getString
(
R
.
string
.
command
,
entry
.
command
));
holder
.
time
.
setText
(
entry
.
getTimeString
());
}
public
void
notifyDBChanged
()
{
logEntries
=
suDB
.
getLogs
();
itemExpanded
.
clear
();
sectionExpanded
.
clear
();
sectionExpanded
.
add
(
0
);
notifyDataSetChanged
();
}
static
class
SectionHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
date
)
TextView
date
;
@BindView
(
R
.
id
.
arrow
)
ImageView
arrow
;
SectionHolder
(
View
itemView
)
{
super
(
itemView
);
new
SuLogAdapter$SectionHolder_ViewBinding
(
this
,
itemView
);
}
}
static
class
LogViewHolder
extends
RecyclerView
.
ViewHolder
{
@BindView
(
R
.
id
.
app_name
)
TextView
appName
;
@BindView
(
R
.
id
.
action
)
TextView
action
;
@BindView
(
R
.
id
.
time
)
TextView
time
;
@BindView
(
R
.
id
.
pid
)
TextView
pid
;
@BindView
(
R
.
id
.
uid
)
TextView
uid
;
@BindView
(
R
.
id
.
cmd
)
TextView
command
;
@BindView
(
R
.
id
.
expand_layout
)
ViewGroup
expandLayout
;
ExpandableViewHolder
expandable
;
LogViewHolder
(
View
itemView
)
{
super
(
itemView
);
new
SuLogAdapter$LogViewHolder_ViewBinding
(
this
,
itemView
);
expandable
=
new
ExpandableViewHolder
(
expandLayout
);
}
}
}
app/src/main/java/com/topjohnwu/magisk/model/adapters/TabFragmentAdapter.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
model
.
adapters
;
import
androidx.fragment.app.Fragment
;
import
androidx.fragment.app.FragmentManager
;
import
androidx.fragment.app.FragmentPagerAdapter
;
import
java.util.ArrayList
;
import
java.util.List
;
public
class
TabFragmentAdapter
extends
FragmentPagerAdapter
{
private
List
<
Fragment
>
fragmentList
;
private
List
<
String
>
titleList
;
public
TabFragmentAdapter
(
FragmentManager
fm
)
{
super
(
fm
);
fragmentList
=
new
ArrayList
<>();
titleList
=
new
ArrayList
<>();
}
@Override
public
Fragment
getItem
(
int
position
)
{
return
fragmentList
.
get
(
position
);
}
@Override
public
int
getCount
()
{
return
fragmentList
.
size
();
}
@Override
public
CharSequence
getPageTitle
(
int
position
)
{
return
titleList
.
get
(
position
);
}
public
void
addTab
(
Fragment
fragment
,
String
title
)
{
fragmentList
.
add
(
fragment
);
titleList
.
add
(
title
);
}
}
app/src/main/java/com/topjohnwu/magisk/model/download/DownloadModuleService.java
View file @
2813d203
...
...
@@ -6,8 +6,6 @@ import android.content.Intent;
import
android.net.Uri
;
import
android.os.IBinder
;
import
androidx.annotation.Nullable
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Const
;
...
...
@@ -31,6 +29,8 @@ import java.util.zip.ZipEntry;
import
java.util.zip.ZipInputStream
;
import
java.util.zip.ZipOutputStream
;
import
androidx.annotation.Nullable
;
public
class
DownloadModuleService
extends
Service
{
private
List
<
ProgressNotification
>
notifications
;
...
...
app/src/main/java/com/topjohnwu/magisk/model/entity/BaseModule.java
View file @
2813d203
...
...
@@ -5,10 +5,10 @@ import android.database.Cursor;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
androidx.annotation.NonNull
;
import
java.util.List
;
import
androidx.annotation.NonNull
;
public
abstract
class
BaseModule
implements
Comparable
<
BaseModule
>,
Parcelable
{
private
String
mId
,
mName
,
mVersion
,
mAuthor
,
mDescription
;
...
...
app/src/main/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity
import
android.content.pm.ApplicationInfo
import
android.graphics.drawable.Drawable
import
com.topjohnwu.magisk.utils.packageInfo
import
com.topjohnwu.magisk.utils.processes
class
HideAppInfo
(
val
info
:
ApplicationInfo
,
val
name
:
String
,
val
icon
:
Drawable
)
{
val
processes
=
info
.
packageInfo
?.
processes
?.
distinct
()
?:
listOf
(
info
.
packageName
)
}
app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt
0 → 100644
View file @
2813d203
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
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/Policy.java
View file @
2813d203
...
...
@@ -4,10 +4,10 @@ import android.content.ContentValues;
import
android.content.pm.ApplicationInfo
;
import
android.content.pm.PackageManager
;
import
androidx.annotation.NonNull
;
import
com.topjohnwu.magisk.utils.Utils
;
import
androidx.annotation.NonNull
;
public
class
Policy
implements
Comparable
<
Policy
>{
public
static
final
int
INTERACTIVE
=
0
;
...
...
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ConsoleRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.topjohnwu.magisk.R
class
ConsoleRvItem
(
val
item
:
String
)
:
ComparableRvItem
<
ConsoleRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_console
override
fun
contentSameAs
(
other
:
ConsoleRvItem
)
=
itemSameAs
(
other
)
override
fun
itemSameAs
(
other
:
ConsoleRvItem
)
=
item
==
other
.
item
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/HideRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.rxbus.RxBus
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.entity.HideAppInfo
import
com.topjohnwu.magisk.model.entity.HideTarget
import
com.topjohnwu.magisk.model.entity.state.IndeterminateState
import
com.topjohnwu.magisk.model.events.HideProcessEvent
import
com.topjohnwu.magisk.utils.inject
import
com.topjohnwu.magisk.utils.toggle
class
HideRvItem
(
val
item
:
HideAppInfo
,
targets
:
List
<
HideTarget
>)
:
ComparableRvItem
<
HideRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_hide_app
val
packageName
=
item
.
info
.
packageName
.
orEmpty
()
val
items
=
DiffObservableList
(
callback
).
also
{
val
items
=
item
.
processes
.
map
{
val
isHidden
=
targets
.
any
{
target
->
packageName
==
target
.
packageName
&&
it
==
target
.
process
}
HideProcessRvItem
(
packageName
,
it
,
isHidden
)
}
it
.
update
(
items
)
}
val
isHiddenState
=
KObservableField
(
currentState
)
val
isExpanded
=
KObservableField
(
false
)
private
val
itemsProcess
get
()
=
items
.
filterIsInstance
<
HideProcessRvItem
>()
private
val
currentState
get
()
=
when
(
itemsProcess
.
count
{
it
.
isHidden
.
value
})
{
items
.
size
->
IndeterminateState
.
CHECKED
in
1
until
items
.
size
->
IndeterminateState
.
INDETERMINATE
else
->
IndeterminateState
.
UNCHECKED
}
init
{
itemsProcess
.
forEach
{
it
.
isHidden
.
addOnPropertyChangedCallback
{
isHiddenState
.
value
=
currentState
}
}
}
fun
toggle
()
{
val
desiredState
=
when
(
isHiddenState
.
value
)
{
IndeterminateState
.
INDETERMINATE
,
IndeterminateState
.
UNCHECKED
->
true
IndeterminateState
.
CHECKED
->
false
}
itemsProcess
.
forEach
{
it
.
isHidden
.
value
=
desiredState
}
isHiddenState
.
value
=
currentState
}
fun
toggleExpansion
()
{
if
(
items
.
size
<=
1
)
return
isExpanded
.
toggle
()
}
override
fun
contentSameAs
(
other
:
HideRvItem
):
Boolean
=
items
.
all
{
other
.
items
.
contains
(
it
)
}
override
fun
itemSameAs
(
other
:
HideRvItem
):
Boolean
=
item
.
info
==
other
.
item
.
info
}
class
HideProcessRvItem
(
val
packageName
:
String
,
val
process
:
String
,
isHidden
:
Boolean
)
:
ComparableRvItem
<
HideProcessRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_hide_process
val
isHidden
=
KObservableField
(
isHidden
)
private
val
rxBus
:
RxBus
by
inject
()
init
{
this
.
isHidden
.
addOnPropertyChangedCallback
{
rxBus
.
post
(
HideProcessEvent
(
this
@HideProcessRvItem
))
}
}
fun
toggle
()
=
isHidden
.
toggle
()
override
fun
contentSameAs
(
other
:
HideProcessRvItem
):
Boolean
=
itemSameAs
(
other
)
override
fun
itemSameAs
(
other
:
HideProcessRvItem
):
Boolean
=
packageName
==
other
.
packageName
&&
process
==
other
.
process
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LogRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
androidx.databinding.ObservableList
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.entity.SuLogEntry
import
com.topjohnwu.magisk.utils.toggle
class
LogRvItem
:
ComparableRvItem
<
LogRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_page_log
val
items
=
DiffObservableList
(
callback
)
fun
update
(
list
:
List
<
LogItemRvItem
>)
{
list
.
firstOrNull
()
?.
isExpanded
?.
value
=
true
items
.
update
(
list
)
}
//two of these will never be present, safe to assume it's unique
override
fun
contentSameAs
(
other
:
LogRvItem
):
Boolean
=
false
override
fun
itemSameAs
(
other
:
LogRvItem
):
Boolean
=
false
}
class
LogItemRvItem
(
val
items
:
ObservableList
<
ComparableRvItem
<*>>
)
:
ComparableRvItem
<
LogItemRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_superuser_log
val
date
=
items
.
filterIsInstance
<
LogItemEntryRvItem
>().
firstOrNull
()
?.
item
?.
dateString
.
orEmpty
()
val
isExpanded
=
KObservableField
(
false
)
fun
toggle
()
=
isExpanded
.
toggle
()
override
fun
contentSameAs
(
other
:
LogItemRvItem
):
Boolean
=
items
.
any
{
!
other
.
items
.
contains
(
it
)
}
override
fun
itemSameAs
(
other
:
LogItemRvItem
):
Boolean
=
date
==
other
.
date
}
class
LogItemEntryRvItem
(
val
item
:
SuLogEntry
)
:
ComparableRvItem
<
LogItemEntryRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_superuser_log_entry
val
isExpanded
=
KObservableField
(
false
)
fun
toggle
()
=
isExpanded
.
toggle
()
override
fun
contentSameAs
(
other
:
LogItemEntryRvItem
)
=
item
.
fromUid
==
other
.
item
.
fromUid
&&
item
.
toUid
==
other
.
item
.
toUid
&&
item
.
fromPid
==
other
.
item
.
fromPid
&&
item
.
packageName
==
other
.
item
.
packageName
&&
item
.
command
==
other
.
item
.
command
&&
item
.
action
==
other
.
item
.
action
&&
item
.
date
==
other
.
item
.
date
override
fun
itemSameAs
(
other
:
LogItemEntryRvItem
)
=
item
.
appName
==
other
.
item
.
appName
}
class
MagiskLogRvItem
:
ComparableRvItem
<
MagiskLogRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_page_magisk_log
val
items
=
DiffObservableList
(
callback
)
fun
update
(
list
:
List
<
ConsoleRvItem
>)
{
items
.
update
(
list
)
}
//two of these will never be present, safe to assume it's unique
override
fun
contentSameAs
(
other
:
MagiskLogRvItem
):
Boolean
=
false
override
fun
itemSameAs
(
other
:
MagiskLogRvItem
):
Boolean
=
false
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
android.content.res.Resources
import
androidx.annotation.StringRes
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.entity.Module
import
com.topjohnwu.magisk.model.entity.Repo
import
com.topjohnwu.magisk.utils.get
import
com.topjohnwu.magisk.utils.toggle
class
ModuleRvItem
(
val
item
:
Module
)
:
ComparableRvItem
<
ModuleRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_module
val
lastActionNotice
=
KObservableField
(
""
)
val
isChecked
=
KObservableField
(
item
.
isEnabled
)
val
isDeletable
=
KObservableField
(
item
.
willBeRemoved
())
init
{
isChecked
.
addOnPropertyChangedCallback
{
when
(
it
)
{
true
->
item
.
removeDisableFile
().
notice
(
R
.
string
.
disable_file_removed
)
false
->
item
.
createDisableFile
().
notice
(
R
.
string
.
disable_file_created
)
}
}
isDeletable
.
addOnPropertyChangedCallback
{
when
(
it
)
{
true
->
item
.
createRemoveFile
().
notice
(
R
.
string
.
remove_file_created
)
false
->
item
.
deleteRemoveFile
().
notice
(
R
.
string
.
remove_file_deleted
)
}
}
when
{
item
.
isUpdated
->
notice
(
R
.
string
.
update_file_created
)
item
.
willBeRemoved
()
->
notice
(
R
.
string
.
remove_file_created
)
}
}
fun
toggle
()
=
isChecked
.
toggle
()
fun
toggleDelete
()
=
isDeletable
.
toggle
()
@Suppress
(
"unused"
)
private
fun
Any
.
notice
(
@StringRes
info
:
Int
)
{
lastActionNotice
.
value
=
get
<
Resources
>().
getString
(
info
)
}
override
fun
contentSameAs
(
other
:
ModuleRvItem
):
Boolean
=
item
.
version
==
other
.
item
.
version
&&
item
.
versionCode
==
other
.
item
.
versionCode
&&
item
.
description
==
other
.
item
.
description
override
fun
itemSameAs
(
other
:
ModuleRvItem
):
Boolean
=
item
.
name
==
other
.
item
.
name
}
class
RepoRvItem
(
val
item
:
Repo
)
:
ComparableRvItem
<
RepoRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_repo
override
fun
contentSameAs
(
other
:
RepoRvItem
):
Boolean
=
item
.
version
==
other
.
item
.
version
&&
item
.
lastUpdate
==
other
.
item
.
lastUpdate
&&
item
.
versionCode
==
other
.
item
.
versionCode
&&
item
.
description
==
other
.
item
.
description
override
fun
itemSameAs
(
other
:
RepoRvItem
):
Boolean
=
item
.
detailUrl
==
other
.
item
.
detailUrl
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/PolicyRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
android.graphics.drawable.Drawable
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.rxbus.RxBus
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.events.PolicyEnableEvent
import
com.topjohnwu.magisk.model.events.PolicyUpdateEvent
import
com.topjohnwu.magisk.utils.inject
import
com.topjohnwu.magisk.utils.toggle
class
PolicyRvItem
(
val
item
:
Policy
,
val
icon
:
Drawable
)
:
ComparableRvItem
<
PolicyRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_policy
val
isExpanded
=
KObservableField
(
false
)
val
isEnabled
=
KObservableField
(
item
.
policy
==
Policy
.
ALLOW
)
val
shouldNotify
=
KObservableField
(
item
.
notification
)
val
shouldLog
=
KObservableField
(
item
.
logging
)
fun
toggle
()
=
isExpanded
.
toggle
()
private
val
rxBus
:
RxBus
by
inject
()
init
{
isEnabled
.
addOnPropertyChangedCallback
{
it
?:
return
@addOnPropertyChangedCallback
rxBus
.
post
(
PolicyEnableEvent
(
this
@PolicyRvItem
,
it
))
}
shouldNotify
.
addOnPropertyChangedCallback
{
it
?:
return
@addOnPropertyChangedCallback
item
.
notification
=
it
rxBus
.
post
(
PolicyUpdateEvent
.
Notification
(
this
@PolicyRvItem
))
}
shouldLog
.
addOnPropertyChangedCallback
{
it
?:
return
@addOnPropertyChangedCallback
item
.
logging
=
it
rxBus
.
post
(
PolicyUpdateEvent
.
Log
(
this
@PolicyRvItem
))
}
}
override
fun
contentSameAs
(
other
:
PolicyRvItem
):
Boolean
=
itemSameAs
(
other
)
override
fun
itemSameAs
(
other
:
PolicyRvItem
):
Boolean
=
item
.
uid
==
other
.
item
.
uid
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.topjohnwu.magisk.R
class
SectionRvItem
(
val
text
:
String
)
:
ComparableRvItem
<
SectionRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_section
override
fun
contentSameAs
(
other
:
SectionRvItem
)
=
itemSameAs
(
other
)
override
fun
itemSameAs
(
other
:
SectionRvItem
)
=
text
==
other
.
text
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.recycler
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.topjohnwu.magisk.R
class
SpinnerRvItem
(
val
item
:
String
)
:
ComparableRvItem
<
SpinnerRvItem
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
item_spinner
override
fun
contentSameAs
(
other
:
SpinnerRvItem
)
=
itemSameAs
(
other
)
override
fun
itemSameAs
(
other
:
SpinnerRvItem
)
=
item
==
other
.
item
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/entity/state/IndeterminateState.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.entity.state
enum
class
IndeterminateState
{
INDETERMINATE
,
CHECKED
,
UNCHECKED
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.events
import
com.skoumal.teanity.rxbus.RxBus
import
com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem
import
com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem
import
com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem
class
HideProcessEvent
(
val
item
:
HideProcessRvItem
)
:
RxBus
.
Event
class
PolicyEnableEvent
(
val
item
:
PolicyRvItem
,
val
enable
:
Boolean
)
:
RxBus
.
Event
sealed
class
PolicyUpdateEvent
(
val
item
:
PolicyRvItem
)
:
RxBus
.
Event
{
class
Notification
(
item
:
PolicyRvItem
)
:
PolicyUpdateEvent
(
item
)
class
Log
(
item
:
PolicyRvItem
)
:
PolicyUpdateEvent
(
item
)
}
class
ModuleUpdatedEvent
(
val
item
:
ModuleRvItem
)
:
RxBus
.
Event
app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.events
import
android.app.Activity
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.entity.Repo
import
io.reactivex.subjects.PublishSubject
data class
OpenLinkEvent
(
val
url
:
String
)
:
ViewEvent
()
class
ManagerInstallEvent
:
ViewEvent
()
class
MagiskInstallEvent
:
ViewEvent
()
class
ManagerChangelogEvent
:
ViewEvent
()
class
MagiskChangelogEvent
:
ViewEvent
()
class
UninstallEvent
:
ViewEvent
()
class
EnvFixEvent
:
ViewEvent
()
class
UpdateSafetyNetEvent
:
ViewEvent
()
class
ViewActionEvent
(
val
action
:
Activity
.()
->
Unit
)
:
ViewEvent
()
class
OpenFilePickerEvent
:
ViewEvent
()
class
OpenChangelogEvent
(
val
item
:
Repo
)
:
ViewEvent
()
class
InstallModuleEvent
(
val
item
:
Repo
)
:
ViewEvent
()
class
PageChangedEvent
:
ViewEvent
()
class
PermissionEvent
(
val
permissions
:
List
<
String
>,
val
callback
:
PublishSubject
<
Boolean
>
)
:
ViewEvent
()
class
BackPressEvent
:
ViewEvent
()
class
SuDialogEvent
(
val
policy
:
Policy
)
:
ViewEvent
()
class
DieEvent
:
ViewEvent
()
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/flash/FlashResultListener.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.flash
interface
FlashResultListener
{
fun
onResult
(
isSuccess
:
Boolean
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/flash/Flashing.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.flash
import
android.content.Context
import
android.net.Uri
import
androidx.core.os.postDelayed
import
com.topjohnwu.magisk.tasks.FlashZip
import
com.topjohnwu.magisk.utils.Utils
import
com.topjohnwu.magisk.utils.inject
import
com.topjohnwu.superuser.Shell
import
com.topjohnwu.superuser.internal.UiThreadHandler
sealed
class
Flashing
(
uri
:
Uri
,
private
val
console
:
MutableList
<
String
>,
log
:
MutableList
<
String
>,
private
val
resultListener
:
FlashResultListener
)
:
FlashZip
(
uri
,
console
,
log
)
{
override
fun
onResult
(
success
:
Boolean
)
{
if
(!
success
)
{
console
.
add
(
"! Installation failed"
)
}
resultListener
.
onResult
(
success
)
}
class
Install
(
uri
:
Uri
,
console
:
MutableList
<
String
>,
log
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Flashing
(
uri
,
console
,
log
,
resultListener
)
{
override
fun
onResult
(
success
:
Boolean
)
{
if
(
success
)
{
Utils
.
loadModules
()
}
super
.
onResult
(
success
)
}
}
class
Uninstall
(
uri
:
Uri
,
console
:
MutableList
<
String
>,
log
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Flashing
(
uri
,
console
,
log
,
resultListener
)
{
private
val
context
:
Context
by
inject
()
override
fun
onResult
(
success
:
Boolean
)
{
if
(
success
)
{
UiThreadHandler
.
handler
.
postDelayed
(
3000
)
{
Shell
.
su
(
"pm uninstall "
+
context
.
packageName
).
exec
()
}
}
super
.
onResult
(
success
)
}
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/flash/Patching.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.flash
import
android.net.Uri
import
com.topjohnwu.magisk.tasks.MagiskInstaller
import
com.topjohnwu.superuser.Shell
sealed
class
Patching
(
private
val
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
private
val
resultListener
:
FlashResultListener
)
:
MagiskInstaller
(
console
,
logs
)
{
override
fun
onResult
(
success
:
Boolean
)
{
if
(
success
)
{
console
.
add
(
"- All done!"
)
}
else
{
Shell
.
sh
(
"rm -rf $installDir"
).
submit
()
console
.
add
(
"! Installation failed"
)
}
resultListener
.
onResult
(
success
)
}
class
File
(
private
val
uri
:
Uri
,
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Patching
(
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
extractZip
()
&&
handleFile
(
uri
)
&&
patchBoot
()
&&
storeBoot
()
}
class
SecondSlot
(
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Patching
(
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
findSecondaryImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
&&
postOTA
()
}
class
Direct
(
console
:
MutableList
<
String
>,
logs
:
MutableList
<
String
>,
resultListener
:
FlashResultListener
)
:
Patching
(
console
,
logs
,
resultListener
)
{
override
fun
operations
()
=
findImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/navigation/MagiskNavigationEvent.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.navigation
import
android.os.Bundle
import
androidx.annotation.AnimRes
import
androidx.annotation.AnimatorRes
import
androidx.fragment.app.Fragment
import
com.skoumal.teanity.viewevents.NavigationDslMarker
import
com.skoumal.teanity.viewevents.ViewEvent
import
kotlin.reflect.KClass
class
MagiskNavigationEvent
(
val
navDirections
:
MagiskNavDirectionsBuilder
,
val
navOptions
:
MagiskNavOptions
,
val
animOptions
:
MagiskAnimBuilder
)
:
ViewEvent
()
{
companion
object
{
operator
fun
invoke
(
builder
:
Builder
.()
->
Unit
)
=
Builder
().
apply
(
builder
).
build
()
}
@NavigationDslMarker
class
Builder
{
private
var
animOptions
:
MagiskAnimBuilder
=
MagiskAnimBuilder
()
private
var
navOptions
:
MagiskNavOptions
=
MagiskNavOptions
()
private
val
directionsBuilder
=
MagiskNavDirectionsBuilder
()
fun
args
(
builder
:
Bundle
.()
->
Unit
)
=
directionsBuilder
.
args
(
builder
)
fun
navAnim
(
builder
:
MagiskAnimBuilder
.()
->
Unit
)
{
animOptions
=
MagiskAnimBuilder
().
apply
(
builder
)
}
fun
navOptions
(
builder
:
MagiskNavOptions
.()
->
Unit
)
{
navOptions
=
MagiskNavOptions
().
apply
(
builder
)
}
fun
navDirections
(
builder
:
MagiskNavDirectionsBuilder
.()
->
Unit
)
{
directionsBuilder
.
apply
(
builder
)
}
internal
fun
build
()
=
MagiskNavigationEvent
(
directionsBuilder
,
navOptions
,
animOptions
)
}
}
@NavigationDslMarker
class
MagiskNavDirectionsBuilder
{
var
destination
:
KClass
<
out
Fragment
>?
=
null
var
isActivity
:
Boolean
=
false
val
args
:
Bundle
=
Bundle
()
fun
args
(
builder
:
Bundle
.()
->
Unit
)
=
args
.
apply
(
builder
)
}
@NavigationDslMarker
class
MagiskNavOptions
{
var
popUpTo
:
KClass
<*>?
=
null
var
inclusive
:
Boolean
=
false
var
clearTask
:
Boolean
=
false
var
singleTop
:
Boolean
=
false
}
@NavigationDslMarker
class
MagiskAnimBuilder
{
@AnimRes
@AnimatorRes
var
enter
=
0
@AnimRes
@AnimatorRes
var
exit
=
0
@AnimRes
@AnimatorRes
var
popEnter
=
0
@AnimRes
@AnimatorRes
var
popExit
=
0
val
anySet
:
Boolean
get
()
=
enter
!=
0
||
exit
!=
0
||
popEnter
!=
0
||
popExit
!=
0
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/navigation/Navigation.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.navigation
import
com.topjohnwu.magisk.ui.hide.MagiskHideFragment
import
com.topjohnwu.magisk.ui.home.MagiskFragment
import
com.topjohnwu.magisk.ui.log.LogFragment
import
com.topjohnwu.magisk.ui.module.ModulesFragment
import
com.topjohnwu.magisk.ui.module.ReposFragment
import
com.topjohnwu.magisk.ui.settings.SettingsFragment
import
com.topjohnwu.magisk.ui.superuser.SuperuserFragment
object
Navigation
{
fun
home
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
MagiskFragment
::
class
}
navOptions
{
popUpTo
=
MagiskFragment
::
class
}
}
fun
superuser
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
SuperuserFragment
::
class
}
}
fun
modules
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
ModulesFragment
::
class
}
}
fun
repos
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
ReposFragment
::
class
}
}
fun
hide
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
MagiskHideFragment
::
class
}
}
fun
log
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
LogFragment
::
class
}
}
fun
settings
()
=
MagiskNavigationEvent
{
navDirections
{
destination
=
SettingsFragment
::
class
}
}
fun
fromSection
(
section
:
String
)
=
when
(
section
)
{
"superuser"
->
superuser
()
"modules"
->
modules
()
"downloads"
->
repos
()
"magiskhide"
->
hide
()
"log"
->
log
()
"settings"
->
settings
()
else
->
home
()
}
object
Main
{
const
val
OPEN_NAV
=
1
}
}
app/src/main/java/com/topjohnwu/magisk/model/navigation/Navigator.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.navigation
import
androidx.fragment.app.Fragment
import
kotlin.reflect.KClass
interface
Navigator
{
//TODO Elevate Fragment to MagiskFragment<*,*> once everything is on board with it
val
baseFragments
:
List
<
KClass
<
out
Fragment
>>
fun
navigateTo
(
event
:
MagiskNavigationEvent
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/observer/Observer.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.observer
import
androidx.databinding.Observable
import
androidx.databinding.ObservableField
import
java.io.Serializable
class
Observer
<
T
>(
vararg
dependencies
:
Observable
,
private
val
observer
:
()
->
T
)
:
ObservableField
<
T
>(*
dependencies
),
Serializable
{
val
value
:
T
get
()
=
observer
()
@Deprecated
(
message
=
"Use KObservableField.value syntax from code"
,
replaceWith
=
ReplaceWith
(
"value"
)
)
override
fun
get
():
T
{
return
value
}
@Deprecated
(
message
=
"Observer cannot be set"
,
level
=
DeprecationLevel
.
HIDDEN
)
override
fun
set
(
newValue
:
T
)
{
}
override
fun
toString
():
String
{
return
"Observer(value=$value)"
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/permissions/PermissionRequestBuilder.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.model.permissions
typealias
SimpleCallback
=
()
->
Unit
typealias
PermissionRationaleCallback
=
(
List
<
String
>)
->
Unit
class
PermissionRequestBuilder
{
private
var
onSuccessCallback
:
SimpleCallback
=
{}
private
var
onFailureCallback
:
SimpleCallback
=
{}
private
var
onShowRationaleCallback
:
PermissionRationaleCallback
=
{}
fun
onSuccess
(
callback
:
SimpleCallback
)
{
onSuccessCallback
=
callback
}
fun
onFailure
(
callback
:
SimpleCallback
)
{
onFailureCallback
=
callback
}
fun
onShowRationale
(
callback
:
PermissionRationaleCallback
)
{
onShowRationaleCallback
=
callback
}
fun
build
():
PermissionRequest
{
return
PermissionRequest
(
onSuccessCallback
,
onFailureCallback
,
onShowRationaleCallback
)
}
}
class
PermissionRequest
(
private
val
onSuccessCallback
:
SimpleCallback
,
private
val
onFailureCallback
:
SimpleCallback
,
private
val
onShowRationaleCallback
:
PermissionRationaleCallback
)
{
fun
onSuccess
()
=
onSuccessCallback
()
fun
onFailure
()
=
onFailureCallback
()
fun
onShowRationale
(
permissions
:
List
<
String
>)
=
onShowRationaleCallback
(
permissions
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/model/receiver/GeneralReceiver.java
View file @
2813d203
...
...
@@ -61,12 +61,12 @@ public class GeneralReceiver extends BroadcastReceiver {
case
Intent
.
ACTION_PACKAGE_REPLACED
:
// This will only work pre-O
if
(
Config
.
get
(
Config
.
Key
.
SU_REAUTH
))
{
app
.
mDB
.
deletePolicy
(
getPkg
(
intent
));
app
.
getDB
()
.
deletePolicy
(
getPkg
(
intent
));
}
break
;
case
Intent
.
ACTION_PACKAGE_FULLY_REMOVED
:
String
pkg
=
getPkg
(
intent
);
app
.
mDB
.
deletePolicy
(
pkg
);
app
.
getDB
()
.
deletePolicy
(
pkg
);
Shell
.
su
(
"magiskhide --rm "
+
pkg
).
submit
();
break
;
case
Intent
.
ACTION_LOCALE_CHANGED
:
...
...
app/src/main/java/com/topjohnwu/magisk/model/worker/DelegateWorker.java
View file @
2813d203
...
...
@@ -4,6 +4,12 @@ import android.content.Context;
import
android.net.Network
;
import
android.net.Uri
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.UUID
;
import
androidx.annotation.MainThread
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
...
...
@@ -11,12 +17,6 @@ import androidx.annotation.RequiresApi;
import
androidx.work.Data
;
import
androidx.work.ListenableWorker
;
import
com.google.common.util.concurrent.ListenableFuture
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.UUID
;
public
abstract
class
DelegateWorker
{
private
ListenableWorker
worker
;
...
...
app/src/main/java/com/topjohnwu/magisk/tasks/UpdateRepos.java
View file @
2813d203
...
...
@@ -34,7 +34,7 @@ import java.util.concurrent.Future;
public
class
UpdateRepos
{
private
static
final
DateFormat
DATE_FORMAT
;
private
App
app
=
App
.
self
;
private
final
App
app
=
App
.
self
;
private
Set
<
String
>
cached
;
private
Queue
<
Pair
<
String
,
Date
>>
moduleQueue
;
...
...
@@ -116,17 +116,17 @@ public class UpdateRepos {
Pair
<
String
,
Date
>
pair
=
moduleQueue
.
poll
();
if
(
pair
==
null
)
return
;
Repo
repo
=
app
.
repoDB
.
getRepo
(
pair
.
first
);
Repo
repo
=
app
.
getRepoDB
()
.
getRepo
(
pair
.
first
);
try
{
if
(
repo
==
null
)
repo
=
new
Repo
(
pair
.
first
);
else
cached
.
remove
(
pair
.
first
);
repo
.
update
(
pair
.
second
);
app
.
repoDB
.
addRepo
(
repo
);
app
.
getRepoDB
()
.
addRepo
(
repo
);
}
catch
(
Repo
.
IllegalRepoException
e
)
{
Logger
.
debug
(
e
.
getMessage
());
app
.
repoDB
.
removeRepo
(
pair
.
first
);
app
.
getRepoDB
()
.
removeRepo
(
pair
.
first
);
}
}
});
...
...
@@ -134,7 +134,7 @@ public class UpdateRepos {
}
private
void
fullReload
()
{
Cursor
c
=
app
.
repoDB
.
getRawCursor
();
Cursor
c
=
app
.
getRepoDB
()
.
getRawCursor
();
runTasks
(()
->
{
while
(
true
)
{
Repo
repo
;
...
...
@@ -145,10 +145,10 @@ public class UpdateRepos {
}
try
{
repo
.
update
();
app
.
repoDB
.
addRepo
(
repo
);
app
.
getRepoDB
()
.
addRepo
(
repo
);
}
catch
(
Repo
.
IllegalRepoException
e
)
{
Logger
.
debug
(
e
.
getMessage
());
app
.
repoDB
.
removeRepo
(
repo
);
app
.
getRepoDB
()
.
removeRepo
(
repo
);
}
}
});
...
...
@@ -157,12 +157,12 @@ public class UpdateRepos {
public
void
exec
(
boolean
force
)
{
Event
.
reset
(
Event
.
REPO_LOAD_DONE
);
App
.
THREAD_POOL
.
execute
(()
->
{
cached
=
Collections
.
synchronizedSet
(
app
.
repoDB
.
getRepoIDSet
());
cached
=
Collections
.
synchronizedSet
(
app
.
getRepoDB
()
.
getRepoIDSet
());
moduleQueue
=
new
ConcurrentLinkedQueue
<>();
if
(
loadPages
())
{
// The leftover cached means they are removed from online repo
app
.
repoDB
.
removeRepo
(
cached
);
app
.
getRepoDB
()
.
removeRepo
(
cached
);
}
else
if
(
force
)
{
fullReload
();
}
...
...
app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
;
import
android.content.Intent
;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.view.Menu
;
import
android.view.MenuItem
;
import
android.view.View
;
import
androidx.annotation.NonNull
;
import
androidx.appcompat.app.ActionBarDrawerToggle
;
import
androidx.appcompat.widget.Toolbar
;
import
androidx.drawerlayout.widget.DrawerLayout
;
import
androidx.fragment.app.Fragment
;
import
androidx.fragment.app.FragmentTransaction
;
import
com.google.android.material.navigation.NavigationView
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.ui.hide.MagiskHideFragment
;
import
com.topjohnwu.magisk.ui.home.MagiskFragment
;
import
com.topjohnwu.magisk.ui.log.LogFragment
;
import
com.topjohnwu.magisk.ui.module.ModulesFragment
;
import
com.topjohnwu.magisk.ui.module.ReposFragment
;
import
com.topjohnwu.magisk.ui.settings.SettingsFragment
;
import
com.topjohnwu.magisk.ui.superuser.SuperuserFragment
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.net.Networking
;
import
com.topjohnwu.superuser.Shell
;
import
butterknife.BindView
;
public
class
MainActivity
extends
BaseActivity
implements
NavigationView
.
OnNavigationItemSelectedListener
{
private
final
Handler
mDrawerHandler
=
new
Handler
();
private
int
mDrawerItem
;
private
static
boolean
fromShortcut
=
false
;
@BindView
(
R
.
id
.
toolbar
)
public
Toolbar
toolbar
;
@BindView
(
R
.
id
.
drawer_layout
)
DrawerLayout
drawer
;
@BindView
(
R
.
id
.
nav_view
)
NavigationView
navigationView
;
private
float
toolbarElevation
;
@Override
public
int
getDarkTheme
()
{
return
R
.
style
.
AppTheme_Dark
;
}
@Override
protected
void
onCreate
(
final
Bundle
savedInstanceState
)
{
if
(!
SplashActivity
.
DONE
)
{
startActivity
(
new
Intent
(
this
,
ClassMap
.
get
(
SplashActivity
.
class
)));
finish
();
}
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_main
);
new
MainActivity_ViewBinding
(
this
);
checkHideSection
();
setSupportActionBar
(
toolbar
);
ActionBarDrawerToggle
toggle
=
new
ActionBarDrawerToggle
(
this
,
drawer
,
toolbar
,
R
.
string
.
magisk
,
R
.
string
.
magisk
)
{
@Override
public
void
onDrawerOpened
(
View
drawerView
)
{
super
.
onDrawerOpened
(
drawerView
);
super
.
onDrawerSlide
(
drawerView
,
0
);
// this disables the arrow @ completed tate
}
@Override
public
void
onDrawerSlide
(
View
drawerView
,
float
slideOffset
)
{
super
.
onDrawerSlide
(
drawerView
,
0
);
// this disables the animation
}
};
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
toolbarElevation
=
toolbar
.
getElevation
();
}
drawer
.
addDrawerListener
(
toggle
);
toggle
.
syncState
();
if
(
savedInstanceState
==
null
)
{
String
section
=
getIntent
().
getStringExtra
(
Const
.
Key
.
OPEN_SECTION
);
fromShortcut
=
section
!=
null
;
navigate
(
section
);
}
navigationView
.
setNavigationItemSelectedListener
(
this
);
}
@Override
public
void
onBackPressed
()
{
if
(
drawer
.
isDrawerOpen
(
navigationView
))
{
drawer
.
closeDrawer
(
navigationView
);
}
else
if
(
mDrawerItem
!=
R
.
id
.
magisk
&&
!
fromShortcut
)
{
navigate
(
R
.
id
.
magisk
);
}
else
{
finish
();
}
}
@Override
public
boolean
onNavigationItemSelected
(
@NonNull
final
MenuItem
menuItem
)
{
mDrawerHandler
.
removeCallbacksAndMessages
(
null
);
mDrawerHandler
.
postDelayed
(()
->
navigate
(
menuItem
.
getItemId
()),
250
);
drawer
.
closeDrawer
(
navigationView
);
return
true
;
}
public
void
checkHideSection
()
{
Menu
menu
=
navigationView
.
getMenu
();
menu
.
findItem
(
R
.
id
.
magiskhide
).
setVisible
(
Shell
.
rootAccess
()
&&
(
boolean
)
Config
.
get
(
Config
.
Key
.
MAGISKHIDE
));
menu
.
findItem
(
R
.
id
.
modules
).
setVisible
(
Shell
.
rootAccess
()
&&
Config
.
magiskVersionCode
>=
0
);
menu
.
findItem
(
R
.
id
.
downloads
).
setVisible
(
Networking
.
checkNetworkStatus
(
this
)
&&
Shell
.
rootAccess
()
&&
Config
.
magiskVersionCode
>=
0
);
menu
.
findItem
(
R
.
id
.
log
).
setVisible
(
Shell
.
rootAccess
());
menu
.
findItem
(
R
.
id
.
superuser
).
setVisible
(
Utils
.
showSuperUser
());
}
public
void
navigate
(
String
item
)
{
int
itemId
=
R
.
id
.
magisk
;
if
(
item
!=
null
)
{
switch
(
item
)
{
case
"superuser"
:
itemId
=
R
.
id
.
superuser
;
break
;
case
"modules"
:
itemId
=
R
.
id
.
modules
;
break
;
case
"downloads"
:
itemId
=
R
.
id
.
downloads
;
break
;
case
"magiskhide"
:
itemId
=
R
.
id
.
magiskhide
;
break
;
case
"log"
:
itemId
=
R
.
id
.
log
;
break
;
case
"settings"
:
itemId
=
R
.
id
.
settings
;
break
;
}
}
navigate
(
itemId
);
}
public
void
navigate
(
int
itemId
)
{
mDrawerItem
=
itemId
;
navigationView
.
setCheckedItem
(
itemId
);
switch
(
itemId
)
{
case
R
.
id
.
magisk
:
fromShortcut
=
false
;
displayFragment
(
new
MagiskFragment
(),
true
);
break
;
case
R
.
id
.
superuser
:
displayFragment
(
new
SuperuserFragment
(),
true
);
break
;
case
R
.
id
.
modules
:
displayFragment
(
new
ModulesFragment
(),
true
);
break
;
case
R
.
id
.
downloads
:
displayFragment
(
new
ReposFragment
(),
true
);
break
;
case
R
.
id
.
magiskhide
:
displayFragment
(
new
MagiskHideFragment
(),
true
);
break
;
case
R
.
id
.
log
:
displayFragment
(
new
LogFragment
(),
false
);
break
;
case
R
.
id
.
settings
:
displayFragment
(
new
SettingsFragment
(),
true
);
break
;
}
}
private
void
displayFragment
(
@NonNull
Fragment
navFragment
,
boolean
setElevation
)
{
supportInvalidateOptionsMenu
();
getSupportFragmentManager
()
.
beginTransaction
()
.
setTransition
(
FragmentTransaction
.
TRANSIT_FRAGMENT_OPEN
)
.
replace
(
R
.
id
.
content_frame
,
navFragment
)
.
commitNow
();
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
toolbar
.
setElevation
(
setElevation
?
toolbarElevation
:
0
);
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/MainActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui
import
android.content.Intent
import
android.os.Bundle
import
androidx.core.view.GravityCompat
import
androidx.fragment.app.Fragment
import
com.topjohnwu.magisk.ClassMap
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.Const.Key.OPEN_SECTION
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.ActivityMainBinding
import
com.topjohnwu.magisk.model.navigation.Navigation
import
com.topjohnwu.magisk.ui.base.MagiskActivity
import
com.topjohnwu.magisk.ui.hide.MagiskHideFragment
import
com.topjohnwu.magisk.ui.log.LogFragment
import
com.topjohnwu.magisk.ui.module.ModulesFragment
import
com.topjohnwu.magisk.ui.module.ReposFragment
import
com.topjohnwu.magisk.ui.settings.SettingsFragment
import
com.topjohnwu.magisk.ui.superuser.SuperuserFragment
import
com.topjohnwu.magisk.utils.Utils
import
com.topjohnwu.net.Networking
import
com.topjohnwu.superuser.Shell
import
org.koin.androidx.viewmodel.ext.android.viewModel
import
kotlin.reflect.KClass
import
com.topjohnwu.magisk.ui.home.MagiskFragment
as
HomeFragment
open
class
MainActivity
:
MagiskActivity
<
MainViewModel
,
ActivityMainBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
activity_main
override
val
viewModel
:
MainViewModel
by
viewModel
()
override
val
navHostId
:
Int
=
R
.
id
.
main_nav_host
override
val
defaultPosition
:
Int
=
0
override
val
baseFragments
:
List
<
KClass
<
out
Fragment
>>
=
listOf
(
HomeFragment
::
class
,
SuperuserFragment
::
class
,
MagiskHideFragment
::
class
,
ModulesFragment
::
class
,
ReposFragment
::
class
,
LogFragment
::
class
,
SettingsFragment
::
class
)
/*
override
fun
getDarkTheme
():
Int
{
return
R
.
style
.
AppTheme_Dark
}*/
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
if
(!
SplashActivity
.
DONE
)
{
startActivity
(
Intent
(
this
,
ClassMap
.
get
<
Any
>(
SplashActivity
::
class
.
java
)))
finish
()
}
super
.
onCreate
(
savedInstanceState
)
checkHideSection
()
setSupportActionBar
(
binding
.
mainInclude
.
mainToolbar
)
if
(
savedInstanceState
==
null
)
{
intent
.
getStringExtra
(
OPEN_SECTION
)
?.
let
{
onEventDispatched
(
Navigation
.
fromSection
(
it
))
}
}
}
override
fun
setTitle
(
title
:
CharSequence
?)
{
supportActionBar
?.
title
=
title
}
override
fun
setTitle
(
titleId
:
Int
)
{
supportActionBar
?.
setTitle
(
titleId
)
}
override
fun
onBackPressed
()
{
if
(
binding
.
drawerLayout
.
isDrawerOpen
(
binding
.
navView
))
{
binding
.
drawerLayout
.
closeDrawer
(
binding
.
navView
)
}
else
{
super
.
onBackPressed
()
}
}
override
fun
onSimpleEventDispatched
(
event
:
Int
)
{
super
.
onSimpleEventDispatched
(
event
)
when
(
event
)
{
Navigation
.
Main
.
OPEN_NAV
->
openNav
()
}
}
private
fun
openNav
()
=
binding
.
drawerLayout
.
openDrawer
(
GravityCompat
.
START
)
fun
checkHideSection
()
{
val
menu
=
binding
.
navView
.
menu
menu
.
findItem
(
R
.
id
.
magiskHideFragment
).
isVisible
=
Shell
.
rootAccess
()
&&
Config
.
get
<
Any
>(
Config
.
Key
.
MAGISKHIDE
)
as
Boolean
menu
.
findItem
(
R
.
id
.
modulesFragment
).
isVisible
=
Shell
.
rootAccess
()
&&
Config
.
magiskVersionCode
>=
0
menu
.
findItem
(
R
.
id
.
reposFragment
).
isVisible
=
(
Networking
.
checkNetworkStatus
(
this
)
&&
Shell
.
rootAccess
()
&&
Config
.
magiskVersionCode
>=
0
)
menu
.
findItem
(
R
.
id
.
logFragment
).
isVisible
=
Shell
.
rootAccess
()
menu
.
findItem
(
R
.
id
.
superuserFragment
).
isVisible
=
Utils
.
showSuperUser
()
}
}
app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui
import
android.view.MenuItem
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.navigation.Navigation
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
class
MainViewModel
:
MagiskViewModel
()
{
fun
navPressed
()
=
Navigation
.
Main
.
OPEN_NAV
.
publish
()
fun
navigationItemPressed
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
R
.
id
.
magiskFragment
->
Navigation
.
home
()
R
.
id
.
superuserFragment
->
Navigation
.
superuser
()
R
.
id
.
magiskHideFragment
->
Navigation
.
hide
()
R
.
id
.
modulesFragment
->
Navigation
.
modules
()
R
.
id
.
reposFragment
->
Navigation
.
repos
()
R
.
id
.
logFragment
->
Navigation
.
log
()
R
.
id
.
settings
->
Navigation
.
settings
()
else
->
null
}
?.
publish
()
?.
let
{
return
@navigationItemPressed
true
}
return
false
}
}
app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
;
import
android.content.Intent
;
import
android.content.pm.PackageManager
;
import
android.os.Bundle
;
import
android.text.TextUtils
;
import
androidx.appcompat.app.AlertDialog
;
import
com.topjohnwu.magisk.BuildConfig
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.data.database.RepoDatabaseHelper
;
import
com.topjohnwu.magisk.tasks.CheckUpdates
;
import
com.topjohnwu.magisk.tasks.UpdateRepos
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.utils.LocaleManager
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.magisk.view.Notifications
;
import
com.topjohnwu.magisk.view.Shortcuts
;
import
com.topjohnwu.net.Networking
;
import
com.topjohnwu.superuser.Shell
;
public
class
SplashActivity
extends
BaseActivity
{
public
static
boolean
DONE
=
false
;
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
Shell
.
getShell
(
shell
->
{
if
(
Config
.
magiskVersionCode
>
0
&&
Config
.
magiskVersionCode
<
Const
.
MAGISK_VER
.
MIN_SUPPORT
)
{
new
AlertDialog
.
Builder
(
this
)
.
setTitle
(
R
.
string
.
unsupport_magisk_title
)
.
setMessage
(
R
.
string
.
unsupport_magisk_message
)
.
setNegativeButton
(
R
.
string
.
ok
,
null
)
.
setOnDismissListener
(
dialog
->
finish
())
.
show
();
}
else
{
initAndStart
();
}
});
}
private
void
initAndStart
()
{
String
pkg
=
Config
.
get
(
Config
.
Key
.
SU_MANAGER
);
if
(
pkg
!=
null
&&
getPackageName
().
equals
(
BuildConfig
.
APPLICATION_ID
))
{
Config
.
remove
(
Config
.
Key
.
SU_MANAGER
);
Shell
.
su
(
"pm uninstall "
+
pkg
).
submit
();
}
if
(
TextUtils
.
equals
(
pkg
,
getPackageName
()))
{
try
{
// We are the manager, remove com.topjohnwu.magisk as it could be malware
getPackageManager
().
getApplicationInfo
(
BuildConfig
.
APPLICATION_ID
,
0
);
Shell
.
su
(
"pm uninstall "
+
BuildConfig
.
APPLICATION_ID
).
submit
();
}
catch
(
PackageManager
.
NameNotFoundException
ignored
)
{}
}
// Dynamic detect all locales
LocaleManager
.
loadAvailableLocales
(
R
.
string
.
app_changelog
);
// Set default configs
Config
.
initialize
();
// Create notification channel on Android O
Notifications
.
setup
(
this
);
// Schedule periodic update checks
Utils
.
scheduleUpdateCheck
();
CheckUpdates
.
check
();
// Setup shortcuts
Shortcuts
.
setup
(
this
);
// Create repo database
app
.
repoDB
=
new
RepoDatabaseHelper
(
this
);
// Magisk working as expected
if
(
Shell
.
rootAccess
()
&&
Config
.
magiskVersionCode
>
0
)
{
// Load modules
Utils
.
loadModules
(
false
);
// Load repos
if
(
Networking
.
checkNetworkStatus
(
this
))
new
UpdateRepos
().
exec
();
}
Intent
intent
=
new
Intent
(
this
,
ClassMap
.
get
(
MainActivity
.
class
));
intent
.
putExtra
(
Const
.
Key
.
OPEN_SECTION
,
getIntent
().
getStringExtra
(
Const
.
Key
.
OPEN_SECTION
));
DONE
=
true
;
startActivity
(
intent
);
finish
();
}
}
app/src/main/java/com/topjohnwu/magisk/ui/SplashActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui
import
android.content.Intent
import
android.os.Bundle
import
android.text.TextUtils
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AppCompatActivity
import
com.topjohnwu.magisk.*
import
com.topjohnwu.magisk.tasks.CheckUpdates
import
com.topjohnwu.magisk.tasks.UpdateRepos
import
com.topjohnwu.magisk.utils.LocaleManager
import
com.topjohnwu.magisk.utils.Utils
import
com.topjohnwu.magisk.view.Notifications
import
com.topjohnwu.magisk.view.Shortcuts
import
com.topjohnwu.net.Networking
import
com.topjohnwu.superuser.Shell
open
class
SplashActivity
:
AppCompatActivity
()
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
Shell
.
getShell
{
if
(
Config
.
magiskVersionCode
>
0
&&
Config
.
magiskVersionCode
<
Const
.
MAGISK_VER
.
MIN_SUPPORT
)
{
AlertDialog
.
Builder
(
this
)
.
setTitle
(
R
.
string
.
unsupport_magisk_title
)
.
setMessage
(
R
.
string
.
unsupport_magisk_message
)
.
setNegativeButton
(
R
.
string
.
ok
,
null
)
.
setOnDismissListener
{
finish
()
}
.
show
()
}
else
{
initAndStart
()
}
}
}
private
fun
initAndStart
()
{
val
pkg
=
Config
.
get
<
String
>(
Config
.
Key
.
SU_MANAGER
)
if
(
pkg
!=
null
&&
packageName
==
BuildConfig
.
APPLICATION_ID
)
{
Config
.
remove
(
Config
.
Key
.
SU_MANAGER
)
Shell
.
su
(
"pm uninstall $pkg"
).
submit
()
}
if
(
TextUtils
.
equals
(
pkg
,
packageName
))
{
runCatching
{
// We are the manager, remove com.topjohnwu.magisk as it could be malware
packageManager
.
getApplicationInfo
(
BuildConfig
.
APPLICATION_ID
,
0
)
Shell
.
su
(
"pm uninstall "
+
BuildConfig
.
APPLICATION_ID
).
submit
()
}
}
// Dynamic detect all locales
LocaleManager
.
loadAvailableLocales
(
R
.
string
.
app_changelog
)
// Set default configs
Config
.
initialize
()
// Create notification channel on Android O
Notifications
.
setup
(
this
)
// Schedule periodic update checks
Utils
.
scheduleUpdateCheck
()
CheckUpdates
.
check
()
// Setup shortcuts
Shortcuts
.
setup
(
this
)
// Magisk working as expected
if
(
Shell
.
rootAccess
()
&&
Config
.
magiskVersionCode
>
0
)
{
// Load modules
Utils
.
loadModules
(
false
)
// Load repos
if
(
Networking
.
checkNetworkStatus
(
this
))
UpdateRepos
().
exec
()
}
val
intent
=
Intent
(
this
,
ClassMap
.
get
<
Any
>(
MainActivity
::
class
.
java
))
intent
.
putExtra
(
Const
.
Key
.
OPEN_SECTION
,
getIntent
().
getStringExtra
(
Const
.
Key
.
OPEN_SECTION
))
DONE
=
true
startActivity
(
intent
)
finish
()
}
companion
object
{
var
DONE
=
false
}
}
app/src/main/java/com/topjohnwu/magisk/ui/base/ActivityResultListener.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.base
import
android.content.Intent
interface
ActivityResultListener
{
fun
onActivityResult
(
resultCode
:
Int
,
data
:
Intent
?)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseActivity.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
base
;
import
android.Manifest
;
import
android.content.Context
;
import
android.content.Intent
;
import
android.content.SharedPreferences
;
import
android.content.pm.ActivityInfo
;
import
android.content.pm.PackageManager
;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.text.TextUtils
;
import
android.view.WindowManager
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.StyleRes
;
import
androidx.appcompat.app.AppCompatActivity
;
import
androidx.collection.SparseArrayCompat
;
import
androidx.core.app.ActivityCompat
;
import
androidx.core.content.ContextCompat
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.Event
;
import
com.topjohnwu.magisk.utils.LocaleManager
;
public
abstract
class
BaseActivity
extends
AppCompatActivity
implements
Event
.
AutoListener
{
static
int
[]
EMPTY_INT_ARRAY
=
new
int
[
0
];
private
SparseArrayCompat
<
ActivityResultListener
>
resultListeners
=
new
SparseArrayCompat
<>();
public
App
app
=
App
.
self
;
@Override
public
int
[]
getListeningEvents
()
{
return
EMPTY_INT_ARRAY
;
}
@Override
public
void
onEvent
(
int
event
)
{}
@StyleRes
public
int
getDarkTheme
()
{
return
-
1
;
}
@Override
protected
void
attachBaseContext
(
Context
base
)
{
super
.
attachBaseContext
(
LocaleManager
.
getLocaleContext
(
base
,
LocaleManager
.
locale
));
}
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
Event
.
register
(
this
);
if
(
getDarkTheme
()
!=
-
1
&&
(
boolean
)
Config
.
get
(
Config
.
Key
.
DARK_THEME
))
{
setTheme
(
getDarkTheme
());
}
super
.
onCreate
(
savedInstanceState
);
}
@Override
protected
void
onDestroy
()
{
Event
.
unregister
(
this
);
super
.
onDestroy
();
}
protected
void
setFloating
()
{
boolean
isTablet
=
getResources
().
getBoolean
(
R
.
bool
.
isTablet
);
if
(
isTablet
)
{
WindowManager
.
LayoutParams
params
=
getWindow
().
getAttributes
();
params
.
height
=
getResources
().
getDimensionPixelSize
(
R
.
dimen
.
floating_height
);
params
.
width
=
getResources
().
getDimensionPixelSize
(
R
.
dimen
.
floating_width
);
params
.
alpha
=
1.0f
;
params
.
dimAmount
=
0.6f
;
params
.
flags
|=
2
;
getWindow
().
setAttributes
(
params
);
setFinishOnTouchOutside
(
true
);
}
}
protected
void
lockOrientation
()
{
if
(
Build
.
VERSION
.
SDK_INT
<
18
)
setRequestedOrientation
(
getResources
().
getConfiguration
().
orientation
);
else
setRequestedOrientation
(
ActivityInfo
.
SCREEN_ORIENTATION_LOCKED
);
}
public
void
runWithExternalRW
(
Runnable
callback
)
{
runWithPermissions
(
new
String
[]
{
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
},
callback
);
}
public
void
runWithPermissions
(
String
[]
permissions
,
Runnable
callback
)
{
runWithPermissions
(
this
,
permissions
,
callback
);
}
public
static
void
runWithPermissions
(
Context
context
,
String
[]
permissions
,
Runnable
callback
)
{
boolean
granted
=
true
;
for
(
String
perm
:
permissions
)
{
if
(
ContextCompat
.
checkSelfPermission
(
context
,
perm
)
!=
PackageManager
.
PERMISSION_GRANTED
)
granted
=
false
;
}
if
(
granted
)
{
Const
.
EXTERNAL_PATH
.
mkdirs
();
callback
.
run
();
}
else
{
// Passed in context should be an activity if not granted, need to show dialog!
if
(
context
instanceof
BaseActivity
)
{
BaseActivity
activity
=
(
BaseActivity
)
context
;
int
code
=
callback
.
hashCode
()
&
0xFFFF
;
activity
.
resultListeners
.
put
(
code
,
((
i
,
d
)
->
callback
.
run
()));
ActivityCompat
.
requestPermissions
(
activity
,
permissions
,
code
);
}
}
}
@Override
protected
void
onActivityResult
(
int
requestCode
,
int
resultCode
,
Intent
data
)
{
onActivityResultListener
(
requestCode
,
resultCode
,
data
);
}
private
void
onActivityResultListener
(
int
requestCode
,
int
resultCode
,
Intent
data
)
{
ActivityResultListener
listener
=
resultListeners
.
get
(
requestCode
);
if
(
listener
!=
null
)
{
resultListeners
.
remove
(
requestCode
);
listener
.
onActivityResult
(
resultCode
,
data
);
}
}
public
void
startActivityForResult
(
Intent
intent
,
int
requestCode
,
ActivityResultListener
listener
)
{
resultListeners
.
put
(
requestCode
,
listener
);
super
.
startActivityForResult
(
intent
,
requestCode
);
}
@Override
public
void
onRequestPermissionsResult
(
int
requestCode
,
@NonNull
String
[]
permissions
,
@NonNull
int
[]
grantResults
)
{
boolean
grant
=
true
;
for
(
int
result
:
grantResults
)
{
if
(
result
!=
PackageManager
.
PERMISSION_GRANTED
)
grant
=
false
;
}
if
(
grant
)
onActivityResultListener
(
requestCode
,
0
,
null
);
else
resultListeners
.
remove
(
requestCode
);
}
public
interface
ActivityResultListener
{
void
onActivityResult
(
int
resultCode
,
Intent
data
);
}
@Override
public
SharedPreferences
getSharedPreferences
(
String
name
,
int
mode
)
{
if
(
TextUtils
.
equals
(
name
,
getPackageName
()
+
"_preferences"
))
return
app
.
prefs
;
return
super
.
getSharedPreferences
(
name
,
mode
);
}
}
app/src/main/java/com/topjohnwu/magisk/ui/base/BaseFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
base
;
import
android.content.Intent
;
import
androidx.fragment.app.Fragment
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.utils.Event
;
import
butterknife.Unbinder
;
public
abstract
class
BaseFragment
extends
Fragment
implements
Event
.
AutoListener
{
public
App
app
=
App
.
self
;
protected
Unbinder
unbinder
=
null
;
@Override
public
void
onResume
()
{
super
.
onResume
();
Event
.
register
(
this
);
}
@Override
public
void
onPause
()
{
Event
.
unregister
(
this
);
super
.
onPause
();
}
@Override
public
void
onDestroyView
()
{
super
.
onDestroyView
();
if
(
unbinder
!=
null
)
unbinder
.
unbind
();
}
@Override
public
void
startActivityForResult
(
Intent
intent
,
int
requestCode
)
{
startActivityForResult
(
intent
,
requestCode
,
(
resultCode
,
data
)
->
onActivityResult
(
requestCode
,
resultCode
,
data
));
}
public
void
startActivityForResult
(
Intent
intent
,
int
requestCode
,
BaseActivity
.
ActivityResultListener
listener
)
{
((
BaseActivity
)
requireActivity
()).
startActivityForResult
(
intent
,
requestCode
,
listener
);
}
protected
void
runWithExternalRW
(
Runnable
callback
)
{
((
BaseActivity
)
requireActivity
()).
runWithExternalRW
(
callback
);
}
@Override
public
int
[]
getListeningEvents
()
{
return
BaseActivity
.
EMPTY_INT_ARRAY
;
}
@Override
public
void
onEvent
(
int
event
)
{}
}
app/src/main/java/com/topjohnwu/magisk/ui/base/BasePreferenceFragment.java
View file @
2813d203
...
...
@@ -8,6 +8,10 @@ import android.view.LayoutInflater;
import
android.view.View
;
import
android.view.ViewGroup
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.Event
;
import
androidx.preference.Preference
;
import
androidx.preference.PreferenceCategory
;
import
androidx.preference.PreferenceFragmentCompat
;
...
...
@@ -16,10 +20,6 @@ import androidx.preference.PreferenceScreen;
import
androidx.preference.PreferenceViewHolder
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.Event
;
public
abstract
class
BasePreferenceFragment
extends
PreferenceFragmentCompat
implements
SharedPreferences
.
OnSharedPreferenceChangeListener
,
Event
.
AutoListener
{
...
...
@@ -28,21 +28,21 @@ public abstract class BasePreferenceFragment extends PreferenceFragmentCompat
@Override
public
View
onCreateView
(
LayoutInflater
inflater
,
ViewGroup
container
,
Bundle
savedInstanceState
)
{
View
v
=
super
.
onCreateView
(
inflater
,
container
,
savedInstanceState
);
app
.
prefs
.
registerOnSharedPreferenceChangeListener
(
this
);
app
.
getPrefs
()
.
registerOnSharedPreferenceChangeListener
(
this
);
Event
.
register
(
this
);
return
v
;
}
@Override
public
void
onDestroyView
()
{
app
.
prefs
.
unregisterOnSharedPreferenceChangeListener
(
this
);
app
.
getPrefs
()
.
unregisterOnSharedPreferenceChangeListener
(
this
);
Event
.
unregister
(
this
);
super
.
onDestroyView
();
}
@Override
public
int
[]
getListeningEvents
()
{
return
BaseActivity
.
EMPTY_INT_ARRAY
;
return
new
int
[
0
]
;
}
@Override
...
...
app/src/main/java/com/topjohnwu/magisk/ui/base/IBaseLeanback.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.base
import
android.content.Intent
interface
IBaseLeanback
{
fun
runWithExternalRW
(
callback
:
Runnable
)
fun
runWithPermissions
(
vararg
permissions
:
String
,
callback
:
Runnable
)
fun
startActivityForResult
(
intent
:
Intent
,
requestCode
:
Int
,
listener
:
ActivityResultListener
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.base
import
android.content.Intent
import
android.os.Bundle
import
androidx.annotation.CallSuper
import
androidx.appcompat.app.AppCompatDelegate
import
androidx.core.net.toUri
import
androidx.databinding.ViewDataBinding
import
androidx.fragment.app.Fragment
import
androidx.fragment.app.FragmentTransaction
import
com.karumi.dexter.Dexter
import
com.karumi.dexter.MultiplePermissionsReport
import
com.karumi.dexter.PermissionToken
import
com.karumi.dexter.listener.PermissionRequest
import
com.karumi.dexter.listener.multi.MultiplePermissionsListener
import
com.ncapdevi.fragnav.FragNavController
import
com.ncapdevi.fragnav.FragNavTransactionOptions
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.model.events.BackPressEvent
import
com.topjohnwu.magisk.model.events.PermissionEvent
import
com.topjohnwu.magisk.model.events.ViewActionEvent
import
com.topjohnwu.magisk.model.navigation.MagiskAnimBuilder
import
com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
import
com.topjohnwu.magisk.model.navigation.Navigator
import
com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
import
com.topjohnwu.magisk.utils.Utils
import
timber.log.Timber
import
kotlin.reflect.KClass
abstract
class
MagiskActivity
<
ViewModel
:
MagiskViewModel
,
Binding
:
ViewDataBinding
>
:
MagiskLeanbackActivity
<
ViewModel
,
Binding
>(),
FragNavController
.
RootFragmentListener
,
Navigator
{
override
val
numberOfRootFragments
:
Int
get
()
=
baseFragments
.
size
override
val
baseFragments
:
List
<
KClass
<
out
Fragment
>>
=
listOf
()
protected
open
val
defaultPosition
:
Int
=
0
protected
val
navigationController
get
()
=
if
(
navHostId
==
0
)
null
else
_navigationController
private
val
_navigationController
by
lazy
{
if
(
navHostId
==
0
)
throw
IllegalStateException
(
"Did you forget to override \"navHostId\"?"
)
FragNavController
(
supportFragmentManager
,
navHostId
)
}
init
{
val
isDarkTheme
=
Config
.
get
<
Boolean
>(
Config
.
Key
.
DARK_THEME
)
val
theme
=
if
(
isDarkTheme
)
{
AppCompatDelegate
.
MODE_NIGHT_YES
}
else
{
AppCompatDelegate
.
MODE_NIGHT_NO
}
AppCompatDelegate
.
setDefaultNightMode
(
theme
)
AppCompatDelegate
.
setCompatVectorFromResourcesEnabled
(
true
)
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
navigationController
?.
apply
{
rootFragmentListener
=
this
@MagiskActivity
initialize
(
defaultPosition
,
savedInstanceState
)
}
}
override
fun
onSaveInstanceState
(
outState
:
Bundle
)
{
super
.
onSaveInstanceState
(
outState
)
navigationController
?.
onSaveInstanceState
(
outState
)
}
@CallSuper
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
BackPressEvent
->
onBackPressed
()
is
MagiskNavigationEvent
->
navigateTo
(
event
)
is
ViewActionEvent
->
event
.
action
(
this
)
is
PermissionEvent
->
withPermissions
(*
event
.
permissions
.
toTypedArray
())
{
onSuccess
{
event
.
callback
.
onNext
(
true
)
}
onFailure
{
event
.
callback
.
onNext
(
false
)
event
.
callback
.
onError
(
SecurityException
(
"User refused permissions"
))
}
}
}
}
override
fun
getRootFragment
(
index
:
Int
)
=
baseFragments
[
index
].
java
.
newInstance
()
override
fun
navigateTo
(
event
:
MagiskNavigationEvent
)
{
val
directions
=
event
.
navDirections
navigationController
?.
defaultTransactionOptions
=
FragNavTransactionOptions
.
newBuilder
()
.
customAnimations
(
event
.
animOptions
)
.
build
()
navigationController
?.
currentStack
?.
indexOfFirst
{
it
.
javaClass
==
event
.
navOptions
.
popUpTo
}
?.
let
{
if
(
it
==
-
1
)
null
else
it
}
// invalidate if class is not found
?.
let
{
if
(
event
.
navOptions
.
inclusive
)
it
+
1
else
it
}
?.
let
{
navigationController
?.
popFragments
(
it
)
}
when
(
directions
.
isActivity
)
{
true
->
navigateToActivity
(
event
)
else
->
navigateToFragment
(
event
)
}
}
private
fun
navigateToActivity
(
event
:
MagiskNavigationEvent
)
{
val
destination
=
event
.
navDirections
.
destination
?.
java
?:
let
{
Timber
.
e
(
"Cannot navigate to null destination"
)
return
}
val
options
=
event
.
navOptions
Intent
(
this
,
destination
)
.
putExtras
(
event
.
navDirections
.
args
)
.
apply
{
if
(
options
.
singleTop
)
addFlags
(
Intent
.
FLAG_ACTIVITY_SINGLE_TOP
)
if
(
options
.
clearTask
)
addFlags
(
Intent
.
FLAG_ACTIVITY_CLEAR_TASK
)
}
.
let
{
startActivity
(
it
)
}
}
private
fun
navigateToFragment
(
event
:
MagiskNavigationEvent
)
{
val
destination
=
event
.
navDirections
.
destination
?.
java
?:
let
{
Timber
.
e
(
"Cannot navigate to null destination"
)
return
}
when
(
val
index
=
baseFragments
.
indexOfFirst
{
it
.
java
.
name
==
destination
.
name
})
{
-
1
->
destination
.
newInstance
()
.
apply
{
arguments
=
event
.
navDirections
.
args
}
.
let
{
navigationController
?.
pushFragment
(
it
)
}
// When it's desired that fragments of same class are put on top of one another edit this
else
->
navigationController
?.
switchTab
(
index
)
}
}
override
fun
onBackPressed
()
{
val
fragment
=
navigationController
?.
currentFrag
as
?
MagiskFragment
<*,
*>
if
(
fragment
?.
onBackPressed
()
==
true
)
{
return
}
try
{
navigationController
?.
popFragment
()
?:
throw
UnsupportedOperationException
()
}
catch
(
e
:
UnsupportedOperationException
)
{
when
{
navigationController
?.
currentStackIndex
!=
defaultPosition
->
{
val
options
=
FragNavTransactionOptions
.
newBuilder
()
.
transition
(
FragmentTransaction
.
TRANSIT_FRAGMENT_CLOSE
)
.
build
()
navigationController
?.
switchTab
(
defaultPosition
,
options
)
}
else
->
super
.
onBackPressed
()
}
}
}
fun
openUrl
(
url
:
String
)
=
Utils
.
openLink
(
this
,
url
.
toUri
())
fun
withPermissions
(
vararg
permissions
:
String
,
builder
:
PermissionRequestBuilder
.()
->
Unit
)
{
val
request
=
PermissionRequestBuilder
().
apply
(
builder
).
build
()
Dexter
.
withActivity
(
this
)
.
withPermissions
(*
permissions
)
.
withListener
(
object
:
MultiplePermissionsListener
{
override
fun
onPermissionsChecked
(
report
:
MultiplePermissionsReport
?)
=
if
(
report
?.
areAllPermissionsGranted
()
==
true
)
{
request
.
onSuccess
()
}
else
{
request
.
onFailure
()
}
override
fun
onPermissionRationaleShouldBeShown
(
permissions
:
MutableList
<
PermissionRequest
>?,
token
:
PermissionToken
?
)
=
request
.
onShowRationale
(
permissions
.
orEmpty
().
map
{
it
.
name
})
})
.
check
()
}
private
fun
FragNavTransactionOptions
.
Builder
.
customAnimations
(
options
:
MagiskAnimBuilder
)
=
customAnimations
(
options
.
enter
,
options
.
exit
,
options
.
popEnter
,
options
.
popExit
).
apply
{
if
(!
options
.
anySet
)
{
transition
(
FragmentTransaction
.
TRANSIT_FRAGMENT_OPEN
)
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.base
import
androidx.annotation.CallSuper
import
androidx.databinding.ViewDataBinding
import
androidx.fragment.app.Fragment
import
com.skoumal.teanity.view.TeanityFragment
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.model.events.BackPressEvent
import
com.topjohnwu.magisk.model.events.PermissionEvent
import
com.topjohnwu.magisk.model.events.ViewActionEvent
import
com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
import
com.topjohnwu.magisk.model.navigation.Navigator
import
com.topjohnwu.magisk.model.permissions.PermissionRequestBuilder
import
kotlin.reflect.KClass
abstract
class
MagiskFragment
<
ViewModel
:
MagiskViewModel
,
Binding
:
ViewDataBinding
>
:
TeanityFragment
<
ViewModel
,
Binding
>(),
Navigator
{
protected
val
magiskActivity
get
()
=
activity
as
MagiskActivity
<*,
*>
// We don't need nested fragments
override
val
baseFragments
:
List
<
KClass
<
Fragment
>>
=
listOf
()
override
fun
navigateTo
(
event
:
MagiskNavigationEvent
)
=
magiskActivity
.
navigateTo
(
event
)
@CallSuper
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
BackPressEvent
->
magiskActivity
.
onBackPressed
()
is
MagiskNavigationEvent
->
navigateTo
(
event
)
is
ViewActionEvent
->
event
.
action
(
requireActivity
())
is
PermissionEvent
->
magiskActivity
.
withPermissions
(*
event
.
permissions
.
toTypedArray
())
{
onSuccess
{
event
.
callback
.
onNext
(
true
)
}
onFailure
{
event
.
callback
.
onNext
(
false
)
event
.
callback
.
onError
(
SecurityException
(
"User refused permissions"
))
}
}
}
}
fun
withPermissions
(
vararg
permissions
:
String
,
builder
:
PermissionRequestBuilder
.()
->
Unit
)
{
magiskActivity
.
withPermissions
(*
permissions
,
builder
=
builder
)
}
fun
openLink
(
url
:
String
)
=
magiskActivity
.
openUrl
(
url
)
open
fun
onBackPressed
():
Boolean
=
false
}
app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskLeanbackActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.base
import
android.Manifest
import
android.content.Intent
import
androidx.collection.SparseArrayCompat
import
androidx.databinding.ViewDataBinding
import
com.karumi.dexter.Dexter
import
com.karumi.dexter.MultiplePermissionsReport
import
com.karumi.dexter.PermissionToken
import
com.karumi.dexter.listener.PermissionRequest
import
com.karumi.dexter.listener.multi.MultiplePermissionsListener
import
com.skoumal.teanity.view.TeanityActivity
import
com.topjohnwu.magisk.Const
abstract
class
MagiskLeanbackActivity
<
ViewModel
:
MagiskViewModel
,
Binding
:
ViewDataBinding
>
:
TeanityActivity
<
ViewModel
,
Binding
>(),
IBaseLeanback
{
private
val
resultListeners
=
SparseArrayCompat
<
ActivityResultListener
>()
@Deprecated
(
"Permissions will be checked in a different streamlined way"
)
fun
runWithExternalRW
(
callback
:
()
->
Unit
)
=
runWithExternalRW
(
Runnable
{
callback
()
})
@Deprecated
(
"Permissions will be checked in a different streamlined way"
)
override
fun
runWithExternalRW
(
callback
:
Runnable
)
{
runWithPermissions
(
Manifest
.
permission
.
WRITE_EXTERNAL_STORAGE
,
callback
=
callback
)
}
@Deprecated
(
"Permissions will be checked in a different streamlined way"
)
override
fun
runWithPermissions
(
vararg
permissions
:
String
,
callback
:
Runnable
)
{
Dexter
.
withActivity
(
this
)
.
withPermissions
(*
permissions
)
.
withListener
(
object
:
MultiplePermissionsListener
{
override
fun
onPermissionsChecked
(
report
:
MultiplePermissionsReport
?)
{
if
(
report
?.
areAllPermissionsGranted
()
==
true
)
{
Const
.
EXTERNAL_PATH
.
mkdirs
()
callback
.
run
()
}
}
override
fun
onPermissionRationaleShouldBeShown
(
permissions
:
MutableList
<
PermissionRequest
>?,
token
:
PermissionToken
?
)
=
Unit
})
.
check
()
}
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
super
.
onActivityResult
(
requestCode
,
resultCode
,
data
)
resultListeners
.
get
(
requestCode
)
?.
apply
{
resultListeners
.
remove
(
requestCode
)
onActivityResult
(
resultCode
,
data
)
}
}
override
fun
startActivityForResult
(
intent
:
Intent
,
requestCode
:
Int
,
listener
:
ActivityResultListener
)
{
resultListeners
.
put
(
requestCode
,
listener
)
startActivityForResult
(
intent
,
requestCode
)
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/base/MagiskViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.base
import
android.app.Activity
import
com.skoumal.teanity.viewmodel.LoadingViewModel
import
com.topjohnwu.magisk.model.events.BackPressEvent
import
com.topjohnwu.magisk.model.events.PermissionEvent
import
com.topjohnwu.magisk.model.events.ViewActionEvent
import
com.topjohnwu.magisk.utils.Event
import
io.reactivex.Observable
import
io.reactivex.subjects.PublishSubject
import
timber.log.Timber
abstract
class
MagiskViewModel
:
LoadingViewModel
(),
Event
.
AutoListener
{
override
fun
onEvent
(
event
:
Int
)
=
Timber
.
i
(
"Event of $event was not handled"
)
override
fun
getListeningEvents
():
IntArray
=
intArrayOf
()
fun
withView
(
action
:
Activity
.()
->
Unit
)
{
ViewActionEvent
(
action
).
publish
()
}
fun
withPermissions
(
vararg
permissions
:
String
):
Observable
<
Boolean
>
{
val
subject
=
PublishSubject
.
create
<
Boolean
>()
return
subject
.
doOnSubscribe
{
PermissionEvent
(
permissions
.
toList
(),
subject
).
publish
()
}
}
fun
back
()
=
BackPressEvent
().
publish
()
}
app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
flash
;
import
android.content.Intent
;
import
android.net.Uri
;
import
android.os.Bundle
;
import
android.view.View
;
import
android.widget.Button
;
import
android.widget.LinearLayout
;
import
android.widget.Toast
;
import
androidx.annotation.NonNull
;
import
androidx.appcompat.app.ActionBar
;
import
androidx.appcompat.widget.Toolbar
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.StringListAdapter
;
import
com.topjohnwu.magisk.tasks.FlashZip
;
import
com.topjohnwu.magisk.tasks.MagiskInstaller
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.utils.RootUtils
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.superuser.CallbackList
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.internal.UiThreadHandler
;
import
java.io.File
;
import
java.io.FileWriter
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.Calendar
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.Locale
;
import
butterknife.BindColor
;
import
butterknife.BindView
;
import
butterknife.OnClick
;
public
class
FlashActivity
extends
BaseActivity
{
@BindView
(
R
.
id
.
toolbar
)
Toolbar
toolbar
;
@BindView
(
R
.
id
.
button_panel
)
LinearLayout
buttonPanel
;
@BindView
(
R
.
id
.
reboot
)
Button
reboot
;
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
rv
;
@BindColor
(
android
.
R
.
color
.
white
)
int
white
;
private
List
<
String
>
console
,
logs
;
@OnClick
(
R
.
id
.
reboot
)
void
reboot
()
{
RootUtils
.
reboot
();
}
@OnClick
(
R
.
id
.
save_logs
)
void
saveLogs
()
{
runWithExternalRW
(()
->
{
Calendar
now
=
Calendar
.
getInstance
();
String
filename
=
String
.
format
(
Locale
.
US
,
"magisk_install_log_%04d%02d%02d_%02d%02d%02d.log"
,
now
.
get
(
Calendar
.
YEAR
),
now
.
get
(
Calendar
.
MONTH
)
+
1
,
now
.
get
(
Calendar
.
DAY_OF_MONTH
),
now
.
get
(
Calendar
.
HOUR_OF_DAY
),
now
.
get
(
Calendar
.
MINUTE
),
now
.
get
(
Calendar
.
SECOND
));
File
logFile
=
new
File
(
Const
.
EXTERNAL_PATH
,
filename
);
try
(
FileWriter
writer
=
new
FileWriter
(
logFile
))
{
for
(
String
s
:
logs
)
{
writer
.
write
(
s
);
writer
.
write
(
'\n'
);
}
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
return
;
}
Utils
.
toast
(
logFile
.
getPath
(),
Toast
.
LENGTH_LONG
);
});
}
@OnClick
(
R
.
id
.
close
)
public
void
close
()
{
finish
();
}
@Override
public
void
onBackPressed
()
{
// Prevent user accidentally press back button
}
@Override
public
int
getDarkTheme
()
{
return
R
.
style
.
AppTheme_NoDrawer_Dark
;
}
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_flash
);
new
FlashActivity_ViewBinding
(
this
);
setSupportActionBar
(
toolbar
);
ActionBar
ab
=
getSupportActionBar
();
if
(
ab
!=
null
)
{
ab
.
setTitle
(
R
.
string
.
flashing
);
}
setFloating
();
setFinishOnTouchOutside
(
false
);
if
(!
Shell
.
rootAccess
())
reboot
.
setVisibility
(
View
.
GONE
);
logs
=
Collections
.
synchronizedList
(
new
ArrayList
<>());
console
=
new
ConsoleList
();
rv
.
setAdapter
(
new
ConsoleAdapter
());
Intent
intent
=
getIntent
();
Uri
uri
=
intent
.
getData
();
switch
(
intent
.
getStringExtra
(
Const
.
Key
.
FLASH_ACTION
))
{
case
Const
.
Value
.
FLASH_ZIP
:
new
FlashModule
(
uri
).
exec
();
break
;
case
Const
.
Value
.
UNINSTALL
:
new
Uninstall
(
uri
).
exec
();
break
;
case
Const
.
Value
.
FLASH_MAGISK
:
new
DirectInstall
().
exec
();
break
;
case
Const
.
Value
.
FLASH_INACTIVE_SLOT
:
new
SecondSlot
().
exec
();
break
;
case
Const
.
Value
.
PATCH_FILE
:
new
PatchFile
(
uri
).
exec
();
break
;
}
}
private
class
ConsoleAdapter
extends
StringListAdapter
<
ConsoleAdapter
.
ViewHolder
>
{
ConsoleAdapter
()
{
super
(
console
,
true
);
}
@Override
protected
int
itemLayoutRes
()
{
return
R
.
layout
.
list_item_console
;
}
@NonNull
@Override
public
ViewHolder
createViewHolder
(
@NonNull
View
v
)
{
return
new
ViewHolder
(
v
);
}
class
ViewHolder
extends
StringListAdapter
.
ViewHolder
{
public
ViewHolder
(
@NonNull
View
itemView
)
{
super
(
itemView
);
txt
.
setTextColor
(
white
);
}
@Override
protected
int
textViewResId
()
{
return
R
.
id
.
txt
;
}
}
}
private
class
ConsoleList
extends
CallbackList
<
String
>
{
ConsoleList
()
{
super
(
new
ArrayList
<>());
}
private
void
updateUI
()
{
rv
.
getAdapter
().
notifyItemChanged
(
size
()
-
1
);
rv
.
postDelayed
(()
->
rv
.
smoothScrollToPosition
(
size
()
-
1
),
10
);
}
@Override
public
void
onAddElement
(
String
s
)
{
logs
.
add
(
s
);
updateUI
();
}
@Override
public
String
set
(
int
i
,
String
s
)
{
String
ret
=
super
.
set
(
i
,
s
);
UiThreadHandler
.
run
(
this
::
updateUI
);
return
ret
;
}
}
private
class
FlashModule
extends
FlashZip
{
FlashModule
(
Uri
uri
)
{
super
(
uri
,
console
,
logs
);
}
@Override
protected
void
onResult
(
boolean
success
)
{
if
(
success
)
{
Utils
.
loadModules
();
}
else
{
console
.
add
(
"! Installation failed"
);
reboot
.
setVisibility
(
View
.
GONE
);
}
buttonPanel
.
setVisibility
(
View
.
VISIBLE
);
}
}
private
class
Uninstall
extends
FlashModule
{
Uninstall
(
Uri
uri
)
{
super
(
uri
);
}
@Override
protected
void
onResult
(
boolean
success
)
{
if
(
success
)
UiThreadHandler
.
handler
.
postDelayed
(
Shell
.
su
(
"pm uninstall "
+
getPackageName
())::
exec
,
3000
);
else
super
.
onResult
(
false
);
}
}
private
abstract
class
BaseInstaller
extends
MagiskInstaller
{
BaseInstaller
()
{
super
(
console
,
logs
);
}
@Override
protected
void
onResult
(
boolean
success
)
{
if
(
success
)
{
console
.
add
(
"- All done!"
);
}
else
{
Shell
.
sh
(
"rm -rf "
+
installDir
).
submit
();
console
.
add
(
"! Installation failed"
);
reboot
.
setVisibility
(
View
.
GONE
);
}
buttonPanel
.
setVisibility
(
View
.
VISIBLE
);
}
}
private
class
DirectInstall
extends
BaseInstaller
{
@Override
protected
boolean
operations
()
{
return
findImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
();
}
}
private
class
SecondSlot
extends
BaseInstaller
{
@Override
protected
boolean
operations
()
{
return
findSecondaryImage
()
&&
extractZip
()
&&
patchBoot
()
&&
flashBoot
()
&&
postOTA
();
}
}
private
class
PatchFile
extends
BaseInstaller
{
private
Uri
uri
;
PatchFile
(
Uri
u
)
{
uri
=
u
;
}
@Override
protected
boolean
operations
()
{
return
extractZip
()
&&
handleFile
(
uri
)
&&
patchBoot
()
&&
storeBoot
();
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.flash
import
com.topjohnwu.magisk.Const
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.ActivityFlashBinding
import
com.topjohnwu.magisk.ui.base.MagiskActivity
import
org.koin.androidx.viewmodel.ext.android.viewModel
import
org.koin.core.parameter.parametersOf
open
class
FlashActivity
:
MagiskActivity
<
FlashViewModel
,
ActivityFlashBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
activity_flash
override
val
viewModel
:
FlashViewModel
by
viewModel
{
val
uri
=
intent
.
data
val
action
=
intent
.
getStringExtra
(
Const
.
Key
.
FLASH_ACTION
)
?:
let
{
finish
();
""
}
parametersOf
(
action
,
uri
)
}
override
fun
onBackPressed
()
{
if
(
viewModel
.
loading
)
return
super
.
onBackPressed
()
}
}
app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.flash
import
android.Manifest.permission.READ_EXTERNAL_STORAGE
import
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import
android.content.res.Resources
import
android.net.Uri
import
android.os.Handler
import
androidx.core.os.postDelayed
import
androidx.databinding.ObservableArrayList
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.subscribeK
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.skoumal.teanity.viewevents.SnackbarEvent
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.Const
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem
import
com.topjohnwu.magisk.model.flash.FlashResultListener
import
com.topjohnwu.magisk.model.flash.Flashing
import
com.topjohnwu.magisk.model.flash.Patching
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.*
import
com.topjohnwu.superuser.Shell
import
me.tatarka.bindingcollectionadapter2.ItemBinding
import
java.io.File
class
FlashViewModel
(
action
:
String
,
uri
:
Uri
?,
private
val
resources
:
Resources
)
:
MagiskViewModel
(),
FlashResultListener
{
val
canShowReboot
=
Shell
.
rootAccess
()
val
showRestartTitle
=
KObservableField
(
false
)
val
behaviorText
=
KObservableField
(
resources
.
getString
(
R
.
string
.
flashing
))
val
items
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemBinding
=
ItemBinding
.
of
<
ComparableRvItem
<*>>
{
itemBinding
,
_
,
item
->
item
.
bind
(
itemBinding
)
itemBinding
.
bindExtra
(
BR
.
viewModel
,
this
@FlashViewModel
)
}
private
val
rawItems
=
ObservableArrayList
<
String
>()
init
{
rawItems
.
sendUpdatesTo
(
items
)
{
it
.
map
{
ConsoleRvItem
(
it
)
}
}
state
=
State
.
LOADING
val
uri
=
uri
?:
Uri
.
EMPTY
when
(
action
)
{
Const
.
Value
.
FLASH_ZIP
->
Flashing
.
Install
(
uri
,
rawItems
,
rawItems
,
this
)
.
exec
()
Const
.
Value
.
UNINSTALL
->
Flashing
.
Uninstall
(
uri
,
rawItems
,
rawItems
,
this
)
.
exec
()
Const
.
Value
.
FLASH_MAGISK
->
Patching
.
Direct
(
rawItems
,
rawItems
,
this
)
.
exec
()
Const
.
Value
.
FLASH_INACTIVE_SLOT
->
Patching
.
SecondSlot
(
rawItems
,
rawItems
,
this
)
.
exec
()
Const
.
Value
.
PATCH_FILE
->
Patching
.
File
(
uri
,
rawItems
,
rawItems
,
this
)
.
exec
()
}
}
override
fun
onResult
(
isSuccess
:
Boolean
)
{
state
=
if
(
isSuccess
)
State
.
LOADED
else
State
.
LOADING_FAILED
behaviorText
.
value
=
when
{
isSuccess
->
resources
.
getString
(
R
.
string
.
done
)
else
->
resources
.
getString
(
R
.
string
.
failure
)
}
if
(
isSuccess
)
{
Handler
().
postDelayed
(
500
)
{
showRestartTitle
.
value
=
true
}
}
}
fun
savePressed
()
=
withPermissions
(
READ_EXTERNAL_STORAGE
,
WRITE_EXTERNAL_STORAGE
)
.
map
{
now
}
.
map
{
it
.
toTime
(
timeFormatFull
)
}
.
map
{
Const
.
MAGISK_INSTALL_LOG_FILENAME
.
format
(
it
)
}
.
map
{
File
(
Const
.
EXTERNAL_PATH
,
it
)
}
.
map
{
file
->
val
log
=
items
.
filterIsInstance
<
ConsoleRvItem
>()
.
joinToString
(
"\n"
)
{
it
.
item
}
file
.
writeText
(
log
)
file
.
path
}
.
subscribeK
{
SnackbarEvent
(
it
).
publish
()
}
.
add
()
fun
restartPressed
()
=
RootUtils
.
reboot
()
fun
backPressed
()
=
back
()
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/hide/HideViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.hide
import
android.content.pm.ApplicationInfo
import
android.content.pm.PackageManager
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.extensions.subscribeK
import
com.skoumal.teanity.rxbus.RxBus
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.App
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.model.entity.HideAppInfo
import
com.topjohnwu.magisk.model.entity.HideTarget
import
com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem
import
com.topjohnwu.magisk.model.entity.recycler.HideRvItem
import
com.topjohnwu.magisk.model.events.HideProcessEvent
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.Utils
import
com.topjohnwu.magisk.utils.toSingle
import
com.topjohnwu.magisk.utils.update
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Single
import
me.tatarka.bindingcollectionadapter2.OnItemBind
import
timber.log.Timber
class
HideViewModel
(
private
val
packageManager
:
PackageManager
,
rxBus
:
RxBus
)
:
MagiskViewModel
()
{
val
query
=
KObservableField
(
""
)
val
isShowSystem
=
KObservableField
(
false
)
private
val
allItems
=
mutableListOf
<
ComparableRvItem
<*>>()
val
items
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemBinding
=
OnItemBind
<
ComparableRvItem
<*>>
{
itemBinding
,
_
,
item
->
item
.
bind
(
itemBinding
)
itemBinding
.
bindExtra
(
BR
.
viewModel
,
this
@HideViewModel
)
}
init
{
rxBus
.
register
<
HideProcessEvent
>()
.
subscribeK
{
toggleItem
(
it
.
item
)
}
.
add
()
isShowSystem
.
addOnPropertyChangedCallback
{
query
()
}
query
.
addOnPropertyChangedCallback
{
query
()
}
refresh
()
}
fun
refresh
()
{
// fetching this for every item is nonsensical, so we add .cache() so the response is all
// the same for every single mapped item, it only actually executes the whole thing the
// first time around.
val
hideTargets
=
Shell
.
su
(
"magiskhide --ls"
).
toSingle
()
.
map
{
it
.
exec
().
out
}
.
flattenAsFlowable
{
it
}
.
map
{
HideTarget
(
it
)
}
.
toList
()
.
cache
()
Single
.
fromCallable
{
packageManager
.
getInstalledApplications
(
0
)
}
.
flattenAsFlowable
{
it
}
.
filter
{
it
.
enabled
&&
!
blacklist
.
contains
(
it
.
packageName
)
}
.
map
{
val
label
=
Utils
.
getAppLabel
(
it
,
packageManager
)
val
icon
=
it
.
loadIcon
(
packageManager
)
HideAppInfo
(
it
,
label
,
icon
)
}
.
filter
{
it
.
processes
.
isNotEmpty
()
}
.
map
{
HideRvItem
(
it
,
hideTargets
.
blockingGet
())
}
.
toList
()
.
map
{
it
.
sortBy
{
it
.
item
.
info
.
name
};
it
}
.
doOnSuccess
{
allItems
.
update
(
it
)
}
.
flatMap
{
queryRaw
()
}
.
applyViewModel
(
this
)
.
subscribeK
(
onError
=
Timber
::
e
)
{
items
.
update
(
it
.
first
,
it
.
second
)
}
.
add
()
}
private
fun
query
()
=
queryRaw
()
.
subscribeK
{
items
.
update
(
it
.
first
,
it
.
second
)
}
.
add
()
private
fun
queryRaw
(
showSystem
:
Boolean
=
isShowSystem
.
value
,
query
:
String
=
this
.
query
.
value
)
=
allItems
.
toSingle
()
.
map
{
it
.
filterIsInstance
<
HideRvItem
>()
}
.
flattenAsFlowable
{
it
}
.
filter
{
it
.
item
.
name
.
contains
(
query
,
ignoreCase
=
true
)
||
it
.
item
.
processes
.
any
{
it
.
contains
(
query
,
ignoreCase
=
true
)
}
}
.
filter
{
if
(
showSystem
)
true
else
it
.
item
.
info
.
flags
and
ApplicationInfo
.
FLAG_SYSTEM
==
0
}
.
toList
()
.
map
{
it
to
items
.
calculateDiff
(
it
)
}
private
fun
toggleItem
(
item
:
HideProcessRvItem
)
{
val
state
=
if
(
item
.
isHidden
.
value
)
"add"
else
"rm"
"magiskhide --%s %s %s"
.
format
(
state
,
item
.
packageName
,
item
.
process
)
.
let
{
Shell
.
su
(
it
)
}
.
toSingle
()
.
map
{
it
.
submit
()
}
.
subscribeK
()
}
companion
object
{
private
val
blacklist
=
listOf
(
App
.
self
.
packageName
,
"android"
,
"com.android.chrome"
,
"com.chrome.beta"
,
"com.chrome.dev"
,
"com.chrome.canary"
,
"com.android.webview"
,
"com.google.android.webview"
)
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
hide
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.Menu
;
import
android.view.MenuInflater
;
import
android.view.MenuItem
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.SearchView
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.recyclerview.widget.RecyclerView
;
import
androidx.swiperefreshlayout.widget.SwipeRefreshLayout
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.ApplicationAdapter
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
com.topjohnwu.magisk.utils.Event
;
import
butterknife.BindView
;
public
class
MagiskHideFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
swipeRefreshLayout
)
SwipeRefreshLayout
mSwipeRefreshLayout
;
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
recyclerView
;
private
SearchView
search
;
private
ApplicationAdapter
adapter
;
private
SearchView
.
OnQueryTextListener
searchListener
;
@Override
public
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setHasOptionsMenu
(
true
);
}
@Nullable
@Override
public
View
onCreateView
(
@NonNull
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
View
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_magisk_hide
,
container
,
false
);
unbinder
=
new
MagiskHideFragment_ViewBinding
(
this
,
view
);
adapter
=
new
ApplicationAdapter
(
requireActivity
());
recyclerView
.
setAdapter
(
adapter
);
mSwipeRefreshLayout
.
setRefreshing
(
true
);
mSwipeRefreshLayout
.
setOnRefreshListener
(
adapter:
:
refresh
);
searchListener
=
new
SearchView
.
OnQueryTextListener
()
{
@Override
public
boolean
onQueryTextSubmit
(
String
query
)
{
adapter
.
filter
(
query
);
return
false
;
}
@Override
public
boolean
onQueryTextChange
(
String
newText
)
{
adapter
.
filter
(
newText
);
return
false
;
}
};
requireActivity
().
setTitle
(
R
.
string
.
magiskhide
);
return
view
;
}
@Override
public
void
onCreateOptionsMenu
(
Menu
menu
,
MenuInflater
inflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_magiskhide
,
menu
);
search
=
(
SearchView
)
menu
.
findItem
(
R
.
id
.
app_search
).
getActionView
();
search
.
setOnQueryTextListener
(
searchListener
);
menu
.
findItem
(
R
.
id
.
show_system
).
setChecked
(
Config
.
get
(
Config
.
Key
.
SHOW_SYSTEM_APP
));
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
if
(
item
.
getItemId
()
==
R
.
id
.
show_system
)
{
boolean
showSystem
=
!
item
.
isChecked
();
item
.
setChecked
(
showSystem
);
Config
.
set
(
Config
.
Key
.
SHOW_SYSTEM_APP
,
showSystem
);
adapter
.
setShowSystem
(
showSystem
);
adapter
.
filter
(
search
.
getQuery
().
toString
());
}
return
true
;
}
@Override
public
int
[]
getListeningEvents
()
{
return
new
int
[]
{
Event
.
MAGISK_HIDE_DONE
};
}
@Override
public
void
onEvent
(
int
event
)
{
mSwipeRefreshLayout
.
setRefreshing
(
false
);
adapter
.
filter
(
search
.
getQuery
().
toString
());
}
}
app/src/main/java/com/topjohnwu/magisk/ui/hide/MagiskHideFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.hide
import
android.view.Menu
import
android.view.MenuInflater
import
android.view.MenuItem
import
android.widget.SearchView
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentMagiskHideBinding
import
com.topjohnwu.magisk.ui.base.MagiskFragment
import
org.koin.androidx.viewmodel.ext.android.viewModel
class
MagiskHideFragment
:
MagiskFragment
<
HideViewModel
,
FragmentMagiskHideBinding
>(),
SearchView
.
OnQueryTextListener
{
override
val
layoutRes
:
Int
=
R
.
layout
.
fragment_magisk_hide
override
val
viewModel
:
HideViewModel
by
viewModel
()
override
fun
onStart
()
{
super
.
onStart
()
setHasOptionsMenu
(
true
)
requireActivity
().
setTitle
(
R
.
string
.
magiskhide
)
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_magiskhide
,
menu
)
menu
.
apply
{
(
findItem
(
R
.
id
.
app_search
).
actionView
as
?
SearchView
)
?.
setOnQueryTextListener
(
this
@MagiskHideFragment
)
val
showSystem
=
Config
.
get
<
Boolean
>(
Config
.
Key
.
SHOW_SYSTEM_APP
)
findItem
(
R
.
id
.
show_system
).
isChecked
=
showSystem
viewModel
.
isShowSystem
.
value
=
showSystem
}
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
if
(
item
.
itemId
==
R
.
id
.
show_system
)
{
val
showSystem
=
!
item
.
isChecked
item
.
isChecked
=
showSystem
Config
.
set
(
Config
.
Key
.
SHOW_SYSTEM_APP
,
showSystem
)
viewModel
.
isShowSystem
.
value
=
showSystem
//adapter!!.setShowSystem(showSystem)
//adapter!!.filter(search!!.query.toString())
}
return
true
}
override
fun
onQueryTextSubmit
(
query
:
String
?):
Boolean
{
viewModel
.
query
.
value
=
query
.
orEmpty
()
return
false
}
override
fun
onQueryTextChange
(
query
:
String
?):
Boolean
{
viewModel
.
query
.
value
=
query
.
orEmpty
()
return
false
}
/*override fun onEvent(event: Int) {
//mSwipeRefreshLayout!!.isRefreshing = false
adapter!!.filter(search!!.query.toString())
}*/
}
app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.home
import
android.content.res.Resources
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.*
import
com.topjohnwu.magisk.model.events.*
import
com.topjohnwu.magisk.model.observer.Observer
import
com.topjohnwu.magisk.tasks.CheckUpdates
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.Event
import
com.topjohnwu.magisk.utils.ISafetyNetHelper
import
com.topjohnwu.magisk.utils.toggle
import
com.topjohnwu.net.Networking
import
com.topjohnwu.superuser.Shell
class
HomeViewModel
(
private
val
resources
:
Resources
,
private
val
app
:
App
)
:
MagiskViewModel
()
{
val
isAdvancedExpanded
=
KObservableField
(
false
)
val
isForceEncryption
=
KObservableField
(
Config
.
keepEnc
)
val
isKeepVerity
=
KObservableField
(
Config
.
keepVerity
)
val
magiskState
=
KObservableField
(
MagiskState
.
LOADING
)
val
magiskStateText
=
Observer
(
magiskState
)
{
when
(
magiskState
.
value
)
{
MagiskState
.
NO_ROOT
->
TODO
()
MagiskState
.
NOT_INSTALLED
->
resources
.
getString
(
R
.
string
.
magisk_version_error
)
MagiskState
.
UP_TO_DATE
->
resources
.
getString
(
R
.
string
.
magisk_up_to_date
)
MagiskState
.
LOADING
->
resources
.
getString
(
R
.
string
.
checking_for_updates
)
MagiskState
.
OBSOLETE
->
resources
.
getString
(
R
.
string
.
magisk_update_title
)
}
}
val
magiskCurrentVersion
=
KObservableField
(
""
)
val
magiskLatestVersion
=
KObservableField
(
""
)
val
magiskAdditionalInfo
=
Observer
(
magiskState
)
{
if
(
Config
.
get
<
Boolean
>(
Config
.
Key
.
COREONLY
))
resources
.
getString
(
R
.
string
.
core_only_enabled
)
else
""
}
val
managerState
=
KObservableField
(
MagiskState
.
LOADING
)
val
managerStateText
=
Observer
(
managerState
)
{
when
(
managerState
.
value
)
{
MagiskState
.
NO_ROOT
->
"wtf"
MagiskState
.
NOT_INSTALLED
->
resources
.
getString
(
R
.
string
.
invalid_update_channel
)
MagiskState
.
UP_TO_DATE
->
resources
.
getString
(
R
.
string
.
manager_up_to_date
)
MagiskState
.
LOADING
->
resources
.
getString
(
R
.
string
.
checking_for_updates
)
MagiskState
.
OBSOLETE
->
resources
.
getString
(
R
.
string
.
manager_update_title
)
}
}
val
managerCurrentVersion
=
KObservableField
(
""
)
val
managerLatestVersion
=
KObservableField
(
""
)
val
managerAdditionalInfo
=
Observer
(
managerState
)
{
if
(
app
.
packageName
!=
BuildConfig
.
APPLICATION_ID
)
"(${app.packageName})"
else
""
}
val
safetyNetTitle
=
KObservableField
(
resources
.
getString
(
R
.
string
.
safetyNet_check_text
))
val
ctsState
=
KObservableField
(
SafetyNetState
.
IDLE
)
val
basicIntegrityState
=
KObservableField
(
SafetyNetState
.
IDLE
)
val
safetyNetState
=
Observer
(
ctsState
,
basicIntegrityState
)
{
val
cts
=
ctsState
.
value
val
basic
=
basicIntegrityState
.
value
val
states
=
listOf
(
cts
,
basic
)
when
{
states
.
any
{
it
==
SafetyNetState
.
LOADING
}
->
State
.
LOADING
states
.
any
{
it
==
SafetyNetState
.
IDLE
}
->
State
.
LOADING
else
->
State
.
LOADED
}
}
val
hasRoot
=
KObservableField
(
false
)
private
var
shownDialog
=
false
private
val
current
=
resources
.
getString
(
R
.
string
.
current_installed
)
private
val
latest
=
resources
.
getString
(
R
.
string
.
latest_version
)
init
{
Event
.
register
(
this
)
isForceEncryption
.
addOnPropertyChangedCallback
{
Config
.
keepEnc
=
it
?:
return
@addOnPropertyChangedCallback
}
isKeepVerity
.
addOnPropertyChangedCallback
{
Config
.
keepVerity
=
it
?:
return
@addOnPropertyChangedCallback
}
refresh
()
}
override
fun
onEvent
(
event
:
Int
)
{
updateSelf
()
ensureEnv
()
}
override
fun
getListeningEvents
():
IntArray
=
intArrayOf
(
Event
.
UPDATE_CHECK_DONE
)
fun
paypalPressed
()
=
OpenLinkEvent
(
Const
.
Url
.
PAYPAL_URL
).
publish
()
fun
patreonPressed
()
=
OpenLinkEvent
(
Const
.
Url
.
PATREON_URL
).
publish
()
fun
twitterPressed
()
=
OpenLinkEvent
(
Const
.
Url
.
TWITTER_URL
).
publish
()
fun
githubPressed
()
=
OpenLinkEvent
(
Const
.
Url
.
SOURCE_CODE_URL
).
publish
()
fun
xdaPressed
()
=
OpenLinkEvent
(
Const
.
Url
.
XDA_THREAD
).
publish
()
fun
uninstallPressed
()
=
UninstallEvent
().
publish
()
fun
advancedPressed
()
=
isAdvancedExpanded
.
toggle
()
fun
installPressed
(
item
:
MagiskItem
)
=
when
(
item
)
{
MagiskItem
.
MANAGER
->
ManagerInstallEvent
().
publish
()
MagiskItem
.
MAGISK
->
MagiskInstallEvent
().
publish
()
}
fun
cardPressed
(
item
:
MagiskItem
)
=
when
(
item
)
{
MagiskItem
.
MANAGER
->
ManagerChangelogEvent
().
publish
()
MagiskItem
.
MAGISK
->
MagiskChangelogEvent
().
publish
()
}
fun
safetyNetPressed
()
{
ctsState
.
value
=
SafetyNetState
.
LOADING
basicIntegrityState
.
value
=
SafetyNetState
.
LOADING
safetyNetTitle
.
value
=
resources
.
getString
(
R
.
string
.
checking_safetyNet_status
)
UpdateSafetyNetEvent
().
publish
()
}
fun
finishSafetyNetCheck
(
response
:
Int
)
=
when
{
response
and
0
x0F
==
0
->
{
val
hasCtsPassed
=
response
and
ISafetyNetHelper
.
CTS_PASS
!=
0
val
hasBasicIntegrityPassed
=
response
and
ISafetyNetHelper
.
BASIC_PASS
!=
0
safetyNetTitle
.
value
=
resources
.
getString
(
R
.
string
.
safetyNet_check_success
)
ctsState
.
value
=
if
(
hasCtsPassed
)
{
SafetyNetState
.
PASS
}
else
{
SafetyNetState
.
FAILED
}
basicIntegrityState
.
value
=
if
(
hasBasicIntegrityPassed
)
{
SafetyNetState
.
PASS
}
else
{
SafetyNetState
.
FAILED
}
}
response
==
-
2
->
{
ctsState
.
value
=
SafetyNetState
.
IDLE
basicIntegrityState
.
value
=
SafetyNetState
.
IDLE
}
else
->
{
ctsState
.
value
=
SafetyNetState
.
IDLE
basicIntegrityState
.
value
=
SafetyNetState
.
IDLE
val
errorString
=
when
(
response
)
{
ISafetyNetHelper
.
RESPONSE_ERR
->
R
.
string
.
safetyNet_res_invalid
else
->
R
.
string
.
safetyNet_api_error
}
safetyNetTitle
.
value
=
resources
.
getString
(
errorString
)
}
}
fun
refresh
()
{
state
=
State
.
LOADING
magiskState
.
value
=
MagiskState
.
LOADING
managerState
.
value
=
MagiskState
.
LOADING
Event
.
reset
(
this
)
Config
.
remoteMagiskVersionString
=
null
Config
.
remoteMagiskVersionCode
=
-
1
hasRoot
.
value
=
Shell
.
rootAccess
()
if
(
Networking
.
checkNetworkStatus
(
app
))
{
CheckUpdates
.
check
()
}
else
{
state
=
State
.
LOADING_FAILED
}
}
private
fun
updateSelf
()
{
state
=
State
.
LOADED
magiskState
.
value
=
when
(
Config
.
magiskVersionCode
)
{
in
Int
.
MIN_VALUE
until
0
->
MagiskState
.
NOT_INSTALLED
!
in
Config
.
remoteMagiskVersionCode
..
Int
.
MAX_VALUE
->
MagiskState
.
OBSOLETE
else
->
MagiskState
.
UP_TO_DATE
}
if
(
magiskState
.
value
!=
MagiskState
.
NOT_INSTALLED
)
{
magiskCurrentVersion
.
value
=
version
.
format
(
Config
.
magiskVersionString
,
Config
.
magiskVersionCode
)
.
let
{
current
.
format
(
it
)
}
}
else
{
magiskCurrentVersion
.
value
=
""
}
magiskLatestVersion
.
value
=
version
.
format
(
Config
.
remoteMagiskVersionString
,
Config
.
remoteMagiskVersionCode
)
.
let
{
latest
.
format
(
it
)
}
managerState
.
value
=
when
(
Config
.
remoteManagerVersionCode
)
{
in
Int
.
MIN_VALUE
until
0
->
MagiskState
.
NOT_INSTALLED
//wrong update channel
in
(
BuildConfig
.
VERSION_CODE
+
1
)
..
Int
.
MAX_VALUE
->
MagiskState
.
OBSOLETE
else
->
MagiskState
.
UP_TO_DATE
}
managerCurrentVersion
.
value
=
version
.
format
(
BuildConfig
.
VERSION_NAME
,
BuildConfig
.
VERSION_CODE
)
.
let
{
current
.
format
(
it
)
}
managerLatestVersion
.
value
=
version
.
format
(
Config
.
remoteManagerVersionString
,
Config
.
remoteManagerVersionCode
)
.
let
{
latest
.
format
(
it
)
}
}
private
fun
ensureEnv
()
{
val
invalidStates
=
listOf
(
MagiskState
.
NOT_INSTALLED
,
MagiskState
.
NO_ROOT
,
MagiskState
.
LOADING
)
// Don't bother checking env when magisk is not installed, loading or already has been shown
if
(
invalidStates
.
any
{
it
==
magiskState
.
value
}
||
shownDialog
)
return
if
(!
Shell
.
su
(
"env_check"
).
exec
().
isSuccess
)
{
shownDialog
=
true
EnvFixEvent
().
publish
()
}
}
companion
object
{
private
const
val
version
=
"%s (%d)"
}
}
app/src/main/java/com/topjohnwu/magisk/ui/home/MagiskFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
home
;
import
android.net.Uri
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.CheckBox
;
import
android.widget.ImageView
;
import
android.widget.LinearLayout
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.cardview.widget.CardView
;
import
androidx.swiperefreshlayout.widget.SwipeRefreshLayout
;
import
androidx.transition.ChangeBounds
;
import
androidx.transition.Fade
;
import
androidx.transition.Transition
;
import
androidx.transition.TransitionManager
;
import
androidx.transition.TransitionSet
;
import
com.topjohnwu.magisk.BuildConfig
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.tasks.CheckUpdates
;
import
com.topjohnwu.magisk.ui.MainActivity
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
com.topjohnwu.magisk.utils.Event
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.magisk.view.ArrowExpandable
;
import
com.topjohnwu.magisk.view.Expandable
;
import
com.topjohnwu.magisk.view.ExpandableViewHolder
;
import
com.topjohnwu.magisk.view.MarkDownWindow
;
import
com.topjohnwu.magisk.view.SafetyNet
;
import
com.topjohnwu.magisk.view.UpdateCardHolder
;
import
com.topjohnwu.magisk.view.dialogs.EnvFixDialog
;
import
com.topjohnwu.magisk.view.dialogs.MagiskInstallDialog
;
import
com.topjohnwu.magisk.view.dialogs.ManagerInstallDialog
;
import
com.topjohnwu.magisk.view.dialogs.UninstallDialog
;
import
com.topjohnwu.net.Networking
;
import
com.topjohnwu.superuser.Shell
;
import
java.util.Locale
;
import
butterknife.BindColor
;
import
butterknife.BindView
;
import
butterknife.OnClick
;
public
class
MagiskFragment
extends
BaseFragment
implements
SwipeRefreshLayout
.
OnRefreshListener
{
private
static
boolean
shownDialog
=
false
;
@BindView
(
R
.
id
.
swipeRefreshLayout
)
SwipeRefreshLayout
mSwipeRefreshLayout
;
@BindView
(
R
.
id
.
linearLayout
)
LinearLayout
root
;
@BindView
(
R
.
id
.
install_option_card
)
CardView
installOptionCard
;
@BindView
(
R
.
id
.
keep_force_enc
)
CheckBox
keepEncChkbox
;
@BindView
(
R
.
id
.
keep_verity
)
CheckBox
keepVerityChkbox
;
@BindView
(
R
.
id
.
install_option_expand
)
ViewGroup
optionExpandLayout
;
@BindView
(
R
.
id
.
arrow
)
ImageView
arrow
;
@BindView
(
R
.
id
.
uninstall_button
)
CardView
uninstallButton
;
@BindColor
(
R
.
color
.
red500
)
int
colorBad
;
@BindColor
(
R
.
color
.
green500
)
int
colorOK
;
@BindColor
(
R
.
color
.
yellow500
)
int
colorWarn
;
@BindColor
(
R
.
color
.
green500
)
int
colorNeutral
;
@BindColor
(
R
.
color
.
blue500
)
int
colorInfo
;
private
UpdateCardHolder
magisk
;
private
UpdateCardHolder
manager
;
private
SafetyNet
safetyNet
;
private
Transition
transition
;
private
Expandable
optionExpand
;
private
void
magiskInstall
(
View
v
)
{
// Show Manager update first
if
(
Config
.
remoteManagerVersionCode
>
BuildConfig
.
VERSION_CODE
)
{
new
ManagerInstallDialog
(
requireActivity
()).
show
();
return
;
}
new
MagiskInstallDialog
((
BaseActivity
)
requireActivity
()).
show
();
}
private
void
managerInstall
(
View
v
)
{
new
ManagerInstallDialog
(
requireActivity
()).
show
();
}
private
void
openLink
(
String
url
)
{
Utils
.
openLink
(
requireActivity
(),
Uri
.
parse
(
url
));
}
@OnClick
(
R
.
id
.
paypal
)
void
paypal
()
{
openLink
(
Const
.
Url
.
PAYPAL_URL
);
}
@OnClick
(
R
.
id
.
patreon
)
void
patreon
()
{
openLink
(
Const
.
Url
.
PATREON_URL
);
}
@OnClick
(
R
.
id
.
twitter
)
void
twitter
()
{
openLink
(
Const
.
Url
.
TWITTER_URL
);
}
@OnClick
(
R
.
id
.
github
)
void
github
()
{
openLink
(
Const
.
Url
.
SOURCE_CODE_URL
);
}
@OnClick
(
R
.
id
.
xda
)
void
xda
()
{
openLink
(
Const
.
Url
.
XDA_THREAD
);
}
@OnClick
(
R
.
id
.
uninstall_button
)
void
uninstall
()
{
new
UninstallDialog
(
requireActivity
()).
show
();
}
@OnClick
(
R
.
id
.
arrow
)
void
expandOptions
()
{
if
(
optionExpand
.
isExpanded
())
optionExpand
.
collapse
();
else
optionExpand
.
expand
();
}
@Nullable
@Override
public
View
onCreateView
(
@NonNull
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
View
v
=
inflater
.
inflate
(
R
.
layout
.
fragment_magisk
,
container
,
false
);
unbinder
=
new
MagiskFragment_ViewBinding
(
this
,
v
);
requireActivity
().
setTitle
(
R
.
string
.
magisk
);
optionExpand
=
new
ArrowExpandable
(
new
ExpandableViewHolder
(
optionExpandLayout
),
arrow
);
safetyNet
=
new
SafetyNet
(
v
);
magisk
=
new
UpdateCardHolder
(
inflater
,
root
);
manager
=
new
UpdateCardHolder
(
inflater
,
root
);
manager
.
setClickable
(
vv
->
MarkDownWindow
.
show
(
requireActivity
(),
null
,
getResources
().
openRawResource
(
R
.
raw
.
changelog
)));
root
.
addView
(
magisk
.
itemView
,
1
);
root
.
addView
(
manager
.
itemView
,
2
);
keepVerityChkbox
.
setChecked
(
Config
.
keepVerity
);
keepVerityChkbox
.
setOnCheckedChangeListener
((
view
,
checked
)
->
Config
.
keepVerity
=
checked
);
keepEncChkbox
.
setChecked
(
Config
.
keepEnc
);
keepEncChkbox
.
setOnCheckedChangeListener
((
view
,
checked
)
->
Config
.
keepEnc
=
checked
);
mSwipeRefreshLayout
.
setOnRefreshListener
(
this
);
magisk
.
install
.
setOnClickListener
(
this
::
magiskInstall
);
manager
.
install
.
setOnClickListener
(
this
::
managerInstall
);
if
(
Config
.
get
(
Config
.
Key
.
COREONLY
))
{
magisk
.
additional
.
setText
(
R
.
string
.
core_only_enabled
);
magisk
.
additional
.
setVisibility
(
View
.
VISIBLE
);
}
if
(!
app
.
getPackageName
().
equals
(
BuildConfig
.
APPLICATION_ID
))
{
manager
.
additional
.
setText
(
"("
+
app
.
getPackageName
()
+
")"
);
manager
.
additional
.
setVisibility
(
View
.
VISIBLE
);
}
transition
=
new
TransitionSet
()
.
setOrdering
(
TransitionSet
.
ORDERING_TOGETHER
)
.
addTransition
(
new
Fade
(
Fade
.
OUT
))
.
addTransition
(
new
ChangeBounds
())
.
addTransition
(
new
Fade
(
Fade
.
IN
));
updateUI
();
return
v
;
}
@Override
public
void
onDestroyView
()
{
super
.
onDestroyView
();
safetyNet
.
unbinder
.
unbind
();
magisk
.
unbinder
.
unbind
();
manager
.
unbinder
.
unbind
();
}
@Override
public
void
onRefresh
()
{
mSwipeRefreshLayout
.
setRefreshing
(
false
);
TransitionManager
.
beginDelayedTransition
(
root
,
transition
);
safetyNet
.
reset
();
magisk
.
reset
();
manager
.
reset
();
Config
.
loadMagiskInfo
();
updateUI
();
Event
.
reset
(
this
);
Config
.
remoteMagiskVersionString
=
null
;
Config
.
remoteMagiskVersionCode
=
-
1
;
shownDialog
=
false
;
// Trigger state check
if
(
Networking
.
checkNetworkStatus
(
app
))
{
CheckUpdates
.
check
();
}
}
@Override
public
int
[]
getListeningEvents
()
{
return
new
int
[]
{
Event
.
UPDATE_CHECK_DONE
};
}
@Override
public
void
onEvent
(
int
event
)
{
updateCheckUI
();
}
private
void
updateUI
()
{
((
MainActivity
)
requireActivity
()).
checkHideSection
();
int
image
,
color
;
String
status
;
if
(
Config
.
magiskVersionCode
<
0
)
{
color
=
colorBad
;
image
=
R
.
drawable
.
ic_cancel
;
status
=
getString
(
R
.
string
.
magisk_version_error
);
magisk
.
status
.
setText
(
status
);
magisk
.
currentVersion
.
setVisibility
(
View
.
GONE
);
}
else
{
color
=
colorOK
;
image
=
R
.
drawable
.
ic_check_circle
;
status
=
getString
(
R
.
string
.
magisk
);
magisk
.
currentVersion
.
setText
(
getString
(
R
.
string
.
current_installed
,
String
.
format
(
Locale
.
US
,
"v%s (%d)"
,
Config
.
magiskVersionString
,
Config
.
magiskVersionCode
)));
}
magisk
.
statusIcon
.
setColorFilter
(
color
);
magisk
.
statusIcon
.
setImageResource
(
image
);
manager
.
statusIcon
.
setColorFilter
(
colorOK
);
manager
.
statusIcon
.
setImageResource
(
R
.
drawable
.
ic_check_circle
);
manager
.
currentVersion
.
setText
(
getString
(
R
.
string
.
current_installed
,
String
.
format
(
Locale
.
US
,
"v%s (%d)"
,
BuildConfig
.
VERSION_NAME
,
BuildConfig
.
VERSION_CODE
)));
if
(!
Networking
.
checkNetworkStatus
(
app
))
{
// No network, updateCheckUI will not be triggered
magisk
.
status
.
setText
(
status
);
manager
.
status
.
setText
(
R
.
string
.
app_name
);
magisk
.
setValid
(
false
);
manager
.
setValid
(
false
);
}
}
private
void
updateCheckUI
()
{
int
image
,
color
;
String
status
,
button
=
""
;
TransitionManager
.
beginDelayedTransition
(
root
,
transition
);
if
(
Config
.
remoteMagiskVersionCode
<
0
)
{
color
=
colorNeutral
;
image
=
R
.
drawable
.
ic_help
;
status
=
getString
(
R
.
string
.
invalid_update_channel
);
}
else
{
magisk
.
latestVersion
.
setText
(
getString
(
R
.
string
.
latest_version
,
String
.
format
(
Locale
.
US
,
"v%s (%d)"
,
Config
.
remoteMagiskVersionString
,
Config
.
remoteMagiskVersionCode
)));
if
(
Config
.
remoteMagiskVersionCode
>
Config
.
magiskVersionCode
)
{
color
=
colorInfo
;
image
=
R
.
drawable
.
ic_update
;
status
=
getString
(
R
.
string
.
magisk_update_title
);
button
=
getString
(
R
.
string
.
update
);
}
else
{
color
=
colorOK
;
image
=
R
.
drawable
.
ic_check_circle
;
status
=
getString
(
R
.
string
.
magisk_up_to_date
);
button
=
getString
(
R
.
string
.
install
);
}
}
if
(
Config
.
magiskVersionCode
>
0
)
{
// Only override status if Magisk is installed
magisk
.
statusIcon
.
setImageResource
(
image
);
magisk
.
statusIcon
.
setColorFilter
(
color
);
magisk
.
status
.
setText
(
status
);
magisk
.
install
.
setText
(
button
);
}
if
(
Config
.
remoteManagerVersionCode
<
0
)
{
color
=
colorNeutral
;
image
=
R
.
drawable
.
ic_help
;
status
=
getString
(
R
.
string
.
invalid_update_channel
);
}
else
{
manager
.
latestVersion
.
setText
(
getString
(
R
.
string
.
latest_version
,
String
.
format
(
Locale
.
US
,
"v%s (%d)"
,
Config
.
remoteManagerVersionString
,
Config
.
remoteManagerVersionCode
)));
if
(
Config
.
remoteManagerVersionCode
>
BuildConfig
.
VERSION_CODE
)
{
color
=
colorInfo
;
image
=
R
.
drawable
.
ic_update
;
status
=
getString
(
R
.
string
.
manager_update_title
);
manager
.
install
.
setText
(
R
.
string
.
update
);
}
else
{
color
=
colorOK
;
image
=
R
.
drawable
.
ic_check_circle
;
status
=
getString
(
R
.
string
.
manager_up_to_date
);
manager
.
install
.
setText
(
R
.
string
.
install
);
}
}
manager
.
statusIcon
.
setImageResource
(
image
);
manager
.
statusIcon
.
setColorFilter
(
color
);
manager
.
status
.
setText
(
status
);
magisk
.
setValid
(
Config
.
remoteMagiskVersionCode
>
0
);
manager
.
setValid
(
Config
.
remoteManagerVersionCode
>
0
);
if
(
Config
.
remoteMagiskVersionCode
<
0
)
{
// Hide install related components
installOptionCard
.
setVisibility
(
View
.
GONE
);
uninstallButton
.
setVisibility
(
View
.
GONE
);
}
else
{
// Show install related components
installOptionCard
.
setVisibility
(
View
.
VISIBLE
);
uninstallButton
.
setVisibility
(
Shell
.
rootAccess
()
?
View
.
VISIBLE
:
View
.
GONE
);
}
if
(!
shownDialog
&&
Config
.
magiskVersionCode
>
0
&&
!
Shell
.
su
(
"env_check"
).
exec
().
isSuccess
())
{
shownDialog
=
true
;
new
EnvFixDialog
(
requireActivity
()).
show
();
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/home/MagiskFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.home
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.Const
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentMagiskBinding
import
com.topjohnwu.magisk.model.events.*
import
com.topjohnwu.magisk.ui.base.MagiskActivity
import
com.topjohnwu.magisk.utils.ISafetyNetHelper
import
com.topjohnwu.magisk.view.MarkDownWindow
import
com.topjohnwu.magisk.view.SafetyNet
import
com.topjohnwu.magisk.view.SafetyNet.EXT_APK
import
com.topjohnwu.magisk.view.dialogs.*
import
com.topjohnwu.net.Networking
import
com.topjohnwu.superuser.Shell
import
org.koin.androidx.viewmodel.ext.android.viewModel
import
com.topjohnwu.magisk.ui.base.MagiskFragment
as
NewMagiskFragment
class
MagiskFragment
:
NewMagiskFragment
<
HomeViewModel
,
FragmentMagiskBinding
>(),
ISafetyNetHelper
.
Callback
{
override
val
layoutRes
:
Int
=
R
.
layout
.
fragment_magisk
override
val
viewModel
:
HomeViewModel
by
viewModel
()
override
fun
onResponse
(
responseCode
:
Int
)
=
viewModel
.
finishSafetyNetCheck
(
responseCode
)
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
OpenLinkEvent
->
openLink
(
event
.
url
)
is
ManagerInstallEvent
->
installManager
()
is
MagiskInstallEvent
->
installMagisk
()
is
UninstallEvent
->
uninstall
()
is
ManagerChangelogEvent
->
changelogManager
()
is
EnvFixEvent
->
fixEnv
()
is
UpdateSafetyNetEvent
->
updateSafetyNet
(
false
)
}
}
override
fun
onStart
()
{
super
.
onStart
()
setHasOptionsMenu
(
true
)
requireActivity
().
setTitle
(
R
.
string
.
magisk
)
}
private
fun
installMagisk
()
{
// Show Manager update first
if
(
Config
.
remoteManagerVersionCode
>
BuildConfig
.
VERSION_CODE
)
{
installManager
()
return
}
MagiskInstallDialog
(
requireActivity
()
as
MagiskActivity
<*,
*>).
show
()
}
private
fun
installManager
()
=
ManagerInstallDialog
(
requireActivity
()).
show
()
private
fun
uninstall
()
=
UninstallDialog
(
requireActivity
()).
show
()
private
fun
fixEnv
()
=
EnvFixDialog
(
requireActivity
()).
show
()
private
fun
changelogManager
()
=
MarkDownWindow
.
show
(
requireActivity
(),
null
,
resources
.
openRawResource
(
R
.
raw
.
changelog
))
private
fun
downloadSafetyNet
(
requiresUserInput
:
Boolean
=
true
)
{
fun
download
()
=
Networking
.
get
(
Const
.
Url
.
SNET_URL
)
.
getAsFile
(
EXT_APK
)
{
updateSafetyNet
(
true
)
}
if
(!
requiresUserInput
)
{
download
()
return
}
CustomAlertDialog
(
requireActivity
())
.
setTitle
(
R
.
string
.
proprietary_title
)
.
setMessage
(
R
.
string
.
proprietary_notice
)
.
setCancelable
(
false
)
.
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
download
()
}
.
setNegativeButton
(
R
.
string
.
no_thanks
)
{
_
,
_
->
viewModel
.
finishSafetyNetCheck
(-
2
)
}
.
show
()
}
private
fun
updateSafetyNet
(
dieOnError
:
Boolean
)
{
try
{
SafetyNet
.
dyRun
(
requireActivity
(),
this
)
}
catch
(
e
:
Exception
)
{
if
(
dieOnError
)
{
viewModel
.
finishSafetyNetCheck
(-
1
)
return
}
Shell
.
sh
(
"rm -rf "
+
EXT_APK
.
parent
).
exec
()
EXT_APK
.
parentFile
?.
mkdir
()
downloadSafetyNet
(!
dieOnError
)
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/home/MagiskItem.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.home
enum
class
MagiskItem
{
MANAGER
,
MAGISK
}
app/src/main/java/com/topjohnwu/magisk/ui/home/MagiskState.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.home
enum
class
MagiskState
{
NO_ROOT
,
NOT_INSTALLED
,
UP_TO_DATE
,
OBSOLETE
,
LOADING
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/home/SafetyNetState.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.home
enum
class
SafetyNetState
{
LOADING
,
PASS
,
FAILED
,
IDLE
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
log
;
import
android.os.Build
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
androidx.viewpager.widget.ViewPager
;
import
com.google.android.material.tabs.TabLayout
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.TabFragmentAdapter
;
import
com.topjohnwu.magisk.ui.MainActivity
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
butterknife.BindView
;
public
class
LogFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
container
)
ViewPager
viewPager
;
@BindView
(
R
.
id
.
tab
)
TabLayout
tab
;
@Override
public
View
onCreateView
(
LayoutInflater
inflater
,
ViewGroup
container
,
Bundle
savedInstanceState
)
{
// Inflate the layout for this fragment
View
v
=
inflater
.
inflate
(
R
.
layout
.
fragment_log
,
container
,
false
);
unbinder
=
new
LogFragment_ViewBinding
(
this
,
v
);
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
((
MainActivity
)
requireActivity
()).
toolbar
.
setElevation
(
0
);
}
TabFragmentAdapter
adapter
=
new
TabFragmentAdapter
(
getChildFragmentManager
());
adapter
.
addTab
(
new
SuLogFragment
(),
getString
(
R
.
string
.
superuser
));
adapter
.
addTab
(
new
MagiskLogFragment
(),
getString
(
R
.
string
.
magisk
));
tab
.
setupWithViewPager
(
viewPager
);
tab
.
setVisibility
(
View
.
VISIBLE
);
viewPager
.
setAdapter
(
adapter
);
return
v
;
}
}
app/src/main/java/com/topjohnwu/magisk/ui/log/LogFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.log
import
android.os.Bundle
import
android.view.Menu
import
android.view.MenuInflater
import
android.view.MenuItem
import
android.view.View
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentLogBinding
import
com.topjohnwu.magisk.model.events.PageChangedEvent
import
com.topjohnwu.magisk.ui.base.MagiskFragment
import
org.koin.androidx.viewmodel.ext.android.viewModel
class
LogFragment
:
MagiskFragment
<
LogViewModel
,
FragmentLogBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
fragment_log
override
val
viewModel
:
LogViewModel
by
viewModel
()
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
PageChangedEvent
->
magiskActivity
.
invalidateOptionsMenu
()
}
}
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
binding
.
logTabs
.
setupWithViewPager
(
binding
.
logContainer
,
true
)
}
override
fun
onStart
()
{
super
.
onStart
()
setHasOptionsMenu
(
true
)
magiskActivity
.
setTitle
(
R
.
string
.
log
)
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_log
,
menu
)
menu
.
findItem
(
R
.
id
.
menu_save
).
isVisible
=
viewModel
.
currentPage
.
value
==
1
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
R
.
id
.
menu_save
->
viewModel
.
saveLog
()
R
.
id
.
menu_clear
->
viewModel
.
clearLog
()
R
.
id
.
menu_refresh
->
viewModel
.
refresh
()
}
return
true
}
}
app/src/main/java/com/topjohnwu/magisk/ui/log/LogViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.log
import
android.content.res.Resources
import
androidx.databinding.ObservableArrayList
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.extensions.doOnSuccessUi
import
com.skoumal.teanity.extensions.subscribeK
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.skoumal.teanity.viewevents.SnackbarEvent
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.Const
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.data.database.MagiskDB
import
com.topjohnwu.magisk.model.entity.recycler.*
import
com.topjohnwu.magisk.model.events.PageChangedEvent
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.toSingle
import
com.topjohnwu.magisk.utils.zip
import
com.topjohnwu.superuser.Shell
import
io.reactivex.Single
import
me.tatarka.bindingcollectionadapter2.BindingViewPagerAdapter
import
me.tatarka.bindingcollectionadapter2.OnItemBind
import
java.io.File
import
java.io.IOException
import
java.util.*
class
LogViewModel
(
private
val
resources
:
Resources
,
private
val
database
:
MagiskDB
)
:
MagiskViewModel
(),
BindingViewPagerAdapter
.
PageTitles
<
ComparableRvItem
<*>>
{
val
items
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemBinding
=
OnItemBind
<
ComparableRvItem
<*>>
{
itemBinding
,
_
,
item
->
item
.
bind
(
itemBinding
)
itemBinding
.
bindExtra
(
BR
.
viewModel
,
this
@LogViewModel
)
}
val
currentPage
=
KObservableField
(
0
)
private
val
currentItem
get
()
=
items
[
currentPage
.
value
]
private
val
logItem
get
()
=
items
[
0
]
as
LogRvItem
private
val
magiskLogItem
get
()
=
items
[
1
]
as
MagiskLogRvItem
init
{
currentPage
.
addOnPropertyChangedCallback
{
it
?:
return
@addOnPropertyChangedCallback
PageChangedEvent
().
publish
()
}
items
.
addAll
(
listOf
(
LogRvItem
(),
MagiskLogRvItem
()))
refresh
()
}
override
fun
getPageTitle
(
position
:
Int
,
item
:
ComparableRvItem
<
*
>?)
=
when
(
item
)
{
is
LogRvItem
->
resources
.
getString
(
R
.
string
.
superuser
)
is
MagiskLogRvItem
->
resources
.
getString
(
R
.
string
.
magisk
)
else
->
""
}
fun
refresh
()
=
zip
(
updateLogs
(),
updateMagiskLog
())
{
_
,
_
->
true
}
.
subscribeK
()
.
add
()
fun
saveLog
()
{
val
now
=
Calendar
.
getInstance
()
val
filename
=
"magisk_log_%04d%02d%02d_%02d%02d%02d.log"
.
format
(
now
.
get
(
Calendar
.
YEAR
),
now
.
get
(
Calendar
.
MONTH
)
+
1
,
now
.
get
(
Calendar
.
DAY_OF_MONTH
),
now
.
get
(
Calendar
.
HOUR_OF_DAY
),
now
.
get
(
Calendar
.
MINUTE
),
now
.
get
(
Calendar
.
SECOND
)
)
val
logFile
=
File
(
Const
.
EXTERNAL_PATH
,
filename
)
try
{
logFile
.
createNewFile
()
}
catch
(
e
:
IOException
)
{
return
}
Shell
.
su
(
"cat ${Const.MAGISK_LOG} > $logFile"
).
submit
{
SnackbarEvent
(
logFile
.
path
).
publish
()
}
}
fun
clearLog
()
=
when
(
currentItem
)
{
is
LogRvItem
->
clearLogs
{
refresh
()
}
is
MagiskLogRvItem
->
clearMagiskLogs
{
refresh
()
}
else
->
Unit
}
private
fun
clearLogs
(
callback
:
()
->
Unit
)
{
Single
.
fromCallable
{
database
.
clearLogs
()
}
.
subscribeK
{
SnackbarEvent
(
R
.
string
.
logs_cleared
).
publish
()
callback
()
}
.
add
()
}
private
fun
clearMagiskLogs
(
callback
:
()
->
Unit
)
{
Shell
.
su
(
"echo -n > "
+
Const
.
MAGISK_LOG
).
submit
{
SnackbarEvent
(
R
.
string
.
logs_cleared
).
publish
()
callback
()
}
}
private
fun
updateLogs
()
=
Single
.
fromCallable
{
database
.
logs
}
.
flattenAsFlowable
{
it
}
.
map
{
it
.
map
{
LogItemEntryRvItem
(
it
)
}
}
.
map
{
LogItemRvItem
(
ObservableArrayList
<
ComparableRvItem
<*>>().
apply
{
addAll
(
it
)
})
}
.
toList
()
.
doOnSuccessUi
{
logItem
.
update
(
it
)
}
private
fun
updateMagiskLog
()
=
Shell
.
su
(
"tail -n 5000 ${Const.MAGISK_LOG}"
).
toSingle
()
.
map
{
it
.
exec
()
}
.
map
{
it
.
out
}
.
flattenAsFlowable
{
it
}
.
map
{
ConsoleRvItem
(
it
)
}
.
toList
()
.
doOnSuccessUi
{
magiskLogItem
.
update
(
it
)
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/log/MagiskLogFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
log
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.Menu
;
import
android.view.MenuInflater
;
import
android.view.MenuItem
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.google.android.material.snackbar.Snackbar
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.StringListAdapter
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.magisk.view.SnackbarMaker
;
import
com.topjohnwu.superuser.Shell
;
import
com.topjohnwu.superuser.internal.NOPList
;
import
java.io.File
;
import
java.io.IOException
;
import
java.util.Calendar
;
import
java.util.List
;
import
butterknife.BindView
;
public
class
MagiskLogFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
rv
;
@Nullable
@Override
public
View
onCreateView
(
LayoutInflater
inflater
,
ViewGroup
container
,
Bundle
savedInstanceState
)
{
View
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_magisk_log
,
container
,
false
);
unbinder
=
new
MagiskLogFragment_ViewBinding
(
this
,
view
);
setHasOptionsMenu
(
true
);
return
view
;
}
@Override
public
void
onStart
()
{
super
.
onStart
();
getActivity
().
setTitle
(
R
.
string
.
log
);
}
@Override
public
void
onResume
()
{
super
.
onResume
();
readLogs
();
}
@Override
public
void
onCreateOptionsMenu
(
Menu
menu
,
MenuInflater
inflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_log
,
menu
);
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
switch
(
item
.
getItemId
())
{
case
R
.
id
.
menu_refresh
:
readLogs
();
return
true
;
case
R
.
id
.
menu_save
:
runWithExternalRW
(
this
::
saveLogs
);
return
true
;
case
R
.
id
.
menu_clear
:
clearLogs
();
rv
.
setAdapter
(
new
MagiskLogAdapter
(
NOPList
.
getInstance
()));
return
true
;
default
:
return
true
;
}
}
private
void
readLogs
()
{
Shell
.
su
(
"tail -n 5000 "
+
Const
.
MAGISK_LOG
).
submit
(
result
->
{
rv
.
setAdapter
(
new
MagiskLogAdapter
(
result
.
getOut
()));
});
}
private
void
saveLogs
()
{
Calendar
now
=
Calendar
.
getInstance
();
String
filename
=
Utils
.
fmt
(
"magisk_log_%04d%02d%02d_%02d%02d%02d.log"
,
now
.
get
(
Calendar
.
YEAR
),
now
.
get
(
Calendar
.
MONTH
)
+
1
,
now
.
get
(
Calendar
.
DAY_OF_MONTH
),
now
.
get
(
Calendar
.
HOUR_OF_DAY
),
now
.
get
(
Calendar
.
MINUTE
),
now
.
get
(
Calendar
.
SECOND
));
File
logFile
=
new
File
(
Const
.
EXTERNAL_PATH
,
filename
);
try
{
logFile
.
createNewFile
();
}
catch
(
IOException
e
)
{
return
;
}
Shell
.
su
(
"cat "
+
Const
.
MAGISK_LOG
+
" > "
+
logFile
)
.
submit
(
result
->
SnackbarMaker
.
make
(
rv
,
logFile
.
getPath
(),
Snackbar
.
LENGTH_SHORT
).
show
());
}
private
void
clearLogs
()
{
Shell
.
su
(
"echo -n > "
+
Const
.
MAGISK_LOG
).
submit
();
SnackbarMaker
.
make
(
rv
,
R
.
string
.
logs_cleared
,
Snackbar
.
LENGTH_SHORT
).
show
();
}
private
class
MagiskLogAdapter
extends
StringListAdapter
<
MagiskLogAdapter
.
ViewHolder
>
{
MagiskLogAdapter
(
List
<
String
>
list
)
{
super
(
list
);
if
(
mList
.
isEmpty
())
mList
.
add
(
requireContext
().
getString
(
R
.
string
.
log_is_empty
));
}
@Override
protected
int
itemLayoutRes
()
{
return
R
.
layout
.
list_item_console
;
}
@NonNull
@Override
public
ViewHolder
createViewHolder
(
@NonNull
View
v
)
{
return
new
ViewHolder
(
v
);
}
@Override
protected
void
onUpdateTextWidth
(
ViewHolder
holder
)
{
super
.
onUpdateTextWidth
(
holder
);
// Find the longest string and update accordingly
int
max
=
0
;
String
maxStr
=
""
;
for
(
String
s
:
mList
)
{
int
len
=
s
.
length
();
if
(
len
>
max
)
{
max
=
len
;
maxStr
=
s
;
}
}
holder
.
txt
.
setText
(
maxStr
);
super
.
onUpdateTextWidth
(
holder
);
}
public
class
ViewHolder
extends
StringListAdapter
.
ViewHolder
{
public
ViewHolder
(
@NonNull
View
itemView
)
{
super
(
itemView
);
}
@Override
protected
int
textViewResId
()
{
return
R
.
id
.
txt
;
}
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/log/SuLogFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
log
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.Menu
;
import
android.view.MenuInflater
;
import
android.view.MenuItem
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.TextView
;
import
androidx.annotation.Nullable
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.SuLogAdapter
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
butterknife.BindView
;
public
class
SuLogFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
empty_rv
)
TextView
emptyRv
;
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
recyclerView
;
private
SuLogAdapter
adapter
;
@Override
public
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setHasOptionsMenu
(
true
);
}
@Override
public
void
onCreateOptionsMenu
(
Menu
menu
,
MenuInflater
inflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_log
,
menu
);
menu
.
findItem
(
R
.
id
.
menu_save
).
setVisible
(
false
);
}
@Override
public
View
onCreateView
(
LayoutInflater
inflater
,
ViewGroup
container
,
Bundle
savedInstanceState
)
{
// Inflate the layout for this fragment
View
v
=
inflater
.
inflate
(
R
.
layout
.
fragment_su_log
,
container
,
false
);
unbinder
=
new
SuLogFragment_ViewBinding
(
this
,
v
);
adapter
=
new
SuLogAdapter
(
app
.
mDB
);
recyclerView
.
setAdapter
(
adapter
);
updateList
();
return
v
;
}
private
void
updateList
()
{
adapter
.
notifyDBChanged
();
if
(
adapter
.
getSectionCount
()
==
0
)
{
emptyRv
.
setVisibility
(
View
.
VISIBLE
);
recyclerView
.
setVisibility
(
View
.
GONE
);
}
else
{
emptyRv
.
setVisibility
(
View
.
GONE
);
recyclerView
.
setVisibility
(
View
.
VISIBLE
);
}
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
switch
(
item
.
getItemId
())
{
case
R
.
id
.
menu_refresh
:
updateList
();
return
true
;
case
R
.
id
.
menu_clear
:
app
.
mDB
.
clearLogs
();
updateList
();
return
true
;
default
:
return
true
;
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.module
import
android.content.res.Resources
import
android.database.Cursor
import
androidx.annotation.StringRes
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.addOnPropertyChangedCallback
import
com.skoumal.teanity.extensions.subscribeK
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.data.database.RepoDatabaseHelper
import
com.topjohnwu.magisk.model.entity.Module
import
com.topjohnwu.magisk.model.entity.Repo
import
com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem
import
com.topjohnwu.magisk.model.entity.recycler.RepoRvItem
import
com.topjohnwu.magisk.model.entity.recycler.SectionRvItem
import
com.topjohnwu.magisk.model.events.InstallModuleEvent
import
com.topjohnwu.magisk.model.events.OpenChangelogEvent
import
com.topjohnwu.magisk.model.events.OpenFilePickerEvent
import
com.topjohnwu.magisk.tasks.UpdateRepos
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.Event
import
com.topjohnwu.magisk.utils.Utils
import
com.topjohnwu.magisk.utils.toSingle
import
com.topjohnwu.magisk.utils.update
import
io.reactivex.Single
import
io.reactivex.disposables.Disposable
import
me.tatarka.bindingcollectionadapter2.OnItemBind
class
ModuleViewModel
(
private
val
repoDatabase
:
RepoDatabaseHelper
,
private
val
resources
:
Resources
)
:
MagiskViewModel
()
{
val
query
=
KObservableField
(
""
)
private
val
allItems
=
mutableListOf
<
ComparableRvItem
<*>>()
val
itemsInstalled
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemsRemote
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemBinding
=
OnItemBind
<
ComparableRvItem
<*>>
{
itemBinding
,
_
,
item
->
item
.
bind
(
itemBinding
)
itemBinding
.
bindExtra
(
BR
.
viewModel
,
this
@ModuleViewModel
)
}
private
var
queryDisposable
:
Disposable
?
=
null
init
{
query
.
addOnPropertyChangedCallback
{
queryDisposable
?.
dispose
()
queryDisposable
=
query
()
}
Event
.
register
(
this
)
refresh
()
}
override
fun
getListeningEvents
():
IntArray
{
return
intArrayOf
(
Event
.
MODULE_LOAD_DONE
,
Event
.
REPO_LOAD_DONE
)
}
override
fun
onEvent
(
event
:
Int
)
=
when
(
event
)
{
Event
.
MODULE_LOAD_DONE
->
updateModules
(
Event
.
getResult
(
event
))
Event
.
REPO_LOAD_DONE
->
updateRepos
()
else
->
Unit
}
fun
fabPressed
()
=
OpenFilePickerEvent
().
publish
()
fun
repoPressed
(
item
:
RepoRvItem
)
=
OpenChangelogEvent
(
item
.
item
).
publish
()
fun
downloadPressed
(
item
:
RepoRvItem
)
=
InstallModuleEvent
(
item
.
item
).
publish
()
fun
refresh
()
{
state
=
State
.
LOADING
Utils
.
loadModules
(
true
)
UpdateRepos
().
exec
(
true
)
}
private
fun
updateModules
(
result
:
Map
<
String
,
Module
>)
=
result
.
values
.
map
{
ModuleRvItem
(
it
)
}
.
let
{
itemsInstalled
.
update
(
it
)
}
internal
fun
updateRepos
()
{
Single
.
fromCallable
{
repoDatabase
.
repoCursor
.
toList
{
Repo
(
it
)
}
}
.
flattenAsFlowable
{
it
}
.
map
{
RepoRvItem
(
it
)
}
.
toList
()
.
doOnSuccess
{
allItems
.
update
(
it
)
}
.
flatMap
{
queryRaw
()
}
.
applyViewModel
(
this
)
.
subscribeK
{
itemsRemote
.
update
(
it
.
first
,
it
.
second
)
}
.
add
()
}
private
fun
query
()
=
queryRaw
()
.
subscribeK
{
itemsRemote
.
update
(
it
.
first
,
it
.
second
)
}
private
fun
queryRaw
(
query
:
String
=
this
.
query
.
value
)
=
allItems
.
toSingle
()
.
map
{
it
.
filterIsInstance
<
RepoRvItem
>()
}
.
flattenAsFlowable
{
it
}
.
filter
{
it
.
item
.
name
.
contains
(
query
,
ignoreCase
=
true
)
||
it
.
item
.
author
.
contains
(
query
,
ignoreCase
=
true
)
||
it
.
item
.
description
.
contains
(
query
,
ignoreCase
=
true
)
}
.
toList
()
.
map
{
if
(
query
.
isEmpty
())
it
.
divide
()
else
it
}
.
map
{
it
to
itemsRemote
.
calculateDiff
(
it
)
}
private
fun
List
<
RepoRvItem
>.
divide
():
List
<
ComparableRvItem
<*
>>
{
val
installed
=
itemsInstalled
.
filterIsInstance
<
ModuleRvItem
>()
val
installedModules
=
filter
{
installed
.
any
{
item
->
it
.
item
.
id
==
item
.
item
.
id
}
}
fun
installedByID
(
id
:
String
)
=
installed
.
firstOrNull
{
it
.
item
.
id
==
id
}
fun
List
<
RepoRvItem
>.
filterObsolete
()
=
filter
{
val
module
=
installedByID
(
it
.
item
.
id
)
?:
return
@filter
false
module
.
item
.
versionCode
!=
it
.
item
.
versionCode
}
val
resultObsolete
=
installedModules
.
filterObsolete
()
val
resultInstalled
=
installedModules
-
resultObsolete
val
resultRemote
=
toList
()
-
installedModules
fun
buildList
(
@StringRes
text
:
Int
,
list
:
List
<
RepoRvItem
>):
List
<
ComparableRvItem
<*
>>
{
return
if
(
list
.
isEmpty
())
list
else
listOf
(
SectionRvItem
(
resources
.
getString
(
text
)))
+
list
}
return
buildList
(
R
.
string
.
update_available
,
resultObsolete
)
+
buildList
(
R
.
string
.
installed
,
resultInstalled
)
+
buildList
(
R
.
string
.
not_installed
,
resultRemote
)
}
private
fun
<
Result
>
Cursor
.
toList
(
transformer
:
(
Cursor
)
->
Result
):
List
<
Result
>
{
val
out
=
mutableListOf
<
Result
>()
while
(
moveToNext
())
out
.
add
(
transformer
(
this
))
return
out
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
module
;
import
android.app.Activity
;
import
android.content.Intent
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.Menu
;
import
android.view.MenuInflater
;
import
android.view.MenuItem
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.TextView
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.recyclerview.widget.RecyclerView
;
import
androidx.swiperefreshlayout.widget.SwipeRefreshLayout
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.ModulesAdapter
;
import
com.topjohnwu.magisk.model.entity.Module
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
com.topjohnwu.magisk.ui.flash.FlashActivity
;
import
com.topjohnwu.magisk.utils.Event
;
import
com.topjohnwu.magisk.utils.RootUtils
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.superuser.Shell
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
butterknife.BindView
;
import
butterknife.OnClick
;
public
class
ModulesFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
swipeRefreshLayout
)
SwipeRefreshLayout
mSwipeRefreshLayout
;
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
recyclerView
;
@BindView
(
R
.
id
.
empty_rv
)
TextView
emptyRv
;
@OnClick
(
R
.
id
.
fab
)
void
selectFile
()
{
runWithExternalRW
(()
->
{
Intent
intent
=
new
Intent
(
Intent
.
ACTION_GET_CONTENT
);
intent
.
setType
(
"application/zip"
);
startActivityForResult
(
intent
,
Const
.
ID
.
FETCH_ZIP
);
});
}
private
List
<
Module
>
listModules
=
new
ArrayList
<>();
@Nullable
@Override
public
View
onCreateView
(
@NonNull
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
View
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_modules
,
container
,
false
);
unbinder
=
new
ModulesFragment_ViewBinding
(
this
,
view
);
setHasOptionsMenu
(
true
);
mSwipeRefreshLayout
.
setOnRefreshListener
(()
->
{
recyclerView
.
setVisibility
(
View
.
GONE
);
Utils
.
loadModules
();
});
recyclerView
.
addOnScrollListener
(
new
RecyclerView
.
OnScrollListener
()
{
@Override
public
void
onScrolled
(
RecyclerView
recyclerView
,
int
dx
,
int
dy
)
{
mSwipeRefreshLayout
.
setEnabled
(
recyclerView
.
getChildAt
(
0
).
getTop
()
>=
0
);
}
@Override
public
void
onScrollStateChanged
(
RecyclerView
recyclerView
,
int
newState
)
{
super
.
onScrollStateChanged
(
recyclerView
,
newState
);
}
});
requireActivity
().
setTitle
(
R
.
string
.
modules
);
return
view
;
}
@Override
public
int
[]
getListeningEvents
()
{
return
new
int
[]
{
Event
.
MODULE_LOAD_DONE
};
}
@Override
public
void
onEvent
(
int
event
)
{
updateUI
(
Event
.
getResult
(
event
));
}
@Override
public
void
onActivityResult
(
int
requestCode
,
int
resultCode
,
Intent
data
)
{
if
(
requestCode
==
Const
.
ID
.
FETCH_ZIP
&&
resultCode
==
Activity
.
RESULT_OK
&&
data
!=
null
)
{
// Get the URI of the selected file
Intent
intent
=
new
Intent
(
getActivity
(),
ClassMap
.
get
(
FlashActivity
.
class
));
intent
.
setData
(
data
.
getData
()).
putExtra
(
Const
.
Key
.
FLASH_ACTION
,
Const
.
Value
.
FLASH_ZIP
);
startActivity
(
intent
);
}
}
@Override
public
void
onCreateOptionsMenu
(
Menu
menu
,
MenuInflater
inflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_reboot
,
menu
);
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
switch
(
item
.
getItemId
())
{
case
R
.
id
.
reboot
:
RootUtils
.
reboot
();
return
true
;
case
R
.
id
.
reboot_recovery
:
Shell
.
su
(
"/system/bin/reboot recovery"
).
submit
();
return
true
;
case
R
.
id
.
reboot_bootloader
:
Shell
.
su
(
"/system/bin/reboot bootloader"
).
submit
();
return
true
;
case
R
.
id
.
reboot_download
:
Shell
.
su
(
"/system/bin/reboot download"
).
submit
();
return
true
;
default
:
return
false
;
}
}
private
void
updateUI
(
Map
<
String
,
Module
>
moduleMap
)
{
listModules
.
clear
();
listModules
.
addAll
(
moduleMap
.
values
());
if
(
listModules
.
size
()
==
0
)
{
emptyRv
.
setVisibility
(
View
.
VISIBLE
);
recyclerView
.
setVisibility
(
View
.
GONE
);
}
else
{
emptyRv
.
setVisibility
(
View
.
GONE
);
recyclerView
.
setVisibility
(
View
.
VISIBLE
);
recyclerView
.
setAdapter
(
new
ModulesAdapter
(
listModules
));
}
mSwipeRefreshLayout
.
setRefreshing
(
false
);
}
}
app/src/main/java/com/topjohnwu/magisk/ui/module/ModulesFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.module
import
android.app.Activity
import
android.content.Intent
import
android.os.Bundle
import
android.view.Menu
import
android.view.MenuInflater
import
android.view.MenuItem
import
android.view.View
import
androidx.recyclerview.widget.RecyclerView
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.ClassMap
import
com.topjohnwu.magisk.Const
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentModulesBinding
import
com.topjohnwu.magisk.model.events.OpenFilePickerEvent
import
com.topjohnwu.magisk.ui.base.MagiskFragment
import
com.topjohnwu.magisk.ui.flash.FlashActivity
import
com.topjohnwu.magisk.utils.RootUtils
import
com.topjohnwu.superuser.Shell
import
org.koin.androidx.viewmodel.ext.android.sharedViewModel
class
ModulesFragment
:
MagiskFragment
<
ModuleViewModel
,
FragmentModulesBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
fragment_modules
override
val
viewModel
:
ModuleViewModel
by
sharedViewModel
()
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
if
(
requestCode
==
Const
.
ID
.
FETCH_ZIP
&&
resultCode
==
Activity
.
RESULT_OK
&&
data
!=
null
)
{
// Get the URI of the selected file
val
intent
=
Intent
(
activity
,
ClassMap
.
get
<
Any
>(
FlashActivity
::
class
.
java
))
intent
.
setData
(
data
.
data
).
putExtra
(
Const
.
Key
.
FLASH_ACTION
,
Const
.
Value
.
FLASH_ZIP
)
startActivity
(
intent
)
}
}
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
binding
.
modulesContent
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
binding
.
modulesRefreshLayout
.
isEnabled
=
recyclerView
.
getChildAt
(
0
).
top
>=
0
}
})
}
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
OpenFilePickerEvent
->
selectFile
()
}
}
override
fun
onStart
()
{
super
.
onStart
()
setHasOptionsMenu
(
true
)
requireActivity
().
setTitle
(
R
.
string
.
modules
)
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_reboot
,
menu
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
R
.
id
.
reboot
->
{
RootUtils
.
reboot
()
return
true
}
R
.
id
.
reboot_recovery
->
{
Shell
.
su
(
"/system/bin/reboot recovery"
).
submit
()
return
true
}
R
.
id
.
reboot_bootloader
->
{
Shell
.
su
(
"/system/bin/reboot bootloader"
).
submit
()
return
true
}
R
.
id
.
reboot_download
->
{
Shell
.
su
(
"/system/bin/reboot download"
).
submit
()
return
true
}
else
->
return
false
}
}
private
fun
selectFile
()
{
magiskActivity
.
runWithExternalRW
{
val
intent
=
Intent
(
Intent
.
ACTION_GET_CONTENT
)
intent
.
type
=
"application/zip"
startActivityForResult
(
intent
,
Const
.
ID
.
FETCH_ZIP
)
}
}
/*override fun getListeningEvents(): IntArray {
return intArrayOf(Event.MODULE_LOAD_DONE)
}
override fun onEvent(event: Int) {
updateUI(Event.getResult(event))
}*/
/*private fun updateUI(moduleMap: Map<String, Module>) {
listModules.clear()
listModules.addAll(moduleMap.values)
if (listModules.size == 0) {
emptyRv!!.visibility = View.VISIBLE
recyclerView!!.visibility = View.GONE
} else {
emptyRv!!.visibility = View.GONE
recyclerView!!.visibility = View.VISIBLE
recyclerView!!.adapter = ModulesAdapter(listModules)
}
mSwipeRefreshLayout!!.isRefreshing = false
}*/
}
app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
module
;
import
android.app.AlertDialog
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.Menu
;
import
android.view.MenuInflater
;
import
android.view.MenuItem
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.SearchView
;
import
android.widget.TextView
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.recyclerview.widget.RecyclerView
;
import
androidx.swiperefreshlayout.widget.SwipeRefreshLayout
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.ReposAdapter
;
import
com.topjohnwu.magisk.tasks.UpdateRepos
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
com.topjohnwu.magisk.utils.Event
;
import
butterknife.BindView
;
public
class
ReposFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
recyclerView
;
@BindView
(
R
.
id
.
empty_rv
)
TextView
emptyRv
;
@BindView
(
R
.
id
.
swipeRefreshLayout
)
SwipeRefreshLayout
mSwipeRefreshLayout
;
private
ReposAdapter
adapter
;
@Override
public
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setHasOptionsMenu
(
true
);
}
@Nullable
@Override
public
View
onCreateView
(
@NonNull
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
View
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_repos
,
container
,
false
);
unbinder
=
new
ReposFragment_ViewBinding
(
this
,
view
);
mSwipeRefreshLayout
.
setRefreshing
(
true
);
mSwipeRefreshLayout
.
setOnRefreshListener
(()
->
new
UpdateRepos
().
exec
(
true
));
adapter
=
new
ReposAdapter
();
recyclerView
.
setAdapter
(
adapter
);
recyclerView
.
setVisibility
(
View
.
GONE
);
requireActivity
().
setTitle
(
R
.
string
.
downloads
);
return
view
;
}
@Override
public
void
onDestroyView
()
{
super
.
onDestroyView
();
Event
.
unregister
(
adapter
);
}
@Override
public
int
[]
getListeningEvents
()
{
return
new
int
[]
{
Event
.
REPO_LOAD_DONE
};
}
@Override
public
void
onEvent
(
int
event
)
{
adapter
.
notifyDBChanged
(
false
);
Event
.
register
(
adapter
);
mSwipeRefreshLayout
.
setRefreshing
(
false
);
boolean
empty
=
adapter
.
getItemCount
()
==
0
;
recyclerView
.
setVisibility
(
empty
?
View
.
GONE
:
View
.
VISIBLE
);
emptyRv
.
setVisibility
(
empty
?
View
.
VISIBLE
:
View
.
GONE
);
}
@Override
public
void
onCreateOptionsMenu
(
Menu
menu
,
MenuInflater
inflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_repo
,
menu
);
SearchView
search
=
(
SearchView
)
menu
.
findItem
(
R
.
id
.
repo_search
).
getActionView
();
adapter
.
setSearchView
(
search
);
}
@Override
public
boolean
onOptionsItemSelected
(
MenuItem
item
)
{
if
(
item
.
getItemId
()
==
R
.
id
.
repo_sort
)
{
new
AlertDialog
.
Builder
(
getActivity
())
.
setTitle
(
R
.
string
.
sorting_order
)
.
setSingleChoiceItems
(
R
.
array
.
sorting_orders
,
Config
.
get
(
Config
.
Key
.
REPO_ORDER
),
(
d
,
which
)
->
{
Config
.
set
(
Config
.
Key
.
REPO_ORDER
,
which
);
adapter
.
notifyDBChanged
(
true
);
d
.
dismiss
();
}).
show
();
}
return
true
;
}
}
app/src/main/java/com/topjohnwu/magisk/ui/module/ReposFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.module
import
android.app.AlertDialog
import
android.content.Intent
import
android.os.Build
import
android.view.Menu
import
android.view.MenuInflater
import
android.view.MenuItem
import
android.widget.SearchView
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.ClassMap
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentReposBinding
import
com.topjohnwu.magisk.model.download.DownloadModuleService
import
com.topjohnwu.magisk.model.entity.Repo
import
com.topjohnwu.magisk.model.events.InstallModuleEvent
import
com.topjohnwu.magisk.model.events.OpenChangelogEvent
import
com.topjohnwu.magisk.ui.base.MagiskFragment
import
com.topjohnwu.magisk.view.MarkDownWindow
import
com.topjohnwu.magisk.view.dialogs.CustomAlertDialog
import
org.koin.androidx.viewmodel.ext.android.sharedViewModel
class
ReposFragment
:
MagiskFragment
<
ModuleViewModel
,
FragmentReposBinding
>(),
SearchView
.
OnQueryTextListener
{
override
val
layoutRes
:
Int
=
R
.
layout
.
fragment_repos
override
val
viewModel
:
ModuleViewModel
by
sharedViewModel
()
override
fun
onStart
()
{
super
.
onStart
()
setHasOptionsMenu
(
true
)
requireActivity
().
setTitle
(
R
.
string
.
downloads
)
}
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
OpenChangelogEvent
->
openChangelog
(
event
.
item
)
is
InstallModuleEvent
->
installModule
(
event
.
item
)
}
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
inflater
.
inflate
(
R
.
menu
.
menu_repo
,
menu
)
(
menu
.
findItem
(
R
.
id
.
repo_search
).
actionView
as
?
SearchView
)
?.
setOnQueryTextListener
(
this
)
}
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
if
(
item
.
itemId
==
R
.
id
.
repo_sort
)
{
AlertDialog
.
Builder
(
activity
)
.
setTitle
(
R
.
string
.
sorting_order
)
.
setSingleChoiceItems
(
R
.
array
.
sorting_orders
,
Config
.
get
<
Int
>(
Config
.
Key
.
REPO_ORDER
)
!!
)
{
d
,
which
->
Config
.
set
(
Config
.
Key
.
REPO_ORDER
,
which
)
viewModel
.
updateRepos
()
d
.
dismiss
()
}.
show
()
}
return
true
}
override
fun
onQueryTextSubmit
(
p0
:
String
?):
Boolean
{
viewModel
.
query
.
value
=
p0
.
orEmpty
()
return
false
}
override
fun
onQueryTextChange
(
p0
:
String
?):
Boolean
{
viewModel
.
query
.
value
=
p0
.
orEmpty
()
return
false
}
private
fun
openChangelog
(
item
:
Repo
)
{
MarkDownWindow
.
show
(
context
,
null
,
item
.
detailUrl
)
}
private
fun
installModule
(
item
:
Repo
)
{
val
context
=
magiskActivity
fun
download
(
install
:
Boolean
)
{
context
.
runWithExternalRW
{
val
intent
=
Intent
(
activity
,
ClassMap
.
get
<
Any
>(
DownloadModuleService
::
class
.
java
))
.
putExtra
(
"repo"
,
item
).
putExtra
(
"install"
,
install
)
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
O
)
{
context
.
startForegroundService
(
intent
)
//hmm, service starts itself in foreground, this seems unnecessary
}
else
{
context
.
startService
(
intent
)
}
}
}
CustomAlertDialog
(
context
)
.
setTitle
(
context
.
getString
(
R
.
string
.
repo_install_title
,
item
.
name
))
.
setMessage
(
context
.
getString
(
R
.
string
.
repo_install_msg
,
item
.
downloadFilename
))
.
setCancelable
(
true
)
.
setPositiveButton
(
R
.
string
.
install
)
{
_
,
_
->
download
(
true
)
}
.
setNeutralButton
(
R
.
string
.
download
)
{
_
,
_
->
download
(
false
)
}
.
setNegativeButton
(
R
.
string
.
no_thanks
,
null
)
.
show
()
}
}
app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.java
View file @
2813d203
...
...
@@ -8,13 +8,6 @@ import android.view.View;
import
android.widget.EditText
;
import
android.widget.Toast
;
import
androidx.appcompat.app.AlertDialog
;
import
androidx.preference.ListPreference
;
import
androidx.preference.Preference
;
import
androidx.preference.PreferenceCategory
;
import
androidx.preference.PreferenceScreen
;
import
androidx.preference.SwitchPreferenceCompat
;
import
com.topjohnwu.magisk.BuildConfig
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
...
...
@@ -35,18 +28,32 @@ import java.io.IOException;
import
java.util.Arrays
;
import
java.util.Locale
;
import
androidx.appcompat.app.AlertDialog
;
import
androidx.preference.ListPreference
;
import
androidx.preference.Preference
;
import
androidx.preference.PreferenceCategory
;
import
androidx.preference.PreferenceScreen
;
import
androidx.preference.SwitchPreferenceCompat
;
public
class
SettingsFragment
extends
BasePreferenceFragment
{
private
ListPreference
updateChannel
,
autoRes
,
suNotification
,
requestTimeout
,
rootConfig
,
multiuserConfig
,
nsConfig
;
@Override
public
void
onStart
()
{
super
.
onStart
();
setHasOptionsMenu
(
true
);
requireActivity
().
setTitle
(
R
.
string
.
settings
);
}
@Override
public
void
onCreatePreferences
(
Bundle
savedInstanceState
,
String
rootKey
)
{
getPreferenceManager
().
setStorageDeviceProtected
();
setPreferencesFromResource
(
R
.
xml
.
app_settings
,
rootKey
);
requireActivity
().
setTitle
(
R
.
string
.
settings
);
boolean
showSuperuser
=
Utils
.
showSuperUser
();
app
.
prefs
.
edit
()
app
.
getPrefs
()
.
edit
()
.
putBoolean
(
Config
.
Key
.
SU_FINGERPRINT
,
FingerprintHelper
.
useFingerprint
())
.
apply
();
...
...
@@ -66,8 +73,8 @@ public class SettingsFragment extends BasePreferenceFragment {
return
true
;
});
findPreference
(
"clear"
).
setOnPreferenceClickListener
(
pref
->
{
app
.
prefs
.
edit
().
remove
(
Config
.
Key
.
ETAG_KEY
).
apply
();
app
.
repoDB
.
clearRepo
();
app
.
getPrefs
()
.
edit
().
remove
(
Config
.
Key
.
ETAG_KEY
).
apply
();
app
.
getRepoDB
()
.
clearRepo
();
Utils
.
toast
(
R
.
string
.
repo_cache_cleared
,
Toast
.
LENGTH_SHORT
);
return
true
;
});
...
...
@@ -94,7 +101,7 @@ public class SettingsFragment extends BasePreferenceFragment {
if
(
channel
==
Config
.
Value
.
CUSTOM_CHANNEL
)
{
View
v
=
LayoutInflater
.
from
(
requireActivity
()).
inflate
(
R
.
layout
.
custom_channel_dialog
,
null
);
EditText
url
=
v
.
findViewById
(
R
.
id
.
custom_url
);
url
.
setText
(
app
.
prefs
.
getString
(
Config
.
Key
.
CUSTOM_CHANNEL
,
""
));
url
.
setText
(
app
.
getPrefs
()
.
getString
(
Config
.
Key
.
CUSTOM_CHANNEL
,
""
));
new
AlertDialog
.
Builder
(
requireActivity
())
.
setTitle
(
R
.
string
.
settings_update_custom
)
.
setView
(
v
)
...
...
@@ -183,7 +190,7 @@ public class SettingsFragment extends BasePreferenceFragment {
case
Config
.
Key
.
ROOT_ACCESS
:
case
Config
.
Key
.
SU_MULTIUSER_MODE
:
case
Config
.
Key
.
SU_MNT_NS
:
app
.
mDB
.
setSettings
(
key
,
Utils
.
getPrefsInt
(
prefs
,
key
));
app
.
getDB
()
.
setSettings
(
key
,
Utils
.
getPrefsInt
(
prefs
,
key
));
break
;
case
Config
.
Key
.
DARK_THEME
:
requireActivity
().
recreate
();
...
...
app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
superuser
;
import
android.content.pm.PackageManager
;
import
android.os.Bundle
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.TextView
;
import
androidx.annotation.Nullable
;
import
androidx.recyclerview.widget.RecyclerView
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.adapters.PolicyAdapter
;
import
com.topjohnwu.magisk.model.entity.Policy
;
import
com.topjohnwu.magisk.ui.base.BaseFragment
;
import
java.util.List
;
import
butterknife.BindView
;
public
class
SuperuserFragment
extends
BaseFragment
{
@BindView
(
R
.
id
.
recyclerView
)
RecyclerView
recyclerView
;
@BindView
(
R
.
id
.
empty_rv
)
TextView
emptyRv
;
private
PackageManager
pm
;
@Nullable
@Override
public
View
onCreateView
(
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
View
view
=
inflater
.
inflate
(
R
.
layout
.
fragment_superuser
,
container
,
false
);
unbinder
=
new
SuperuserFragment_ViewBinding
(
this
,
view
);
pm
=
requireActivity
().
getPackageManager
();
return
view
;
}
@Override
public
void
onStart
()
{
super
.
onStart
();
requireActivity
().
setTitle
(
getString
(
R
.
string
.
superuser
));
}
@Override
public
void
onResume
()
{
super
.
onResume
();
displayPolicyList
();
}
private
void
displayPolicyList
()
{
List
<
Policy
>
policyList
=
app
.
mDB
.
getPolicyList
();
if
(
policyList
.
size
()
==
0
)
{
emptyRv
.
setVisibility
(
View
.
VISIBLE
);
recyclerView
.
setVisibility
(
View
.
GONE
);
}
else
{
recyclerView
.
setAdapter
(
new
PolicyAdapter
(
policyList
,
app
.
mDB
,
pm
));
emptyRv
.
setVisibility
(
View
.
GONE
);
recyclerView
.
setVisibility
(
View
.
VISIBLE
);
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.superuser
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.FragmentSuperuserBinding
import
com.topjohnwu.magisk.ui.base.MagiskFragment
import
org.koin.androidx.viewmodel.ext.android.viewModel
class
SuperuserFragment
:
MagiskFragment
<
SuperuserViewModel
,
FragmentSuperuserBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
fragment_superuser
override
val
viewModel
:
SuperuserViewModel
by
viewModel
()
override
fun
onStart
()
{
super
.
onStart
()
setHasOptionsMenu
(
true
)
requireActivity
().
setTitle
(
R
.
string
.
superuser
)
}
override
fun
onResume
()
{
super
.
onResume
()
viewModel
.
updatePolicies
()
}
}
app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.superuser
import
android.content.pm.PackageManager
import
android.content.res.Resources
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.extensions.applySchedulers
import
com.skoumal.teanity.extensions.subscribeK
import
com.skoumal.teanity.rxbus.RxBus
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.viewevents.SnackbarEvent
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.data.database.MagiskDB
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem
import
com.topjohnwu.magisk.model.events.PolicyEnableEvent
import
com.topjohnwu.magisk.model.events.PolicyUpdateEvent
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.FingerprintHelper
import
com.topjohnwu.magisk.utils.toggle
import
com.topjohnwu.magisk.view.dialogs.CustomAlertDialog
import
com.topjohnwu.magisk.view.dialogs.FingerprintAuthDialog
import
io.reactivex.Single
import
me.tatarka.bindingcollectionadapter2.ItemBinding
class
SuperuserViewModel
(
private
val
database
:
MagiskDB
,
private
val
packageManager
:
PackageManager
,
private
val
resources
:
Resources
,
rxBus
:
RxBus
)
:
MagiskViewModel
()
{
val
items
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemBinding
=
ItemBinding
.
of
<
ComparableRvItem
<*>>
{
itemBinding
,
_
,
item
->
item
.
bind
(
itemBinding
)
itemBinding
.
bindExtra
(
BR
.
viewModel
,
this
@SuperuserViewModel
)
}
private
var
ignoreNext
:
PolicyRvItem
?
=
null
init
{
rxBus
.
register
<
PolicyEnableEvent
>()
.
subscribeK
{
togglePolicy
(
it
.
item
,
it
.
enable
)
}
.
add
()
rxBus
.
register
<
PolicyUpdateEvent
>()
.
subscribeK
{
updatePolicy
(
it
)
}
.
add
()
updatePolicies
()
}
fun
updatePolicies
()
{
Single
.
fromCallable
{
database
.
policyList
}
.
flattenAsFlowable
{
it
}
.
map
{
PolicyRvItem
(
it
,
it
.
info
.
loadIcon
(
packageManager
))
}
.
toList
()
.
applySchedulers
()
.
applyViewModel
(
this
)
.
subscribeK
{
items
.
update
(
it
)
}
.
add
()
}
fun
deletePressed
(
item
:
PolicyRvItem
)
{
fun
updateState
()
=
deletePolicy
(
item
.
item
)
.
map
{
items
.
filterIsInstance
<
PolicyRvItem
>().
toMutableList
()
}
.
map
{
it
.
removeAll
{
it
.
item
.
packageName
==
item
.
item
.
packageName
};
it
}
.
map
{
it
to
items
.
calculateDiff
(
it
)
}
.
subscribeK
{
items
.
update
(
it
.
first
,
it
.
second
)
}
.
add
()
withView
{
if
(
FingerprintHelper
.
useFingerprint
())
{
FingerprintAuthDialog
(
this
)
{
updateState
()
}.
show
()
}
else
{
CustomAlertDialog
(
this
)
.
setTitle
(
R
.
string
.
su_revoke_title
)
.
setMessage
(
getString
(
R
.
string
.
su_revoke_msg
,
item
.
item
.
appName
))
.
setPositiveButton
(
R
.
string
.
yes
)
{
_
,
_
->
updateState
()
}
.
setNegativeButton
(
R
.
string
.
no_thanks
,
null
)
.
setCancelable
(
true
)
.
show
()
}
}
}
private
fun
updatePolicy
(
it
:
PolicyUpdateEvent
)
=
when
(
it
)
{
is
PolicyUpdateEvent
.
Notification
->
updatePolicy
(
it
.
item
)
{
val
textId
=
if
(
it
.
logging
)
R
.
string
.
su_snack_notif_on
else
R
.
string
.
su_snack_notif_off
val
text
=
resources
.
getString
(
textId
).
format
(
it
.
appName
)
SnackbarEvent
(
text
).
publish
()
}
is
PolicyUpdateEvent
.
Log
->
updatePolicy
(
it
.
item
)
{
val
textId
=
if
(
it
.
notification
)
R
.
string
.
su_snack_log_on
else
R
.
string
.
su_snack_log_off
val
text
=
resources
.
getString
(
textId
).
format
(
it
.
appName
)
SnackbarEvent
(
text
).
publish
()
}
}
private
fun
updatePolicy
(
item
:
PolicyRvItem
,
onSuccess
:
(
Policy
)
->
Unit
)
=
updatePolicy
(
item
.
item
)
.
subscribeK
{
onSuccess
(
it
)
}
.
add
()
private
fun
togglePolicy
(
item
:
PolicyRvItem
,
enable
:
Boolean
)
{
fun
updateState
()
{
item
.
item
.
policy
=
if
(
enable
)
Policy
.
ALLOW
else
Policy
.
DENY
updatePolicy
(
item
.
item
)
.
map
{
it
.
policy
==
Policy
.
ALLOW
}
.
subscribeK
{
val
textId
=
if
(
it
)
R
.
string
.
su_snack_grant
else
R
.
string
.
su_snack_deny
val
text
=
resources
.
getString
(
textId
).
format
(
item
.
item
.
appName
)
SnackbarEvent
(
text
).
publish
()
}
.
add
()
}
if
(
FingerprintHelper
.
useFingerprint
())
{
withView
{
FingerprintAuthDialog
(
this
,
{
updateState
()
},
{
ignoreNext
=
item
item
.
isEnabled
.
toggle
()
}).
show
()
}
}
else
{
updateState
()
}
}
private
fun
updatePolicy
(
policy
:
Policy
)
=
Single
.
fromCallable
{
database
.
updatePolicy
(
policy
);
policy
}
.
applySchedulers
()
private
fun
deletePolicy
(
policy
:
Policy
)
=
Single
.
fromCallable
{
database
.
deletePolicy
(
policy
);
policy
}
.
applySchedulers
()
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
ui
.
surequest
;
import
android.annotation.SuppressLint
;
import
android.content.Intent
;
import
android.content.SharedPreferences
;
import
android.content.pm.PackageManager
;
import
android.hardware.fingerprint.FingerprintManager
;
import
android.os.Bundle
;
import
android.os.CountDownTimer
;
import
android.text.TextUtils
;
import
android.view.View
;
import
android.view.Window
;
import
android.widget.ArrayAdapter
;
import
android.widget.Button
;
import
android.widget.ImageView
;
import
android.widget.LinearLayout
;
import
android.widget.Spinner
;
import
android.widget.TextView
;
import
androidx.annotation.Nullable
;
import
androidx.appcompat.content.res.AppCompatResources
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.BuildConfig
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.model.entity.Policy
;
import
com.topjohnwu.magisk.ui.base.BaseActivity
;
import
com.topjohnwu.magisk.utils.FingerprintHelper
;
import
com.topjohnwu.magisk.utils.SuConnector
;
import
com.topjohnwu.magisk.utils.SuLogger
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
butterknife.BindView
;
import
java9.lang.Iterables
;
public
class
SuRequestActivity
extends
BaseActivity
{
@BindView
(
R
.
id
.
su_popup
)
LinearLayout
suPopup
;
@BindView
(
R
.
id
.
timeout
)
Spinner
timeout
;
@BindView
(
R
.
id
.
app_icon
)
ImageView
appIcon
;
@BindView
(
R
.
id
.
app_name
)
TextView
appNameView
;
@BindView
(
R
.
id
.
package_name
)
TextView
packageNameView
;
@BindView
(
R
.
id
.
grant_btn
)
Button
grant_btn
;
@BindView
(
R
.
id
.
deny_btn
)
Button
deny_btn
;
@BindView
(
R
.
id
.
fingerprint
)
ImageView
fingerprintImg
;
@BindView
(
R
.
id
.
warning
)
TextView
warning
;
private
ActionHandler
handler
;
private
Policy
policy
;
private
SharedPreferences
timeoutPrefs
;
public
static
final
String
REQUEST
=
"request"
;
public
static
final
String
LOG
=
"log"
;
public
static
final
String
NOTIFY
=
"notify"
;
@Override
public
int
getDarkTheme
()
{
return
R
.
style
.
SuRequest_Dark
;
}
@Override
public
void
onBackPressed
()
{
handler
.
handleAction
(
Policy
.
DENY
,
-
1
);
}
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
lockOrientation
();
supportRequestWindowFeature
(
Window
.
FEATURE_NO_TITLE
);
timeoutPrefs
=
App
.
deContext
.
getSharedPreferences
(
"su_timeout"
,
0
);
Intent
intent
=
getIntent
();
String
action
=
intent
.
getAction
();
if
(
TextUtils
.
equals
(
action
,
REQUEST
))
{
if
(!
handleRequest
())
finish
();
return
;
}
if
(
TextUtils
.
equals
(
action
,
LOG
))
SuLogger
.
handleLogs
(
intent
);
else
if
(
TextUtils
.
equals
(
action
,
NOTIFY
))
SuLogger
.
handleNotify
(
intent
);
finish
();
}
private
boolean
handleRequest
()
{
String
socketName
=
getIntent
().
getStringExtra
(
"socket"
);
if
(
socketName
==
null
)
return
false
;
SuConnector
connector
;
try
{
connector
=
new
SuConnector
(
socketName
)
{
@Override
protected
void
onResponse
()
throws
IOException
{
out
.
writeInt
(
policy
.
policy
);
}
};
Bundle
bundle
=
connector
.
readSocketInput
();
int
uid
=
Integer
.
parseInt
(
bundle
.
getString
(
"uid"
));
app
.
mDB
.
clearOutdated
();
policy
=
app
.
mDB
.
getPolicy
(
uid
);
if
(
policy
==
null
)
{
policy
=
new
Policy
(
uid
,
getPackageManager
());
}
}
catch
(
IOException
|
PackageManager
.
NameNotFoundException
e
)
{
e
.
printStackTrace
();
return
false
;
}
handler
=
new
ActionHandler
()
{
@Override
void
handleAction
()
{
connector
.
response
();
done
();
}
@Override
void
handleAction
(
int
action
)
{
int
pos
=
timeout
.
getSelectedItemPosition
();
timeoutPrefs
.
edit
().
putInt
(
policy
.
packageName
,
pos
).
apply
();
handleAction
(
action
,
Config
.
Value
.
TIMEOUT_LIST
[
pos
]);
}
@Override
void
handleAction
(
int
action
,
int
time
)
{
policy
.
policy
=
action
;
if
(
time
>=
0
)
{
policy
.
until
=
(
time
==
0
)
?
0
:
(
System
.
currentTimeMillis
()
/
1000
+
time
*
60
);
app
.
mDB
.
updatePolicy
(
policy
);
}
handleAction
();
}
};
// Never allow com.topjohnwu.magisk (could be malware)
if
(
TextUtils
.
equals
(
policy
.
packageName
,
BuildConfig
.
APPLICATION_ID
))
return
false
;
// If not interactive, response directly
if
(
policy
.
policy
!=
Policy
.
INTERACTIVE
)
{
handler
.
handleAction
();
return
true
;
}
switch
((
int
)
Config
.
get
(
Config
.
Key
.
SU_AUTO_RESPONSE
))
{
case
Config
.
Value
.
SU_AUTO_DENY
:
handler
.
handleAction
(
Policy
.
DENY
,
0
);
return
true
;
case
Config
.
Value
.
SU_AUTO_ALLOW
:
handler
.
handleAction
(
Policy
.
ALLOW
,
0
);
return
true
;
}
showUI
();
return
true
;
}
@SuppressLint
(
"ClickableViewAccessibility"
)
private
void
showUI
()
{
setContentView
(
R
.
layout
.
activity_request
);
new
SuRequestActivity_ViewBinding
(
this
);
appIcon
.
setImageDrawable
(
policy
.
info
.
loadIcon
(
getPackageManager
()));
appNameView
.
setText
(
policy
.
appName
);
packageNameView
.
setText
(
policy
.
packageName
);
warning
.
setCompoundDrawablesRelativeWithIntrinsicBounds
(
AppCompatResources
.
getDrawable
(
this
,
R
.
drawable
.
ic_warning
),
null
,
null
,
null
);
ArrayAdapter
<
CharSequence
>
adapter
=
ArrayAdapter
.
createFromResource
(
this
,
R
.
array
.
allow_timeout
,
android
.
R
.
layout
.
simple_spinner_item
);
adapter
.
setDropDownViewResource
(
android
.
R
.
layout
.
simple_spinner_dropdown_item
);
timeout
.
setAdapter
(
adapter
);
timeout
.
setSelection
(
timeoutPrefs
.
getInt
(
policy
.
packageName
,
0
));
CountDownTimer
timer
=
new
CountDownTimer
(
(
int
)
Config
.
get
(
Config
.
Key
.
SU_REQUEST_TIMEOUT
)
*
1000
,
1000
)
{
@Override
public
void
onTick
(
long
remains
)
{
deny_btn
.
setText
(
getString
(
R
.
string
.
deny
)
+
"("
+
remains
/
1000
+
")"
);
}
@Override
public
void
onFinish
()
{
deny_btn
.
setText
(
getString
(
R
.
string
.
deny
));
handler
.
handleAction
(
Policy
.
DENY
);
}
};
timer
.
start
();
Runnable
cancelTimer
=
()
->
{
timer
.
cancel
();
deny_btn
.
setText
(
getString
(
R
.
string
.
deny
));
};
handler
.
addCancel
(
cancelTimer
);
boolean
useFP
=
FingerprintHelper
.
useFingerprint
();
if
(
useFP
)
try
{
FingerprintHelper
helper
=
new
SuFingerprint
();
helper
.
authenticate
();
handler
.
addCancel
(
helper:
:
cancel
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
useFP
=
false
;
}
if
(!
useFP
)
{
grant_btn
.
setOnClickListener
(
v
->
{
handler
.
handleAction
(
Policy
.
ALLOW
);
timer
.
cancel
();
});
grant_btn
.
requestFocus
();
}
grant_btn
.
setVisibility
(
useFP
?
View
.
GONE
:
View
.
VISIBLE
);
fingerprintImg
.
setVisibility
(
useFP
?
View
.
VISIBLE
:
View
.
GONE
);
deny_btn
.
setOnClickListener
(
v
->
{
handler
.
handleAction
(
Policy
.
DENY
);
timer
.
cancel
();
});
suPopup
.
setOnClickListener
(
v
->
cancelTimer
.
run
());
timeout
.
setOnTouchListener
((
v
,
event
)
->
{
cancelTimer
.
run
();
return
false
;
});
}
private
class
SuFingerprint
extends
FingerprintHelper
{
SuFingerprint
()
throws
Exception
{}
@Override
public
void
onAuthenticationError
(
int
errorCode
,
CharSequence
errString
)
{
warning
.
setText
(
errString
);
}
@Override
public
void
onAuthenticationHelp
(
int
helpCode
,
CharSequence
helpString
)
{
warning
.
setText
(
helpString
);
}
@Override
public
void
onAuthenticationSucceeded
(
FingerprintManager
.
AuthenticationResult
result
)
{
handler
.
handleAction
(
Policy
.
ALLOW
);
}
@Override
public
void
onAuthenticationFailed
()
{
warning
.
setText
(
R
.
string
.
auth_fail
);
}
}
private
class
ActionHandler
{
private
List
<
Runnable
>
cancelTasks
=
new
ArrayList
<>();
void
handleAction
()
{
done
();
}
void
handleAction
(
int
action
)
{
done
();
}
void
handleAction
(
int
action
,
int
time
)
{
done
();
}
void
addCancel
(
Runnable
r
)
{
cancelTasks
.
add
(
r
);
}
void
done
()
{
Iterables
.
forEach
(
cancelTasks
,
Runnable:
:
run
);
finish
();
}
}
}
app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.surequest
import
android.content.pm.ActivityInfo
import
android.os.Build
import
android.os.Bundle
import
android.text.TextUtils
import
android.view.Window
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.ActivityRequestBinding
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.events.DieEvent
import
com.topjohnwu.magisk.ui.base.MagiskActivity
import
com.topjohnwu.magisk.utils.SuLogger
import
org.koin.androidx.viewmodel.ext.android.viewModel
open
class
SuRequestActivity
:
MagiskActivity
<
SuRequestViewModel
,
ActivityRequestBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
activity_request
override
val
viewModel
:
SuRequestViewModel
by
viewModel
()
override
fun
onBackPressed
()
{
viewModel
.
handler
?.
handleAction
(
Policy
.
DENY
,
-
1
)
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
supportRequestWindowFeature
(
Window
.
FEATURE_NO_TITLE
)
lockOrientation
()
super
.
onCreate
(
savedInstanceState
)
val
intent
=
intent
val
action
=
intent
.
action
if
(
TextUtils
.
equals
(
action
,
REQUEST
))
{
if
(!
viewModel
.
handleRequest
(
intent
)
{})
finish
()
return
}
if
(
TextUtils
.
equals
(
action
,
LOG
))
SuLogger
.
handleLogs
(
intent
)
else
if
(
TextUtils
.
equals
(
action
,
NOTIFY
))
SuLogger
.
handleNotify
(
intent
)
finish
()
}
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
DieEvent
->
finish
()
}
}
private
fun
lockOrientation
()
{
requestedOrientation
=
if
(
Build
.
VERSION
.
SDK_INT
<
18
)
resources
.
configuration
.
orientation
else
ActivityInfo
.
SCREEN_ORIENTATION_LOCKED
}
companion
object
{
const
val
REQUEST
=
"request"
const
val
LOG
=
"log"
const
val
NOTIFY
=
"notify"
}
}
app/src/main/java/com/topjohnwu/magisk/ui/surequest/SuRequestViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.surequest
import
android.annotation.SuppressLint
import
android.content.Intent
import
android.content.SharedPreferences
import
android.content.pm.PackageManager
import
android.content.res.Resources
import
android.graphics.drawable.Drawable
import
android.hardware.fingerprint.FingerprintManager
import
android.os.CountDownTimer
import
android.text.TextUtils
import
com.skoumal.teanity.databinding.ComparableRvItem
import
com.skoumal.teanity.util.DiffObservableList
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.data.database.MagiskDB
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem
import
com.topjohnwu.magisk.model.events.DieEvent
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.FingerprintHelper
import
com.topjohnwu.magisk.utils.SuConnector
import
com.topjohnwu.magisk.utils.now
import
me.tatarka.bindingcollectionadapter2.ItemBinding
import
java.io.IOException
import
java.util.concurrent.TimeUnit.*
class
SuRequestViewModel
(
private
val
packageManager
:
PackageManager
,
private
val
database
:
MagiskDB
,
private
val
timeoutPrefs
:
SharedPreferences
,
private
val
resources
:
Resources
)
:
MagiskViewModel
()
{
val
icon
=
KObservableField
<
Drawable
?>(
null
)
val
title
=
KObservableField
(
""
)
val
packageName
=
KObservableField
(
""
)
val
denyText
=
KObservableField
(
resources
.
getString
(
R
.
string
.
deny
))
val
warningText
=
KObservableField
<
CharSequence
>(
resources
.
getString
(
R
.
string
.
su_warning
))
val
canUseFingerprint
=
KObservableField
(
FingerprintHelper
.
useFingerprint
())
val
selectedItemPosition
=
KObservableField
(
0
)
val
items
=
DiffObservableList
(
ComparableRvItem
.
callback
)
val
itemBinding
=
ItemBinding
.
of
<
ComparableRvItem
<*>>
{
binding
,
_
,
item
->
item
.
bind
(
binding
)
}
var
handler
:
ActionHandler
?
=
null
private
var
timer
:
CountDownTimer
?
=
null
private
var
policy
:
Policy
?
=
null
set
(
value
)
{
field
=
value
updatePolicy
(
value
)
}
init
{
resources
.
getStringArray
(
R
.
array
.
allow_timeout
)
.
map
{
SpinnerRvItem
(
it
)
}
.
let
{
items
.
update
(
it
)
}
}
private
fun
updatePolicy
(
policy
:
Policy
?)
{
policy
?:
return
icon
.
value
=
policy
.
info
.
loadIcon
(
packageManager
)
title
.
value
=
policy
.
appName
packageName
.
value
=
policy
.
packageName
selectedItemPosition
.
value
=
timeoutPrefs
.
getInt
(
policy
.
packageName
,
0
)
}
private
fun
cancelTimer
()
{
timer
?.
cancel
()
denyText
.
value
=
resources
.
getString
(
R
.
string
.
deny
)
}
fun
grantPressed
()
{
handler
?.
handleAction
(
Policy
.
ALLOW
)
timer
?.
cancel
()
}
fun
denyPressed
()
{
handler
?.
handleAction
(
Policy
.
DENY
)
timer
?.
cancel
()
}
fun
spinnerTouched
():
Boolean
{
cancelTimer
()
return
false
}
fun
handleRequest
(
intent
:
Intent
,
createUICallback
:
()
->
Unit
):
Boolean
{
val
socketName
=
intent
.
getStringExtra
(
"socket"
)
?:
return
false
val
connector
:
SuConnector
try
{
connector
=
object
:
SuConnector
(
socketName
)
{
@Throws
(
IOException
::
class
)
override
fun
onResponse
()
{
out
.
writeInt
(
policy
?.
policy
?:
return
)
}
}
val
bundle
=
connector
.
readSocketInput
()
val
uid
=
bundle
.
getString
(
"uid"
)
?.
toIntOrNull
()
?:
return
false
database
.
clearOutdated
()
policy
=
database
.
getPolicy
(
uid
)
?:
Policy
(
uid
,
packageManager
)
}
catch
(
e
:
IOException
)
{
e
.
printStackTrace
()
return
false
}
catch
(
e
:
PackageManager
.
NameNotFoundException
)
{
e
.
printStackTrace
()
return
false
}
handler
=
object
:
ActionHandler
()
{
override
fun
handleAction
()
{
connector
.
response
()
done
()
}
override
fun
handleAction
(
action
:
Int
)
{
val
pos
=
selectedItemPosition
.
value
timeoutPrefs
.
edit
().
putInt
(
policy
?.
packageName
,
pos
).
apply
()
handleAction
(
action
,
Config
.
Value
.
TIMEOUT_LIST
[
pos
])
}
override
fun
handleAction
(
action
:
Int
,
time
:
Int
)
{
policy
?.
apply
{
policy
=
action
if
(
time
>=
0
)
{
until
=
if
(
time
==
0
)
{
0
}
else
{
MILLISECONDS
.
toSeconds
(
now
)
+
MINUTES
.
toSeconds
(
time
.
toLong
())
}
database
.
updatePolicy
(
this
)
}
}
policy
?.
policy
=
action
handleAction
()
}
}
// Never allow com.topjohnwu.magisk (could be malware)
if
(
TextUtils
.
equals
(
policy
?.
packageName
,
BuildConfig
.
APPLICATION_ID
))
return
false
// If not interactive, response directly
if
(
policy
?.
policy
!=
Policy
.
INTERACTIVE
)
{
handler
?.
handleAction
()
return
true
}
when
(
Config
.
get
<
Any
>(
Config
.
Key
.
SU_AUTO_RESPONSE
)
as
Int
)
{
Config
.
Value
.
SU_AUTO_DENY
->
{
handler
?.
handleAction
(
Policy
.
DENY
,
0
)
return
true
}
Config
.
Value
.
SU_AUTO_ALLOW
->
{
handler
?.
handleAction
(
Policy
.
ALLOW
,
0
)
return
true
}
}
createUICallback
()
showUI
()
return
true
}
@SuppressLint
(
"ClickableViewAccessibility"
)
private
fun
showUI
()
{
val
seconds
=
Config
.
get
<
Int
>(
Config
.
Key
.
SU_REQUEST_TIMEOUT
).
toLong
()
val
millis
=
SECONDS
.
toMillis
(
seconds
)
timer
=
object
:
CountDownTimer
(
millis
,
1000
)
{
override
fun
onTick
(
remains
:
Long
)
{
denyText
.
value
=
"%s (%d)"
.
format
(
resources
.
getString
(
R
.
string
.
deny
),
remains
/
1000
)
}
override
fun
onFinish
()
{
denyText
.
value
=
resources
.
getString
(
R
.
string
.
deny
)
handler
?.
handleAction
(
Policy
.
DENY
)
}
}
timer
?.
start
()
handler
?.
addCancel
(
Runnable
{
cancelTimer
()
})
val
useFP
=
canUseFingerprint
.
value
if
(
useFP
)
try
{
val
helper
=
SuFingerprint
()
helper
.
authenticate
()
handler
?.
addCancel
(
Runnable
{
helper
.
cancel
()
})
}
catch
(
e
:
Exception
)
{
e
.
printStackTrace
()
}
}
private
inner
class
SuFingerprint
@Throws
(
Exception
::
class
)
internal
constructor
()
:
FingerprintHelper
()
{
override
fun
onAuthenticationError
(
errorCode
:
Int
,
errString
:
CharSequence
)
{
warningText
.
value
=
errString
}
override
fun
onAuthenticationHelp
(
helpCode
:
Int
,
helpString
:
CharSequence
)
{
warningText
.
value
=
helpString
}
override
fun
onAuthenticationSucceeded
(
result
:
FingerprintManager
.
AuthenticationResult
)
{
handler
?.
handleAction
(
Policy
.
ALLOW
)
}
override
fun
onAuthenticationFailed
()
{
warningText
.
value
=
resources
.
getString
(
R
.
string
.
auth_fail
)
}
}
open
inner
class
ActionHandler
{
private
val
cancelTasks
=
mutableListOf
<
Runnable
>()
internal
open
fun
handleAction
()
{
done
()
}
internal
open
fun
handleAction
(
action
:
Int
)
{
done
()
}
internal
open
fun
handleAction
(
action
:
Int
,
time
:
Int
)
{
done
()
}
internal
fun
addCancel
(
r
:
Runnable
)
{
cancelTasks
.
add
(
r
)
}
internal
fun
done
()
{
cancelTasks
.
forEach
{
it
.
run
()
}
DieEvent
().
publish
()
}
}
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/ui/surequest/_SuRequestActivity.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.surequest
import
android.hardware.fingerprint.FingerprintManager
import
android.os.CountDownTimer
import
android.text.SpannableStringBuilder
import
android.widget.Toast
import
androidx.core.text.bold
import
com.skoumal.teanity.viewevents.ViewEvent
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.ActivitySuRequestBinding
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.events.DieEvent
import
com.topjohnwu.magisk.model.events.SuDialogEvent
import
com.topjohnwu.magisk.ui.base.MagiskActivity
import
com.topjohnwu.magisk.utils.FingerprintHelper
import
com.topjohnwu.magisk.utils.feature.WIP
import
com.topjohnwu.magisk.view.MagiskDialog
import
org.koin.androidx.viewmodel.ext.android.viewModel
import
org.koin.core.parameter.parametersOf
import
timber.log.Timber
import
java.util.concurrent.TimeUnit.MILLISECONDS
import
java.util.concurrent.TimeUnit.SECONDS
@WIP
open
class
_SuRequestActivity
:
MagiskActivity
<
_SuRequestViewModel
,
ActivitySuRequestBinding
>()
{
override
val
layoutRes
:
Int
=
R
.
layout
.
activity_su_request
override
val
viewModel
:
_SuRequestViewModel
by
viewModel
{
parametersOf
(
intent
,
intent
.
action
)
}
//private val timeoutPrefs: SharedPreferences by inject(SUTimeout)
private
val
canUseFingerprint
get
()
=
FingerprintHelper
.
useFingerprint
()
private
val
countdown
by
lazy
{
val
seconds
=
Config
.
get
<
Int
>(
Config
.
Key
.
SU_REQUEST_TIMEOUT
).
toLong
()
val
millis
=
SECONDS
.
toMillis
(
seconds
)
object
:
CountDownTimer
(
millis
,
1000
)
{
override
fun
onFinish
()
{
viewModel
.
deny
()
}
override
fun
onTick
(
millisUntilFinished
:
Long
)
{
dialog
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
Timber
.
e
(
"Tick, tock"
)
title
=
"%s (%d)"
.
format
(
getString
(
R
.
string
.
deny
),
MILLISECONDS
.
toSeconds
(
millisUntilFinished
)
)
}
}
}
}
private
var
fingerprintHelper
:
SuFingerprint
?
=
null
private
lateinit
var
dialog
:
MagiskDialog
override
fun
onEventDispatched
(
event
:
ViewEvent
)
{
super
.
onEventDispatched
(
event
)
when
(
event
)
{
is
SuDialogEvent
->
showDialog
(
event
.
policy
)
is
DieEvent
->
finish
()
}
}
override
fun
onBackPressed
()
{
if
(
::
dialog
.
isInitialized
&&
dialog
.
isShowing
)
{
return
}
super
.
onBackPressed
()
}
override
fun
onDestroy
()
{
if
(
this
::
dialog
.
isInitialized
&&
dialog
.
isShowing
)
{
dialog
.
dismiss
()
}
fingerprintHelper
?.
cancel
()
countdown
.
cancel
()
super
.
onDestroy
()
}
private
fun
showDialog
(
policy
:
Policy
)
{
val
titleText
=
SpannableStringBuilder
(
"Allow "
)
.
bold
{
append
(
policy
.
appName
)
}
.
append
(
" to access superuser rights?"
)
val
messageText
=
StringBuilder
()
.
appendln
(
policy
.
packageName
)
.
append
(
getString
(
R
.
string
.
su_warning
))
dialog
=
MagiskDialog
(
this
)
.
applyIcon
(
policy
.
info
.
loadIcon
(
packageManager
))
.
applyTitle
(
titleText
)
.
applyMessage
(
messageText
)
//.applyView()) {} //todo add a spinner
.
cancellable
(
false
)
.
applyButton
(
MagiskDialog
.
ButtonType
.
POSITIVE
)
{
titleRes
=
R
.
string
.
grant
onClick
{
viewModel
.
grant
()
}
if
(
canUseFingerprint
)
{
icon
=
R
.
drawable
.
ic_fingerprint
}
}
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEUTRAL
)
{
title
=
"%s %s"
.
format
(
getString
(
R
.
string
.
grant
),
getString
(
R
.
string
.
once
))
onClick
{
viewModel
.
grant
(-
1
)
}
}
.
applyButton
(
MagiskDialog
.
ButtonType
.
NEGATIVE
)
{
titleRes
=
R
.
string
.
deny
onClick
{
viewModel
.
deny
()
}
}
.
onDismiss
{
finish
()
}
.
onShow
{
startTimer
().
also
{
Timber
.
e
(
"Starting timer"
)
}
if
(
canUseFingerprint
)
{
startFingerprintQuery
()
}
}
.
reveal
()
}
private
fun
startTimer
()
{
countdown
.
start
()
}
private
fun
startFingerprintQuery
()
{
val
result
=
runCatching
{
fingerprintHelper
=
SuFingerprint
().
apply
{
authenticate
()
}
}
if
(
result
.
isFailure
)
{
dialog
.
applyButton
(
MagiskDialog
.
ButtonType
.
POSITIVE
)
{
icon
=
0
}
}
}
private
inner
class
SuFingerprint
@Throws
(
Exception
::
class
)
internal
constructor
()
:
FingerprintHelper
()
{
override
fun
onAuthenticationError
(
errorCode
:
Int
,
errString
:
CharSequence
)
{
Toast
.
makeText
(
this
@_SuRequestActivity
,
errString
,
Toast
.
LENGTH_LONG
).
show
()
}
override
fun
onAuthenticationHelp
(
helpCode
:
Int
,
helpString
:
CharSequence
)
{
Toast
.
makeText
(
this
@_SuRequestActivity
,
helpString
,
Toast
.
LENGTH_LONG
).
show
()
}
override
fun
onAuthenticationSucceeded
(
result
:
FingerprintManager
.
AuthenticationResult
)
{
viewModel
.
grant
()
}
override
fun
onAuthenticationFailed
()
{
Toast
.
makeText
(
this
@_SuRequestActivity
,
R
.
string
.
auth_fail
,
Toast
.
LENGTH_LONG
).
show
()
}
}
companion
object
{
const
val
REQUEST
=
"request"
const
val
LOG
=
"log"
const
val
NOTIFY
=
"notify"
}
}
app/src/main/java/com/topjohnwu/magisk/ui/surequest/_SuRequestViewModel.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.ui.surequest
import
android.content.Intent
import
android.content.pm.PackageManager
import
com.skoumal.teanity.extensions.subscribeK
import
com.topjohnwu.magisk.BuildConfig
import
com.topjohnwu.magisk.Config
import
com.topjohnwu.magisk.data.database.MagiskDB
import
com.topjohnwu.magisk.model.entity.Policy
import
com.topjohnwu.magisk.model.events.DieEvent
import
com.topjohnwu.magisk.model.events.SuDialogEvent
import
com.topjohnwu.magisk.ui.base.MagiskViewModel
import
com.topjohnwu.magisk.utils.SuConnector
import
com.topjohnwu.magisk.utils.SuLogger
import
com.topjohnwu.magisk.utils.feature.WIP
import
com.topjohnwu.magisk.utils.now
import
io.reactivex.Single
import
timber.log.Timber
import
java.util.concurrent.TimeUnit.MILLISECONDS
import
java.util.concurrent.TimeUnit.MINUTES
@WIP
class
_SuRequestViewModel
(
intent
:
Intent
,
action
:
String
,
private
val
packageManager
:
PackageManager
,
private
val
database
:
MagiskDB
)
:
MagiskViewModel
()
{
private
val
connector
:
Single
<
SuConnector
>
=
Single
.
fromCallable
{
val
socketName
=
intent
.
extras
?.
getString
(
"socket"
)
?:
let
{
deny
()
throw
IllegalStateException
(
"Socket is empty or null"
)
}
object
:
SuConnector
(
socketName
)
{
override
fun
onResponse
()
{
policy
.
subscribeK
{
out
.
writeInt
(
it
.
policy
)
}
//this just might be incorrect, lol
}
}
as
SuConnector
}.
cache
()
private
val
policy
:
Single
<
Policy
>
=
connector
.
map
{
val
bundle
=
it
.
readSocketInput
()
?:
throw
IllegalStateException
(
"Socket bundle is null"
)
val
uid
=
bundle
.
getString
(
"uid"
)
?.
toIntOrNull
()
?:
let
{
deny
()
throw
IllegalStateException
(
"UID is empty or null"
)
}
database
.
clearOutdated
()
database
.
getPolicy
(
uid
)
?:
Policy
(
uid
,
packageManager
)
}.
cache
()
init
{
when
(
action
)
{
SuRequestActivity
.
LOG
->
SuLogger
.
handleLogs
(
intent
).
also
{
die
()
}
SuRequestActivity
.
NOTIFY
->
SuLogger
.
handleNotify
(
intent
).
also
{
die
()
}
SuRequestActivity
.
REQUEST
->
process
()
else
->
back
()
// invalid action, should ignore
}
}
private
fun
process
()
{
policy
.
subscribeK
(
onError
=
::
deny
)
{
process
(
it
)
}
}
private
fun
process
(
policy
:
Policy
)
{
if
(
policy
.
packageName
==
BuildConfig
.
APPLICATION_ID
)
deny
().
also
{
return
}
if
(
policy
.
policy
!=
Policy
.
INTERACTIVE
)
grant
().
also
{
return
}
when
(
Config
.
get
<
Int
>(
Config
.
Key
.
SU_AUTO_RESPONSE
))
{
Config
.
Value
.
SU_AUTO_DENY
->
deny
().
also
{
return
}
Config
.
Value
.
SU_AUTO_ALLOW
->
grant
().
also
{
return
}
}
requestDialog
(
policy
)
}
fun
deny
(
e
:
Throwable
?
=
null
)
=
updatePolicy
(
Policy
.
DENY
,
0
).
also
{
Timber
.
e
(
e
)
}
fun
grant
(
time
:
Long
=
0
)
=
updatePolicy
(
Policy
.
ALLOW
,
time
)
private
fun
updatePolicy
(
action
:
Int
,
time
:
Long
)
{
fun
finish
(
e
:
Throwable
?
=
null
)
=
die
().
also
{
Timber
.
e
(
e
)
}
policy
.
map
{
it
.
policy
=
action
;
it
}
.
doOnSuccess
{
if
(
time
>=
0
)
{
it
.
until
=
if
(
time
==
0L
)
{
0
}
else
{
MILLISECONDS
.
toSeconds
(
now
)
+
MINUTES
.
toSeconds
(
time
)
}
database
.
updatePolicy
(
it
)
}
}
.
flatMap
{
connector
}
.
subscribeK
(
onError
=
::
finish
)
{
it
.
response
()
finish
()
}
}
private
fun
requestDialog
(
policy
:
Policy
)
{
SuDialogEvent
(
policy
).
publish
()
}
private
fun
die
()
=
DieEvent
().
publish
()
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
android.view.View
import
android.widget.AdapterView
import
android.widget.Spinner
import
android.widget.TextView
import
androidx.annotation.ColorInt
import
androidx.annotation.DrawableRes
import
androidx.appcompat.widget.AppCompatImageView
import
androidx.appcompat.widget.Toolbar
import
androidx.databinding.BindingAdapter
import
androidx.databinding.InverseBindingAdapter
import
androidx.databinding.InverseBindingListener
import
androidx.drawerlayout.widget.DrawerLayout
import
androidx.interpolator.view.animation.FastOutSlowInInterpolator
import
androidx.viewpager.widget.ViewPager
import
com.google.android.material.navigation.NavigationView
import
com.skoumal.teanity.extensions.subscribeK
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.model.entity.state.IndeterminateState
import
io.reactivex.Observable
import
io.reactivex.disposables.Disposable
import
java.util.concurrent.TimeUnit
@BindingAdapter
(
"onNavigationClick"
)
fun
setOnNavigationClickedListener
(
view
:
Toolbar
,
listener
:
View
.
OnClickListener
)
{
view
.
setNavigationOnClickListener
(
listener
)
}
@BindingAdapter
(
"onNavigationClick"
)
fun
setOnNavigationClickedListener
(
view
:
NavigationView
,
listener
:
NavigationView
.
OnNavigationItemSelectedListener
)
{
view
.
setNavigationItemSelectedListener
{
(
view
.
parent
as
?
DrawerLayout
)
?.
closeDrawers
()
listener
.
onNavigationItemSelected
(
it
)
}
}
@BindingAdapter
(
"srcCompat"
)
fun
setImageResource
(
view
:
AppCompatImageView
,
@DrawableRes
resId
:
Int
)
{
view
.
setImageResource
(
resId
)
}
@BindingAdapter
(
"app:tint"
)
fun
setTint
(
view
:
AppCompatImageView
,
@ColorInt
tint
:
Int
)
{
view
.
setColorFilter
(
tint
)
}
@BindingAdapter
(
"isChecked"
)
fun
setChecked
(
view
:
AppCompatImageView
,
isChecked
:
Boolean
)
{
val
state
=
when
(
isChecked
)
{
true
->
IndeterminateState
.
CHECKED
else
->
IndeterminateState
.
UNCHECKED
}
setChecked
(
view
,
state
)
}
@BindingAdapter
(
"isChecked"
)
fun
setChecked
(
view
:
AppCompatImageView
,
isChecked
:
IndeterminateState
)
{
view
.
setImageResource
(
when
(
isChecked
)
{
IndeterminateState
.
INDETERMINATE
->
R
.
drawable
.
ic_indeterminate
IndeterminateState
.
CHECKED
->
R
.
drawable
.
ic_checked
IndeterminateState
.
UNCHECKED
->
R
.
drawable
.
ic_unchecked
}
)
}
@BindingAdapter
(
"position"
)
fun
setPosition
(
view
:
ViewPager
,
position
:
Int
)
{
view
.
currentItem
=
position
}
@InverseBindingAdapter
(
attribute
=
"position"
,
event
=
"positionChanged"
)
fun
getPosition
(
view
:
ViewPager
)
=
view
.
currentItem
@BindingAdapter
(
"positionChanged"
)
fun
setPositionChangedListener
(
view
:
ViewPager
,
listener
:
InverseBindingListener
)
{
view
.
addOnPageChangeListener
(
object
:
ViewPager
.
OnPageChangeListener
{
override
fun
onPageSelected
(
position
:
Int
)
=
listener
.
onChange
()
override
fun
onPageScrollStateChanged
(
state
:
Int
)
=
listener
.
onChange
()
override
fun
onPageScrolled
(
position
:
Int
,
positionOffset
:
Float
,
positionOffsetPixels
:
Int
)
=
listener
.
onChange
()
})
}
@BindingAdapter
(
"invisibleScale"
)
fun
setInvisibleWithScale
(
view
:
View
,
isInvisible
:
Boolean
)
{
view
.
animate
()
.
scaleX
(
if
(
isInvisible
)
0f
else
1f
)
.
scaleY
(
if
(
isInvisible
)
0f
else
1f
)
.
setInterpolator
(
FastOutSlowInInterpolator
())
.
start
()
}
@BindingAdapter
(
"movieBehavior"
,
"movieBehaviorText"
)
fun
setMovieBehavior
(
view
:
TextView
,
isMovieBehavior
:
Boolean
,
text
:
String
)
{
(
view
.
tag
as
?
Disposable
)
?.
dispose
()
if
(
isMovieBehavior
)
{
val
observer
=
Observable
.
interval
(
150
,
TimeUnit
.
MILLISECONDS
)
.
subscribeK
{
view
.
text
=
text
.
replaceRandomWithSpecial
()
}
view
.
tag
=
observer
}
else
{
view
.
text
=
text
}
}
@BindingAdapter
(
"android:selectedItemPosition"
)
fun
setSelectedItemPosition
(
view
:
Spinner
,
position
:
Int
)
{
view
.
setSelection
(
position
)
}
@InverseBindingAdapter
(
attribute
=
"android:selectedItemPosition"
,
event
=
"android:selectedItemPositionAttrChanged"
)
fun
getSelectedItemPosition
(
view
:
Spinner
)
=
view
.
selectedItemPosition
@BindingAdapter
(
"android:selectedItemPositionAttrChanged"
)
fun
setSelectedItemPositionListener
(
view
:
Spinner
,
listener
:
InverseBindingListener
)
{
view
.
onItemSelectedListener
=
object
:
AdapterView
.
OnItemSelectedListener
{
override
fun
onNothingSelected
(
p0
:
AdapterView
<
*
>?)
{
listener
.
onChange
()
}
override
fun
onItemSelected
(
p0
:
AdapterView
<
*
>?,
p1
:
View
?,
p2
:
Int
,
p3
:
Long
)
{
listener
.
onChange
()
}
}
}
@BindingAdapter
(
"onTouch"
)
fun
setOnTouchListener
(
view
:
View
,
listener
:
View
.
OnTouchListener
)
{
view
.
setOnTouchListener
(
listener
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java
View file @
2813d203
...
...
@@ -3,8 +3,6 @@ package com.topjohnwu.magisk.utils;
import
android.content.ComponentName
;
import
android.widget.Toast
;
import
androidx.core.app.NotificationCompat
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.BuildConfig
;
import
com.topjohnwu.magisk.ClassMap
;
...
...
@@ -29,6 +27,8 @@ import java.util.ArrayList;
import
java.util.List
;
import
java.util.jar.JarEntry
;
import
androidx.core.app.NotificationCompat
;
public
class
PatchAPK
{
public
static
final
String
LOWERALPHA
=
"abcdefghijklmnopqrstuvwxyz"
;
...
...
app/src/main/java/com/topjohnwu/magisk/utils/SuLogger.java
View file @
2813d203
...
...
@@ -37,7 +37,7 @@ public class SuLogger {
}
}
else
{
// Doesn't report whether notify or not, check database ourselves
policy
=
app
.
mDB
.
getPolicy
(
fromUid
);
policy
=
app
.
getDB
()
.
getPolicy
(
fromUid
);
if
(
policy
==
null
)
return
;
notify
=
policy
.
notification
;
...
...
@@ -62,7 +62,7 @@ public class SuLogger {
log
.
fromPid
=
pid
;
log
.
command
=
command
;
log
.
date
=
new
Date
();
app
.
mDB
.
addLog
(
log
);
app
.
getDB
()
.
addLog
(
log
);
}
private
static
void
handleNotify
(
Policy
policy
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/utils/Utils.java
View file @
2813d203
...
...
@@ -12,12 +12,6 @@ import android.net.Uri;
import
android.provider.OpenableColumns
;
import
android.widget.Toast
;
import
androidx.work.Constraints
;
import
androidx.work.ExistingPeriodicWorkPolicy
;
import
androidx.work.NetworkType
;
import
androidx.work.PeriodicWorkRequest
;
import
androidx.work.WorkManager
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.BuildConfig
;
import
com.topjohnwu.magisk.ClassMap
;
...
...
@@ -36,6 +30,12 @@ import java.util.Locale;
import
java.util.Map
;
import
java.util.concurrent.TimeUnit
;
import
androidx.work.Constraints
;
import
androidx.work.ExistingPeriodicWorkPolicy
;
import
androidx.work.NetworkType
;
import
androidx.work.PeriodicWorkRequest
;
import
androidx.work.WorkManager
;
public
class
Utils
{
public
static
void
toast
(
CharSequence
msg
,
int
duration
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
android.content.pm.ApplicationInfo
import
android.content.pm.ComponentInfo
import
android.content.pm.PackageInfo
import
android.content.pm.PackageManager
import
android.content.pm.PackageManager.*
val
PackageInfo
.
processes
get
()
=
activities
?.
processNames
.
orEmpty
()
+
services
?.
processNames
.
orEmpty
()
+
receivers
?.
processNames
.
orEmpty
()
+
providers
?.
processNames
.
orEmpty
()
val
Array
<
out
ComponentInfo
>.
processNames
get
()
=
mapNotNull
{
it
.
processName
}
val
ApplicationInfo
.
packageInfo
:
PackageInfo
?
get
()
{
val
pm
:
PackageManager
by
inject
()
return
try
{
val
request
=
GET_ACTIVITIES
or
GET_SERVICES
or
GET_RECEIVERS
or
GET_PROVIDERS
pm
.
getPackageInfo
(
packageName
,
request
)
}
catch
(
e1
:
Exception
)
{
try
{
pm
.
activities
(
packageName
).
apply
{
services
=
pm
.
services
(
packageName
)
receivers
=
pm
.
receivers
(
packageName
)
providers
=
pm
.
providers
(
packageName
)
}
}
catch
(
e2
:
Exception
)
{
null
}
}
}
fun
PackageManager
.
activities
(
packageName
:
String
)
=
getPackageInfo
(
packageName
,
GET_ACTIVITIES
)
fun
PackageManager
.
services
(
packageName
:
String
)
=
getPackageInfo
(
packageName
,
GET_SERVICES
).
services
fun
PackageManager
.
receivers
(
packageName
:
String
)
=
getPackageInfo
(
packageName
,
GET_RECEIVERS
).
receivers
fun
PackageManager
.
providers
(
packageName
:
String
)
=
getPackageInfo
(
packageName
,
GET_PROVIDERS
).
providers
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/XBinding.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
com.skoumal.teanity.util.KObservableField
fun
KObservableField
<
Boolean
>.
toggle
()
{
value
=
!
value
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/XKoin.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
org.koin.core.context.GlobalContext
import
org.koin.core.parameter.ParametersDefinition
import
org.koin.core.qualifier.Qualifier
import
org.koin.core.scope.Scope
fun
getKoin
()
=
GlobalContext
.
get
().
koin
inline
fun
<
reified
T
:
Any
>
inject
(
qualifier
:
Qualifier
?
=
null
,
scope
:
Scope
?
=
null
,
noinline
parameters
:
ParametersDefinition
?
=
null
)
=
lazy
{
get
<
T
>(
qualifier
,
scope
,
parameters
)
}
inline
fun
<
reified
T
:
Any
>
get
(
qualifier
:
Qualifier
?
=
null
,
scope
:
Scope
?
=
null
,
noinline
parameters
:
ParametersDefinition
?
=
null
):
T
=
getKoin
().
get
(
qualifier
,
scope
,
parameters
)
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/XList.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
androidx.databinding.ObservableList
import
com.skoumal.teanity.extensions.subscribeK
import
com.skoumal.teanity.util.DiffObservableList
import
io.reactivex.disposables.Disposable
fun
<
T
>
MutableList
<
T
>.
update
(
newList
:
List
<
T
>)
{
clear
()
addAll
(
newList
)
}
fun
<
T1
,
T2
>
ObservableList
<
T1
>.
sendUpdatesTo
(
target
:
DiffObservableList
<
T2
>,
mapper
:
(
List
<
T1
>)
->
List
<
T2
>
)
{
addOnListChangedCallback
(
object
:
ObservableList
.
OnListChangedCallback
<
ObservableList
<
T1
>>()
{
override
fun
onChanged
(
sender
:
ObservableList
<
T1
>?)
{
updateAsync
(
sender
?:
return
)
}
override
fun
onItemRangeRemoved
(
sender
:
ObservableList
<
T1
>?,
p0
:
Int
,
p1
:
Int
)
{
updateAsync
(
sender
?:
return
)
}
override
fun
onItemRangeMoved
(
sender
:
ObservableList
<
T1
>?,
p0
:
Int
,
p1
:
Int
,
p2
:
Int
)
{
updateAsync
(
sender
?:
return
)
}
override
fun
onItemRangeInserted
(
sender
:
ObservableList
<
T1
>?,
p0
:
Int
,
p1
:
Int
)
{
updateAsync
(
sender
?:
return
)
}
override
fun
onItemRangeChanged
(
sender
:
ObservableList
<
T1
>?,
p0
:
Int
,
p1
:
Int
)
{
updateAsync
(
sender
?:
return
)
}
private
var
updater
:
Disposable
?
=
null
private
fun
updateAsync
(
sender
:
List
<
T1
>)
{
updater
?.
dispose
()
updater
=
sender
.
toSingle
()
.
map
{
mapper
(
it
)
}
.
map
{
it
to
target
.
calculateDiff
(
it
)
}
.
subscribeK
{
target
.
update
(
it
.
first
,
it
.
second
)
}
}
})
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/XRx.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
io.reactivex.Single
import
io.reactivex.functions.BiFunction
fun
<
T
:
Any
>
T
.
toSingle
()
=
Single
.
just
(
this
)
fun
<
T1
,
T2
,
R
>
zip
(
t1
:
Single
<
T1
>,
t2
:
Single
<
T2
>,
zipper
:
(
T1
,
T2
)
->
R
)
=
Single
.
zip
(
t1
,
t2
,
BiFunction
<
T1
,
T2
,
R
>
{
rt1
,
rt2
->
zipper
(
rt1
,
rt2
)
})
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/XString.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
val
specialChars
=
arrayOf
(
'!'
,
'@'
,
'#'
,
'$'
,
'%'
,
'&'
,
'?'
)
fun
String
.
replaceRandomWithSpecial
():
String
{
var
random
:
Char
do
{
random
=
random
()
}
while
(
random
==
'.'
)
return
replace
(
random
,
specialChars
.
random
())
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/XTime.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils
import
java.text.ParseException
import
java.text.SimpleDateFormat
import
java.util.*
val
now
get
()
=
System
.
currentTimeMillis
()
fun
Long
.
toTime
(
format
:
SimpleDateFormat
)
=
format
.
format
(
this
).
orEmpty
()
fun
String
.
toTime
(
format
:
SimpleDateFormat
)
=
try
{
format
.
parse
(
this
)
?.
time
?:
-
1
}
catch
(
e
:
ParseException
)
{
-
1L
}
private
val
locale
get
()
=
Locale
.
getDefault
()
val
timeFormatFull
by
lazy
{
SimpleDateFormat
(
"YYYY/MM/DD_HH:mm:ss"
,
locale
)
}
val
timeFormatStandard
by
lazy
{
SimpleDateFormat
(
"YYYY-MM-DD'T'HH:mm:ss'Z'"
,
locale
)
}
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/utils/feature/Annotations.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.utils.feature
@Retention
(
AnnotationRetention
.
SOURCE
)
annotation
class
WIP
@Retention
(
AnnotationRetention
.
SOURCE
)
annotation
class
Beta
@Retention
(
AnnotationRetention
.
SOURCE
)
annotation
class
Alpha
@Retention
(
AnnotationRetention
.
SOURCE
)
annotation
class
Unstable
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/view/ArrowExpandable.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
view
;
import
android.view.View
;
import
android.view.animation.Animation
;
import
android.view.animation.RotateAnimation
;
public
class
ArrowExpandable
extends
Expandable
{
protected
Expandable
mBase
;
private
View
arrow
;
public
ArrowExpandable
(
Expandable
base
,
View
arrow
)
{
mBase
=
base
;
this
.
arrow
=
arrow
;
}
@Override
public
void
onExpand
()
{
mBase
.
onExpand
();
setRotate
(
new
RotateAnimation
(
0
,
180
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
));
}
@Override
public
void
onCollapse
()
{
mBase
.
onCollapse
();
setRotate
(
new
RotateAnimation
(
180
,
0
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
,
Animation
.
RELATIVE_TO_SELF
,
0.5f
));
}
@Override
public
void
onSetExpanded
(
boolean
expanded
)
{
mBase
.
onSetExpanded
(
expanded
);
if
(
arrow
!=
null
)
arrow
.
setRotation
(
expanded
?
180
:
0
);
}
private
void
setRotate
(
RotateAnimation
rotate
)
{
rotate
.
setDuration
(
300
);
rotate
.
setFillAfter
(
true
);
arrow
.
startAnimation
(
rotate
);
}
}
app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt
0 → 100644
View file @
2813d203
package
com.topjohnwu.magisk.view
import
android.content.Context
import
android.content.DialogInterface
import
android.graphics.Color
import
android.graphics.drawable.ColorDrawable
import
android.graphics.drawable.Drawable
import
android.os.Bundle
import
android.view.LayoutInflater
import
androidx.annotation.DrawableRes
import
androidx.annotation.StringRes
import
androidx.appcompat.app.AlertDialog
import
androidx.databinding.DataBindingUtil
import
androidx.databinding.ViewDataBinding
import
com.skoumal.teanity.util.KObservableField
import
com.topjohnwu.magisk.BR
import
com.topjohnwu.magisk.R
import
com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding
class
MagiskDialog
@JvmOverloads
constructor
(
context
:
Context
,
theme
:
Int
=
0
)
:
AlertDialog
(
context
,
theme
)
{
private
val
binding
:
DialogMagiskBaseBinding
private
val
data
=
Data
()
init
{
val
layoutInflater
=
LayoutInflater
.
from
(
context
)
binding
=
DataBindingUtil
.
inflate
(
layoutInflater
,
R
.
layout
.
dialog_magisk_base
,
null
,
false
)
binding
.
setVariable
(
BR
.
data
,
data
)
super
.
setView
(
binding
.
root
)
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
window
?.
setBackgroundDrawable
(
ColorDrawable
(
Color
.
TRANSPARENT
))
}
inner
class
Data
{
val
icon
=
KObservableField
(
0
)
val
iconRaw
=
KObservableField
<
Drawable
?>(
null
)
val
title
=
KObservableField
<
CharSequence
>(
""
)
val
message
=
KObservableField
<
CharSequence
>(
""
)
val
buttonPositive
=
Button
()
val
buttonNeutral
=
Button
()
val
buttonNegative
=
Button
()
val
buttonIDGAF
=
Button
()
}
enum
class
ButtonType
{
POSITIVE
,
NEUTRAL
,
NEGATIVE
,
IDGAF
}
inner
class
Button
{
val
icon
=
KObservableField
(
0
)
val
title
=
KObservableField
<
CharSequence
>(
""
)
val
isEnabled
=
KObservableField
(
true
)
var
onClickAction
:
OnDialogButtonClickListener
=
{}
fun
clicked
()
{
onClickAction
(
this
@MagiskDialog
)
dismiss
()
}
}
inner
class
ButtonBuilder
(
private
val
button
:
Button
)
{
var
icon
:
Int
get
()
=
button
.
icon
.
value
set
(
value
)
{
button
.
icon
.
value
=
value
}
var
title
:
CharSequence
get
()
=
button
.
title
.
value
set
(
value
)
{
button
.
title
.
value
=
value
}
var
titleRes
:
Int
get
()
=
0
set
(
value
)
{
button
.
title
.
value
=
context
.
getString
(
value
)
}
var
isEnabled
:
Boolean
get
()
=
button
.
isEnabled
.
value
set
(
value
)
{
button
.
isEnabled
.
value
=
value
}
fun
onClick
(
listener
:
OnDialogButtonClickListener
)
{
button
.
onClickAction
=
listener
}
}
fun
applyTitle
(
@StringRes
stringRes
:
Int
)
=
apply
{
data
.
title
.
value
=
context
.
getString
(
stringRes
)
}
fun
applyTitle
(
title
:
CharSequence
)
=
apply
{
data
.
title
.
value
=
title
}
fun
applyMessage
(
@StringRes
stringRes
:
Int
)
=
apply
{
data
.
message
.
value
=
context
.
getString
(
stringRes
)
}
fun
applyMessage
(
message
:
CharSequence
)
=
apply
{
data
.
message
.
value
=
message
}
fun
applyIcon
(
@DrawableRes
drawableRes
:
Int
)
=
apply
{
data
.
icon
.
value
=
drawableRes
}
fun
applyIcon
(
drawable
:
Drawable
)
=
apply
{
data
.
iconRaw
.
value
=
drawable
}
fun
applyButton
(
buttonType
:
ButtonType
,
builder
:
ButtonBuilder
.()
->
Unit
)
=
apply
{
val
button
=
when
(
buttonType
)
{
ButtonType
.
POSITIVE
->
data
.
buttonPositive
ButtonType
.
NEUTRAL
->
data
.
buttonNeutral
ButtonType
.
NEGATIVE
->
data
.
buttonNegative
ButtonType
.
IDGAF
->
data
.
buttonIDGAF
}
ButtonBuilder
(
button
).
apply
(
builder
)
}
fun
cancellable
(
isCancellable
:
Boolean
)
=
apply
{
setCancelable
(
isCancellable
)
}
fun
<
Binding
:
ViewDataBinding
>
applyView
(
binding
:
Binding
,
body
:
Binding
.()
->
Unit
)
=
apply
{
this
.
binding
.
dialogBaseContainer
.
removeAllViews
()
this
.
binding
.
dialogBaseContainer
.
addView
(
binding
.
root
)
binding
.
apply
(
body
)
}
fun
onDismiss
(
callback
:
OnDialogButtonClickListener
)
=
apply
{
setOnDismissListener
(
callback
)
}
fun
onShow
(
callback
:
OnDialogButtonClickListener
)
=
apply
{
setOnShowListener
(
callback
)
}
fun
reveal
()
=
apply
{
super
.
show
()
}
//region Deprecated Members
@Deprecated
(
"Use applyTitle instead"
,
ReplaceWith
(
"applyTitle"
))
override
fun
setTitle
(
title
:
CharSequence
?)
=
Unit
@Deprecated
(
"Use applyTitle instead"
,
ReplaceWith
(
"applyTitle"
))
override
fun
setTitle
(
titleId
:
Int
)
=
Unit
@Deprecated
(
"Use reveal()"
,
ReplaceWith
(
"reveal()"
))
override
fun
show
()
{
}
//endregion
}
typealias
OnDialogButtonClickListener
=
(
DialogInterface
)
->
Unit
\ No newline at end of file
app/src/main/java/com/topjohnwu/magisk/view/MarkDownWindow.java
View file @
2813d203
package
com
.
topjohnwu
.
magisk
.
view
;
import
android.
app.Activity
;
import
android.
content.Context
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
androidx.appcompat.app.AlertDialog
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.net.Networking
;
import
com.topjohnwu.net.ResponseListener
;
...
...
@@ -13,6 +11,7 @@ import com.topjohnwu.net.ResponseListener;
import
java.io.InputStream
;
import
java.util.Scanner
;
import
androidx.appcompat.app.AlertDialog
;
import
ru.noties.markwon.Markwon
;
import
ru.noties.markwon.html.HtmlPlugin
;
import
ru.noties.markwon.image.ImagesPlugin
;
...
...
@@ -20,11 +19,11 @@ import ru.noties.markwon.image.svg.SvgPlugin;
public
class
MarkDownWindow
{
public
static
void
show
(
Activity
activity
,
String
title
,
String
url
)
{
public
static
void
show
(
Context
activity
,
String
title
,
String
url
)
{
Networking
.
get
(
url
).
getAsString
(
new
Listener
(
activity
,
title
));
}
public
static
void
show
(
Activity
activity
,
String
title
,
InputStream
is
)
{
public
static
void
show
(
Context
activity
,
String
title
,
InputStream
is
)
{
try
(
Scanner
s
=
new
Scanner
(
is
,
"UTF-8"
))
{
s
.
useDelimiter
(
"\\A"
);
new
Listener
(
activity
,
title
).
onResponse
(
s
.
next
());
...
...
@@ -33,10 +32,10 @@ public class MarkDownWindow {
static
class
Listener
implements
ResponseListener
<
String
>
{
Activity
activity
;
Context
activity
;
String
title
;
Listener
(
Activity
a
,
String
t
)
{
Listener
(
Context
a
,
String
t
)
{
activity
=
a
;
title
=
t
;
}
...
...
app/src/main/java/com/topjohnwu/magisk/view/Notifications.java
View file @
2813d203
...
...
@@ -7,10 +7,6 @@ import android.content.Context;
import
android.content.Intent
;
import
android.os.Build
;
import
androidx.core.app.NotificationCompat
;
import
androidx.core.app.NotificationManagerCompat
;
import
androidx.core.app.TaskStackBuilder
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Config
;
...
...
@@ -20,6 +16,10 @@ import com.topjohnwu.magisk.model.receiver.GeneralReceiver;
import
com.topjohnwu.magisk.ui.SplashActivity
;
import
com.topjohnwu.magisk.utils.Utils
;
import
androidx.core.app.NotificationCompat
;
import
androidx.core.app.NotificationManagerCompat
;
import
androidx.core.app.TaskStackBuilder
;
public
class
Notifications
{
public
static
NotificationManagerCompat
mgr
=
NotificationManagerCompat
.
from
(
App
.
self
);
...
...
app/src/main/java/com/topjohnwu/magisk/view/ProgressNotification.java
View file @
2813d203
...
...
@@ -5,13 +5,13 @@ import android.app.PendingIntent;
import
android.content.Intent
;
import
android.widget.Toast
;
import
androidx.core.app.NotificationCompat
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.net.DownloadProgressListener
;
import
androidx.core.app.NotificationCompat
;
public
class
ProgressNotification
implements
DownloadProgressListener
{
private
NotificationCompat
.
Builder
builder
;
...
...
app/src/main/java/com/topjohnwu/magisk/view/SafetyNet.java
View file @
2813d203
...
...
@@ -10,9 +10,6 @@ import android.widget.ImageView;
import
android.widget.ProgressBar
;
import
android.widget.TextView
;
import
androidx.annotation.StringRes
;
import
androidx.cardview.widget.CardView
;
import
com.topjohnwu.magisk.App
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
...
...
@@ -23,6 +20,8 @@ import com.topjohnwu.superuser.Shell;
import
java.io.File
;
import
androidx.annotation.StringRes
;
import
androidx.cardview.widget.CardView
;
import
butterknife.BindColor
;
import
butterknife.BindView
;
import
butterknife.OnClick
;
...
...
@@ -31,10 +30,10 @@ import dalvik.system.DexClassLoader;
public
class
SafetyNet
implements
ISafetyNetHelper
.
Callback
{
p
rivate
static
final
File
EXT_APK
=
p
ublic
static
final
File
EXT_APK
=
new
File
(
App
.
self
.
getFilesDir
().
getParent
()
+
"/snet"
,
"snet.apk"
);
@BindView
(
R
.
id
.
safetyNet_card
)
CardView
safetyNetCard
;
/*@BindView(R.id.safetyNet_card) */
CardView
safetyNetCard
;
@BindView
(
R
.
id
.
safetyNet_refresh
)
ImageView
safetyNetRefreshIcon
;
@BindView
(
R
.
id
.
safetyNet_status
)
TextView
safetyNetStatusText
;
@BindView
(
R
.
id
.
safetyNet_check_progress
)
ProgressBar
safetyNetProgress
;
...
...
@@ -48,7 +47,7 @@ public class SafetyNet implements ISafetyNetHelper.Callback {
@BindColor
(
R
.
color
.
green500
)
int
colorOK
;
public
Unbinder
unbinder
;
private
ExpandableViewHolder
expandable
;
private
final
ExpandableViewHolder
expandable
;
public
SafetyNet
(
View
v
)
{
unbinder
=
new
SafetyNet_ViewBinding
(
this
,
v
);
...
...
@@ -58,27 +57,16 @@ public class SafetyNet implements ISafetyNetHelper.Callback {
View
.
VISIBLE
:
View
.
GONE
);
}
@OnClick
(
R
.
id
.
safetyNet_refresh
)
void
safetyNet
(
View
v
)
{
Runnable
task
=
()
->
{
safetyNetProgress
.
setVisibility
(
View
.
VISIBLE
);
safetyNetRefreshIcon
.
setVisibility
(
View
.
INVISIBLE
);
safetyNetStatusText
.
setText
(
R
.
string
.
checking_safetyNet_status
);
check
((
Activity
)
v
.
getContext
());
expandable
.
collapse
();
};
if
(!
SafetyNet
.
EXT_APK
.
exists
())
{
// Show dialog
new
CustomAlertDialog
((
Activity
)
v
.
getContext
())
.
setTitle
(
R
.
string
.
proprietary_title
)
.
setMessage
(
R
.
string
.
proprietary_notice
)
.
setCancelable
(
true
)
.
setPositiveButton
(
R
.
string
.
yes
,
(
d
,
i
)
->
task
.
run
())
.
setNegativeButton
(
R
.
string
.
no_thanks
,
null
)
.
show
();
}
else
{
task
.
run
();
}
public
static
void
dyRun
(
Activity
activity
,
Object
callback
)
throws
Exception
{
DexClassLoader
loader
=
new
DexClassLoader
(
EXT_APK
.
getPath
(),
EXT_APK
.
getParent
(),
null
,
ISafetyNetHelper
.
class
.
getClassLoader
());
Class
<?>
clazz
=
loader
.
loadClass
(
"com.topjohnwu.snet.Snet"
);
ISafetyNetHelper
helper
=
(
ISafetyNetHelper
)
clazz
.
getMethod
(
"newHelper"
,
Class
.
class
,
String
.
class
,
Activity
.
class
,
Object
.
class
)
.
invoke
(
null
,
ISafetyNetHelper
.
class
,
EXT_APK
.
getPath
(),
activity
,
callback
);
if
(
helper
.
getVersion
()
<
Const
.
SNET_EXT_VER
)
throw
new
Exception
();
helper
.
attest
();
}
public
void
reset
()
{
...
...
@@ -120,27 +108,38 @@ public class SafetyNet implements ISafetyNetHelper.Callback {
}
}
private
void
dyRun
(
Activity
activity
)
throws
Exception
{
DexClassLoader
loader
=
new
DexClassLoader
(
EXT_APK
.
getPath
(),
EXT_APK
.
getParent
(),
null
,
ISafetyNetHelper
.
class
.
getClassLoader
());
Class
<?>
clazz
=
loader
.
loadClass
(
"com.topjohnwu.snet.Snet"
);
ISafetyNetHelper
helper
=
(
ISafetyNetHelper
)
clazz
.
getMethod
(
"newHelper"
,
Class
.
class
,
String
.
class
,
Activity
.
class
,
Object
.
class
)
.
invoke
(
null
,
ISafetyNetHelper
.
class
,
EXT_APK
.
getPath
(),
activity
,
this
);
if
(
helper
.
getVersion
()
<
Const
.
SNET_EXT_VER
)
throw
new
Exception
();
helper
.
attest
();
@OnClick
(
R
.
id
.
safetyNet_refresh
)
void
safetyNet
(
View
v
)
{
Runnable
task
=
()
->
{
safetyNetProgress
.
setVisibility
(
View
.
VISIBLE
);
safetyNetRefreshIcon
.
setVisibility
(
View
.
INVISIBLE
);
safetyNetStatusText
.
setText
(
R
.
string
.
checking_safetyNet_status
);
check
((
Activity
)
v
.
getContext
());
expandable
.
collapse
();
};
if
(!
EXT_APK
.
exists
())
{
// Show dialog
new
CustomAlertDialog
(
v
.
getContext
())
.
setTitle
(
R
.
string
.
proprietary_title
)
.
setMessage
(
R
.
string
.
proprietary_notice
)
.
setCancelable
(
true
)
.
setPositiveButton
(
R
.
string
.
yes
,
(
d
,
i
)
->
task
.
run
())
.
setNegativeButton
(
R
.
string
.
no_thanks
,
null
)
.
show
();
}
else
{
task
.
run
();
}
}
private
void
check
(
Activity
activity
)
{
try
{
dyRun
(
activity
);
dyRun
(
activity
,
this
);
}
catch
(
Exception
ignored
)
{
Shell
.
sh
(
"rm -rf "
+
EXT_APK
.
getParent
()).
exec
();
EXT_APK
.
getParentFile
().
mkdir
();
Networking
.
get
(
Const
.
Url
.
SNET_URL
).
getAsFile
(
EXT_APK
,
f
->
{
try
{
dyRun
(
activity
);
dyRun
(
activity
,
this
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
onResponse
(-
1
);
...
...
app/src/main/java/com/topjohnwu/magisk/view/Shortcuts.java
View file @
2813d203
...
...
@@ -7,8 +7,6 @@ import android.content.pm.ShortcutManager;
import
android.graphics.drawable.Icon
;
import
android.os.Build
;
import
androidx.annotation.RequiresApi
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
...
...
@@ -19,6 +17,8 @@ import com.topjohnwu.superuser.Shell;
import
java.util.ArrayList
;
import
androidx.annotation.RequiresApi
;
public
class
Shortcuts
{
public
static
void
setup
(
Context
context
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.java
View file @
2813d203
...
...
@@ -5,12 +5,12 @@ import android.net.Uri;
import
android.view.View
;
import
android.widget.TextView
;
import
androidx.annotation.StringRes
;
import
com.google.android.material.snackbar.Snackbar
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.Utils
;
import
androidx.annotation.StringRes
;
public
class
SnackbarMaker
{
public
static
Snackbar
make
(
Activity
activity
,
CharSequence
text
,
int
duration
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/view/UpdateCardHolder.java
deleted
100644 → 0
View file @
4040a024
package
com
.
topjohnwu
.
magisk
.
view
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.Button
;
import
android.widget.ImageView
;
import
android.widget.ProgressBar
;
import
android.widget.TextView
;
import
com.topjohnwu.magisk.R
;
import
butterknife.BindView
;
import
butterknife.Unbinder
;
public
class
UpdateCardHolder
{
@BindView
(
R
.
id
.
status_icon
)
public
ImageView
statusIcon
;
@BindView
(
R
.
id
.
progress
)
public
ProgressBar
progress
;
@BindView
(
R
.
id
.
status
)
public
TextView
status
;
@BindView
(
R
.
id
.
current_version
)
public
TextView
currentVersion
;
@BindView
(
R
.
id
.
latest_version
)
public
TextView
latestVersion
;
@BindView
(
R
.
id
.
additional
)
public
TextView
additional
;
@BindView
(
R
.
id
.
install
)
public
Button
install
;
public
View
itemView
;
public
Unbinder
unbinder
;
public
UpdateCardHolder
(
LayoutInflater
inflater
,
ViewGroup
root
)
{
itemView
=
inflater
.
inflate
(
R
.
layout
.
update_card
,
root
,
false
);
unbinder
=
new
UpdateCardHolder_ViewBinding
(
this
,
itemView
);
}
public
void
setClickable
(
View
.
OnClickListener
listener
)
{
itemView
.
setClickable
(
true
);
itemView
.
setFocusable
(
true
);
itemView
.
setOnClickListener
(
listener
);
}
public
void
setValid
(
boolean
valid
)
{
progress
.
setVisibility
(
View
.
GONE
);
statusIcon
.
setVisibility
(
View
.
VISIBLE
);
if
(
valid
)
{
install
.
setVisibility
(
View
.
VISIBLE
);
latestVersion
.
setVisibility
(
View
.
VISIBLE
);
}
else
{
install
.
setVisibility
(
View
.
GONE
);
latestVersion
.
setVisibility
(
View
.
GONE
);
}
}
public
void
reset
()
{
progress
.
setVisibility
(
View
.
VISIBLE
);
statusIcon
.
setVisibility
(
View
.
INVISIBLE
);
latestVersion
.
setVisibility
(
View
.
GONE
);
install
.
setVisibility
(
View
.
GONE
);
status
.
setText
(
R
.
string
.
checking_for_updates
);
}
}
app/src/main/java/com/topjohnwu/magisk/view/dialogs/CustomAlertDialog.java
View file @
2813d203
package
com
.
topjohnwu
.
magisk
.
view
.
dialogs
;
import
android.
app.Activity
;
import
android.
content.Context
;
import
android.content.DialogInterface
;
import
android.view.LayoutInflater
;
import
android.view.View
;
...
...
@@ -8,14 +8,13 @@ import android.widget.Button;
import
android.widget.LinearLayout
;
import
android.widget.TextView
;
import
com.topjohnwu.magisk.R
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.StringRes
;
import
androidx.annotation.StyleRes
;
import
androidx.appcompat.app.AlertDialog
;
import
com.topjohnwu.magisk.R
;
import
butterknife.BindView
;
public
class
CustomAlertDialog
extends
AlertDialog
.
Builder
{
...
...
@@ -53,11 +52,11 @@ public class CustomAlertDialog extends AlertDialog.Builder {
}
public
CustomAlertDialog
(
@NonNull
Activity
context
)
{
public
CustomAlertDialog
(
@NonNull
Context
context
)
{
super
(
context
);
}
public
CustomAlertDialog
(
@NonNull
Activity
context
,
@StyleRes
int
themeResId
)
{
public
CustomAlertDialog
(
@NonNull
Context
context
,
@StyleRes
int
themeResId
)
{
super
(
context
,
themeResId
);
}
...
...
@@ -89,7 +88,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
vh
.
positive
.
setVisibility
(
View
.
VISIBLE
);
vh
.
positive
.
setText
(
text
);
positiveListener
=
listener
;
vh
.
positive
.
setOnClickListener
(
(
v
)
->
{
vh
.
positive
.
setOnClickListener
(
v
->
{
if
(
positiveListener
!=
null
)
{
positiveListener
.
onClick
(
dialog
,
DialogInterface
.
BUTTON_POSITIVE
);
}
...
...
@@ -109,7 +108,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
vh
.
negative
.
setVisibility
(
View
.
VISIBLE
);
vh
.
negative
.
setText
(
text
);
negativeListener
=
listener
;
vh
.
negative
.
setOnClickListener
(
(
v
)
->
{
vh
.
negative
.
setOnClickListener
(
v
->
{
if
(
negativeListener
!=
null
)
{
negativeListener
.
onClick
(
dialog
,
DialogInterface
.
BUTTON_NEGATIVE
);
}
...
...
@@ -129,7 +128,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
vh
.
neutral
.
setVisibility
(
View
.
VISIBLE
);
vh
.
neutral
.
setText
(
text
);
neutralListener
=
listener
;
vh
.
neutral
.
setOnClickListener
(
(
v
)
->
{
vh
.
neutral
.
setOnClickListener
(
v
->
{
if
(
neutralListener
!=
null
)
{
neutralListener
.
onClick
(
dialog
,
DialogInterface
.
BUTTON_NEUTRAL
);
}
...
...
app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java
View file @
2813d203
...
...
@@ -4,8 +4,6 @@ import android.app.Activity;
import
android.app.ProgressDialog
;
import
android.widget.Toast
;
import
androidx.annotation.NonNull
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.tasks.MagiskInstaller
;
import
com.topjohnwu.magisk.utils.RootUtils
;
...
...
@@ -14,6 +12,8 @@ import com.topjohnwu.superuser.Shell;
import
com.topjohnwu.superuser.internal.UiThreadHandler
;
import
com.topjohnwu.superuser.io.SuFile
;
import
androidx.annotation.NonNull
;
public
class
EnvFixDialog
extends
CustomAlertDialog
{
public
EnvFixDialog
(
@NonNull
Activity
activity
)
{
...
...
app/src/main/java/com/topjohnwu/magisk/view/dialogs/FingerprintAuthDialog.java
View file @
2813d203
...
...
@@ -11,20 +11,23 @@ import android.os.Build;
import
android.view.Gravity
;
import
android.widget.Toast
;
import
androidx.annotation.NonNull
;
import
androidx.appcompat.app.AlertDialog
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.utils.FingerprintHelper
;
import
com.topjohnwu.magisk.utils.Utils
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.appcompat.app.AlertDialog
;
@TargetApi
(
Build
.
VERSION_CODES
.
M
)
public
class
FingerprintAuthDialog
extends
CustomAlertDialog
{
private
Runnable
callback
;
private
final
Runnable
callback
;
@Nullable
private
Runnable
failureCallback
;
private
DialogFingerprintHelper
helper
;
public
FingerprintAuthDialog
(
@NonNull
Activity
activity
,
Runnable
onSuccess
)
{
public
FingerprintAuthDialog
(
@NonNull
Activity
activity
,
@NonNull
Runnable
onSuccess
)
{
super
(
activity
);
callback
=
onSuccess
;
Drawable
fingerprint
=
activity
.
getResources
().
getDrawable
(
R
.
drawable
.
ic_fingerprint
);
...
...
@@ -37,13 +40,28 @@ public class FingerprintAuthDialog extends CustomAlertDialog {
vh
.
messageView
.
setCompoundDrawablePadding
(
Utils
.
dpInPx
(
20
));
vh
.
messageView
.
setGravity
(
Gravity
.
CENTER
);
setMessage
(
R
.
string
.
auth_fingerprint
);
setNegativeButton
(
R
.
string
.
close
,
(
d
,
w
)
->
helper
.
cancel
());
setOnCancelListener
(
d
->
helper
.
cancel
());
setNegativeButton
(
R
.
string
.
close
,
(
d
,
w
)
->
{
helper
.
cancel
();
if
(
failureCallback
!=
null
)
{
failureCallback
.
run
();
}
});
setOnCancelListener
(
d
->
{
helper
.
cancel
();
if
(
failureCallback
!=
null
)
{
failureCallback
.
run
();
}
});
try
{
helper
=
new
DialogFingerprintHelper
();
}
catch
(
Exception
ignored
)
{}
}
public
FingerprintAuthDialog
(
@NonNull
Activity
activity
,
@NonNull
Runnable
onSuccess
,
@NonNull
Runnable
onFailure
)
{
this
(
activity
,
onSuccess
);
failureCallback
=
onFailure
;
}
@Override
public
AlertDialog
show
()
{
create
();
...
...
app/src/main/java/com/topjohnwu/magisk/view/dialogs/InstallMethodDialog.java
View file @
2813d203
...
...
@@ -4,14 +4,12 @@ import android.app.Activity;
import
android.content.Intent
;
import
android.widget.Toast
;
import
androidx.appcompat.app.AlertDialog
;
import
com.google.android.material.snackbar.Snackbar
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.ui.base.
BaseActivity
;
import
com.topjohnwu.magisk.ui.base.
IBaseLeanback
;
import
com.topjohnwu.magisk.ui.flash.FlashActivity
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.magisk.view.ProgressNotification
;
...
...
@@ -21,9 +19,11 @@ import com.topjohnwu.net.Networking;
import
java.io.File
;
import
java.util.List
;
import
androidx.appcompat.app.AlertDialog
;
class
InstallMethodDialog
extends
AlertDialog
.
Builder
{
InstallMethodDialog
(
BaseActivity
activity
,
List
<
String
>
options
)
{
<
Ctxt
extends
Activity
&
IBaseLeanback
>
InstallMethodDialog
(
Ctxt
activity
,
List
<
String
>
options
)
{
super
(
activity
);
setTitle
(
R
.
string
.
select_method
);
setItems
(
options
.
toArray
(
new
String
[
0
]),
(
dialog
,
idx
)
->
{
...
...
@@ -48,7 +48,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
});
}
private
void
patchBoot
(
BaseActivity
activity
)
{
private
<
Ctxt
extends
Activity
&
IBaseLeanback
>
void
patchBoot
(
Ctxt
activity
)
{
activity
.
runWithExternalRW
(()
->
{
Utils
.
toast
(
R
.
string
.
patch_file_msg
,
Toast
.
LENGTH_LONG
);
Intent
intent
=
new
Intent
(
Intent
.
ACTION_GET_CONTENT
)
...
...
@@ -66,7 +66,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
});
}
private
void
downloadOnly
(
BaseActivity
activity
)
{
private
<
Ctxt
extends
Activity
&
IBaseLeanback
>
void
downloadOnly
(
Ctxt
activity
)
{
activity
.
runWithExternalRW
(()
->
{
String
filename
=
Utils
.
fmt
(
"Magisk-v%s(%d).zip"
,
Config
.
remoteMagiskVersionString
,
Config
.
remoteMagiskVersionCode
);
...
...
@@ -74,7 +74,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
ProgressNotification
progress
=
new
ProgressNotification
(
filename
);
Networking
.
get
(
Config
.
magiskLink
)
.
setDownloadProgressListener
(
progress
)
.
setErrorHandler
((
(
conn
,
e
)
->
progress
.
dlFail
()
))
.
setErrorHandler
((
conn
,
e
)
->
progress
.
dlFail
(
))
.
getAsFile
(
zip
,
f
->
{
progress
.
dlDone
();
SnackbarMaker
.
make
(
activity
,
...
...
@@ -84,7 +84,7 @@ class InstallMethodDialog extends AlertDialog.Builder {
});
}
private
void
installInactiveSlot
(
BaseActivity
activity
)
{
private
<
Ctxt
extends
Activity
&
IBaseLeanback
>
void
installInactiveSlot
(
Ctxt
activity
)
{
new
CustomAlertDialog
(
activity
)
.
setTitle
(
R
.
string
.
warning
)
.
setMessage
(
R
.
string
.
install_inactive_slot_msg
)
...
...
app/src/main/java/com/topjohnwu/magisk/view/dialogs/MagiskInstallDialog.java
View file @
2813d203
package
com
.
topjohnwu
.
magisk
.
view
.
dialogs
;
import
android.app.Activity
;
import
android.net.Uri
;
import
android.text.TextUtils
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.R
;
import
com.topjohnwu.magisk.ui.base.
BaseActivity
;
import
com.topjohnwu.magisk.ui.base.
IBaseLeanback
;
import
com.topjohnwu.magisk.utils.Utils
;
import
com.topjohnwu.magisk.view.MarkDownWindow
;
import
com.topjohnwu.superuser.Shell
;
...
...
@@ -15,7 +16,7 @@ import java.util.ArrayList;
import
java.util.List
;
public
class
MagiskInstallDialog
extends
CustomAlertDialog
{
public
MagiskInstallDialog
(
BaseActivity
a
)
{
public
<
Ctxt
extends
Activity
&
IBaseLeanback
>
MagiskInstallDialog
(
Ctxt
a
)
{
super
(
a
);
String
filename
=
Utils
.
fmt
(
"Magisk-v%s(%d).zip"
,
Config
.
remoteMagiskVersionString
,
Config
.
remoteMagiskVersionCode
);
...
...
app/src/main/java/com/topjohnwu/magisk/view/dialogs/UninstallDialog.java
View file @
2813d203
...
...
@@ -7,8 +7,6 @@ import android.net.Uri;
import
android.text.TextUtils
;
import
android.widget.Toast
;
import
androidx.annotation.NonNull
;
import
com.topjohnwu.magisk.ClassMap
;
import
com.topjohnwu.magisk.Config
;
import
com.topjohnwu.magisk.Const
;
...
...
@@ -21,6 +19,8 @@ import com.topjohnwu.superuser.Shell;
import
java.io.File
;
import
androidx.annotation.NonNull
;
public
class
UninstallDialog
extends
CustomAlertDialog
{
public
UninstallDialog
(
@NonNull
Activity
activity
)
{
...
...
app/src/main/res/drawable/ic_back.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24"
android:viewportHeight=
"24"
>
<path
android:fillColor=
"@color/colorText"
android:pathData=
"M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_checked.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24"
android:viewportHeight=
"24"
>
<path
android:fillColor=
"#000"
android:pathData=
"M20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4C12.76,4 13.5,4.11 14.2,4.31L15.77,2.74C14.61,2.26 13.34,2 12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12M7.91,10.08L6.5,11.5L11,16L21,6L19.59,4.58L11,13.17L7.91,10.08Z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_indeterminate.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24"
android:viewportHeight=
"24"
>
<path
android:fillColor=
"#000"
android:pathData=
"M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M7,13H17V11H7"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_menu.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24"
android:viewportHeight=
"24"
>
<path
android:fillColor=
"#fff"
android:pathData=
"M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z"
/>
</vector>
app/src/main/res/drawable/ic_restart.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24"
android:viewportHeight=
"24"
>
<path
android:fillColor=
"#000"
android:pathData=
"M12,4C14.1,4 16.1,4.8 17.6,6.3C20.7,9.4 20.7,14.5 17.6,17.6C15.8,19.5 13.3,20.2 10.9,19.9L11.4,17.9C13.1,18.1 14.9,17.5 16.2,16.2C18.5,13.9 18.5,10.1 16.2,7.7C15.1,6.6 13.5,6 12,6V10.6L7,5.6L12,0.6V4M6.3,17.6C3.7,15 3.3,11 5.1,7.9L6.6,9.4C5.5,11.6 5.9,14.4 7.8,16.2C8.3,16.7 8.9,17.1 9.6,17.4L9,19.4C8,19 7.1,18.4 6.3,17.6Z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_unchecked.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24"
android:viewportHeight=
"24"
>
<path
android:fillColor=
"#000"
android:pathData=
"M12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4A8,8 0 0,1 20,12A8,8 0 0,1 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"
/>
</vector>
\ No newline at end of file
app/src/main/res/layout/activity_flash.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.flash.FlashViewModel"
/>
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"@color/flashing_background_color"
android:orientation=
"vertical"
>
android:layout_height=
"match_parent"
>
<include
layout=
"@layout/toolbar"
/>
<com.google.android.material.appbar.AppBarLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:theme=
"@style/AppBarLayoutTheme.Flashing"
>
<HorizontalScrollView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
<androidx.appcompat.widget.Toolbar
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
android:layout_weight=
"1"
>
android:layout_height=
"?attr/actionBarSize"
app:contentInsetLeft=
"0dp"
app:contentInsetStart=
"0dp"
app:popupTheme=
"@style/ToolbarPopupTheme.Flashing"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recyclerView"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:animateLayoutChanges=
"true"
>
<FrameLayout
invisibleScale=
"@{viewModel.loading}"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:background=
"?attr/selectableItemBackgroundBorderless"
android:onClick=
"@{() -> viewModel.backPressed()}"
app:layout_constraintDimensionRatio=
"1:1"
app:layout_constraintLeft_toLeftOf=
"parent"
>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon"
android:layout_gravity=
"center"
android:background=
"@android:color/transparent"
app:srcCompat=
"@drawable/ic_back"
app:tint=
"@android:color/white"
/>
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.Emphasize"
movieBehavior=
"@{viewModel.loading}"
movieBehaviorText=
"@{viewModel.behaviorText}"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:fontFamily=
"monospace"
android:textColor=
"@android:color/white"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"Flashing..."
/>
<FrameLayout
invisibleScale=
"@{viewModel.loading}"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:paddingStart=
"8dp"
android:paddingEnd=
"8dp"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
/>
android:background=
"?attr/selectableItemBackgroundBorderless"
android:onClick=
"@{() -> viewModel.savePressed()}"
app:layout_constraintDimensionRatio=
"1:1"
app:layout_constraintRight_toRightOf=
"parent"
>
</HorizontalScrollView>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon"
android:layout_gravity=
"center"
android:background=
"@android:color/transparent"
app:srcCompat=
"@drawable/ic_save"
app:tint=
"@android:color/white"
/>
<LinearLayout
android:id=
"@+id/button_panel"
style=
"?android:buttonStyle"
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
app:layout_behavior=
"@string/appbar_scrolling_view_behavior"
>
<androidx.recyclerview.widget.RecyclerView
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.items}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_console"
/>
<com.google.android.material.card.MaterialCardView
invisibleScale=
"@{!viewModel.loaded || !viewModel.canShowReboot}"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"10dp"
android:orientation=
"horizontal"
android:visibility=
"gone"
>
android:layout_margin=
"@dimen/margin_generic"
app:cardBackgroundColor=
"@color/colorSecondary"
app:cardCornerRadius=
"26dp"
app:cardPreventCornerOverlap=
"true"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
>
<Button
android:id=
"@+id/close"
style=
"?android:borderlessButtonStyle"
android:layout_width=
"0dp"
android:layout_height=
"50dp"
android:layout_weight=
"1"
android:text=
"@string/close"
/>
<LinearLayout
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:animateLayoutChanges=
"true"
android:background=
"?attr/selectableItemBackground"
android:gravity=
"center"
android:onClick=
"@{() -> viewModel.restartPressed()}"
android:padding=
"@dimen/margin_generic_half"
>
<Button
android:id=
"@+id/save_logs
"
style=
"?android:borderlessButtonStyle
"
android:layout_width=
"0dp
"
android:layout_height=
"50dp
"
android:layout_weight=
"1
"
android:text=
"@string/menuSaveLog
"
/>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon
"
android:layout_width=
"wrap_content
"
android:layout_height=
"wrap_content
"
android:background=
"@android:color/transparent
"
app:srcCompat=
"@drawable/ic_restart
"
app:tint=
"@color/colorTextTinted
"
/>
<Button
android:id=
"@+id/reboot"
style=
"?android:borderlessButtonStyle"
android:layout_width=
"0dp"
android:layout_height=
"50dp"
android:layout_weight=
"1"
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.Emphasize.Tinted"
gone=
"@{!viewModel.showRestartTitle}"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginLeft=
"@dimen/margin_generic_half"
android:paddingRight=
"@dimen/margin_generic_half"
android:text=
"@string/reboot"
/>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
\ No newline at end of file
app/src/main/res/layout/activity_main.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<
androidx.drawerlayout.widget.DrawerL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.MainViewModel"
/>
</data>
<androidx.drawerlayout.widget.DrawerLayout
android:id=
"@+id/drawer_layout"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:fitsSystemWindows=
"true"
tools:openDrawer=
"start"
>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"?android:windowBackground"
android:orientation=
"vertical"
>
<FrameLayout
android:layout_marginTop=
"?attr/actionBarSize"
android:id=
"@+id/content_frame"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
<include
layout=
"@layout/toolbar"
/>
</FrameLayout>
<include
android:id=
"@+id/main_include"
layout=
"@layout/activity_main_content"
viewModel=
"@{viewModel}"
/>
<com.google.android.material.navigation.NavigationView
android:id=
"@+id/nav_view"
...
...
@@ -30,6 +29,10 @@
android:layout_height=
"match_parent"
android:layout_gravity=
"start"
android:fitsSystemWindows=
"true"
onNavigationClick=
"@{(item) -> viewModel.navigationItemPressed(item)}"
app:menu=
"@menu/drawer"
/>
</androidx.drawerlayout.widget.DrawerLayout>
</androidx.drawerlayout.widget.DrawerLayout>
</layout>
app/src/main/res/layout/activity_main_content.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.MainViewModel"
/>
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
<com.google.android.material.appbar.AppBarLayout
android:layout_width=
"match_parent"
app:elevation=
"0dp"
android:layout_height=
"wrap_content"
>
<androidx.appcompat.widget.Toolbar
android:id=
"@+id/main_toolbar"
onNavigationClick=
"@{() -> viewModel.navPressed()}"
android:layout_width=
"match_parent"
android:layout_height=
"?attr/actionBarSize"
android:background=
"@color/primary"
app:navigationIcon=
"@drawable/ic_menu"
app:title=
"@string/magisk"
app:titleTextColor=
"@android:color/white"
/>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:id=
"@+id/main_nav_host"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"?android:windowBackground"
app:layout_behavior=
"@string/appbar_scrolling_view_behavior"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
app/src/main/res/layout/activity_request.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<
LinearL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.surequest.SuRequestViewModel"
/>
</data>
<LinearLayout
android:id=
"@+id/su_popup"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
...
...
@@ -9,7 +20,7 @@
android:minWidth=
"350dp"
android:orientation=
"vertical"
>
<
TextView
<androidx.appcompat.widget.AppCompat
TextView
android:id=
"@+id/request_title"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
...
...
@@ -30,14 +41,16 @@
android:paddingStart=
"10dp"
android:paddingEnd=
"10dp"
>
<
ImageView
<androidx.appcompat.widget.AppCompat
ImageView
android:id=
"@+id/app_icon"
android:layout_width=
"50dp"
android:layout_height=
"50dp"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"5dp"
android:layout_marginEnd=
"10dp"
android:layout_weight=
"0"
/>
android:layout_weight=
"0"
android:src=
"@{viewModel.icon}"
tools:src=
"@drawable/ic_delete"
/>
<LinearLayout
android:layout_width=
"wrap_content"
...
...
@@ -47,7 +60,7 @@
android:gravity=
"center_vertical"
android:orientation=
"vertical"
>
<
TextView
<androidx.appcompat.widget.AppCompat
TextView
android:id=
"@+id/app_name"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
...
...
@@ -55,10 +68,12 @@
android:maxWidth=
"300dp"
android:maxLines=
"1"
android:minWidth=
"200dp"
android:text=
"@{viewModel.title}"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textColor=
"?android:textColorPrimary"
/>
android:textColor=
"?android:textColorPrimary"
tools:text=
"Magisk"
/>
<
TextView
<androidx.appcompat.widget.AppCompat
TextView
android:id=
"@+id/package_name"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
...
...
@@ -66,25 +81,34 @@
android:maxWidth=
"300dp"
android:maxLines=
"1"
android:minWidth=
"200dp"
android:textColor=
"?android:textColorSecondary"
/>
android:text=
"@{viewModel.packageName}"
android:textColor=
"?android:textColorSecondary"
tools:text=
"com.topjohnwu.magisk"
/>
</LinearLayout>
</LinearLayout>
<
Spinner
<androidx.appcompat.widget.AppCompat
Spinner
android:id=
"@+id/timeout"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.items}"
onTouch=
"@{() -> viewModel.spinnerTouched()}"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_horizontal"
/>
android:layout_gravity=
"center_horizontal"
android:selectedItemPosition=
"@={viewModel.selectedItemPosition}"
/>
itemDropDownLayout="@{android.R.layout.simple_spinner_dropdown_item}"
android:onClick="@{() -> viewModel.spinnerPressed()}"
<
TextView
<androidx.appcompat.widget.AppCompat
TextView
android:id=
"@+id/warning"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_horizontal"
android:layout_margin=
"5dp"
android:drawablePadding=
"10dp"
android:text=
"@string/su_warning"
android:textColor=
"?android:textColorSecondary"
/>
android:text=
"@{viewModel.warningText}"
android:textColor=
"?android:textColorSecondary"
tools:text=
"@string/su_warning"
/>
<LinearLayout
style=
"?android:buttonBarStyle"
...
...
@@ -95,24 +119,29 @@
android:paddingStart=
"30dp"
android:paddingEnd=
"30dp"
>
<
Button
<com.google.android.material.button.Material
Button
android:id=
"@+id/deny_btn"
style=
"?android:buttonBarButtonStyle
"
style=
"@style/Widget.Button.Text
"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_weight=
"1"
android:text=
"@string/deny_with_str"
/>
android:onClick=
"@{() -> viewModel.denyPressed()}"
android:text=
"@{viewModel.denyText}"
tools:text=
"@string/deny"
/>
<
Button
<com.google.android.material.button.Material
Button
android:id=
"@+id/grant_btn"
style=
"?android:buttonBarButtonStyle"
style=
"@style/Widget.Button.Text"
gone=
"@{viewModel.canUseFingerprint}"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_weight=
"1"
android:onClick=
"@{() -> viewModel.grantPressed()}"
android:text=
"@string/grant"
/>
<
ImageView
<androidx.appcompat.widget.AppCompat
ImageView
android:id=
"@+id/fingerprint"
gone=
"@{!viewModel.canUseFingerprint}"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:layout_weight=
"1"
...
...
@@ -121,4 +150,6 @@
app:srcCompat=
"@drawable/ic_fingerprint"
/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</layout>
\ No newline at end of file
app/src/main/res/layout/activity_su_request.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<data>
<variable
name=
"viewModel"
type=
"Object"
/>
</data>
<View
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"@android:color/transparent"
/>
</layout>
\ No newline at end of file
app/src/main/res/layout/alert_dialog.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:id=
"@+id/dialog_layout"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
...
...
@@ -27,32 +28,35 @@
android:orientation=
"horizontal"
android:padding=
"2dp"
>
<Button
<
com.google.android.material.button.Material
Button
android:id=
"@+id/negative"
style=
"
?android:attr/buttonBarButtonStyle
"
style=
"
@style/Widget.Button.Text
"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:layout_gravity=
"start"
android:layout_weight=
"1"
android:maxLines=
"2"
/>
android:maxLines=
"2"
tools:text=
"Negative"
/>
<Button
<
com.google.android.material.button.Material
Button
android:id=
"@+id/neutral"
style=
"
?android:attr/buttonBarButtonStyle
"
style=
"
@style/Widget.Button.Text
"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:layout_gravity=
"center_horizontal"
android:layout_weight=
"1"
android:maxLines=
"2"
/>
android:maxLines=
"2"
tools:text=
"Neutral"
/>
<Button
<
com.google.android.material.button.Material
Button
android:id=
"@+id/positive"
style=
"
?android:attr/buttonBarButtonStyle
"
style=
"
@style/Widget.Button.Text
"
android:layout_width=
"0dp"
android:layout_height=
"match_parent"
android:layout_gravity=
"end"
android:layout_weight=
"1"
android:maxLines=
"2"
/>
android:maxLines=
"2"
tools:text=
"Positive"
/>
</LinearLayout>
...
...
app/src/main/res/layout/dialog_magisk_base.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"data"
type=
"com.topjohnwu.magisk.view.MagiskDialog.Data"
/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<com.google.android.material.card.MaterialCardView
style=
"@style/Widget.Card"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:cardElevation=
"@dimen/margin_generic"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintWidth_default=
"percent"
app:layout_constraintWidth_max=
"400dp"
app:layout_constraintWidth_percent=
".9"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/dialog_base_start"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_begin=
"16dp"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/dialog_base_end"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_end=
"16dp"
/>
<FrameLayout
android:id=
"@+id/dialog_base_icon"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon"
gone=
"@{data.icon == 0}"
srcCompat=
"@{data.icon}"
android:layout_gravity=
"center"
android:layout_marginTop=
"@dimen/margin_generic"
app:tint=
"@color/colorSecondary"
tools:src=
"@drawable/ic_delete"
/>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon.Large"
gone=
"@{data.iconRaw == null}"
android:layout_gravity=
"center"
android:layout_marginTop=
"@dimen/margin_generic"
android:src=
"@{data.iconRaw}"
tools:src=
"@drawable/ic_delete"
/>
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/dialog_base_title"
style=
"@style/Widget.Text.Title"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"@dimen/margin_generic_half"
android:gravity=
"center"
android:text=
"@{data.title}"
app:layout_constraintLeft_toLeftOf=
"@+id/dialog_base_start"
app:layout_constraintRight_toRightOf=
"@+id/dialog_base_end"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_icon"
tools:lines=
"1"
tools:text=
"@tools:sample/lorem/random"
/>
<androidx.core.widget.NestedScrollView
android:id=
"@+id/dialog_base_scroll"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintLeft_toLeftOf=
"@+id/dialog_base_start"
app:layout_constraintRight_toRightOf=
"@+id/dialog_base_end"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_title"
>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/dialog_base_message"
style=
"@style/Widget.Text"
gone=
"@{data.message.length == 0}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"@dimen/margin_generic_half"
android:gravity=
"center"
android:text=
"@{data.message}"
tools:lines=
"3"
tools:text=
"@tools:sample/lorem/random"
/>
<FrameLayout
android:id=
"@+id/dialog_base_container"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<Space
android:id=
"@+id/dialog_base_space"
android:layout_width=
"wrap_content"
android:layout_height=
"@dimen/margin_generic"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_scroll"
/>
<View
android:id=
"@+id/dialog_base_button_0_divider"
style=
"@style/Widget.Divider.Horizontal"
gone=
"@{data.buttonPositive.icon == 0 && data.buttonPositive.title.length == 0}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_space"
/>
<LinearLayout
android:id=
"@+id/dialog_base_button_1"
style=
"@style/Widget.DialogButton"
gone=
"@{data.buttonPositive.icon == 0 && data.buttonPositive.title.length == 0}"
android:clickable=
"@{data.buttonPositive.isEnabled()}"
android:filterTouchesWhenObscured=
"true"
android:focusable=
"@{data.buttonPositive.isEnabled()}"
android:onClick=
"@{() -> data.buttonPositive.clicked()}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_0_divider"
>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon.DialogButton"
gone=
"@{data.buttonPositive.icon == 0}"
srcCompat=
"@{data.buttonPositive.icon}"
tools:src=
"@drawable/ic_delete"
/>
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.DialogButton"
gone=
"@{data.buttonPositive.title.length == 0}"
android:text=
"@{data.buttonPositive.title}"
tools:text=
"Button 1"
/>
</LinearLayout>
<View
android:id=
"@+id/dialog_base_button_1_divider"
style=
"@style/Widget.Divider.Horizontal"
gone=
"@{data.buttonNeutral.icon == 0 && data.buttonNeutral.title.length == 0}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_1"
/>
<LinearLayout
android:id=
"@+id/dialog_base_button_2"
style=
"@style/Widget.DialogButton"
gone=
"@{data.buttonNeutral.icon == 0 && data.buttonNeutral.title.length == 0}"
android:clickable=
"@{data.buttonNeutral.isEnabled()}"
android:filterTouchesWhenObscured=
"true"
android:focusable=
"@{data.buttonNeutral.isEnabled()}"
android:onClick=
"@{() -> data.buttonNeutral.clicked()}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_1_divider"
>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon.DialogButton"
gone=
"@{data.buttonNeutral.icon == 0}"
srcCompat=
"@{data.buttonNeutral.icon}"
tools:src=
"@drawable/ic_delete"
/>
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.DialogButton"
gone=
"@{data.buttonNeutral.title.length == 0}"
android:text=
"@{data.buttonNeutral.title}"
tools:text=
"Button 2"
/>
</LinearLayout>
<View
android:id=
"@+id/dialog_base_button_2_divider"
style=
"@style/Widget.Divider.Horizontal"
gone=
"@{data.buttonNegative.icon == 0 && data.buttonNegative.title.length == 0}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_2"
/>
<LinearLayout
android:id=
"@+id/dialog_base_button_3"
style=
"@style/Widget.DialogButton"
gone=
"@{data.buttonNegative.icon == 0 && data.buttonNegative.title.length == 0}"
android:clickable=
"@{data.buttonNegative.isEnabled()}"
android:filterTouchesWhenObscured=
"true"
android:focusable=
"@{data.buttonNegative.isEnabled()}"
android:onClick=
"@{() -> data.buttonNegative.clicked()}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_2_divider"
>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon.DialogButton"
gone=
"@{data.buttonNegative.icon == 0}"
srcCompat=
"@{data.buttonNegative.icon}"
tools:src=
"@drawable/ic_delete"
/>
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.DialogButton"
gone=
"@{data.buttonNegative.title.length == 0}"
android:text=
"@{data.buttonNegative.title}"
tools:text=
"Button 3"
/>
</LinearLayout>
<View
android:id=
"@+id/dialog_base_button_3_divider"
style=
"@style/Widget.Divider.Horizontal"
gone=
"@{data.buttonIDGAF.icon == 0 && data.buttonIDGAF.title.length == 0}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_3"
/>
<LinearLayout
android:id=
"@+id/dialog_base_button_4"
style=
"@style/Widget.DialogButton"
gone=
"@{data.buttonIDGAF.icon == 0 && data.buttonIDGAF.title.length == 0}"
android:clickable=
"@{data.buttonIDGAF.isEnabled()}"
android:filterTouchesWhenObscured=
"true"
android:focusable=
"@{data.buttonIDGAF.isEnabled()}"
android:onClick=
"@{() -> data.buttonIDGAF.clicked()}"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/dialog_base_button_3_divider"
>
<androidx.appcompat.widget.AppCompatImageView
style=
"@style/Widget.Icon.DialogButton"
gone=
"@{data.buttonIDGAF.icon == 0}"
srcCompat=
"@{data.buttonIDGAF.icon}"
tools:src=
"@drawable/ic_delete"
/>
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.DialogButton"
gone=
"@{data.buttonIDGAF.title.length == 0}"
android:text=
"@{data.buttonIDGAF.title}"
tools:text=
"Button 4"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
\ No newline at end of file
app/src/main/res/layout/fragment_log.xml
View file @
2813d203
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.log.LogViewModel"
/>
</data>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
<com.google.android.material.tabs.TabLayout
android:id=
"@+id/tab
"
android:id=
"@+id/log_tabs
"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"?attr/colorPrimary"
android:elevation=
"4dp"
android:visibility=
"gone"
app:tabPaddingEnd=
"20dp"
app:tabPaddingStart=
"20dp"
>
</com.google.android.material.tabs.TabLayout>
app:elevation=
"4dp"
app:tabSelectedTextColor=
"@android:color/white"
app:tabTextColor=
"@android:color/secondary_text_dark"
/>
<androidx.viewpager.widget.ViewPager
android:id=
"@+id/container"
android:id=
"@+id/log_container"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.items}"
pageTitles=
"@{viewModel}"
position=
"@={viewModel.currentPage}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
</LinearLayout>
</LinearLayout>
</layout>
\ No newline at end of file
app/src/main/res/layout/fragment_magisk.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<
androidx.swiperefreshlayout.widget.SwipeRefreshL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<import
type=
"com.skoumal.teanity.viewmodel.LoadingViewModel.State"
/>
<import
type=
"com.topjohnwu.magisk.ui.home.SafetyNetState"
/>
<import
type=
"com.topjohnwu.magisk.ui.home.MagiskState"
/>
<import
type=
"com.topjohnwu.magisk.ui.home.MagiskItem"
/>
<import
type=
"com.topjohnwu.magisk.R"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.home.HomeViewModel"
/>
</data>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id=
"@+id/swipeRefreshLayout"
android:layout_width=
"match_parent"
android:layout_height=
"fill_parent"
android:orientation=
"vertical"
>
android:orientation=
"vertical"
app:onRefreshListener=
"@{() -> viewModel.refresh()}"
app:refreshing=
"@{viewModel.loading}"
>
<ScrollView
android:layout_width=
"match_parent"
...
...
@@ -16,25 +39,12 @@
android:id=
"@+id/linearLayout"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
>
<androidx.cardview.widget.CardView
style=
"?attr/cardStyle"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"5dp"
android:layout_marginTop=
"4dp"
android:layout_marginEnd=
"5dp"
android:layout_marginBottom=
"4dp"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
android:orientation=
"vertical"
android:padding=
"@dimen/margin_generic"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"5dp"
android:layout_marginBottom=
"5dp"
>
android:layout_height=
"wrap_content"
>
<ImageView
android:id=
"@+id/icon"
...
...
@@ -54,8 +64,8 @@
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:paddingTop=
"10dp"
android:paddingBottom=
"10dp"
android:paddingTop=
"10dp"
android:text=
"@string/app_name"
android:textAppearance=
"@style/TextAppearance.AppCompat.Headline"
app:layout_constraintBottom_toBottomOf=
"parent"
...
...
@@ -66,32 +76,67 @@
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id=
"@+id/install_option_card"
style=
"?attr/cardStyle"
<View
style=
"@style/Widget.Divider.Horizontal"
android:layout_width=
"match_parent"
android:layout_margin=
"@dimen/margin_generic"
/>
<include
android:id=
"@+id/home_magisk_version"
additionalInfo=
"@{viewModel.magiskAdditionalInfo}"
currentVersion=
"@{viewModel.magiskCurrentVersion}"
item=
"@{MagiskItem.MAGISK}"
latestVersion=
"@{viewModel.magiskLatestVersion}"
layout=
"@layout/include_update_card"
state=
"@{viewModel.magiskState}"
text=
"@{viewModel.magiskStateText}"
viewModel=
"@{viewModel}"
/>
<include
android:id=
"@+id/home_manager_version"
additionalInfo=
"@{viewModel.managerAdditionalInfo}"
currentVersion=
"@{viewModel.managerCurrentVersion}"
item=
"@{MagiskItem.MANAGER}"
latestVersion=
"@{viewModel.managerLatestVersion}"
layout=
"@layout/include_update_card"
state=
"@{viewModel.managerState}"
text=
"@{viewModel.managerStateText}"
viewModel=
"@{viewModel}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"5dp"
android:layout_marginTop=
"4dp"
android:layout_marginEnd=
"5dp"
android:layout_marginBottom=
"4dp"
android:visibility=
"gone"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
android:layout_marginTop=
"@dimen/margin_generic"
/>
<View
style=
"@style/Widget.Divider.Horizontal"
gone=
"@{!viewModel.hasRoot}"
android:layout_width=
"match_parent"
android:layout_margin=
"@dimen/margin_generic"
/>
<LinearLayout
gone=
"@{!viewModel.hasRoot}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:
orientation=
"vertical
"
android:
paddingTop=
"10dp
"
android:
paddingBottom=
"10dp
"
>
android:
animateLayoutChanges=
"true
"
android:
onClick=
"@{() -> viewModel.advancedPressed()}
"
android:
orientation=
"vertical
"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
android:layout_height=
"wrap_content"
android:background=
"?attr/selectableItemBackground"
android:minHeight=
"48dp"
>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/arrow"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:rotation=
"@{viewModel.isAdvancedExpanded ? 180 : 0}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_arrow"
app:tint=
"?attr/imageColorTint"
/>
<TextView
android:id=
"@+id/title"
...
...
@@ -105,126 +150,109 @@
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<ImageView
android:id=
"@+id/arrow"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:tint=
"?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_arrow"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<
Linear
Layout
<
androidx.constraintlayout.widget.Constraint
Layout
android:id=
"@+id/install_option_expand"
gone=
"@{!viewModel.isAdvancedExpanded}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
>
<CheckBox
android:id=
"@+id/keep_force_enc"
android:layout_width=
"
match_parent
"
android:layout_width=
"
0dp
"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"50dp"
android:layout_marginEnd=
"50dp"
android:text=
"@string/keep_force_encryption"
/>
android:checked=
"@={viewModel.isForceEncryption}"
android:text=
"@string/keep_force_encryption"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintWidth_default=
"wrap"
app:layout_constraintWidth_min=
"300dp"
/>
<CheckBox
android:id=
"@+id/keep_verity"
android:layout_width=
"
match_parent
"
android:layout_width=
"
0dp
"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"50dp"
android:layout_marginEnd=
"50dp"
android:text=
"@string/keep_dm_verity"
/>
android:checked=
"@={viewModel.isKeepVerity}"
android:text=
"@string/keep_dm_verity"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/keep_force_enc"
app:layout_constraintWidth_default=
"wrap"
app:layout_constraintWidth_min=
"300dp"
/>
</
Linear
Layout>
</
androidx.constraintlayout.widget.Constraint
Layout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.Card
View
android:id=
"@+id/safetyNet_card
"
style=
"?attr/cardStyle
"
<
View
style=
"@style/Widget.Divider.Horizontal
"
gone=
"@{!viewModel.hasRoot}
"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_horizontal"
android:layout_marginStart=
"5dp"
android:layout_marginTop=
"4dp"
android:layout_marginEnd=
"5dp"
android:layout_marginBottom=
"4dp"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
android:layout_margin=
"@dimen/margin_generic"
/>
<LinearLayout
gone=
"@{!viewModel.hasRoot}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
android:layout_height=
"wrap_content"
android:paddingBottom=
"@dimen/margin_generic_half"
android:paddingTop=
"@dimen/margin_generic_half"
>
<ImageView
android:id=
"@+id/sn_logo"
android:layout_width=
"30dp"
android:layout_height=
"30dp"
android:layout_marginTop=
"12dp"
android:layout_marginEnd=
"24dp"
android:layout_marginBottom=
"12dp"
android:tint=
"@color/green500"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/safetyNet_status"
app:layout_constraintHorizontal_chainStyle=
"packed"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_safetynet"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/sn_status_start"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_percent=
"0.25"
/>
<TextView
android:id=
"@+id/safetyNet_status"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginLeft=
"@dimen/margin_generic"
android:layout_marginRight=
"@dimen/margin_generic"
android:gravity=
"center"
android:maxLines=
"1"
android:minWidth=
"200dp"
android:text=
"@string/safetyNet_check_text"
android:text=
"@{viewModel.safetyNetTitle}"
android:textStyle=
"bold"
app:autoSizeMaxTextSize=
"14sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/sn_status_end"
app:layout_constraintStart_toStartOf=
"@+id/sn_status_start"
app:layout_constraintTop_toTopOf=
"parent"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/sn_status_end"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_percent=
"0.75"
/>
app:layout_constraintEnd_toStartOf=
"@+id/safetyNet_refresh"
app:layout_constraintStart_toEndOf=
"@+id/sn_logo"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintWidth_min=
"200dp"
/>
<ImageView
android:id=
"@+id/safetyNet_refresh"
invisible=
"@{viewModel.ctsState == SafetyNetState.LOADING || viewModel.basicIntegrityState == SafetyNetState.LOADING}"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:
layout_marginStart=
"24dp
"
android:
onClick=
"@{() -> viewModel.safetyNetPressed()}
"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/safetyNet_status"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_refresh"
/>
<ProgressBar
android:id=
"@+id/safetyNet_check_progress"
style=
"@style/Widget.Progress"
goneUnless=
"@{viewModel.ctsState == SafetyNetState.LOADING || viewModel.basicIntegrityState == SafetyNetState.LOADING}"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
android:visibility=
"gone"
app:layout_constraintBottom_toBottomOf=
"@+id/safetyNet_refresh"
app:layout_constraintEnd_toEndOf=
"@+id/safetyNet_refresh"
app:layout_constraintStart_toStartOf=
"@+id/safetyNet_refresh"
...
...
@@ -234,31 +262,26 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:id=
"@+id/safetyNet_expand"
gone=
"@{viewModel.safetyNetState == State.LOADING}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:paddingBottom=
"10dp"
>
<ImageView
<
androidx.appcompat.widget.AppCompat
ImageView
android:id=
"@+id/cts_status_icon"
srcCompat=
"@{viewModel.ctsState == SafetyNetState.PASS ? R.drawable.ic_check_circle : R.drawable.ic_cancel}"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_marginEnd=
"8dp"
app:layout_constraintBottom_toBottomOf=
"@+id/cts_status"
app:layout_constraintEnd_toStartOf=
"@+id/cts_status"
app:layout_constraintTop_toTopOf=
"@+id/cts_status"
/>
<TextView
android:id=
"@+id/cts_status"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:padding=
"6dp"
android:textStyle=
"bold"
app:layout_constraintEnd_toEndOf=
"@+id/basic_status"
app:layout_constraintStart_toStartOf=
"@+id/basic_status"
app:layout_constraintTop_toTopOf=
"parent"
/>
app:layout_constraintTop_toTopOf=
"@+id/cts_status"
app:tint=
"@{viewModel.ctsState == SafetyNetState.PASS ? @color/colorCorrect : @color/colorError}"
tools:srcCompat=
"@drawable/ic_check_circle"
/>
<ImageView
<
androidx.appcompat.widget.AppCompat
ImageView
android:id=
"@+id/basic_status_icon"
srcCompat=
"@{viewModel.basicIntegrityState == SafetyNetState.PASS ? R.drawable.ic_check_circle : R.drawable.ic_cancel}"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_marginEnd=
"8dp"
...
...
@@ -267,7 +290,21 @@
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintHorizontal_chainStyle=
"packed"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"@+id/basic_status"
/>
app:layout_constraintTop_toTopOf=
"@+id/basic_status"
app:tint=
"@{viewModel.basicIntegrityState == SafetyNetState.PASS ? @color/colorCorrect : @color/colorError}"
tools:srcCompat=
"@drawable/ic_check_circle"
/>
<TextView
android:id=
"@+id/cts_status"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:padding=
"6dp"
android:text=
"@{String.format(`ctsProfile: %b`, viewModel.ctsState == SafetyNetState.PASS)}"
android:textStyle=
"bold"
app:layout_constraintEnd_toEndOf=
"@+id/basic_status"
app:layout_constraintStart_toStartOf=
"@+id/basic_status"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"ctsProfile: true"
/>
<TextView
android:id=
"@+id/basic_status"
...
...
@@ -275,97 +312,29 @@
android:layout_height=
"wrap_content"
android:layout_marginTop=
"4dp"
android:padding=
"6dp"
android:text=
"@{String.format(`basicIntegrity: %b`, viewModel.basicIntegrityState == SafetyNetState.PASS)}"
android:textStyle=
"bold"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/basic_status_icon"
app:layout_constraintTop_toBottomOf=
"@+id/cts_status"
/>
app:layout_constraintTop_toBottomOf=
"@+id/cts_status"
tools:text=
"basicIntegrity: true"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
style=
"?attr/cardStyle"
<View
style=
"@style/Widget.Divider.Horizontal"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_marginStart=
"5dp"
android:layout_marginTop=
"4dp"
android:layout_marginEnd=
"5dp"
android:layout_marginBottom=
"4dp"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
android:layout_margin=
"@dimen/margin_generic"
/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
>
<LinearLayout
android:id=
"@+id/paypal"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:gravity=
"center_horizontal"
android:orientation=
"horizontal"
app:layout_constraintEnd_toStartOf=
"@+id/patreon"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
>
<ImageView
android:layout_width=
"35dp"
android:layout_height=
"35dp"
android:layout_gravity=
"center"
android:layout_margin=
"10dp"
android:tint=
"?attr/imageColorTint"
app:srcCompat=
"@drawable/ic_paypal"
/>
<TextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_margin=
"5dp"
android:text=
"PayPal"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textStyle=
"bold"
/>
</LinearLayout>
<LinearLayout
android:id=
"@+id/patreon"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:gravity=
"center_horizontal"
android:orientation=
"horizontal"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/paypal"
app:layout_constraintTop_toTopOf=
"parent"
>
<ImageView
android:layout_width=
"35dp"
android:layout_height=
"35dp"
android:layout_gravity=
"center"
android:layout_margin=
"10dp"
android:tint=
"?attr/imageColorTint"
app:srcCompat=
"@drawable/ic_patreon"
/>
<TextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_margin=
"5dp"
android:text=
"Patreon"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textStyle=
"bold"
/>
</LinearLayout>
<FrameLayout
android:id=
"@+id/twitter"
android:layout_width=
"0dp"
...
...
@@ -373,6 +342,7 @@
android:clickable=
"true"
android:focusable=
"true"
android:foreground=
"?android:attr/selectableItemBackground"
android:onClick=
"@{() -> viewModel.twitterPressed()}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/github"
app:layout_constraintStart_toStartOf=
"parent"
...
...
@@ -395,6 +365,7 @@
android:clickable=
"true"
android:focusable=
"true"
android:foreground=
"?android:attr/selectableItemBackground"
android:onClick=
"@{() -> viewModel.githubPressed()}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/xda"
app:layout_constraintStart_toEndOf=
"@+id/twitter"
>
...
...
@@ -416,6 +387,7 @@
android:clickable=
"true"
android:focusable=
"true"
android:foreground=
"?android:attr/selectableItemBackground"
android:onClick=
"@{() -> viewModel.xdaPressed()}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/github"
>
...
...
@@ -430,43 +402,90 @@
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id=
"@+id/patreon"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:gravity=
"center_horizontal"
android:onClick=
"@{() -> viewModel.patreonPressed()}"
android:orientation=
"horizontal"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/paypal"
app:layout_constraintTop_toTopOf=
"parent"
>
</androidx.cardview.widget.CardView>
<ImageView
android:layout_width=
"35dp"
android:layout_height=
"35dp"
android:layout_gravity=
"center"
android:layout_margin=
"10dp"
android:tint=
"?attr/imageColorTint"
app:srcCompat=
"@drawable/ic_patreon"
/>
<androidx.cardview.widget.CardView
android:id=
"@+id/uninstall_button"
style=
"?attr/cardStyle"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
<TextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"5dp"
android:layout_marginTop=
"4dp"
android:layout_marginEnd=
"5dp"
android:layout_marginBottom=
"4dp"
android:layout_margin=
"5dp"
android:text=
"Patreon"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textStyle=
"bold"
/>
</LinearLayout>
<LinearLayout
android:id=
"@+id/paypal"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:foreground=
"?android:attr/selectableItemBackground"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
android:gravity=
"center_horizontal"
android:onClick=
"@{() -> viewModel.paypalPressed()}"
android:orientation=
"horizontal"
app:layout_constraintEnd_toStartOf=
"@+id/patreon"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
>
<ImageView
android:layout_width=
"35dp"
android:layout_height=
"35dp"
android:layout_gravity=
"center"
android:layout_margin=
"10dp"
android:tint=
"?attr/imageColorTint"
app:srcCompat=
"@drawable/ic_paypal"
/>
<TextView
android:layout_width=
"match_par
ent"
android:layout_width=
"wrap_cont
ent"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"10dp"
android:layout_marginBottom=
"10dp"
android:ems=
"10"
android:fontFamily=
"sans-serif"
android:gravity=
"center"
android:text=
"@string/uninstall"
android:textAllCaps=
"false"
android:textSize=
"20sp"
android:layout_gravity=
"center"
android:layout_margin=
"5dp"
android:text=
"PayPal"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textStyle=
"bold"
/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
<View
style=
"@style/Widget.Divider.Horizontal"
gone=
"@{!viewModel.hasRoot}"
android:layout_width=
"match_parent"
android:layout_margin=
"@dimen/margin_generic"
/>
<com.google.android.material.button.MaterialButton
style=
"@style/Widget.Button.Text"
gone=
"@{!viewModel.hasRoot}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:onClick=
"@{() -> viewModel.uninstallPressed()}"
android:text=
"@string/uninstall"
/>
</LinearLayout>
</ScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</layout>
app/src/main/res/layout/fragment_magisk_hide.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<
androidx.swiperefreshlayout.widget.SwipeRefreshL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/swipeRefreshLayout"
android:layout_width=
"match_parent"
android:layout_height=
"fill_parent"
android:orientation=
"vertical"
>
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.hide.HideViewModel"
/>
</data>
<
Linear
Layout
<
androidx.swiperefreshlayout.widget.SwipeRefresh
Layout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
app:onRefreshListener=
"@{() -> viewModel.refresh()}"
app:refreshing=
"@{viewModel.loading}"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recyclerView"
dividerColor=
"@{@android:color/transparent}"
dividerSize=
"@{@dimen/margin_generic}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.items}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:clipToPadding=
"false"
android:dividerHeight=
"@dimen/card_divider_space"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
/>
android:orientation=
"vertical"
android:padding=
"@dimen/margin_generic"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_hide_app"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearL
ayout>
</l
ayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_modules.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<
androidx.swiperefreshlayout.widget.SwipeRefreshL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:id=
"@+id/swipeRefreshLayout"
android:layout_width=
"match_parent"
android:layout_height=
"fill_parent"
android:orientation=
"vertical"
>
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id=
"@+id/coordinator"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
>
android:layout_height=
"match_parent"
>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id=
"@+id/modules_refresh_layout"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
app:layout_behavior=
"@string/appbar_scrolling_view_behavior"
app:onRefreshListener=
"@{() -> viewModel.refresh()}"
app:refreshing=
"@{viewModel.loading}"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recyclerView"
android:id=
"@+id/modules_content"
dividerColor=
"@{@android:color/transparent}"
dividerSize=
"@{@dimen/margin_generic}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.itemsInstalled}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:clipToPadding=
"false"
android:dividerHeight=
"@dimen/card_divider_space"
android:orientation=
"vertical"
android:padding=
"@dimen/margin_generic"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior=
"@string/appbar_scrolling_view_behavior"
/>
tools:listitem=
"@layout/item_module"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<TextView
android:id=
"@+id/
empty_rv
"
<
androidx.appcompat.widget.AppCompat
TextView
android:id=
"@+id/
modules_status_text
"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"center"
...
...
@@ -33,19 +50,19 @@
android:text=
"@string/no_modules_found"
android:textSize=
"20sp"
android:textStyle=
"italic"
android:visibility=
"gone"
/>
android:visibility=
"gone"
tools:visibility=
"visible"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=
"@+id/fab"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"bottom|center_horizontal"
android:layout_margin=
"@dimen/fab_padding"
android:
elevation=
"6dp
"
app:
srcCompat=
"@drawable/ic_add
"
tools:fabSize=
"normal
"
tools:pressedTranslationZ=
"12dp
"
/>
android:
onClick=
"@{() -> viewModel.fabPressed()}
"
app:
fabSize=
"normal
"
app:layout_behavior=
"com.google.android.material.floatingactionbutton.FloatingActionButton$Behavior
"
app:srcCompat=
"@drawable/ic_add
"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
</layout>
\ No newline at end of file
app/src/main/res/layout/fragment_repos.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<
androidx.swiperefreshlayout.widget.SwipeRefreshL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/swipeRefreshLayout"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"fill
_parent"
android:layout_height=
"match
_parent"
android:orientation=
"vertical"
>
<LinearLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id=
"@+id/repos_refresh_layout"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
android:orientation=
"vertical"
app:onRefreshListener=
"@{() -> viewModel.refresh()}"
app:refreshing=
"@{viewModel.loading}"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/repos_content"
dividerColor=
"@{@android:color/transparent}"
dividerSize=
"@{@dimen/margin_generic}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.itemsRemote}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:clipToPadding=
"false"
android:orientation=
"vertical"
android:padding=
"@dimen/margin_generic"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_repo"
/>
<TextView
android:id=
"@+id/empty_rv"
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/repos_status_text"
gone=
"@{!(viewModel.loaded && viewModel.itemsRemote.size == 0)}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"center"
...
...
@@ -21,14 +51,6 @@
android:text=
"@string/no_modules_found"
android:textSize=
"20sp"
android:textStyle=
"italic"
/>
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recyclerView"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:dividerHeight=
"@dimen/card_divider_space"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
/>
</LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
\ No newline at end of file
</layout>
\ No newline at end of file
app/src/main/res/layout/fragment_superuser.xml
View file @
2813d203
<
LinearL
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
l
ayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.superuser.SuperuserViewModel"
/>
</data>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/superuser_content"
dividerColor=
"@{@android:color/transparent}"
dividerSize=
"@{@dimen/margin_generic}"
gone=
"@{!viewModel.loaded}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{viewModel.items}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
android:clipToPadding=
"false"
android:orientation=
"vertical"
android:padding=
"@dimen/margin_generic"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_policy"
/>
<TextView
android:id=
"@+id/empty_rv"
<ProgressBar
style=
"@style/Widget.Progress"
gone=
"@{!viewModel.loading}"
android:layout_gravity=
"center"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/superuser_empty"
gone=
"@{!viewModel.loaded || viewModel.items.size > 0}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"center"
...
...
@@ -13,14 +44,10 @@
android:gravity=
"center"
android:text=
"@string/no_apps_found"
android:textSize=
"20sp"
android:textStyle=
"italic"
android:visibility=
"gone"
/>
android:textStyle=
"italic"
/>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recyclerView"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:dividerHeight=
"@dimen/card_divider_space"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
/>
</FrameLayout>
</layout>
</LinearLayout>
app/src/main/res/layout/include_update_card.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<import
type=
"com.topjohnwu.magisk.ui.home.MagiskState"
/>
<import
type=
"com.topjohnwu.magisk.R"
/>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.ui.home.MagiskItem"
/>
<variable
name=
"state"
type=
"com.topjohnwu.magisk.ui.home.MagiskState"
/>
<variable
name=
"text"
type=
"String"
/>
<variable
name=
"currentVersion"
type=
"String"
/>
<variable
name=
"latestVersion"
type=
"String"
/>
<variable
name=
"additionalInfo"
type=
"String"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.home.HomeViewModel"
/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:animateLayoutChanges=
"true"
android:background=
"?android:attr/selectableItemBackground"
android:onClick=
"@{() -> viewModel.cardPressed(item)}"
>
<com.google.android.material.button.MaterialButton
android:id=
"@+id/install"
style=
"@style/Widget.Button.Text"
gone=
"@{state == MagiskState.LOADING}"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"8dp"
android:onClick=
"@{() -> viewModel.installPressed(item)}"
android:text=
"@{state != MagiskState.OBSOLETE ? @string/install : @string/update}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"@string/install"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/status_icon"
invisible=
"@{state == MagiskState.LOADING}"
srcCompat=
"@{state == MagiskState.UP_TO_DATE ? R.drawable.ic_check_circle : (state == MagiskState.OBSOLETE ? R.drawable.ic_update : R.drawable.ic_help)}"
android:layout_width=
"25dp"
android:layout_height=
"25dp"
android:layout_marginStart=
"16dp"
android:layout_marginEnd=
"16dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/status"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:tint=
"@{state == MagiskState.UP_TO_DATE ? @color/colorCorrect : (state == MagiskState.OBSOLETE ? @color/colorUpdate : @color/colorError)}"
/>
<ProgressBar
android:id=
"@+id/progress"
style=
"@style/Widget.Progress"
gone=
"@{state != MagiskState.LOADING}"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
app:layout_constraintBottom_toBottomOf=
"@+id/status_icon"
app:layout_constraintEnd_toEndOf=
"@+id/status_icon"
app:layout_constraintStart_toStartOf=
"@+id/status_icon"
app:layout_constraintTop_toTopOf=
"@+id/status_icon"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/status"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"8dp"
android:maxLines=
"1"
android:paddingTop=
"3dp"
android:paddingBottom=
"3dp"
android:text=
"@{text}"
android:textAppearance=
"?android:attr/textAppearanceMedium"
app:autoSizeMinTextSize=
"1sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintBottom_toTopOf=
"@+id/current_version"
app:layout_constraintEnd_toStartOf=
"@+id/install"
app:layout_constraintStart_toEndOf=
"@+id/status_icon"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_chainStyle=
"packed"
tools:text=
"Magisk is up to date"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/current_version"
gone=
"@{state == MagiskState.LOADING || currentVersion.length == 0}"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:maxLines=
"1"
android:text=
"@{state != MagiskState.LOADING ? currentVersion : @string/checking_for_updates}"
app:autoSizeMinTextSize=
"1sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintBottom_toTopOf=
"@+id/latest_version"
app:layout_constraintEnd_toEndOf=
"@+id/status"
app:layout_constraintStart_toStartOf=
"@+id/status"
app:layout_constraintTop_toBottomOf=
"@+id/status"
tools:text=
"@string/checking_for_updates"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/latest_version"
gone=
"@{currentVersion == latestVersion}"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:maxLines=
"1"
android:text=
"@{state != MagiskState.LOADING ? latestVersion : @string/checking_for_updates}"
app:autoSizeMinTextSize=
"1sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintBottom_toTopOf=
"@+id/additional"
app:layout_constraintEnd_toEndOf=
"@+id/status"
app:layout_constraintStart_toStartOf=
"@+id/status"
app:layout_constraintTop_toBottomOf=
"@+id/current_version"
tools:text=
"@string/checking_for_updates"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/additional"
gone=
"@{additionalInfo.length == 0}"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:maxLines=
"1"
android:text=
"@{additionalInfo}"
app:autoSizeMinTextSize=
"1sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"@+id/status"
app:layout_constraintStart_toStartOf=
"@+id/status"
app:layout_constraintTop_toBottomOf=
"@+id/latest_version"
tools:text=
"@string/checking_for_updates"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
app/src/main/res/layout/item_console.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.ConsoleRvItem"
/>
<!--no actions are required-->
<variable
name=
"viewModel"
type=
"Object"
/>
</data>
<TextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:fontFamily=
"monospace"
android:text=
"@{item.item}"
android:textSize=
"10sp"
tools:text=
"@tools:sample/lorem/random"
/>
</layout>
app/src/main/res/layout/item_hide_app.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.HideRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.hide.HideViewModel"
/>
</data>
<com.google.android.material.card.MaterialCardView
style=
"@style/Widget.Card"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:foreground=
"?attr/selectableItemBackground"
android:minHeight=
"?android:attr/listPreferredItemHeight"
android:onClick=
"@{() -> item.toggleExpansion()}"
app:cardElevation=
"@dimen/card_elevation"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:padding=
"@dimen/margin_generic_half"
>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/hide_app_icon"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:gravity=
"end"
android:src=
"@{item.item.icon}"
app:layout_constraintBottom_toTopOf=
"@+id/hide_app_processes"
app:layout_constraintEnd_toStartOf=
"@+id/hide_app_name"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:src=
"@drawable/ic_magisk"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/hide_app_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"@dimen/margin_generic"
android:layout_marginEnd=
"@dimen/margin_generic"
android:ellipsize=
"end"
android:maxLines=
"1"
android:text=
"@{item.item.name}"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/hide_app_package"
app:layout_constraintEnd_toStartOf=
"@+id/hide_app_checkbox"
app:layout_constraintStart_toEndOf=
"@+id/hide_app_icon"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_chainStyle=
"packed"
tools:text=
"Magisk"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/hide_app_package"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:maxLines=
"1"
android:text=
"@{item.packageName}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/hide_app_processes"
app:layout_constraintEnd_toStartOf=
"@id/hide_app_arrow"
app:layout_constraintStart_toStartOf=
"@+id/hide_app_name"
app:layout_constraintTop_toBottomOf=
"@+id/hide_app_name"
tools:text=
"com.topjohnwu.magisk"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/hide_app_arrow"
gone=
"@{item.items.size == 1}"
android:layout_width=
"24dp"
android:layout_height=
"24dp"
android:layout_marginStart=
"5dp"
android:layout_marginEnd=
"5dp"
android:background=
"@android:color/transparent"
android:rotation=
"@{item.isExpanded() ? 180 : 0}"
app:layout_constraintBottom_toBottomOf=
"@+id/hide_app_package"
app:layout_constraintEnd_toEndOf=
"@+id/hide_app_name"
app:layout_constraintStart_toEndOf=
"@+id/hide_app_package"
app:layout_constraintTop_toTopOf=
"@+id/hide_app_package"
app:srcCompat=
"@drawable/ic_arrow"
app:tint=
"?attr/imageColorTint"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/hide_app_checkbox"
style=
"@style/Widget.Icon"
isChecked=
"@{item.isHiddenState}"
android:onClick=
"@{() -> item.toggle()}"
app:layout_constraintBottom_toTopOf=
"@+id/hide_app_processes"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/hide_app_name"
app:layout_constraintTop_toTopOf=
"parent"
app:tint=
"?attr/imageColorTint"
tools:src=
"@drawable/ic_checked"
/>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/hide_app_processes"
gone=
"@{!item.isExpanded}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{item.items}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
android:paddingTop=
"@dimen/margin_generic_half"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf=
"parent"
tools:listitem=
"@layout/item_hide_process"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
app/src/main/res/layout/
list_
item_hide_process.xml
→
app/src/main/res/layout/item_hide_process.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/cardView"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"end"
android:layout_marginStart=
"@dimen/card_horizontal_margin"
android:layout_marginEnd=
"@dimen/card_horizontal_margin"
app:cardCornerRadius=
"0dp"
app:cardElevation=
"@dimen/card_elevation"
>
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.hide.HideViewModel"
/>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:id=
"@+id/info_layout"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:paddingTop=
"2dp"
android:paddingBottom=
"2dp"
>
android:layout_height=
"wrap_content"
android:onClick=
"@{() -> item.toggle()}"
>
<TextView
android:id=
"@+id/process"
android:layout_width=
"wrap_content"
<androidx.appcompat.widget.AppCompatTextView
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:ellipsize=
"end"
android:maxLines=
"1"
android:layout_marginEnd=
"@dimen/margin_generic"
android:ellipsize=
"marquee"
android:singleLine=
"true"
android:text=
"@{item.process}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/hide_process_icon"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_bias=
"0.506
"
/>
tools:text=
"com.topjohnwu.magisk.process
"
/>
<CheckBox
android:id=
"@+id/checkbox"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"5dp"
android:layout_marginEnd=
"5dp"
android:checked=
"false"
android:focusable=
"false"
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/hide_process_icon"
style=
"@style/Widget.Icon"
isChecked=
"@{item.isHidden}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
app:layout_constraintTop_toTopOf=
"parent"
app:tint=
"?attr/imageColorTint"
tools:src=
"@drawable/ic_checked"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</layout>
app/src/main/res/layout/item_module.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<import
type=
"com.topjohnwu.magisk.R"
/>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<com.google.android.material.card.MaterialCardView
style=
"@style/Widget.Card"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:foreground=
"?attr/selectableItemBackground"
android:minHeight=
"?android:attr/listPreferredItemHeight"
app:cardElevation=
"2dp"
tools:layout_gravity=
"center"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:padding=
"@dimen/margin_generic"
>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/title"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.item.name}"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/version_name"
app:layout_constraintEnd_toStartOf=
"@+id/checkbox"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"Magisk Module"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/version_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.item.version == null || item.item.version.length == 0 ? @string/no_info_provided : item.item.version}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/author"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/title"
tools:text=
"v1"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/author"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.item.author == null || item.item.author.length == 0 ? @string/no_info_provided : @string/author(item.item.author)}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/description"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/version_name"
tools:text=
"topjohnwu"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/description"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:text=
"@{item.item.description == null || item.item.description.length == 0 ? @string/no_info_provided : item.item.description}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/notice"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/author"
tools:lines=
"4"
tools:text=
"@tools:sample/lorem/random"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/notice"
gone=
"@{item.lastActionNotice.length == 0}"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:text=
"@{item.lastActionNotice}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@color/red500"
android:visibility=
"visible"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/description"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/checkbox"
style=
"@style/Widget.Icon"
isChecked=
"@{item.isChecked}"
android:layout_marginEnd=
"@dimen/margin_generic"
android:onClick=
"@{() -> item.toggle()}"
app:layout_constraintBottom_toTopOf=
"@+id/description"
app:layout_constraintEnd_toStartOf=
"@+id/delete"
app:layout_constraintStart_toEndOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"parent"
app:tint=
"?attr/imageColorTint"
tools:src=
"@drawable/ic_checked"
/>
<View
style=
"@style/Widget.Divider.Vertical"
android:backgroundTint=
"?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf=
"@+id/checkbox"
app:layout_constraintEnd_toStartOf=
"@+id/delete"
app:layout_constraintStart_toEndOf=
"@+id/checkbox"
app:layout_constraintTop_toTopOf=
"@+id/checkbox"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/delete"
style=
"@style/Widget.Icon"
srcCompat=
"@{item.isDeletable ? R.drawable.ic_undelete : R.drawable.ic_delete}"
android:onClick=
"@{() -> item.toggleDelete()}"
app:layout_constraintBottom_toBottomOf=
"@+id/checkbox"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/checkbox"
app:layout_constraintTop_toTopOf=
"@+id/checkbox"
app:tint=
"?attr/imageColorTint"
tools:ignore=
"ContentDescription"
tools:src=
"@drawable/ic_delete"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
app/src/main/res/layout/item_page_log.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.LogRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.log.LogViewModel"
/>
</data>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<androidx.recyclerview.widget.RecyclerView
dividerColor=
"@{@android:color/transparent}"
dividerSize=
"@{@dimen/margin_generic}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{item.items}"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:clipToPadding=
"false"
android:orientation=
"vertical"
android:padding=
"@dimen/margin_generic"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_superuser_log"
/>
</FrameLayout>
</layout>
\ No newline at end of file
app/src/main/res/layout/item_page_magisk_log.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.MagiskLogRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.log.LogViewModel"
/>
</data>
<FrameLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<HorizontalScrollView
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<androidx.recyclerview.widget.RecyclerView
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{item.items}"
android:layout_width=
"wrap_content"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem=
"@layout/item_console"
/>
</HorizontalScrollView>
</FrameLayout>
</layout>
\ No newline at end of file
app/src/main/res/layout/item_policy.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.superuser.SuperuserViewModel"
/>
</data>
<com.google.android.material.card.MaterialCardView
style=
"@style/Widget.Card"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:foreground=
"?attr/selectableItemBackground"
android:minHeight=
"?android:attr/listPreferredItemHeight"
android:onClick=
"@{() -> item.toggle()}"
app:cardCornerRadius=
"@dimen/radius_generic"
app:cardElevation=
"2dp"
app:cardPreventCornerOverlap=
"true"
>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:animateLayoutChanges=
"true"
android:orientation=
"vertical"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:padding=
"@dimen/margin_generic_half"
>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/app_icon"
android:layout_width=
"50dp"
android:layout_height=
"50dp"
android:gravity=
"end"
android:src=
"@{item.icon}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/app_name"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:srcCompat=
"@drawable/ic_logo"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/app_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginLeft=
"@dimen/margin_generic"
android:layout_marginRight=
"@dimen/margin_generic"
android:ellipsize=
"end"
android:maxLines=
"1"
android:text=
"@{item.item.appName}"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/package_name"
app:layout_constraintEnd_toStartOf=
"@+id/master_switch"
app:layout_constraintStart_toEndOf=
"@+id/app_icon"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintVertical_chainStyle=
"packed"
tools:text=
"@string/app_name"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/package_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:maxLines=
"1"
android:text=
"@{item.item.packageName}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"@id/app_name"
app:layout_constraintStart_toStartOf=
"@id/app_name"
app:layout_constraintTop_toBottomOf=
"@id/app_name"
tools:text=
"com.topjohnwu.magisk"
/>
<androidx.appcompat.widget.SwitchCompat
android:id=
"@+id/master_switch"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:checked=
"@={item.isEnabled}"
android:gravity=
"center_vertical"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/app_name"
app:layout_constraintTop_toTopOf=
"parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id=
"@+id/expand_layout"
gone=
"@{!item.isExpanded}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:paddingBottom=
"@dimen/margin_generic_half"
android:paddingTop=
"@dimen/margin_generic_half"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/package_name"
>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/bell"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"16dp"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/notification_switch"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_notifications"
/>
<androidx.appcompat.widget.SwitchCompat
android:id=
"@+id/notification_switch"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:checked=
"@={item.shouldNotify}"
android:gravity=
"center_vertical"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/guideline"
app:layout_constraintStart_toEndOf=
"@+id/bell"
app:layout_constraintTop_toTopOf=
"parent"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/guideline"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_percent=
"0.4"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/bug"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/logging_switch"
app:layout_constraintStart_toStartOf=
"@+id/guideline"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_bug_report"
/>
<androidx.appcompat.widget.SwitchCompat
android:id=
"@+id/logging_switch"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginEnd=
"16dp"
android:checked=
"@={item.shouldLog}"
android:gravity=
"center_vertical"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/guideline2"
app:layout_constraintStart_toEndOf=
"@+id/bug"
app:layout_constraintTop_toTopOf=
"parent"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/guideline2"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_percent=
"0.8"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/more_info"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_weight=
"1"
android:background=
"?android:attr/selectableItemBackground"
android:tint=
"@color/icon_grey"
android:visibility=
"gone"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/delete"
app:layout_constraintStart_toStartOf=
"@id/guideline2"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_more"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/delete"
style=
"@style/Widget.Icon"
android:layout_gravity=
"center"
android:layout_weight=
"1"
android:onClick=
"@{() -> viewModel.deletePressed(item)}"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/more_info"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_delete"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
\ No newline at end of file
app/src/main/res/layout/item_repo.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.RepoRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.module.ModuleViewModel"
/>
</data>
<com.google.android.material.card.MaterialCardView
style=
"@style/Widget.Card"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:foreground=
"?attr/selectableItemBackground"
android:minHeight=
"?android:attr/listPreferredItemHeight"
android:onClick=
"@{() -> viewModel.repoPressed(item)}"
app:cardElevation=
"2dp"
tools:layout_gravity=
"center"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:padding=
"@dimen/margin_generic"
>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/title"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.item.name}"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/version_name"
app:layout_constraintEnd_toStartOf=
"@+id/download"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"Magisk Module"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/version_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.item.version == null || item.item.version.length == 0 ? @string/no_info_provided : item.item.version}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/author"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/title"
tools:text=
"v1"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/author"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:text=
"@{item.item.author == null || item.item.author.length == 0 ? @string/no_info_provided : @string/author(item.item.author)}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/description"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/version_name"
tools:text=
"topjohnwu"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/description"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:text=
"@{item.item.description == null || item.item.description.length == 0 ? @string/no_info_provided : item.item.description}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/update_time"
app:layout_constraintEnd_toEndOf=
"@id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/author"
tools:lines=
"4"
tools:text=
"@tools:sample/lorem/random"
/>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@+id/update_time"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:text=
"@{@string/updated_on(item.item.lastUpdateString)}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/description"
tools:text=
"@tools:sample/date/ddmmyy"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/download"
style=
"@style/Widget.Icon"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"8dp"
android:onClick=
"@{() -> viewModel.downloadPressed(item)}"
android:tint=
"?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_file_download_black"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
app/src/main/res/layout/item_section.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.SectionRvItem"
/>
<variable
name=
"viewModel"
type=
"Object"
/>
</data>
<!--Paddings are trimmed from default section title because recycler adds these paddings for us already-->
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.SectionTitle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:paddingStart=
"0dp"
android:paddingLeft=
"0dp"
android:paddingTop=
"@dimen/margin_generic"
android:paddingEnd=
"0dp"
android:paddingRight=
"0dp"
android:paddingBottom=
"0dp"
android:text=
"@{item.text}"
android:textAppearance=
"@style/TextAppearance.Emphasize"
tools:text=
"@string/update_available"
/>
</layout>
\ No newline at end of file
app/src/main/res/layout/item_spinner.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.SpinnerRvItem"
/>
</data>
<androidx.appcompat.widget.AppCompatTextView
android:id=
"@android:id/text1"
style=
"?android:attr/spinnerItemStyle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:ellipsize=
"marquee"
android:gravity=
"center_vertical"
android:minHeight=
"?attr/listPreferredItemHeightSmall"
android:singleLine=
"true"
android:text=
"@{item.item}"
android:textAlignment=
"inherit"
tools:text=
"Forever"
/>
</layout>
\ No newline at end of file
app/src/main/res/layout/item_superuser_log.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.LogItemRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.log.LogViewModel"
/>
</data>
<com.google.android.material.card.MaterialCardView
style=
"@style/Widget.Card"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
app:cardElevation=
"2dp"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:animateLayoutChanges=
"true"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:id=
"@+id/log_header"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"?attr/selectableItemBackground"
android:onClick=
"@{() -> item.toggle()}"
android:orientation=
"horizontal"
android:paddingLeft=
"@dimen/margin_generic"
android:paddingTop=
"@dimen/margin_generic_half"
android:paddingRight=
"@dimen/margin_generic"
android:paddingBottom=
"@dimen/margin_generic_half"
app:layout_constraintTop_toTopOf=
"parent"
>
<androidx.appcompat.widget.AppCompatTextView
style=
"@style/Widget.Text.Emphasize"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
android:gravity=
"center_vertical"
android:text=
"@{item.date}"
android:textColor=
"?android:attr/textColorPrimary"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/log_header_indicator"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"@tools:sample/date/ddmmyy"
/>
<androidx.appcompat.widget.AppCompatImageView
android:id=
"@+id/log_header_indicator"
style=
"@style/Widget.Icon"
android:background=
"@android:color/transparent"
android:rotation=
"@{item.isExpanded() ? 180 : 0}"
android:tint=
"?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_arrow"
tools:rotation=
"180"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView
gone=
"@{!item.isExpanded}"
itemBinding=
"@{viewModel.itemBinding}"
items=
"@{item.items}"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layoutManager=
"androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintTop_toBottomOf=
"@+id/log_header"
tools:itemCount=
"3"
tools:listitem=
"@layout/item_superuser_log_entry"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
\ No newline at end of file
app/src/main/res/layout/
list_item_sulog
.xml
→
app/src/main/res/layout/
item_superuser_log_entry
.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:card_view=
"http://schemas.android.com/apk/res-auto"
style=
"?attr/cardStyle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"@dimen/card_horizontal_margin"
android:layout_marginTop=
"2dp"
android:layout_marginEnd=
"@dimen/card_horizontal_margin"
android:layout_marginBottom=
"2dp"
card_view:cardCornerRadius=
"@dimen/card_corner_radius"
card_view:cardElevation=
"@dimen/card_elevation"
>
<layout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
>
<data>
<variable
name=
"item"
type=
"com.topjohnwu.magisk.model.entity.recycler.LogItemEntryRvItem"
/>
<variable
name=
"viewModel"
type=
"com.topjohnwu.magisk.ui.log.LogViewModel"
/>
</data>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:animateLayoutChanges=
"true"
android:background=
"?attr/selectableItemBackground"
android:onClick=
"@{() -> item.toggle()}"
android:orientation=
"vertical"
>
<LinearLayout
android:id=
"@+id/info_layout"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"horizontal"
android:padding=
"10dp"
>
android:paddingLeft=
"@dimen/margin_generic"
android:paddingTop=
"@dimen/margin_generic_half"
android:paddingRight=
"@dimen/margin_generic"
android:paddingBottom=
"@dimen/margin_generic_half"
>
<TextView
android:id=
"@+id/app_name"
<androidx.appcompat.widget.AppCompatTextView
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_weight=
"2"
android:ellipsize=
"end"
android:maxLines=
"1"
android:text=
"@{item.item.appName}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"?android:attr/textColorPrimary"
android:textIsSelectable=
"false"
/>
android:textIsSelectable=
"false"
tools:text=
"@tools:sample/lorem"
/>
<TextView
android:id=
"@+id/action"
<androidx.appcompat.widget.AppCompatTextView
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_weight=
"1"
android:gravity=
"center_horizontal"
android:text=
"@{item.item.action ? @string/grant : @string/deny}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"?android:attr/textColorSecondary"
/>
android:textColor=
"?android:attr/textColorSecondary"
tools:text=
"@string/grant"
/>
<TextView
<
androidx.appcompat.widget.AppCompat
TextView
android:id=
"@+id/time"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_weight=
"1"
android:gravity=
"center_horizontal"
android:text=
"@{item.item.timeString}"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"?android:attr/textColorSecondary"
/>
android:textColor=
"?android:attr/textColorSecondary"
tools:text=
"@tools:sample/date/hhmm"
/>
</LinearLayout>
<LinearLayout
android:id=
"@+id/expand_layout
"
gone=
"@{!item.isExpanded}
"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_horizontal"
android:gravity=
"center"
android:orientation=
"horizontal"
android:paddingStart=
"
10dp
"
android:paddingTop=
"
5dp
"
android:paddingEnd=
"
10dp
"
android:paddingBottom=
"
5dp
"
>
android:paddingStart=
"
@dimen/margin_generic
"
android:paddingTop=
"
@dimen/margin_generic_half
"
android:paddingEnd=
"
@dimen/margin_generic
"
android:paddingBottom=
"
@dimen/margin_generic_half
"
>
<TextView
android:id=
"@+id/pid"
<androidx.appcompat.widget.AppCompatTextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_weight=
"1"
android:text=
"@
string/pid
"
android:text=
"@
{@string/pid(item.item.fromPid)}
"
android:textColor=
"?android:attr/textColorSecondary"
android:textSize=
"12sp"
/>
android:textSize=
"12sp"
tools:text=
"@string/pid"
/>
<TextView
android:id=
"@+id/uid"
<androidx.appcompat.widget.AppCompatTextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_weight=
"1"
android:text=
"@
string/target_uid
"
android:text=
"@
{@string/target_uid(item.item.toUid)}
"
android:textColor=
"?android:attr/textColorSecondary"
android:textSize=
"12sp"
/>
android:textSize=
"12sp"
tools:text=
"@string/target_uid"
/>
<TextView
android:id=
"@+id/cmd"
<androidx.appcompat.widget.AppCompatTextView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_weight=
"2"
android:text=
"@
string/command
"
android:text=
"@
{@string/command(item.item.command)}
"
android:textColor=
"?android:attr/textColorSecondary"
android:textSize=
"12sp"
/>
android:textSize=
"12sp"
tools:text=
"@string/command"
/>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</layout>
\ No newline at end of file
app/src/main/res/layout/list_item_console.xml
deleted
100644 → 0
View file @
4040a024
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:id=
"@+id/txt"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:fontFamily=
"monospace"
android:textSize=
"10sp"
/>
app/src/main/res/layout/list_item_hide_app.xml
deleted
100644 → 0
View file @
4040a024
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
style=
"?attr/cardStyle"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"@dimen/card_horizontal_margin"
android:layout_marginTop=
"@dimen/card_vertical_margin"
android:layout_marginEnd=
"@dimen/card_horizontal_margin"
android:layout_marginBottom=
"@dimen/card_vertical_margin"
android:minHeight=
"?android:attr/listPreferredItemHeight"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:id=
"@+id/info_layout"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<ImageView
android:id=
"@+id/app_icon"
android:layout_width=
"50dp"
android:layout_height=
"50dp"
android:layout_marginStart=
"8dp"
android:layout_marginTop=
"8dp"
android:layout_marginEnd=
"8dp"
android:layout_marginBottom=
"8dp"
android:gravity=
"end"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/app_name"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/app_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/package_name"
app:layout_constraintEnd_toStartOf=
"@+id/checkbox"
app:layout_constraintStart_toEndOf=
"@+id/app_icon"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/package_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@id/arrow"
app:layout_constraintStart_toStartOf=
"@+id/app_name"
app:layout_constraintTop_toBottomOf=
"@+id/app_name"
/>
<ImageView
android:id=
"@+id/arrow"
android:layout_width=
"20dp"
android:layout_height=
"20dp"
android:layout_marginStart=
"5dp"
android:layout_marginEnd=
"5dp"
android:tint=
"?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf=
"@+id/package_name"
app:layout_constraintEnd_toEndOf=
"@+id/app_name"
app:layout_constraintStart_toEndOf=
"@+id/package_name"
app:layout_constraintTop_toTopOf=
"@+id/package_name"
app:srcCompat=
"@drawable/ic_arrow"
/>
<View
android:id=
"@+id/trigger"
android:layout_width=
"35dp"
android:layout_height=
"0dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"@+id/arrow"
app:layout_constraintStart_toStartOf=
"@+id/arrow"
app:layout_constraintTop_toTopOf=
"parent"
/>
<com.buildware.widget.indeterm.IndeterminateCheckBox
android:id=
"@+id/checkbox"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"5dp"
android:layout_marginEnd=
"5dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/app_name"
app:layout_constraintTop_toTopOf=
"parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
app/src/main/res/layout/list_item_module.xml
deleted
100644 → 0
View file @
4040a024
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
style=
"?attr/cardStyle"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"@dimen/card_horizontal_margin"
android:layout_marginTop=
"@dimen/card_vertical_margin"
android:layout_marginEnd=
"@dimen/card_horizontal_margin"
android:layout_marginBottom=
"@dimen/card_vertical_margin"
android:minHeight=
"?android:attr/listPreferredItemHeight"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:paddingStart=
"10dp"
android:paddingTop=
"5dp"
android:paddingEnd=
"10dp"
android:paddingBottom=
"8dp"
>
<TextView
android:id=
"@+id/title"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/version_name"
app:layout_constraintEnd_toStartOf=
"@+id/checkbox"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/version_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/author"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/title"
/>
<TextView
android:id=
"@+id/author"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/description"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/version_name"
/>
<TextView
android:id=
"@+id/description"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/notice"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/author"
/>
<TextView
android:id=
"@+id/notice"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:text=
"@string/remove_file_created"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@color/red500"
android:visibility=
"visible"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/description"
/>
<CheckBox
android:id=
"@+id/checkbox"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"5dp"
android:layout_marginEnd=
"5dp"
android:focusable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/description"
app:layout_constraintEnd_toStartOf=
"@+id/delete"
app:layout_constraintStart_toEndOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"parent"
tools:ignore=
"ContentDescription"
/>
<ImageView
android:id=
"@+id/delete"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:focusable=
"false"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"@+id/checkbox"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/checkbox"
app:layout_constraintTop_toTopOf=
"@+id/checkbox"
app:srcCompat=
"@drawable/ic_delete"
tools:ignore=
"ContentDescription"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
\ No newline at end of file
app/src/main/res/layout/list_item_policy.xml
deleted
100644 → 0
View file @
4040a024
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
style=
"?attr/cardStyle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"@dimen/card_horizontal_margin"
android:layout_marginTop=
"@dimen/card_vertical_margin"
android:layout_marginEnd=
"@dimen/card_horizontal_margin"
android:layout_marginBottom=
"@dimen/card_vertical_margin"
android:minHeight=
"?android:attr/listPreferredItemHeight"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
<LinearLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<ImageView
android:id=
"@+id/app_icon"
android:layout_width=
"50dp"
android:layout_height=
"50dp"
android:layout_marginStart=
"8dp"
android:layout_marginTop=
"8dp"
android:layout_marginEnd=
"8dp"
android:layout_marginBottom=
"8dp"
android:gravity=
"end"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/app_name"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/app_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/package_name"
app:layout_constraintEnd_toStartOf=
"@+id/master_switch"
app:layout_constraintStart_toEndOf=
"@+id/app_icon"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/package_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@id/arrow"
app:layout_constraintStart_toStartOf=
"@id/app_name"
app:layout_constraintTop_toBottomOf=
"@id/app_name"
/>
<ImageView
android:id=
"@+id/arrow"
android:layout_width=
"20dp"
android:layout_height=
"20dp"
android:layout_marginStart=
"5dp"
android:tint=
"?attr/imageColorTint"
app:layout_constraintBottom_toBottomOf=
"@+id/package_name"
app:layout_constraintEnd_toEndOf=
"@+id/app_name"
app:layout_constraintStart_toEndOf=
"@+id/package_name"
app:layout_constraintTop_toTopOf=
"@+id/package_name"
app:srcCompat=
"@drawable/ic_arrow"
/>
<View
android:id=
"@+id/trigger"
android:layout_width=
"35dp"
android:layout_height=
"0dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"@+id/arrow"
app:layout_constraintStart_toStartOf=
"@+id/arrow"
app:layout_constraintTop_toTopOf=
"parent"
/>
<androidx.appcompat.widget.SwitchCompat
android:id=
"@+id/master_switch"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"8dp"
android:layout_marginEnd=
"8dp"
android:checked=
"false"
android:gravity=
"center_vertical"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/app_name"
app:layout_constraintTop_toTopOf=
"parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id=
"@+id/expand_layout"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:paddingTop=
"5dp"
android:paddingBottom=
"5dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/package_name"
>
<ImageView
android:id=
"@+id/bell"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"16dp"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/notification_switch"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_notifications"
/>
<androidx.appcompat.widget.SwitchCompat
android:id=
"@+id/notification_switch"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:checked=
"false"
android:gravity=
"center_vertical"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/guideline"
app:layout_constraintStart_toEndOf=
"@+id/bell"
app:layout_constraintTop_toTopOf=
"parent"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/guideline"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_percent=
"0.4"
/>
<ImageView
android:id=
"@+id/bug"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/logging_switch"
app:layout_constraintStart_toStartOf=
"@+id/guideline"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_bug_report"
/>
<androidx.appcompat.widget.SwitchCompat
android:id=
"@+id/logging_switch"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginEnd=
"16dp"
android:checked=
"false"
android:gravity=
"center_vertical"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/guideline2"
app:layout_constraintStart_toEndOf=
"@+id/bug"
app:layout_constraintTop_toTopOf=
"parent"
/>
<androidx.constraintlayout.widget.Guideline
android:id=
"@+id/guideline2"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
app:layout_constraintGuide_percent=
"0.8"
/>
<ImageView
android:id=
"@+id/more_info"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_weight=
"1"
android:background=
"?android:attr/selectableItemBackground"
android:tint=
"@color/icon_grey"
android:visibility=
"gone"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/delete"
app:layout_constraintStart_toStartOf=
"@id/guideline2"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_more"
/>
<ImageView
android:id=
"@+id/delete"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_weight=
"1"
android:background=
"?android:attr/selectableItemBackground"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/more_info"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_delete"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
app/src/main/res/layout/list_item_repo.xml
deleted
100644 → 0
View file @
4040a024
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
style=
"?attr/cardStyle"
android:layout_width=
"fill_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:layout_marginStart=
"@dimen/card_horizontal_margin"
android:layout_marginTop=
"@dimen/card_vertical_margin"
android:layout_marginEnd=
"@dimen/card_horizontal_margin"
android:layout_marginBottom=
"@dimen/card_vertical_margin"
android:minHeight=
"?android:attr/listPreferredItemHeight"
app:cardCornerRadius=
"@dimen/card_corner_radius"
app:cardElevation=
"@dimen/card_elevation"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:paddingStart=
"10dp"
android:paddingTop=
"5dp"
android:paddingEnd=
"10dp"
android:paddingBottom=
"8dp"
>
<TextView
android:id=
"@+id/title"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceMedium"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/version_name"
app:layout_constraintEnd_toStartOf=
"@+id/download"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/version_name"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/author"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/title"
/>
<TextView
android:id=
"@+id/author"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textIsSelectable=
"false"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toTopOf=
"@+id/description"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/version_name"
/>
<TextView
android:id=
"@+id/description"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textIsSelectable=
"false"
app:layout_constraintBottom_toTopOf=
"@+id/update_time"
app:layout_constraintEnd_toEndOf=
"@id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/author"
/>
<TextView
android:id=
"@+id/update_time"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"@android:color/tertiary_text_dark"
android:textStyle=
"bold|italic"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toBottomOf=
"@+id/description"
/>
<View
android:id=
"@+id/info_layout"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
app:layout_constraintBottom_toBottomOf=
"@+id/update_time"
app:layout_constraintEnd_toEndOf=
"@+id/title"
app:layout_constraintStart_toStartOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"@+id/title"
/>
<ImageView
android:id=
"@+id/download"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"8dp"
android:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:gravity=
"end"
android:tint=
"@color/icon_grey"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/title"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_file_download_black"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
app/src/main/res/layout/list_item_sulog_group.xml
deleted
100644 → 0
View file @
4040a024
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"?attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:orientation=
"horizontal"
android:padding=
"5dp"
>
<ImageView
android:id=
"@+id/arrow"
android:layout_width=
"wrap_content"
android:layout_height=
"25dp"
android:layout_marginEnd=
"10dp"
android:tint=
"?attr/imageColorTint"
app:srcCompat=
"@drawable/ic_arrow"
/>
<TextView
android:id=
"@+id/date"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:gravity=
"center_vertical"
android:text=
"2017/1/1"
android:textAppearance=
"?android:attr/textAppearanceSmall"
android:textColor=
"?android:attr/textColorPrimary"
/>
</LinearLayout>
\ No newline at end of file
app/src/main/res/layout/toolbar.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar
android:id=
"@+id/toolbar"
xmlns:android=
"http://schemas.android.com/apk/res/android"
<androidx.appcompat.widget.Toolbar
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/toolbar"
android:layout_width=
"match_parent"
android:layout_height=
"?attr/actionBarSize"
android:background=
"?attr/colorPrimary"
a
ndroid:elevation=
"4dp"
/>
a
pp:elevation=
"4dp"
/>
app/src/main/res/layout/update_card.xml
View file @
2813d203
...
...
@@ -75,7 +75,6 @@
android:layout_height=
"wrap_content"
android:maxLines=
"1"
android:text=
"@string/checking_for_updates"
android:visibility=
"gone"
app:autoSizeMinTextSize=
"1sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintEnd_toEndOf=
"@+id/status"
...
...
@@ -87,7 +86,6 @@
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:maxLines=
"1"
android:visibility=
"gone"
app:autoSizeMinTextSize=
"1sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintEnd_toEndOf=
"@+id/status"
...
...
@@ -101,7 +99,6 @@
android:layout_marginEnd=
"8dp"
android:maxLines=
"1"
android:text=
"@string/install"
android:visibility=
"gone"
app:autoSizeMaxTextSize=
"14sp"
app:autoSizeTextType=
"uniform"
app:layout_constraintBottom_toBottomOf=
"parent"
...
...
app/src/main/res/menu/drawer.xml
View file @
2813d203
...
...
@@ -6,17 +6,17 @@
android:id=
"@+id/main_group"
>
<item
android:id=
"@+id/magisk"
android:id=
"@+id/magisk
Fragment
"
android:icon=
"@drawable/ic_magisk_outline"
android:title=
"@string/magisk"
/>
<item
android:id=
"@+id/superuser"
android:id=
"@+id/superuser
Fragment
"
android:icon=
"@drawable/ic_superuser"
android:title=
"@string/superuser"
android:visible=
"false"
/>
<item
android:id=
"@+id/magisk
hide
"
android:id=
"@+id/magisk
HideFragment
"
android:icon=
"@drawable/ic_magiskhide"
android:title=
"@string/magiskhide"
android:visible=
"false"
/>
...
...
@@ -28,13 +28,13 @@
android:id=
"@+id/second_group"
>
<item
android:id=
"@+id/modules"
android:id=
"@+id/modules
Fragment
"
android:icon=
"@drawable/ic_extension"
android:title=
"@string/modules"
android:visible=
"false"
/>
<item
android:id=
"@+id/
downloads
"
android:id=
"@+id/
reposFragment
"
android:icon=
"@drawable/ic_cloud_download"
android:title=
"@string/downloads"
/>
...
...
@@ -45,7 +45,7 @@
android:id=
"@+id/third_group"
>
<item
android:id=
"@+id/log"
android:id=
"@+id/log
Fragment
"
android:icon=
"@drawable/ic_bug_report"
android:title=
"@string/log"
/>
...
...
app/src/main/res/values-night/colors.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color
name=
"colorSecondary"
>
#FFC107
</color>
<color
name=
"colorSecondaryDark"
>
#AD8305
</color>
<color
name=
"dividerGray"
>
#888888
</color>
<color
name=
"colorText"
>
#FFFFFF
</color>
<color
name=
"colorTextInactive"
>
#ADADAD
</color>
<color
name=
"colorTextInverse"
>
#1D1D1D
</color>
<color
name=
"colorTextTinted"
>
#FFFFFF
</color>
<color
name=
"colorCardStroke"
>
#2AFFFFFF
</color>
<color
name=
"colorCardStrokeInverse"
>
#2AE4E4E4
</color>
<color
name=
"colorBackground"
>
#313131
</color>
<color
name=
"colorTintedBackground"
>
@color/colorPrimary
</color>
</resources>
\ No newline at end of file
app/src/main/res/values-night/styles_new.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"ThemeFoundation"
parent=
"Theme.MaterialComponents.NoActionBar"
/>
<style
name=
"MagiskTheme.SUFoundation"
parent=
"Theme.MaterialComponents.Dialog"
/>
<style
name=
"MagiskTheme"
parent=
"ThemeFoundation.Colored.ExtraProps"
>
<!--All of these attributes are deprecated and will be removed-->
<item
name=
"cardStyle"
>
@style/CardViewStyle.Dark
</item>
<item
name=
"imageColorTint"
>
@color/dark_secondary_text
</item>
<item
name=
"colorControl"
>
?android:attr/textColorSecondary
</item>
<item
name=
"colorAccentFallback"
>
@color/accentFallback
</item>
<item
name=
"android:windowBackground"
>
@android:color/black
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/colors.xml
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--new colors-->
<color
name=
"colorPrimary"
>
#009688
</color>
<color
name=
"colorPrimaryDark"
>
#00796B
</color>
<color
name=
"colorSecondary"
>
#D50000
</color>
<color
name=
"colorSecondaryDark"
>
#6D1111
</color>
<color
name=
"dividerGray"
>
#888888
</color>
<color
name=
"colorText"
>
#1D1D1D
</color>
<color
name=
"colorTextInactive"
>
#6a6868
</color>
<color
name=
"colorTextInverse"
>
#fff
</color>
<color
name=
"colorTextTinted"
>
#FFFFFF
</color>
<color
name=
"colorCardStroke"
>
#2AFFFFFF
</color>
<color
name=
"colorCardStrokeInverse"
>
#2A000000
</color>
<color
name=
"colorBackground"
>
#fff
</color>
<color
name=
"colorTintedBackground"
>
@color/colorPrimary
</color>
<color
name=
"colorInactive"
>
#66000000
</color>
<color
name=
"colorCorrect"
>
@color/green500
</color>
<color
name=
"colorError"
>
@color/red500
</color>
<color
name=
"colorUpdate"
>
@color/blue500
</color>
<!--new colors ^^-->
<color
name=
"primary"
>
#009688
</color>
<color
name=
"primary_dark"
>
#00796B
</color>
<color
name=
"accent"
>
#FFC107
</color>
...
...
app/src/main/res/values/dimens.xml
View file @
2813d203
...
...
@@ -8,4 +8,21 @@
<dimen
name=
"card_corner_radius"
>
2dp
</dimen>
<dimen
name=
"card_elevation"
>
2dp
</dimen>
<dimen
name=
"fab_padding"
>
16dp
</dimen>
<!--new dimens-->
<dimen
name=
"margin_generic_quadruple"
>
64dp
</dimen>
<dimen
name=
"margin_generic_triple"
>
48dp
</dimen>
<dimen
name=
"margin_generic_double"
>
32dp
</dimen>
<dimen
name=
"margin_generic"
>
16dp
</dimen>
<dimen
name=
"margin_generic_half"
>
8dp
</dimen>
<dimen
name=
"margin_generic_quarter"
>
4dp
</dimen>
<dimen
name=
"margin_generic_eighth"
>
2dp
</dimen>
<dimen
name=
"radius_generic"
>
8dp
</dimen>
<dimen
name=
"supplemental_animation_height"
>
20dp
</dimen>
<dimen
name=
"divider_size"
>
1dp
</dimen>
<!--new dimens ^^-->
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
View file @
2813d203
...
...
@@ -99,7 +99,9 @@
<string
name=
"dtbo_patched_title"
>
DTBO was patched!
</string>
<string
name=
"dtbo_patched_reboot"
>
Magisk Manager has patched dtbo.img. Please reboot.
</string>
<string
name=
"flashing"
>
Flashing
</string>
<string
name=
"flashing"
>
Flashing…
</string>
<string
name=
"done"
>
Done!
</string>
<string
name=
"failure"
>
Failed
</string>
<string
name=
"hide_manager_title"
>
Hiding Magisk Manager…
</string>
<string
name=
"hide_manager_fail_toast"
>
Hide Magisk Manager failed.
</string>
<string
name=
"open_link_failed_toast"
>
No application found to open the link.
</string>
...
...
app/src/main/res/values/styles_new.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"ThemeFoundation"
parent=
"Theme.MaterialComponents.Light.NoActionBar"
/>
<style
name=
"ThemeFoundation.Colored"
>
<item
name=
"colorPrimary"
>
@color/colorPrimary
</item>
<item
name=
"colorPrimaryDark"
>
@color/colorPrimaryDark
</item>
<item
name=
"colorPrimaryVariant"
>
@color/colorPrimaryDark
</item>
<item
name=
"colorAccent"
>
@color/colorSecondary
</item>
<item
name=
"colorSecondary"
>
@color/colorSecondary
</item>
<item
name=
"colorSecondaryVariant"
>
@color/colorSecondaryDark
</item>
<item
name=
"android:windowBackground"
>
@android:color/white
</item>
</style>
<style
name=
"ThemeFoundation.Colored.ExtraProps"
/>
<style
name=
"MagiskTheme"
parent=
"ThemeFoundation.Colored.ExtraProps"
>
<!--All of these attributes are deprecated and will be removed-->
<item
name=
"cardStyle"
>
@style/CardViewStyle.Light
</item>
<item
name=
"imageColorTint"
>
@color/icon_grey
</item>
<item
name=
"colorControl"
>
?android:attr/textColorSecondary
</item>
<item
name=
"colorAccentFallback"
>
@color/accentFallback
</item>
</style>
<style
name=
"MagiskTheme.Flashing"
parent=
"Theme.MaterialComponents.NoActionBar"
>
<item
name=
"colorPrimary"
>
@android:color/black
</item>
<item
name=
"colorPrimaryDark"
>
@android:color/black
</item>
<item
name=
"colorAccent"
>
@color/colorSecondary
</item>
<item
name=
"colorSecondary"
>
@color/colorSecondary
</item>
<item
name=
"colorSecondaryVariant"
>
@color/colorSecondaryDark
</item>
<item
name=
"android:windowBackground"
>
@android:color/black
</item>
</style>
<style
name=
"MagiskTheme.SUFoundation"
parent=
"Theme.MaterialComponents.Light.Dialog"
/>
<style
name=
"MagiskTheme.SU"
parent=
"MagiskTheme.SUFoundation"
>
<item
name=
"android:windowBackground"
>
@android:color/transparent
</item>
<item
name=
"android:colorBackgroundCacheHint"
>
@null
</item>
<item
name=
"android:windowIsFloating"
>
true
</item>
<item
name=
"android:backgroundDimEnabled"
>
false
</item>
<item
name=
"android:windowContentOverlay"
>
@null
</item>
<item
name=
"android:windowIsTranslucent"
>
true
</item>
<item
name=
"windowActionBar"
>
false
</item>
<item
name=
"windowNoTitle"
>
true
</item>
</style>
<!--Overlays-->
<style
name=
"AppBarLayoutTheme"
parent=
"ThemeOverlay.AppCompat.Dark"
/>
<style
name=
"AppBarLayoutTheme.Flashing"
parent=
"ThemeOverlay.AppCompat.Dark"
/>
<style
name=
"ToolbarPopupTheme"
parent=
"ThemeOverlay.AppCompat.Light"
/>
<style
name=
"ToolbarPopupTheme.Flashing"
parent=
"ThemeOverlay.AppCompat.Dark"
/>
</resources>
app/src/main/res/values/view_appearances.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"TextAppearance"
parent=
"TextAppearance.AppCompat.Body1"
>
<item
name=
"android:textColor"
>
@color/colorText
</item>
</style>
<style
name=
"TextAppearance.Inactive"
>
<item
name=
"android:textColor"
>
@color/colorTextInactive
</item>
</style>
<style
name=
"TextAppearance.Emphasize"
parent=
"TextAppearance.AppCompat.Body1"
>
<item
name=
"android:textColor"
>
@color/colorText
</item>
<item
name=
"android:textSize"
>
16sp
</item>
<item
name=
"android:textStyle"
>
bold
</item>
</style>
<style
name=
"TextAppearance.Emphasize.Inactive"
>
<item
name=
"android:textColor"
>
@color/colorTextInactive
</item>
</style>
<style
name=
"TextAppearance.Emphasize.Tinted"
>
<item
name=
"android:textColor"
>
@color/colorTextTinted
</item>
</style>
<style
name=
"TextAppearance.Title"
parent=
"TextAppearance.AppCompat.Title"
>
<item
name=
"android:textColor"
>
@color/colorText
</item>
</style>
<style
name=
"TextAppearance.Title.Inactive"
>
<item
name=
"android:textColor"
>
@color/colorTextInactive
</item>
</style>
<style
name=
"TextAppearance.Caption"
parent=
"TextAppearance.AppCompat.Caption"
>
<item
name=
"android:textColor"
>
@color/colorText
</item>
</style>
<style
name=
"TextAppearance.Caption.Inactive"
>
<item
name=
"android:textColor"
>
@color/colorTextInactive
</item>
</style>
<style
name=
"TextAppearance.Caption.Tinted"
>
<item
name=
"android:textColor"
>
@color/colorTextTinted
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_button_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.Button"
parent=
"Widget.MaterialComponents.Button"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
<style
name=
"Widget.Button.Iconified"
parent=
"Widget.MaterialComponents.Button.Icon"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
<style
name=
"Widget.Button.Text"
parent=
"Widget.MaterialComponents.Button.TextButton"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
<item
name=
"android:textColor"
>
@color/colorSecondary
</item>
<item
name=
"rippleColor"
>
@color/colorSecondary
</item>
</style>
<style
name=
"Widget.Button.Text.Iconified"
parent=
"Widget.MaterialComponents.Button.TextButton.Icon"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
<item
name=
"android:textColor"
>
@color/colorSecondary
</item>
<item
name=
"iconTint"
>
@color/colorSecondary
</item>
<item
name=
"rippleColor"
>
@color/colorSecondary
</item>
</style>
<style
name=
"Widget.Button.Flat"
parent=
"Widget.MaterialComponents.Button.UnelevatedButton"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
<style
name=
"Widget.Button.Flat.Iconified"
parent=
"Widget.MaterialComponents.Button.UnelevatedButton.Icon"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
<style
name=
"Widget.Button.Outlined"
parent=
"Widget.MaterialComponents.Button.OutlinedButton"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
<item
name=
"strokeColor"
>
@color/colorSecondary
</item>
<item
name=
"rippleColor"
>
@color/colorSecondary
</item>
<item
name=
"android:textColor"
>
@color/colorSecondary
</item>
</style>
<style
name=
"Widget.Button.Outlined.Iconified"
parent=
"Widget.MaterialComponents.Button.OutlinedButton.Icon"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
<item
name=
"strokeColor"
>
@color/colorSecondary
</item>
<item
name=
"rippleColor"
>
@color/colorSecondary
</item>
<item
name=
"android:textColor"
>
@color/colorSecondary
</item>
<item
name=
"iconTint"
>
@color/colorSecondary
</item>
</style>
<style
name=
"Widget.FAB"
parent=
"Widget.MaterialComponents.FloatingActionButton"
>
<item
name=
"tint"
>
@color/colorTextInverse
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_card_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.Card"
parent=
"Widget.MaterialComponents.CardView"
>
<item
name=
"cardCornerRadius"
>
@dimen/radius_generic
</item>
<item
name=
"cardElevation"
>
0dp
</item>
</style>
<style
name=
"Widget.Card.Outlined"
>
<item
name=
"cardBackgroundColor"
>
@color/colorBackground
</item>
<item
name=
"strokeColor"
>
@color/colorCardStrokeInverse
</item>
<item
name=
"strokeWidth"
>
1dp
</item>
</style>
<style
name=
"Widget.Card.Outlined.Tinted"
>
<item
name=
"cardBackgroundColor"
>
@color/colorPrimary
</item>
<item
name=
"strokeColor"
>
@color/colorCardStroke
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_dialog_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.DialogButton"
>
<item
name=
"android:background"
>
?attr/selectableItemBackground
</item>
<item
name=
"android:gravity"
>
center
</item>
<item
name=
"android:layout_width"
>
0dp
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_divider_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.Divider"
>
<item
name=
"android:background"
>
@color/colorCardStrokeInverse
</item>
</style>
<style
name=
"Widget.Divider.Horizontal"
>
<item
name=
"android:layout_width"
>
0dp
</item>
<item
name=
"android:layout_height"
>
1dp
</item>
</style>
<style
name=
"Widget.Divider.Vertical"
>
<item
name=
"android:layout_width"
>
1dp
</item>
<item
name=
"android:layout_height"
>
0dp
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_image_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.Icon"
>
<item
name=
"android:layout_width"
>
36dp
</item>
<item
name=
"android:layout_height"
>
36dp
</item>
<item
name=
"android:padding"
>
6dp
</item>
<item
name=
"android:background"
>
?attr/selectableItemBackgroundBorderless
</item>
</style>
<style
name=
"Widget.Icon.Large"
>
<item
name=
"android:layout_width"
>
48dp
</item>
<item
name=
"android:layout_height"
>
48dp
</item>
<item
name=
"android:padding"
>
6dp
</item>
<item
name=
"android:background"
>
?attr/selectableItemBackgroundBorderless
</item>
</style>
<style
name=
"Widget.Icon.Accent"
>
<item
name=
"tint"
>
@color/colorSecondary
</item>
</style>
<style
name=
"Widget.Icon.DialogButton"
parent=
"Widget.Icon.Accent"
>
<item
name=
"android:layout_gravity"
>
center
</item>
<item
name=
"android:background"
>
@android:color/transparent
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_input_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.Input.Layout"
parent=
"@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
>
<item
name=
"android:layout_width"
>
match_parent
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
<item
name=
"passwordToggleEnabled"
>
true
</item>
</style>
<style
name=
"Widget.Input.EditText"
parent=
"@style/Widget.MaterialComponents.TextInputEditText.OutlinedBox.Dense"
>
<item
name=
"android:layout_width"
>
match_parent
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_progress_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.Progress"
parent=
"Widget.AppCompat.ProgressBar.Horizontal"
>
<item
name=
"android:indeterminate"
>
true
</item>
<item
name=
"android:layout_width"
>
64dp
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
</resources>
\ No newline at end of file
app/src/main/res/values/view_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget"
/>
</resources>
\ No newline at end of file
app/src/main/res/values/view_text_styles.xml
0 → 100644
View file @
2813d203
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Widget.BaseText"
>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
<style
name=
"Widget.Text"
parent=
"Widget.BaseText"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance
</item>
<item
name=
"android:ellipsize"
>
end
</item>
</style>
<style
name=
"Widget.Text.Inactive"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Inactive
</item>
</style>
<style
name=
"Widget.Text.Emphasize"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Emphasize
</item>
</style>
<style
name=
"Widget.Text.Emphasize.Inactive"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Emphasize.Inactive
</item>
</style>
<style
name=
"Widget.Text.Emphasize.Tinted"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Emphasize.Tinted
</item>
</style>
<style
name=
"Widget.Text.Title"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Title
</item>
</style>
<style
name=
"Widget.Text.Title.Inactive"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Title.Inactive
</item>
</style>
<style
name=
"Widget.Text.Caption"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Caption
</item>
</style>
<style
name=
"Widget.Text.Caption.Inactive"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Caption.Inactive
</item>
</style>
<style
name=
"Widget.Text.Caption.Tinted"
>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Caption.Tinted
</item>
</style>
<style
name=
"Widget.Text.SectionTitle"
>
<item
name=
"android:textStyle"
>
bold
</item>
<item
name=
"android:paddingTop"
>
@dimen/margin_generic_double
</item>
<item
name=
"android:paddingBottom"
>
@dimen/margin_generic
</item>
<item
name=
"android:paddingLeft"
>
@dimen/margin_generic
</item>
<item
name=
"android:paddingRight"
>
@dimen/margin_generic
</item>
<item
name=
"android:textAppearance"
>
@style/TextAppearance.Caption
</item>
<item
name=
"android:textColor"
>
@color/colorSecondary
</item>
</style>
<style
name=
"Widget.Text.DialogButton"
>
<item
name=
"android:padding"
>
@dimen/margin_generic
</item>
<item
name=
"android:textColor"
>
@color/colorSecondary
</item>
<item
name=
"android:textStyle"
>
bold
</item>
<item
name=
"android:layout_width"
>
wrap_content
</item>
<item
name=
"android:layout_height"
>
wrap_content
</item>
</style>
</resources>
\ No newline at end of file
build.gradle
View file @
2813d203
...
...
@@ -15,8 +15,8 @@ buildscript {
}
dependencies
{
classpath
'com.android.tools:r8:1.4.79'
classpath
'com.android.tools.build:gradle:3.5.0-alpha1
2
'
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.
21
"
classpath
'com.android.tools.build:gradle:3.5.0-alpha1
3
'
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.
30
"
// NOTE: Do not place your application dependencies here; they belong
...
...
@@ -36,7 +36,9 @@ subprojects {
repositories
{
google
()
jcenter
()
maven
{
url
"https://maven.fabric.io/public"
}
maven
{
url
"https://jitpack.io"
}
maven
{
url
"http://oss.sonatype.org/content/repositories/snapshots"
}
}
afterEvaluate
{
if
(
getPlugins
().
hasPlugin
(
'com.android.library'
)
||
...
...
gradle/wrapper/gradle-wrapper.properties
View file @
2813d203
#
Mon Apr 15 22:35:08 ED
T 2019
#
Fri Apr 26 14:06:46 CES
T 2019
distributionBase
=
GRADLE_USER_HOME
distributionPath
=
wrapper/dists
zipStoreBase
=
GRADLE_USER_HOME
zipStorePath
=
wrapper/dists
distributionUrl
=
https
\:
//services.gradle.org/distributions/gradle-5.
3.
1-all.zip
distributionUrl
=
https
\:
//services.gradle.org/distributions/gradle-5.
4-rc-
1-all.zip
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