├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.MD ├── app ├── .gitignore ├── build.gradle ├── proguard-kotlin.pro ├── proguard-rules.pro ├── res-ids.txt └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── a │ │ ├── a.java │ │ ├── b.java │ │ ├── c.java │ │ ├── e.java │ │ ├── f.java │ │ ├── g.java │ │ ├── h.java │ │ ├── j.java │ │ ├── m.java │ │ └── w.java │ └── com │ │ └── topjohnwu │ │ └── magisk │ │ ├── App.kt │ │ ├── Config.kt │ │ ├── Const.kt │ │ ├── Hacks.kt │ │ ├── Info.kt │ │ ├── base │ │ ├── BaseActivity.kt │ │ ├── BaseFragment.kt │ │ ├── BasePreferenceFragment.kt │ │ ├── BaseReceiver.kt │ │ ├── BaseService.kt │ │ ├── DelegateWorker.kt │ │ └── viewmodel │ │ │ ├── BaseViewModel.kt │ │ │ ├── LoadingViewModel.kt │ │ │ ├── ObservableViewModel.kt │ │ │ ├── StatefulViewModel.kt │ │ │ └── TeanityViewModel.kt │ │ ├── data │ │ ├── database │ │ │ ├── PolicyDao.kt │ │ │ ├── RepoDao.kt │ │ │ ├── SettingsDao.kt │ │ │ ├── StringDao.kt │ │ │ ├── SuLogDao.kt │ │ │ └── magiskdb │ │ │ │ ├── BaseDao.kt │ │ │ │ └── Query.kt │ │ ├── network │ │ │ └── GithubServices.kt │ │ └── repository │ │ │ ├── DBConfig.kt │ │ │ ├── LogRepository.kt │ │ │ ├── MagiskRepository.kt │ │ │ └── StringRepository.kt │ │ ├── databinding │ │ ├── AdaptersGeneric.kt │ │ ├── AdaptersRecycler.kt │ │ ├── BindingBoundAdapter.kt │ │ └── RecyclerViewItems.kt │ │ ├── di │ │ ├── ApplicationModule.kt │ │ ├── DatabaseModule.kt │ │ ├── Modules.kt │ │ ├── NetworkingModule.kt │ │ ├── RepositoryModule.kt │ │ └── ViewModelsModule.kt │ │ ├── extensions │ │ ├── DataBinding.kt │ │ ├── Dimens.kt │ │ ├── RxJava.kt │ │ ├── Snackbar.kt │ │ ├── XAndroid.kt │ │ ├── XBinding.kt │ │ ├── XJava.kt │ │ ├── XKoin.kt │ │ ├── XList.kt │ │ ├── XSU.kt │ │ ├── XString.kt │ │ ├── XTime.kt │ │ └── XView.kt │ │ ├── model │ │ ├── binding │ │ │ └── BindingAdapter.kt │ │ ├── download │ │ │ ├── DownloadService.kt │ │ │ ├── ManagerUpgrade.kt │ │ │ ├── ModuleProcessor.kt │ │ │ ├── NotificationService.kt │ │ │ └── RemoteFileService.kt │ │ ├── entity │ │ │ ├── HideAppInfo.kt │ │ │ ├── HideTarget.kt │ │ │ ├── MagiskLog.kt │ │ │ ├── MagiskPolicy.kt │ │ │ ├── UpdateInfo.kt │ │ │ ├── internal │ │ │ │ ├── Configuration.kt │ │ │ │ └── DownloadSubject.kt │ │ │ ├── module │ │ │ │ ├── BaseModule.kt │ │ │ │ ├── Module.kt │ │ │ │ └── Repo.kt │ │ │ ├── recycler │ │ │ │ ├── ConsoleRvItem.kt │ │ │ │ ├── HideRvItem.kt │ │ │ │ ├── LenientRvItem.kt │ │ │ │ ├── LogRvItem.kt │ │ │ │ ├── ModuleRvItem.kt │ │ │ │ ├── PolicyRvItem.kt │ │ │ │ ├── SectionRvItem.kt │ │ │ │ └── SpinnerRvItem.kt │ │ │ └── state │ │ │ │ └── IndeterminateState.kt │ │ ├── events │ │ │ ├── EventHandler.kt │ │ │ ├── RxEvents.kt │ │ │ ├── SimpleViewEvent.kt │ │ │ ├── SnackbarEvent.kt │ │ │ ├── ViewEventObserver.kt │ │ │ └── ViewEvents.kt │ │ ├── flash │ │ │ ├── FlashResultListener.kt │ │ │ ├── Flashing.kt │ │ │ └── Patching.kt │ │ ├── navigation │ │ │ ├── MagiskNavigationEvent.kt │ │ │ ├── Navigation.kt │ │ │ └── Navigator.kt │ │ ├── observer │ │ │ └── Observer.kt │ │ ├── permissions │ │ │ └── PermissionRequestBuilder.kt │ │ ├── preference │ │ │ ├── BooleanProperty.kt │ │ │ ├── FloatProperty.kt │ │ │ ├── IntProperty.kt │ │ │ ├── LongProperty.kt │ │ │ ├── PreferenceModel.kt │ │ │ ├── Property.kt │ │ │ ├── StringProperty.kt │ │ │ └── StringSetProperty.kt │ │ ├── receiver │ │ │ └── GeneralReceiver.kt │ │ ├── update │ │ │ └── UpdateCheckService.kt │ │ └── zip │ │ │ └── Zip.kt │ │ ├── tasks │ │ ├── FlashZip.kt │ │ ├── MagiskInstaller.kt │ │ └── RepoUpdater.kt │ │ ├── ui │ │ ├── MainActivity.kt │ │ ├── MainViewModel.kt │ │ ├── SplashActivity.kt │ │ ├── flash │ │ │ ├── FlashActivity.kt │ │ │ └── FlashViewModel.kt │ │ ├── hide │ │ │ ├── HideViewModel.kt │ │ │ └── MagiskHideFragment.kt │ │ ├── home │ │ │ ├── HomeFragment.kt │ │ │ └── HomeViewModel.kt │ │ ├── log │ │ │ ├── LogFragment.kt │ │ │ └── LogViewModel.kt │ │ ├── module │ │ │ ├── ModuleViewModel.kt │ │ │ ├── ModulesFragment.kt │ │ │ └── ReposFragment.kt │ │ ├── settings │ │ │ └── SettingsFragment.kt │ │ ├── superuser │ │ │ ├── SuperuserFragment.kt │ │ │ └── SuperuserViewModel.kt │ │ └── surequest │ │ │ ├── SuRequestActivity.kt │ │ │ └── SuRequestViewModel.kt │ │ ├── utils │ │ ├── BiometricHelper.kt │ │ ├── CachedValue.kt │ │ ├── DataBindingAdapters.kt │ │ ├── DiffObservableList.kt │ │ ├── KItemDecoration.kt │ │ ├── KObservableField.kt │ │ ├── Keygen.kt │ │ ├── Locales.kt │ │ ├── PatchAPK.kt │ │ ├── ProgressInputStream.kt │ │ ├── RootInit.kt │ │ ├── RxBus.kt │ │ ├── SafetyNetHelper.kt │ │ ├── SuConnector.kt │ │ ├── SuHandler.kt │ │ ├── Utils.kt │ │ └── ZipUtils.kt │ │ └── view │ │ ├── MarkDownWindow.kt │ │ ├── Notifications.kt │ │ ├── Shortcuts.kt │ │ └── dialogs │ │ ├── CustomAlertDialog.kt │ │ ├── EnvFixDialog.kt │ │ ├── InstallMethodDialog.kt │ │ ├── MagiskInstallDialog.kt │ │ ├── ManagerInstallDialog.kt │ │ └── UninstallDialog.kt │ └── res │ ├── drawable-anydpi-v21 │ └── ic_magisk_outline.xml │ ├── drawable-hdpi │ ├── ic_launcher.png │ └── ic_magisk_outline.png │ ├── drawable-mdpi │ ├── ic_launcher.png │ └── ic_magisk_outline.png │ ├── drawable-nodpi │ └── logo.png │ ├── drawable-v26 │ ├── sc_cloud_download.xml │ ├── sc_extension.xml │ ├── sc_magiskhide.xml │ └── sc_superuser.xml │ ├── drawable-xhdpi │ ├── ic_launcher.png │ └── ic_magisk_outline.png │ ├── drawable-xxhdpi │ ├── ic_launcher.png │ └── ic_magisk_outline.png │ ├── drawable-xxxhdpi │ ├── ic_launcher.png │ └── ic_magisk_outline.png │ ├── drawable │ ├── ic_add.xml │ ├── ic_arrow.xml │ ├── ic_arrow_down.xml │ ├── ic_back.xml │ ├── ic_bug_report.xml │ ├── ic_cancel.xml │ ├── ic_check_circle.xml │ ├── ic_checked.xml │ ├── ic_cloud_download.xml │ ├── ic_delete.xml │ ├── ic_extension.xml │ ├── ic_file_download_black.xml │ ├── ic_fingerprint.xml │ ├── ic_github.xml │ ├── ic_help.xml │ ├── ic_indeterminate.xml │ ├── ic_magiskhide.xml │ ├── ic_menu.xml │ ├── ic_more.xml │ ├── ic_notifications.xml │ ├── ic_patreon.xml │ ├── ic_paypal.xml │ ├── ic_refresh.xml │ ├── ic_restart.xml │ ├── ic_safetynet.xml │ ├── ic_save.xml │ ├── ic_save_compat.xml │ ├── ic_settings.xml │ ├── ic_sort.xml │ ├── ic_splash_activity.xml │ ├── ic_superuser.xml │ ├── ic_twitter.xml │ ├── ic_unchecked.xml │ ├── ic_undelete.xml │ ├── ic_update.xml │ ├── ic_warning.xml │ ├── ic_xda.xml │ ├── sc_cloud_download.xml │ ├── sc_extension.xml │ ├── sc_magiskhide.xml │ └── sc_superuser.xml │ ├── layout │ ├── activity_flash.xml │ ├── activity_main.xml │ ├── activity_main_content.xml │ ├── activity_request.xml │ ├── alert_dialog.xml │ ├── custom_channel_dialog.xml │ ├── custom_download_dialog.xml │ ├── dialog_custom_name.xml │ ├── fragment_log.xml │ ├── fragment_magisk.xml │ ├── fragment_magisk_hide.xml │ ├── fragment_modules.xml │ ├── fragment_repos.xml │ ├── fragment_superuser.xml │ ├── include_update_card.xml │ ├── item_console.xml │ ├── item_hide_app.xml │ ├── item_hide_process.xml │ ├── item_module.xml │ ├── item_page_log.xml │ ├── item_page_magisk_log.xml │ ├── item_policy.xml │ ├── item_repo.xml │ ├── item_section.xml │ ├── item_spinner.xml │ ├── item_superuser_log.xml │ ├── item_superuser_log_entry.xml │ └── markdown_window.xml │ ├── menu │ ├── drawer.xml │ ├── menu_log.xml │ ├── menu_magiskhide.xml │ ├── menu_reboot.xml │ └── menu_repo.xml │ ├── raw │ ├── .gitignore │ ├── changelog.md │ ├── nonroot_utils.sh │ └── utils.sh │ ├── values-ar │ └── strings.xml │ ├── values-az │ └── strings.xml │ ├── values-bg │ └── strings.xml │ ├── values-ca │ └── strings.xml │ ├── values-cs │ └── strings.xml │ ├── values-de │ └── strings.xml │ ├── values-el │ └── strings.xml │ ├── values-es │ └── strings.xml │ ├── values-et │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-hi │ └── strings.xml │ ├── values-hr │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-ko │ └── strings.xml │ ├── values-lt │ └── strings.xml │ ├── values-mk │ └── strings.xml │ ├── values-nb │ └── strings.xml │ ├── values-night │ ├── colors.xml │ └── styles.xml │ ├── values-nl │ └── strings.xml │ ├── values-pl │ └── strings.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-pt-rPT │ └── strings.xml │ ├── values-ro │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-sk │ └── strings.xml │ ├── values-sr │ └── strings.xml │ ├── values-sv │ └── strings.xml │ ├── values-sw600dp │ └── dimens.xml │ ├── values-th │ └── strings.xml │ ├── values-tr │ └── strings.xml │ ├── values-uk │ └── strings.xml │ ├── values-v19 │ └── styles.xml │ ├── values-vi │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ ├── values │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── dimens.xml │ ├── ids.xml │ ├── resources.xml │ ├── strings.xml │ ├── styles.xml │ ├── view_appearances.xml │ ├── view_button_styles.xml │ ├── view_card_styles.xml │ ├── view_dialog_styles.xml │ ├── view_divider_styles.xml │ ├── view_image_styles.xml │ ├── view_progress_styles.xml │ ├── view_styles.xml │ └── view_text_styles.xml │ └── xml │ └── app_settings.xml ├── build.gradle ├── build.py ├── config.prop.sample ├── docs ├── README.md ├── deploy.md ├── details.md ├── guides.md ├── images │ ├── beta_channel.png │ ├── core_only.png │ ├── disable_auto_ota.png │ ├── flashfire.png │ ├── hide_manager.png │ ├── install_inactive_slot.png │ ├── manager_reboot.png │ ├── ota_done.png │ ├── restore_img.png │ └── safetynet.png ├── install.md ├── procedures.html ├── tools.md └── tutorials.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── native ├── .gitignore ├── build.gradle ├── jni │ ├── .gitignore │ ├── Android.mk │ ├── Application.mk │ ├── core │ │ ├── applet_stub.cpp │ │ ├── applets.cpp │ │ ├── bootstages.cpp │ │ ├── daemon.cpp │ │ ├── db.cpp │ │ ├── magisk.cpp │ │ ├── scripting.cpp │ │ └── socket.cpp │ ├── external │ │ ├── Android.mk │ │ ├── include │ │ │ ├── sqlite3.h │ │ │ ├── xz.h │ │ │ └── xz_config │ │ │ │ └── config.h │ │ ├── stubs │ │ │ ├── selinux_stub.c │ │ │ └── sqlite3_stub.c │ │ └── xz-embedded │ │ │ ├── xz_config.h │ │ │ ├── xz_crc32.c │ │ │ ├── xz_dec_lzma2.c │ │ │ ├── xz_dec_stream.c │ │ │ ├── xz_lzma2.h │ │ │ ├── xz_private.h │ │ │ └── xz_stream.h │ ├── include │ │ ├── daemon.h │ │ ├── db.h │ │ ├── flags.h │ │ ├── magisk.h │ │ ├── magiskpolicy.h │ │ └── resetprop.h │ ├── init │ │ ├── getinfo.cpp │ │ ├── init.cpp │ │ ├── init.h │ │ ├── magiskrc.h │ │ ├── mount.cpp │ │ └── rootdir.cpp │ ├── magiskboot │ │ ├── bootimg.cpp │ │ ├── bootimg.h │ │ ├── compress.cpp │ │ ├── compress.h │ │ ├── dtb.cpp │ │ ├── dtb.h │ │ ├── format.cpp │ │ ├── format.h │ │ ├── hexpatch.cpp │ │ ├── magiskboot.h │ │ ├── main.cpp │ │ ├── pattern.cpp │ │ └── ramdisk.cpp │ ├── magiskhide │ │ ├── hide_policy.cpp │ │ ├── hide_utils.cpp │ │ ├── magiskhide.cpp │ │ ├── magiskhide.h │ │ └── proc_monitor.cpp │ ├── magiskpolicy │ │ ├── api.cpp │ │ ├── magiskpolicy.cpp │ │ ├── policydb.cpp │ │ ├── rules.cpp │ │ ├── sepolicy.c │ │ ├── sepolicy.h │ │ └── statement.cpp │ ├── resetprop │ │ ├── persist_properties.cpp │ │ ├── private │ │ │ ├── _system_properties.h │ │ │ ├── bionic_defs.h │ │ │ ├── bionic_macros.h │ │ │ ├── redefs.h │ │ │ ├── resetprop.h │ │ │ └── system_properties.h │ │ ├── resetprop.cpp │ │ ├── system_property_api.cpp │ │ └── system_property_set.cpp │ ├── su │ │ ├── connect.cpp │ │ ├── pts.cpp │ │ ├── pts.h │ │ ├── su.cpp │ │ ├── su.h │ │ └── su_daemon.cpp │ ├── systemproperties │ │ ├── Android.mk │ │ ├── context_node.cpp │ │ ├── contexts_serialized.cpp │ │ ├── contexts_split.cpp │ │ ├── include │ │ │ ├── async_safe │ │ │ │ └── log.h │ │ │ ├── hacks.h │ │ │ ├── private │ │ │ │ ├── ErrnoRestorer.h │ │ │ │ ├── bionic_futex.h │ │ │ │ ├── bionic_lock.h │ │ │ │ └── bionic_macros.h │ │ │ ├── property_info_parser │ │ │ │ └── property_info_parser.h │ │ │ └── system_properties │ │ │ │ ├── context_node.h │ │ │ │ ├── contexts.h │ │ │ │ ├── contexts_pre_split.h │ │ │ │ ├── contexts_serialized.h │ │ │ │ ├── contexts_split.h │ │ │ │ ├── prop_area.h │ │ │ │ ├── prop_info.h │ │ │ │ └── system_properties.h │ │ ├── prop_area.cpp │ │ ├── prop_info.cpp │ │ ├── property_info_parser.cpp │ │ └── system_properties.cpp │ └── utils │ │ ├── Android.mk │ │ ├── cpio.cpp │ │ ├── file.cpp │ │ ├── files.h │ │ ├── include │ │ ├── blocking_queue.h │ │ ├── cpio.h │ │ ├── logging.h │ │ ├── selinux.h │ │ ├── stream.h │ │ └── utils.h │ │ ├── logging.cpp │ │ ├── misc.cpp │ │ ├── misc.h │ │ ├── missing.cpp │ │ ├── missing.h │ │ ├── new.cpp │ │ ├── selinux.cpp │ │ ├── stream.cpp │ │ ├── xwrap.cpp │ │ └── xwrap.h └── src │ └── main │ └── AndroidManifest.xml ├── scripts ├── addon.d.sh ├── boot_patch.sh ├── emulator.sh ├── flash_script.sh ├── magisk_uninstaller.sh ├── module_installer.sh ├── update_binary.sh └── util_functions.sh ├── settings.gradle ├── shared ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── a │ │ ├── p.java │ │ └── r.java │ └── com │ │ └── topjohnwu │ │ └── magisk │ │ ├── DynAPK.java │ │ ├── FileProvider.java │ │ ├── ProcessPhoenix.java │ │ ├── ProviderCallHandler.java │ │ ├── net │ │ ├── BadRequest.java │ │ ├── ErrorHandler.java │ │ ├── Networking.java │ │ ├── NoSSLv3SocketFactory.java │ │ ├── Request.java │ │ ├── ResponseListener.java │ │ └── SSLSocketWrapper.java │ │ └── utils │ │ ├── APKInstall.java │ │ ├── CompoundEnumeration.java │ │ └── DynamicClassLoader.java │ └── res │ ├── drawable-anydpi-v23 │ └── ic_splash_activity.xml │ ├── drawable-anydpi-v26 │ └── ic_launcher.xml │ ├── drawable │ ├── ic_logo.xml │ ├── ic_magisk.xml │ └── ic_magisk_padded.xml │ ├── values-anydpi-v21 │ └── drawable.xml │ ├── values │ ├── colors.xml │ └── styles.xml │ └── xml │ └── file_paths.xml ├── signing ├── .gitignore ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── topjohnwu │ │ └── signing │ │ ├── BootSigner.java │ │ ├── ByteArrayStream.java │ │ ├── CryptoUtils.java │ │ ├── JarMap.java │ │ ├── SignAPK.java │ │ ├── SignBoot.java │ │ ├── ZipAdjust.java │ │ └── ZipSigner.java │ └── resources │ └── keys │ ├── testkey.pk8 │ ├── testkey.x509.pem │ ├── verity.pk8 │ └── verity.x509.pem ├── snet ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── topjohnwu │ └── snet │ └── SafetyNetHelper.java ├── stub ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── a │ │ ├── a.java │ │ ├── c.java │ │ ├── e.java │ │ └── h.java │ └── com │ │ └── topjohnwu │ │ └── magisk │ │ ├── DelegateApplication.java │ │ ├── DelegateComponentFactory.java │ │ ├── DownloadActivity.java │ │ ├── dummy │ │ ├── DummyActivity.java │ │ ├── DummyProvider.java │ │ ├── DummyReceiver.java │ │ └── DummyService.java │ │ └── obfuscate │ │ ├── Mapping.java │ │ └── RawData.java │ └── res │ ├── values-ar │ └── strings.xml │ ├── values-az │ └── strings.xml │ ├── values-bg │ └── strings.xml │ ├── values-ca │ └── strings.xml │ ├── values-cs │ └── strings.xml │ ├── values-de │ └── strings.xml │ ├── values-el │ └── strings.xml │ ├── values-es │ └── strings.xml │ ├── values-et │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-hi │ └── strings.xml │ ├── values-hr │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-ko │ └── strings.xml │ ├── values-lt │ └── strings.xml │ ├── values-mk │ └── strings.xml │ ├── values-nb │ └── strings.xml │ ├── values-nl │ └── strings.xml │ ├── values-pl │ └── strings.xml │ ├── values-pt-rBR │ └── strings.xml │ ├── values-pt-rPT │ └── strings.xml │ ├── values-ro │ └── strings.xml │ ├── values-ru │ └── strings.xml │ ├── values-sk │ └── strings.xml │ ├── values-sr │ └── strings.xml │ ├── values-sv │ └── strings.xml │ ├── values-th │ └── strings.xml │ ├── values-tr │ └── strings.xml │ ├── values-uk │ └── strings.xml │ ├── values-v28 │ └── styles.xml │ ├── values-vi │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ └── values │ └── strings.xml └── tools ├── elf-cleaner.exe ├── futility ├── kernel.keyblock └── kernel_data_key.vbprivk /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text eol=lf 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | # *.c text 7 | # *.h text 8 | 9 | # Declare files that will always have CRLF line endings on checkout. 10 | *.cmd text eol=crlf 11 | *.bat text eol=crlf 12 | 13 | # Denote all files that are truly binary and should not be modified. 14 | tools/** binary 15 | *.jar binary 16 | *.exe binary 17 | *.apk binary 18 | *.png binary 19 | *.jpg binary 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | *.zip 3 | *.jks 4 | *.apk 5 | /config.prop 6 | /update.sh 7 | 8 | # Built binaries 9 | native/out 10 | 11 | # Android Studio / Gradle 12 | *.iml 13 | .gradle 14 | /local.properties 15 | /.idea 16 | /build 17 | /captures 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "selinux"] 2 | path = native/jni/external/selinux 3 | url = https://github.com/topjohnwu/selinux.git 4 | [submodule "busybox"] 5 | path = native/jni/external/busybox 6 | url = https://github.com/topjohnwu/ndk-busybox.git 7 | [submodule "dtc"] 8 | path = native/jni/external/dtc 9 | url = https://github.com/dgibson/dtc 10 | [submodule "lz4"] 11 | path = native/jni/external/lz4 12 | url = https://github.com/lz4/lz4.git 13 | [submodule "bzip2"] 14 | path = native/jni/external/bzip2 15 | url = https://github.com/nemequ/bzip2.git 16 | [submodule "xz"] 17 | path = native/jni/external/xz 18 | url = https://github.com/xz-mirror/xz.git 19 | [submodule "nanopb"] 20 | path = native/jni/external/nanopb 21 | url = https://github.com/nanopb/nanopb.git 22 | [submodule "mincrypt"] 23 | path = native/jni/external/mincrypt 24 | url = https://github.com/topjohnwu/mincrypt.git 25 | [submodule "termux-elf-cleaner"] 26 | path = tools/termux-elf-cleaner 27 | url = https://github.com/termux/termux-elf-cleaner.git 28 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea/ 5 | /build 6 | app/release 7 | *.hprof 8 | .externalNativeBuild/ 9 | public.certificate.x509.pem 10 | private.key.pk8 11 | *.apk 12 | -------------------------------------------------------------------------------- /app/proguard-kotlin.pro: -------------------------------------------------------------------------------- 1 | ## So every class is case insensitive to avoid some bizare problems 2 | -dontusemixedcaseclassnames 3 | 4 | ## If reflection issues come up uncomment this, that should temporarily fix it 5 | #-keep class kotlin.** { *; } 6 | #-keep class kotlin.Metadata { *; } 7 | #-keepclassmembers class kotlin.Metadata { 8 | # public ; 9 | #} 10 | 11 | ## Never warn about Kotlin, it should work as-is 12 | -dontwarn kotlin.** 13 | 14 | ## Removes runtime null checks - doesn't really matter if it crashes on kotlin or java NPE 15 | -assumenosideeffects class kotlin.jvm.internal.Intrinsics { 16 | static void checkParameterIsNotNull(java.lang.Object, java.lang.String); 17 | } 18 | 19 | ## Useless option for dex 20 | -dontpreverify -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/topjohnwu/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Snet 20 | -keepclassmembers class com.topjohnwu.magisk.utils.SafetyNetHelper { *; } 21 | -keep,allowobfuscation interface com.topjohnwu.magisk.utils.SafetyNetHelper$Callback 22 | -keepclassmembers class * implements com.topjohnwu.magisk.utils.SafetyNetHelper$Callback { 23 | void onResponse(int); 24 | } 25 | 26 | # Keep all fragment constructors 27 | -keepclassmembers class * extends androidx.fragment.app.Fragment { 28 | public (...); 29 | } 30 | 31 | # DelegateWorker 32 | -keep,allowobfuscation class * extends com.topjohnwu.magisk.base.DelegateWorker 33 | 34 | # BootSigner 35 | -keep class a.a { *; } 36 | 37 | # Workaround R8 bug 38 | -keep,allowobfuscation class com.topjohnwu.magisk.model.receiver.GeneralReceiver 39 | -keepclassmembers class a.e { *; } 40 | 41 | # Strip logging 42 | -assumenosideeffects class timber.log.Timber.Tree { *; } 43 | 44 | # Excessive obfuscation 45 | -repackageclasses 'a' 46 | -allowaccessmodification 47 | 48 | # QOL 49 | -dontnote ** 50 | -dontwarn com.caverock.androidsvg.** 51 | -dontwarn ru.noties.markwon.** 52 | -------------------------------------------------------------------------------- /app/res-ids.txt: -------------------------------------------------------------------------------- 1 | com.topjohnwu.magisk:color/xxxxxxxx = 0x7f010000 2 | com.topjohnwu.magisk:drawable/xxxxxxxx = 0x7f020000 3 | com.topjohnwu.magisk:string/xxxxxxxx = 0x7f030000 4 | com.topjohnwu.magisk:style/xxxxxxxx = 0x7f040000 5 | com.topjohnwu.magisk:xml/xxxxxxxx = 0x7f050000 6 | -------------------------------------------------------------------------------- /app/src/main/java/a/a.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.utils.PatchAPK; 4 | import com.topjohnwu.signing.BootSigner; 5 | 6 | public class a { 7 | 8 | @Deprecated 9 | public static boolean patchAPK(String in, String out, String pkg) { 10 | return PatchAPK.patch(in, out, pkg); 11 | } 12 | 13 | public static boolean patchAPK(String in, String out, String pkg, String label) { 14 | return PatchAPK.patch(in, out, pkg, label); 15 | } 16 | 17 | public static void main(String[] args) throws Exception { 18 | BootSigner.main(args); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/a/b.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.ui.MainActivity; 4 | 5 | public class b extends MainActivity { 6 | /* stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/a/c.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.ui.SplashActivity; 4 | 5 | public class c extends SplashActivity { 6 | /* stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/a/e.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.App; 4 | 5 | public class e extends App { 6 | public e() { 7 | super(); 8 | } 9 | 10 | public e(Object o) { 11 | super(o); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/a/f.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.ui.flash.FlashActivity; 4 | 5 | public class f extends FlashActivity { 6 | /* stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/a/g.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import android.content.Context; 4 | 5 | import com.topjohnwu.magisk.model.update.UpdateCheckService; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.work.WorkerParameters; 9 | 10 | public class g extends w { 11 | /* Stub */ 12 | public g(@NonNull Context context, @NonNull WorkerParameters workerParams) { 13 | super(context, workerParams); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/a/h.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.model.receiver.GeneralReceiver; 4 | 5 | public class h extends GeneralReceiver { 6 | /* stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/a/j.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.model.download.DownloadService; 4 | 5 | public class j extends DownloadService { 6 | /* stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/a/m.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.ui.surequest.SuRequestActivity; 4 | 5 | public class m extends SuRequestActivity { 6 | /* stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/a/w.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.work.Worker; 7 | import androidx.work.WorkerParameters; 8 | 9 | import com.topjohnwu.magisk.base.DelegateWorker; 10 | 11 | import java.lang.reflect.ParameterizedType; 12 | 13 | public abstract class w extends Worker { 14 | 15 | /* Wrapper class to workaround Proguard -keep class * extends Worker */ 16 | 17 | private T base; 18 | 19 | @SuppressWarnings("unchecked") 20 | w(@NonNull Context context, @NonNull WorkerParameters workerParams) { 21 | super(context, workerParams); 22 | try { 23 | base = ((Class) ((ParameterizedType) getClass().getGenericSuperclass()) 24 | .getActualTypeArguments()[0]).newInstance(); 25 | base.attachWorker(this); 26 | } catch (Exception ignored) {} 27 | } 28 | 29 | @NonNull 30 | @Override 31 | public Result doWork() { 32 | if (base == null) 33 | return Result.failure(); 34 | return base.doWork(); 35 | } 36 | 37 | @Override 38 | public void onStopped() { 39 | if (base != null) 40 | base.onStopped(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/BaseReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base 2 | 3 | import android.content.BroadcastReceiver 4 | import android.content.Context 5 | import android.content.ContextWrapper 6 | import android.content.Intent 7 | import com.topjohnwu.magisk.wrap 8 | import org.koin.core.KoinComponent 9 | 10 | abstract class BaseReceiver : BroadcastReceiver(), KoinComponent { 11 | 12 | final override fun onReceive(context: Context, intent: Intent?) { 13 | onReceive(context.wrap() as ContextWrapper, intent) 14 | } 15 | 16 | abstract fun onReceive(context: ContextWrapper, intent: Intent?) 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/BaseService.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base 2 | 3 | import android.app.Service 4 | import android.content.Context 5 | import com.topjohnwu.magisk.wrap 6 | import org.koin.core.KoinComponent 7 | 8 | abstract class BaseService : Service(), KoinComponent { 9 | override fun attachBaseContext(base: Context) { 10 | super.attachBaseContext(base.wrap()) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/DelegateWorker.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base 2 | 3 | import android.content.Context 4 | import android.net.Network 5 | import android.net.Uri 6 | import androidx.annotation.MainThread 7 | import androidx.annotation.RequiresApi 8 | import androidx.work.Data 9 | import androidx.work.ListenableWorker 10 | import com.google.common.util.concurrent.ListenableFuture 11 | import java.util.* 12 | 13 | abstract class DelegateWorker { 14 | 15 | private lateinit var worker: ListenableWorker 16 | 17 | val applicationContext: Context 18 | get() = worker.applicationContext 19 | 20 | val id: UUID 21 | get() = worker.id 22 | 23 | val inputData: Data 24 | get() = worker.inputData 25 | 26 | val tags: Set 27 | get() = worker.tags 28 | 29 | val triggeredContentUris: List 30 | @RequiresApi(24) 31 | get() = worker.triggeredContentUris 32 | 33 | val triggeredContentAuthorities: List 34 | @RequiresApi(24) 35 | get() = worker.triggeredContentAuthorities 36 | 37 | val network: Network? 38 | @RequiresApi(28) 39 | get() = worker.network 40 | 41 | val runAttemptCount: Int 42 | get() = worker.runAttemptCount 43 | 44 | val isStopped: Boolean 45 | get() = worker.isStopped 46 | 47 | abstract fun doWork(): ListenableWorker.Result 48 | 49 | fun onStopped() {} 50 | 51 | fun attachWorker(w: ListenableWorker) { 52 | worker = w 53 | } 54 | 55 | @MainThread 56 | fun startWork(): ListenableFuture { 57 | return worker.startWork() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/viewmodel/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base.viewmodel 2 | 3 | import com.topjohnwu.magisk.base.BaseActivity 4 | import com.topjohnwu.magisk.extensions.doOnSubscribeUi 5 | import com.topjohnwu.magisk.model.events.BackPressEvent 6 | import com.topjohnwu.magisk.model.events.PermissionEvent 7 | import com.topjohnwu.magisk.model.events.ViewActionEvent 8 | import com.topjohnwu.magisk.utils.KObservableField 9 | import io.reactivex.Observable 10 | import io.reactivex.subjects.PublishSubject 11 | import com.topjohnwu.magisk.Info.isConnected as gIsConnected 12 | 13 | 14 | abstract class BaseViewModel( 15 | initialState: State = State.LOADING 16 | ) : LoadingViewModel(initialState) { 17 | 18 | val isConnected = object : KObservableField(gIsConnected.value, gIsConnected) { 19 | override fun get(): Boolean { 20 | return gIsConnected.value 21 | } 22 | } 23 | 24 | fun withView(action: BaseActivity<*, *>.() -> Unit) { 25 | ViewActionEvent(action).publish() 26 | } 27 | 28 | fun withPermissions(vararg permissions: String): Observable { 29 | val subject = PublishSubject.create() 30 | return subject.doOnSubscribeUi { PermissionEvent(permissions.toList(), subject).publish() } 31 | } 32 | 33 | fun back() = BackPressEvent().publish() 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/viewmodel/ObservableViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base.viewmodel 2 | 3 | import androidx.databinding.Observable 4 | import androidx.databinding.PropertyChangeRegistry 5 | import androidx.lifecycle.ViewModel 6 | 7 | /** 8 | * Copy of [android.databinding.BaseObservable] which extends [ViewModel] 9 | */ 10 | abstract class ObservableViewModel : TeanityViewModel(), Observable { 11 | 12 | @Transient 13 | private var callbacks: PropertyChangeRegistry? = null 14 | 15 | @Synchronized 16 | override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) { 17 | if (callbacks == null) { 18 | callbacks = PropertyChangeRegistry() 19 | } 20 | callbacks?.add(callback) 21 | } 22 | 23 | @Synchronized 24 | override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) { 25 | callbacks?.remove(callback) 26 | } 27 | 28 | /** 29 | * Notifies listeners that all properties of this instance have changed. 30 | */ 31 | @Synchronized 32 | fun notifyChange() { 33 | callbacks?.notifyCallbacks(this, 0, null) 34 | } 35 | 36 | /** 37 | * Notifies listeners that a specific property has changed. The getter for the property 38 | * that changes should be marked with [android.databinding.Bindable] to generate a field in 39 | * `BR` to be used as `fieldId`. 40 | * 41 | * @param fieldId The generated BR id for the Bindable field. 42 | */ 43 | fun notifyPropertyChanged(fieldId: Int) { 44 | callbacks?.notifyCallbacks(this, fieldId, null) 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/viewmodel/StatefulViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base.viewmodel 2 | 3 | abstract class StatefulViewModel>( 4 | val defaultState: State 5 | ) : ObservableViewModel() { 6 | 7 | var state: State = defaultState 8 | set(value) { 9 | field = value 10 | notifyStateChanged() 11 | } 12 | 13 | open fun notifyStateChanged() = Unit 14 | 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/base/viewmodel/TeanityViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.base.viewmodel 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import com.topjohnwu.magisk.model.events.SimpleViewEvent 7 | import com.topjohnwu.magisk.model.events.ViewEvent 8 | import io.reactivex.disposables.CompositeDisposable 9 | import io.reactivex.disposables.Disposable 10 | 11 | abstract class TeanityViewModel : ViewModel() { 12 | 13 | private val disposables = CompositeDisposable() 14 | private val _viewEvents = MutableLiveData() 15 | val viewEvents: LiveData get() = _viewEvents 16 | 17 | override fun onCleared() { 18 | super.onCleared() 19 | disposables.clear() 20 | } 21 | 22 | fun Event.publish() { 23 | _viewEvents.value = this 24 | } 25 | 26 | fun Int.publish() { 27 | _viewEvents.value = SimpleViewEvent(this) 28 | } 29 | 30 | fun Disposable.add() { 31 | disposables.add(this) 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/database/SettingsDao.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.database 2 | 3 | import com.topjohnwu.magisk.data.database.magiskdb.Delete 4 | import com.topjohnwu.magisk.data.database.magiskdb.BaseDao 5 | import com.topjohnwu.magisk.data.database.magiskdb.Replace 6 | import com.topjohnwu.magisk.data.database.magiskdb.Select 7 | 8 | class SettingsDao : BaseDao() { 9 | 10 | override val table = Table.SETTINGS 11 | 12 | fun delete(key: String) = query { 13 | condition { equals("key", key) } 14 | }.ignoreElement() 15 | 16 | fun put(key: String, value: Int) = query { 17 | values("key" to key, "value" to value) 18 | }.ignoreElement() 19 | 20 | fun fetch(key: String, default: Int = -1) = query { 21 | fields("value") 22 | condition { equals("key", key) } 23 | }.map { it.firstOrNull()?.values?.firstOrNull() ?: default } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/database/SuLogDao.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.database 2 | 3 | import androidx.room.* 4 | import com.topjohnwu.magisk.model.entity.MagiskLog 5 | import io.reactivex.Completable 6 | import io.reactivex.Single 7 | import java.util.* 8 | 9 | @Database(version = 1, entities = [MagiskLog::class]) 10 | abstract class SuLogDatabase : RoomDatabase() { 11 | 12 | abstract fun suLogDao(): SuLogDao 13 | } 14 | 15 | @Dao 16 | abstract class SuLogDao(private val db: SuLogDatabase) { 17 | 18 | private val twoWeeksAgo = 19 | Calendar.getInstance().apply { add(Calendar.WEEK_OF_YEAR, -2) }.timeInMillis 20 | 21 | fun deleteAll() = Completable.fromAction { db.clearAllTables() } 22 | 23 | fun fetchAll() = deleteOutdated().andThen(fetch()) 24 | 25 | @Query("SELECT * FROM logs ORDER BY time DESC") 26 | protected abstract fun fetch(): Single> 27 | 28 | @Insert 29 | abstract fun insert(log: MagiskLog): Completable 30 | 31 | @Query("DELETE FROM logs WHERE time < :timeout") 32 | protected abstract fun deleteOutdated( 33 | timeout: Long = twoWeeksAgo 34 | ): Completable 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/database/magiskdb/BaseDao.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.database.magiskdb 2 | 3 | import androidx.annotation.StringDef 4 | import com.topjohnwu.superuser.Shell 5 | import io.reactivex.Single 6 | 7 | abstract class BaseDao { 8 | 9 | object Table { 10 | const val POLICY = "policies" 11 | const val LOG = "logs" 12 | const val SETTINGS = "settings" 13 | const val STRINGS = "strings" 14 | } 15 | 16 | @StringDef(Table.POLICY, Table.LOG, Table.SETTINGS, Table.STRINGS) 17 | @Retention(AnnotationRetention.SOURCE) 18 | annotation class TableStrict 19 | 20 | @TableStrict 21 | abstract val table: String 22 | 23 | inline fun query(builder: Builder.() -> Unit = {}) = 24 | Builder::class.java.newInstance() 25 | .apply { table = this@BaseDao.table } 26 | .apply(builder) 27 | .toString() 28 | .let { Query(it) } 29 | .query() 30 | 31 | } 32 | 33 | fun Query.query() = query.su() 34 | 35 | private fun String.suRaw() = Single.fromCallable { Shell.su(this).exec().out } 36 | private fun String.su() = suRaw().map { it.toMap() } 37 | 38 | private fun List.toMap() = map { it.split(Regex("\\|")) } 39 | .map { it.toMapInternal() } 40 | 41 | private fun List.toMapInternal() = map { it.split("=", limit = 2) } 42 | .filter { it.size == 2 } 43 | .map { Pair(it[0], it[1]) } 44 | .toMap() 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.repository 2 | 3 | import com.topjohnwu.magisk.Const 4 | import com.topjohnwu.magisk.data.database.SuLogDao 5 | import com.topjohnwu.magisk.model.entity.MagiskLog 6 | import com.topjohnwu.magisk.model.entity.WrappedMagiskLog 7 | import com.topjohnwu.superuser.Shell 8 | import io.reactivex.Completable 9 | import io.reactivex.Single 10 | import java.util.concurrent.TimeUnit 11 | 12 | 13 | class LogRepository( 14 | private val logDao: SuLogDao 15 | ) { 16 | 17 | fun fetchLogs() = logDao.fetchAll().map { it.wrap() } 18 | 19 | fun fetchMagiskLogs() = Single.fromCallable { 20 | Shell.su("tail -n 5000 ${Const.MAGISK_LOG}").exec().out 21 | }.flattenAsFlowable { it }.filter { it.isNotEmpty() } 22 | 23 | fun clearLogs() = logDao.deleteAll() 24 | 25 | fun clearMagiskLogs() = Completable.fromAction { 26 | Shell.su("echo -n > ${Const.MAGISK_LOG}").exec() 27 | } 28 | 29 | fun insert(log: MagiskLog) = logDao.insert(log) 30 | 31 | private fun List.wrap(): List { 32 | val day = TimeUnit.DAYS.toMillis(1) 33 | return groupBy { it.time / day } 34 | .map { WrappedMagiskLog(it.key * day, it.value) } 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/repository/StringRepository.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.repository 2 | 3 | import com.topjohnwu.magisk.data.network.GithubRawServices 4 | import com.topjohnwu.magisk.model.entity.module.Repo 5 | 6 | class StringRepository( 7 | private val api: GithubRawServices 8 | ) { 9 | 10 | fun getString(url: String) = api.fetchString(url) 11 | 12 | fun getMetadata(repo: Repo) = api.fetchModuleInfo(repo.id, "module.prop") 13 | 14 | fun getReadme(repo: Repo) = api.fetchModuleInfo(repo.id, "README.md") 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/databinding/AdaptersGeneric.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.databinding 2 | 3 | import android.view.View 4 | import androidx.core.view.isGone 5 | import androidx.core.view.isInvisible 6 | import androidx.databinding.BindingAdapter 7 | 8 | @BindingAdapter("gone") 9 | fun setGone(view: View, gone: Boolean) { 10 | view.isGone = gone 11 | } 12 | 13 | @BindingAdapter("invisible") 14 | fun setInvisible(view: View, invisible: Boolean) { 15 | view.isInvisible = invisible 16 | } 17 | 18 | @BindingAdapter("goneUnless") 19 | fun setGoneUnless(view: View, goneUnless: Boolean) { 20 | setGone(view, goneUnless.not()) 21 | } 22 | 23 | @BindingAdapter("invisibleUnless") 24 | fun setInvisibleUnless(view: View, invisibleUnless: Boolean) { 25 | setInvisible(view, invisibleUnless.not()) 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/databinding/BindingBoundAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.databinding 2 | 3 | import androidx.databinding.ViewDataBinding 4 | import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter 5 | 6 | open class BindingBoundAdapter : BindingRecyclerViewAdapter() { 7 | 8 | override fun onBindBinding(binding: ViewDataBinding, variableId: Int, layoutRes: Int, position: Int, item: RvItem) { 9 | super.onBindBinding(binding, variableId, layoutRes, position, item) 10 | 11 | item.onBindingBound(binding) 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/di/DatabaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.di 2 | 3 | import android.content.Context 4 | import androidx.room.Room 5 | import com.topjohnwu.magisk.data.database.* 6 | import com.topjohnwu.magisk.tasks.RepoUpdater 7 | import org.koin.dsl.module 8 | 9 | 10 | val databaseModule = module { 11 | single { PolicyDao(get()) } 12 | single { SettingsDao() } 13 | single { StringDao() } 14 | single { createRepoDatabase(get()).repoDao() } 15 | single { createSuLogDatabase(get(Protected)).suLogDao() } 16 | single { RepoUpdater(get(), get()) } 17 | } 18 | 19 | fun createRepoDatabase(context: Context) = 20 | Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") 21 | .fallbackToDestructiveMigration() 22 | .build() 23 | 24 | fun createSuLogDatabase(context: Context) = 25 | Room.databaseBuilder(context, SuLogDatabase::class.java, "sulogs.db") 26 | .fallbackToDestructiveMigration() 27 | .build() 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/di/Modules.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.di 2 | 3 | val koinModules = listOf( 4 | applicationModule, 5 | networkingModule, 6 | databaseModule, 7 | repositoryModule, 8 | viewModelModules 9 | ) 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/di/RepositoryModule.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.di 2 | 3 | import com.topjohnwu.magisk.data.repository.LogRepository 4 | import com.topjohnwu.magisk.data.repository.MagiskRepository 5 | import com.topjohnwu.magisk.data.repository.StringRepository 6 | import org.koin.dsl.module 7 | 8 | 9 | val repositoryModule = module { 10 | single { MagiskRepository(get(), get()) } 11 | single { LogRepository(get()) } 12 | single { StringRepository(get()) } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/di/ViewModelsModule.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.di 2 | 3 | import android.net.Uri 4 | import com.topjohnwu.magisk.ui.MainViewModel 5 | import com.topjohnwu.magisk.ui.flash.FlashViewModel 6 | import com.topjohnwu.magisk.ui.hide.HideViewModel 7 | import com.topjohnwu.magisk.ui.home.HomeViewModel 8 | import com.topjohnwu.magisk.ui.log.LogViewModel 9 | import com.topjohnwu.magisk.ui.module.ModuleViewModel 10 | import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel 11 | import com.topjohnwu.magisk.ui.surequest.SuRequestViewModel 12 | import org.koin.androidx.viewmodel.dsl.viewModel 13 | import org.koin.dsl.module 14 | 15 | 16 | val viewModelModules = module { 17 | viewModel { MainViewModel() } 18 | viewModel { HomeViewModel(get()) } 19 | viewModel { SuperuserViewModel(get(), get(), get(), get()) } 20 | viewModel { HideViewModel(get(), get()) } 21 | viewModel { ModuleViewModel(get(), get(), get()) } 22 | viewModel { LogViewModel(get(), get()) } 23 | viewModel { (action: String, file: Uri, additional: Uri) -> 24 | FlashViewModel(action, file, additional, get()) 25 | } 26 | viewModel { SuRequestViewModel(get(), get(), get(SUTimeout), get()) } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/Dimens.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import android.content.res.Resources 4 | import kotlin.math.ceil 5 | import kotlin.math.roundToInt 6 | 7 | fun Int.toDp(): Int = ceil(this / Resources.getSystem().displayMetrics.density).roundToInt() 8 | 9 | fun Int.toPx(): Int = (this * Resources.getSystem().displayMetrics.density).roundToInt() 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/XBinding.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import com.topjohnwu.magisk.utils.KObservableField 4 | 5 | 6 | fun KObservableField.toggle() { 7 | value = !value 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/XKoin.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import org.koin.core.context.GlobalContext 4 | import org.koin.core.parameter.ParametersDefinition 5 | import org.koin.core.qualifier.Qualifier 6 | 7 | fun getKoin() = GlobalContext.get().koin 8 | 9 | inline fun inject( 10 | qualifier: Qualifier? = null, 11 | noinline parameters: ParametersDefinition? = null 12 | ) = lazy { get(qualifier, parameters) } 13 | 14 | inline fun get( 15 | qualifier: Qualifier? = null, 16 | noinline parameters: ParametersDefinition? = null 17 | ): T = getKoin().get(qualifier, parameters) -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/XSU.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import com.topjohnwu.magisk.Info 4 | import com.topjohnwu.superuser.Shell 5 | import com.topjohnwu.superuser.io.SuFileInputStream 6 | import com.topjohnwu.superuser.io.SuFileOutputStream 7 | import java.io.File 8 | 9 | fun reboot(reason: String = if (Info.recovery) "recovery" else "") { 10 | Shell.su("/system/bin/svc power reboot $reason || /system/bin/reboot $reason").submit() 11 | } 12 | 13 | fun File.suOutputStream() = SuFileOutputStream(this) 14 | fun File.suInputStream() = SuFileInputStream(this) -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/XString.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import android.content.res.Resources 4 | 5 | val specialChars = arrayOf('!', '@', '#', '$', '%', '&', '?') 6 | 7 | fun String.replaceRandomWithSpecial(): String { 8 | var random: Char 9 | do { 10 | random = random() 11 | } while (random == '.') 12 | return replace(random, specialChars.random()) 13 | } 14 | 15 | fun StringBuilder.appendIf(condition: Boolean, builder: StringBuilder.() -> Unit) = 16 | if (condition) apply(builder) else this 17 | 18 | fun Int.res(vararg args: Any): String { 19 | val resources: Resources by inject() 20 | return resources.getString(this, *args) 21 | } 22 | 23 | fun String.trimEmptyToNull(): String? = if (isBlank()) null else this 24 | 25 | fun String.legalFilename() = replace(" ", "_").replace("'", "").replace("\"", "") 26 | .replace("$", "").replace("`", "").replace("*", "").replace("/", "_") 27 | .replace("#", "").replace("@", "").replace("\\", "_") 28 | 29 | fun String.isEmptyInternal() = isNullOrBlank() -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/XTime.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import com.topjohnwu.magisk.utils.currentLocale 4 | import java.text.DateFormat 5 | import java.text.ParseException 6 | import java.text.SimpleDateFormat 7 | 8 | val now get() = System.currentTimeMillis() 9 | 10 | fun Long.toTime(format: DateFormat) = format.format(this).orEmpty() 11 | fun String.toTime(format: DateFormat) = try { 12 | format.parse(this)?.time ?: -1 13 | } catch (e: ParseException) { 14 | -1L 15 | } 16 | 17 | val timeFormatFull by lazy { SimpleDateFormat("yyyy/MM/dd_HH:mm:ss", currentLocale) } 18 | val timeFormatStandard by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", currentLocale) } 19 | val timeFormatMedium by lazy { DateFormat.getDateInstance(DateFormat.MEDIUM, currentLocale) } 20 | val timeFormatTime by lazy { SimpleDateFormat("h:mm a", currentLocale) } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/extensions/XView.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.extensions 2 | 3 | import android.view.View 4 | import android.view.ViewTreeObserver 5 | 6 | fun View.setOnViewReadyListener(callback: () -> Unit) = addOnGlobalLayoutListener(true, callback) 7 | 8 | fun View.addOnGlobalLayoutListener(oneShot: Boolean = false, callback: () -> Unit) = 9 | viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { 10 | override fun onGlobalLayout() { 11 | if (oneShot) viewTreeObserver.removeOnGlobalLayoutListener(this) 12 | callback() 13 | } 14 | }) -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/binding/BindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.binding 2 | 3 | import androidx.databinding.ViewDataBinding 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.topjohnwu.magisk.databinding.ComparableRvItem 6 | import com.topjohnwu.magisk.model.entity.recycler.LenientRvItem 7 | import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter 8 | 9 | class BindingAdapter : BindingRecyclerViewAdapter>() { 10 | 11 | private var recyclerView: RecyclerView? = null 12 | 13 | override fun onBindBinding( 14 | binding: ViewDataBinding, 15 | variableId: Int, 16 | layoutRes: Int, 17 | position: Int, 18 | item: ComparableRvItem<*> 19 | ) { 20 | super.onBindBinding(binding, variableId, layoutRes, position, item) 21 | 22 | when (item) { 23 | is LenientRvItem -> { 24 | val recycler = recyclerView ?: return 25 | item.onBindingBound(binding) 26 | item.onBindingBound(binding, recycler) 27 | } 28 | else -> item.onBindingBound(binding) 29 | } 30 | } 31 | 32 | override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { 33 | super.onAttachedToRecyclerView(recyclerView) 34 | this.recyclerView = recyclerView 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/download/ModuleProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.download 2 | 3 | import com.topjohnwu.magisk.extensions.withStreams 4 | import java.io.File 5 | import java.io.InputStream 6 | import java.util.zip.ZipEntry 7 | import java.util.zip.ZipInputStream 8 | import java.util.zip.ZipOutputStream 9 | 10 | fun InputStream.toModule(file: File, installer: InputStream) { 11 | 12 | val input = ZipInputStream(buffered()) 13 | val output = ZipOutputStream(file.outputStream().buffered()) 14 | 15 | withStreams(input, output) { zin, zout -> 16 | zout.putNextEntry(ZipEntry("META-INF/")) 17 | zout.putNextEntry(ZipEntry("META-INF/com/")) 18 | zout.putNextEntry(ZipEntry("META-INF/com/google/")) 19 | zout.putNextEntry(ZipEntry("META-INF/com/google/android/")) 20 | zout.putNextEntry(ZipEntry("META-INF/com/google/android/update-binary")) 21 | installer.copyTo(zout) 22 | 23 | zout.putNextEntry(ZipEntry("META-INF/com/google/android/updater-script")) 24 | zout.write("#MAGISK\n".toByteArray(charset("UTF-8"))) 25 | 26 | var off = -1 27 | var entry: ZipEntry? = zin.nextEntry 28 | while (entry != null) { 29 | if (off < 0) { 30 | off = entry.name.indexOf('/') + 1 31 | } 32 | 33 | val path = entry.name.substring(off) 34 | if (path.isNotEmpty() && !path.startsWith("META-INF")) { 35 | zout.putNextEntry(ZipEntry(path)) 36 | if (!entry.isDirectory) { 37 | zin.copyTo(zout) 38 | } 39 | } 40 | 41 | entry = zin.nextEntry 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/HideAppInfo.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity 2 | 3 | import android.content.pm.ApplicationInfo 4 | import android.graphics.drawable.Drawable 5 | import com.topjohnwu.magisk.extensions.packageInfo 6 | import com.topjohnwu.magisk.extensions.processes 7 | 8 | class HideAppInfo( 9 | val info: ApplicationInfo, 10 | val name: String, 11 | val icon: Drawable 12 | ) { 13 | 14 | val processes = info.packageInfo?.processes?.distinct() ?: listOf(info.packageName) 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/HideTarget.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity 2 | 3 | class HideTarget(line: String) { 4 | 5 | private val split = line.split(Regex("\\|"), 2) 6 | 7 | val packageName = split[0] 8 | val process = split.getOrElse(1) { packageName } 9 | 10 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/MagiskLog.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity 2 | 3 | import androidx.room.Entity 4 | import androidx.room.Ignore 5 | import androidx.room.PrimaryKey 6 | import com.topjohnwu.magisk.extensions.now 7 | import com.topjohnwu.magisk.extensions.timeFormatTime 8 | import com.topjohnwu.magisk.extensions.toTime 9 | import com.topjohnwu.magisk.model.entity.MagiskPolicy.Companion.ALLOW 10 | 11 | @Entity(tableName = "logs") 12 | data class MagiskLog( 13 | val fromUid: Int, 14 | val toUid: Int, 15 | val fromPid: Int, 16 | val packageName: String, 17 | val appName: String, 18 | val command: String, 19 | val action: Boolean, 20 | val time: Long = -1 21 | ) { 22 | @PrimaryKey(autoGenerate = true) var id: Int = 0 23 | @Ignore val timeString = time.toTime(timeFormatTime) 24 | } 25 | 26 | data class WrappedMagiskLog( 27 | val time: Long, 28 | val items: List 29 | ) 30 | 31 | fun MagiskPolicy.toLog( 32 | toUid: Int, 33 | fromPid: Int, 34 | command: String 35 | ) = MagiskLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, now) 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/UpdateInfo.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity 2 | 3 | import android.os.Parcelable 4 | import kotlinx.android.parcel.Parcelize 5 | import se.ansman.kotshi.JsonSerializable 6 | 7 | @JsonSerializable 8 | data class UpdateInfo( 9 | val app: ManagerJson = ManagerJson(), 10 | val uninstaller: UninstallerJson = UninstallerJson(), 11 | val magisk: MagiskJson = MagiskJson(), 12 | val stub: StubJson = StubJson() 13 | ) 14 | 15 | @JsonSerializable 16 | data class UninstallerJson( 17 | val link: String = "" 18 | ) 19 | 20 | @JsonSerializable 21 | data class MagiskJson( 22 | val version: String = "", 23 | val versionCode: Int = -1, 24 | val link: String = "", 25 | val note: String = "", 26 | val md5: String = "" 27 | ) 28 | 29 | @Parcelize 30 | @JsonSerializable 31 | data class ManagerJson( 32 | val version: String = "", 33 | val versionCode: Int = -1, 34 | val link: String = "", 35 | val note: String = "" 36 | ) : Parcelable 37 | 38 | @JsonSerializable 39 | data class StubJson( 40 | val versionCode: Int = -1, 41 | val link: String = "" 42 | ) 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/internal/Configuration.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.internal 2 | 3 | import android.net.Uri 4 | import android.os.Parcelable 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | sealed class Configuration : Parcelable { 8 | 9 | sealed class Flash : Configuration() { 10 | 11 | @Parcelize 12 | object Primary : Flash() 13 | 14 | @Parcelize 15 | object Secondary : Flash() 16 | 17 | } 18 | 19 | sealed class APK : Configuration() { 20 | 21 | @Parcelize 22 | object Upgrade : APK() 23 | 24 | @Parcelize 25 | object Restore : APK() 26 | } 27 | 28 | @Parcelize 29 | object Download : Configuration() 30 | 31 | @Parcelize 32 | object Uninstall : Configuration() 33 | 34 | @Parcelize 35 | data class Patch(val fileUri: Uri) : Configuration() 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/module/BaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.module 2 | 3 | abstract class BaseModule : Comparable { 4 | abstract var id: String 5 | protected set 6 | abstract var name: String 7 | protected set 8 | abstract var author: String 9 | protected set 10 | abstract var version: String 11 | protected set 12 | abstract var versionCode: Int 13 | protected set 14 | abstract var description: String 15 | protected set 16 | 17 | @Throws(NumberFormatException::class) 18 | protected fun parseProps(props: List) { 19 | for (line in props) { 20 | val prop = line.split("=".toRegex(), 2).map { it.trim() } 21 | if (prop.size != 2) 22 | continue 23 | 24 | val key = prop[0] 25 | val value = prop[1] 26 | if (key.isEmpty() || key[0] == '#') 27 | continue 28 | 29 | when (key) { 30 | "id" -> id = value 31 | "name" -> name = value 32 | "version" -> version = value 33 | "versionCode" -> versionCode = value.toInt() 34 | "author" -> author = value 35 | "description" -> description = value 36 | } 37 | } 38 | } 39 | 40 | override operator fun compareTo(other: BaseModule) = name.compareTo(other.name, true) 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ConsoleRvItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.recycler 2 | 3 | import android.widget.TextView 4 | import androidx.core.view.updateLayoutParams 5 | import androidx.databinding.ViewDataBinding 6 | import androidx.recyclerview.widget.RecyclerView 7 | import com.topjohnwu.magisk.R 8 | 9 | class ConsoleRvItem(val item: String) : LenientRvItem() { 10 | override val layoutRes: Int = R.layout.item_console 11 | 12 | override fun onBindingBound(binding: ViewDataBinding, recyclerView: RecyclerView) { 13 | val view = binding.root as TextView 14 | view.measure(0, 0) 15 | val desiredWidth = view.measuredWidth 16 | 17 | view.updateLayoutParams { width = desiredWidth } 18 | 19 | if (recyclerView.width < desiredWidth) { 20 | recyclerView.requestLayout() 21 | } 22 | } 23 | 24 | override fun contentSameAs(other: ConsoleRvItem) = itemSameAs(other) 25 | override fun itemSameAs(other: ConsoleRvItem) = item == other.item 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/LenientRvItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.recycler 2 | 3 | import androidx.databinding.ViewDataBinding 4 | import androidx.recyclerview.widget.RecyclerView 5 | import com.topjohnwu.magisk.databinding.ComparableRvItem 6 | 7 | /** 8 | * This item addresses issues where enclosing recycler has to be invalidated or generally 9 | * manipulated with. This shouldn't be however necessary for 99.9% of use-cases. Refrain from using 10 | * this item as it provides virtually no additional functionality. Stick with ComparableRvItem. 11 | * */ 12 | abstract class LenientRvItem : ComparableRvItem() { 13 | 14 | open fun onBindingBound(binding: ViewDataBinding, recyclerView: RecyclerView) {} 15 | 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SectionRvItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.recycler 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.databinding.ComparableRvItem 5 | 6 | class SectionRvItem(val text: String) : ComparableRvItem() { 7 | override val layoutRes: Int = R.layout.item_section 8 | 9 | override fun contentSameAs(other: SectionRvItem) = itemSameAs(other) 10 | override fun itemSameAs(other: SectionRvItem) = text == other.text 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SpinnerRvItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.recycler 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.databinding.ComparableRvItem 5 | 6 | class SpinnerRvItem(val item: String) : ComparableRvItem() { 7 | 8 | override val layoutRes: Int = R.layout.item_spinner 9 | 10 | override fun contentSameAs(other: SpinnerRvItem) = itemSameAs(other) 11 | override fun itemSameAs(other: SpinnerRvItem) = item == other.item 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/entity/state/IndeterminateState.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.entity.state 2 | 3 | enum class IndeterminateState { 4 | CHECKED, INDETERMINATE, UNCHECKED 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/events/EventHandler.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.events 2 | 3 | internal interface EventHandler { 4 | 5 | /** 6 | * Called for all [ViewEvent]s published by associated viewModel. 7 | * For [SimpleViewEvent]s, both this and [onSimpleEventDispatched] 8 | * methods are called - you can choose the way how you handle them. 9 | */ 10 | fun onEventDispatched(event: ViewEvent) {} 11 | 12 | /** 13 | * Called for all [SimpleViewEvent]s published by associated viewModel. 14 | * Both this and [onEventDispatched] methods are called - you can choose 15 | * the way how you handle them. 16 | */ 17 | fun onSimpleEventDispatched(event: Int) {} 18 | 19 | val viewEventObserver get() = ViewEventObserver { 20 | onEventDispatched(it) 21 | if (it is SimpleViewEvent) { 22 | onSimpleEventDispatched(it.event) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/events/RxEvents.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.events 2 | 3 | import com.topjohnwu.magisk.model.entity.MagiskPolicy 4 | import com.topjohnwu.magisk.model.entity.recycler.HideProcessRvItem 5 | import com.topjohnwu.magisk.model.entity.recycler.ModuleRvItem 6 | import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem 7 | import com.topjohnwu.magisk.utils.RxBus 8 | 9 | class HideProcessEvent(val item: HideProcessRvItem) : RxBus.Event 10 | 11 | class PolicyEnableEvent(val item: PolicyRvItem, val enable: Boolean) : RxBus.Event 12 | sealed class PolicyUpdateEvent(val item: MagiskPolicy) : RxBus.Event { 13 | class Notification(item: MagiskPolicy) : PolicyUpdateEvent(item) 14 | class Log(item: MagiskPolicy) : PolicyUpdateEvent(item) 15 | } 16 | 17 | class ModuleUpdatedEvent(val item: ModuleRvItem) : RxBus.Event 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/events/SimpleViewEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.events 2 | 3 | class SimpleViewEvent( 4 | val event: Int 5 | ) : ViewEvent() -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.events 2 | 3 | import android.content.Context 4 | import androidx.annotation.StringRes 5 | import com.google.android.material.snackbar.Snackbar 6 | 7 | class SnackbarEvent private constructor( 8 | @StringRes private val messageRes: Int, 9 | private val messageString: String?, 10 | val length: Int, 11 | val f: Snackbar.() -> Unit 12 | ) : ViewEvent() { 13 | 14 | constructor( 15 | @StringRes messageRes: Int, 16 | length: Int = Snackbar.LENGTH_SHORT, 17 | f: Snackbar.() -> Unit = {} 18 | ) : this(messageRes, null, length, f) 19 | 20 | constructor( 21 | message: String, 22 | length: Int = Snackbar.LENGTH_SHORT, 23 | f: Snackbar.() -> Unit = {} 24 | ) : this(-1, message, length, f) 25 | 26 | fun message(context: Context): String = messageString ?: context.getString(messageRes) 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.events 2 | 3 | import androidx.lifecycle.Observer 4 | 5 | /** 6 | * Observer for [ViewEvent]s, which automatically checks if event was handled 7 | */ 8 | class ViewEventObserver(private val onEventUnhandled: (ViewEvent) -> Unit) : Observer { 9 | override fun onChanged(event: ViewEvent?) { 10 | event?.let { 11 | if (!it.handled) { 12 | it.handled = true 13 | onEventUnhandled(it) 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.events 2 | 3 | import com.topjohnwu.magisk.base.BaseActivity 4 | import com.topjohnwu.magisk.model.entity.module.Repo 5 | import io.reactivex.subjects.PublishSubject 6 | 7 | /** 8 | * Class for passing events from ViewModels to Activities/Fragments 9 | * Variable [handled] used so each event is handled only once 10 | * (see https://medium.com/google-developers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150) 11 | * Use [ViewEventObserver] for observing these events 12 | */ 13 | abstract class ViewEvent { 14 | 15 | var handled = false 16 | } 17 | 18 | data class OpenLinkEvent(val url: String) : ViewEvent() 19 | 20 | class ManagerInstallEvent : ViewEvent() 21 | class MagiskInstallEvent : ViewEvent() 22 | 23 | class ManagerChangelogEvent : ViewEvent() 24 | class MagiskChangelogEvent : ViewEvent() 25 | 26 | class UninstallEvent : ViewEvent() 27 | class EnvFixEvent : ViewEvent() 28 | 29 | class UpdateSafetyNetEvent : ViewEvent() 30 | 31 | class ViewActionEvent(val action: BaseActivity<*, *>.() -> Unit) : ViewEvent() 32 | 33 | class OpenFilePickerEvent : ViewEvent() 34 | 35 | class OpenChangelogEvent(val item: Repo) : ViewEvent() 36 | class InstallModuleEvent(val item: Repo) : ViewEvent() 37 | 38 | class PageChangedEvent : ViewEvent() 39 | 40 | class PermissionEvent( 41 | val permissions: List, 42 | val callback: PublishSubject 43 | ) : ViewEvent() 44 | 45 | class BackPressEvent : ViewEvent() 46 | 47 | class DieEvent : ViewEvent() 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/flash/FlashResultListener.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.flash 2 | 3 | interface FlashResultListener { 4 | 5 | fun onResult(isSuccess: Boolean) 6 | 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/navigation/Navigator.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.navigation 2 | 3 | import androidx.fragment.app.Fragment 4 | import kotlin.reflect.KClass 5 | 6 | interface Navigator { 7 | 8 | //TODO Elevate Fragment to MagiskFragment<*,*> once everything is on board with it 9 | val baseFragments: List> 10 | 11 | fun navigateTo(event: MagiskNavigationEvent) 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/observer/Observer.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.observer 2 | 3 | import androidx.databinding.Observable 4 | import androidx.databinding.ObservableField 5 | import java.io.Serializable 6 | 7 | 8 | class Observer(vararg dependencies: Observable, private val observer: () -> T) : 9 | ObservableField(*dependencies), Serializable { 10 | 11 | val value: T get() = observer() 12 | 13 | @Deprecated( 14 | message = "Use KObservableField.value syntax from code", 15 | replaceWith = ReplaceWith("value") 16 | ) 17 | override fun get(): T { 18 | return value 19 | } 20 | 21 | @Deprecated( 22 | message = "Observer cannot be set", 23 | level = DeprecationLevel.HIDDEN 24 | ) 25 | override fun set(newValue: T) { 26 | } 27 | 28 | override fun toString(): String { 29 | return "Observer(value=$value)" 30 | } 31 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/permissions/PermissionRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.permissions 2 | 3 | typealias SimpleCallback = () -> Unit 4 | typealias PermissionRationaleCallback = (List) -> Unit 5 | 6 | class PermissionRequestBuilder { 7 | 8 | private var onSuccessCallback: SimpleCallback = {} 9 | private var onFailureCallback: SimpleCallback = {} 10 | private var onShowRationaleCallback: PermissionRationaleCallback = {} 11 | 12 | fun onSuccess(callback: SimpleCallback) { 13 | onSuccessCallback = callback 14 | } 15 | 16 | fun onFailure(callback: SimpleCallback) { 17 | onFailureCallback = callback 18 | } 19 | 20 | fun onShowRationale(callback: PermissionRationaleCallback) { 21 | onShowRationaleCallback = callback 22 | } 23 | 24 | fun build(): PermissionRequest { 25 | return PermissionRequest(onSuccessCallback, onFailureCallback, onShowRationaleCallback) 26 | } 27 | 28 | } 29 | 30 | class PermissionRequest( 31 | private val onSuccessCallback: SimpleCallback, 32 | private val onFailureCallback: SimpleCallback, 33 | private val onShowRationaleCallback: PermissionRationaleCallback 34 | ) { 35 | 36 | fun onSuccess() = onSuccessCallback() 37 | fun onFailure() = onFailureCallback() 38 | fun onShowRationale(permissions: List) = onShowRationaleCallback(permissions) 39 | 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/BooleanProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.extensions.trimEmptyToNull 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class BooleanProperty( 9 | private val name: String, 10 | private val default: Boolean, 11 | private val commit: Boolean 12 | ) : Property(), ReadWriteProperty { 13 | 14 | override operator fun getValue( 15 | thisRef: PreferenceModel, 16 | property: KProperty<*> 17 | ): Boolean { 18 | val prefName = name.trimEmptyToNull() ?: property.name 19 | return thisRef.prefs.get(prefName, default) 20 | } 21 | 22 | override operator fun setValue( 23 | thisRef: PreferenceModel, 24 | property: KProperty<*>, 25 | value: Boolean 26 | ) { 27 | val prefName = name.trimEmptyToNull() ?: property.name 28 | thisRef.prefs.edit(commit) { put(prefName, value) } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/FloatProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.extensions.trimEmptyToNull 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class FloatProperty( 9 | private val name: String, 10 | private val default: Float, 11 | private val commit: Boolean 12 | ) : Property(), ReadWriteProperty { 13 | 14 | override operator fun getValue( 15 | thisRef: PreferenceModel, 16 | property: KProperty<*> 17 | ): Float { 18 | val prefName = name.trimEmptyToNull() ?: property.name 19 | return thisRef.prefs.get(prefName, default) 20 | } 21 | 22 | override operator fun setValue( 23 | thisRef: PreferenceModel, 24 | property: KProperty<*>, 25 | value: Float 26 | ) { 27 | val prefName = name.trimEmptyToNull() ?: property.name 28 | thisRef.prefs.edit(commit) { put(prefName, value) } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/IntProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.extensions.trimEmptyToNull 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class IntProperty( 9 | private val name: String, 10 | private val default: Int, 11 | private val commit: Boolean 12 | ) : Property(), ReadWriteProperty { 13 | 14 | override operator fun getValue( 15 | thisRef: PreferenceModel, 16 | property: KProperty<*> 17 | ): Int { 18 | val prefName = name.trimEmptyToNull() ?: property.name 19 | return thisRef.prefs.get(prefName, default) 20 | } 21 | 22 | override operator fun setValue( 23 | thisRef: PreferenceModel, 24 | property: KProperty<*>, 25 | value: Int 26 | ) { 27 | val prefName = name.trimEmptyToNull() ?: property.name 28 | thisRef.prefs.edit(commit) { put(prefName, value) } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/LongProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.extensions.trimEmptyToNull 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class LongProperty( 9 | private val name: String, 10 | private val default: Long, 11 | private val commit: Boolean 12 | ) : Property(), ReadWriteProperty { 13 | 14 | override operator fun getValue( 15 | thisRef: PreferenceModel, 16 | property: KProperty<*> 17 | ): Long { 18 | val prefName = name.trimEmptyToNull() ?: property.name 19 | return thisRef.prefs.get(prefName, default) 20 | } 21 | 22 | override operator fun setValue( 23 | thisRef: PreferenceModel, 24 | property: KProperty<*>, 25 | value: Long 26 | ) { 27 | val prefName = name.trimEmptyToNull() ?: property.name 28 | thisRef.prefs.edit(commit) { put(prefName, value) } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/Property.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import android.content.SharedPreferences 4 | 5 | abstract class Property { 6 | 7 | fun SharedPreferences.Editor.put(name: String, value: Boolean) = putBoolean(name, value) 8 | fun SharedPreferences.Editor.put(name: String, value: Float) = putFloat(name, value) 9 | fun SharedPreferences.Editor.put(name: String, value: Int) = putInt(name, value) 10 | fun SharedPreferences.Editor.put(name: String, value: Long) = putLong(name, value) 11 | fun SharedPreferences.Editor.put(name: String, value: String) = putString(name, value) 12 | fun SharedPreferences.Editor.put(name: String, value: Set) = putStringSet(name, value) 13 | 14 | fun SharedPreferences.get(name: String, value: Boolean) = getBoolean(name, value) 15 | fun SharedPreferences.get(name: String, value: Float) = getFloat(name, value) 16 | fun SharedPreferences.get(name: String, value: Int) = getInt(name, value) 17 | fun SharedPreferences.get(name: String, value: Long) = getLong(name, value) 18 | fun SharedPreferences.get(name: String, value: String) = getString(name, value) ?: value 19 | fun SharedPreferences.get(name: String, value: Set) = getStringSet(name, value) ?: value 20 | 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/StringProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.extensions.trimEmptyToNull 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class StringProperty( 9 | private val name: String, 10 | private val default: String, 11 | private val commit: Boolean 12 | ) : Property(), ReadWriteProperty { 13 | 14 | override operator fun getValue( 15 | thisRef: PreferenceModel, 16 | property: KProperty<*> 17 | ): String { 18 | val prefName = name.trimEmptyToNull() ?: property.name 19 | return thisRef.prefs.get(prefName, default) 20 | } 21 | 22 | override operator fun setValue( 23 | thisRef: PreferenceModel, 24 | property: KProperty<*>, 25 | value: String 26 | ) { 27 | val prefName = name.trimEmptyToNull() ?: property.name 28 | thisRef.prefs.edit(commit) { put(prefName, value) } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/preference/StringSetProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.extensions.trimEmptyToNull 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class StringSetProperty( 9 | private val name: String, 10 | private val default: Set, 11 | private val commit: Boolean 12 | ) : Property(), ReadWriteProperty> { 13 | 14 | override operator fun getValue( 15 | thisRef: PreferenceModel, 16 | property: KProperty<*> 17 | ): Set { 18 | val prefName = name.trimEmptyToNull() ?: property.name 19 | return thisRef.prefs.get(prefName, default) 20 | } 21 | 22 | override operator fun setValue( 23 | thisRef: PreferenceModel, 24 | property: KProperty<*>, 25 | value: Set 26 | ) { 27 | val prefName = name.trimEmptyToNull() ?: property.name 28 | thisRef.prefs.edit(commit) { put(prefName, value) } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/model/update/UpdateCheckService.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.model.update 2 | 3 | import androidx.work.ListenableWorker 4 | import com.topjohnwu.magisk.BuildConfig 5 | import com.topjohnwu.magisk.Info 6 | import com.topjohnwu.magisk.base.DelegateWorker 7 | import com.topjohnwu.magisk.data.repository.MagiskRepository 8 | import com.topjohnwu.magisk.extensions.inject 9 | import com.topjohnwu.magisk.view.Notifications 10 | import com.topjohnwu.superuser.Shell 11 | 12 | class UpdateCheckService : DelegateWorker() { 13 | 14 | private val magiskRepo: MagiskRepository by inject() 15 | 16 | override fun doWork(): ListenableWorker.Result { 17 | // Make sure shell initializer was ran 18 | Shell.getShell() 19 | return runCatching { 20 | magiskRepo.fetchUpdate().blockingGet() 21 | if (BuildConfig.VERSION_CODE < Info.remote.app.versionCode) 22 | Notifications.managerUpdate(applicationContext) 23 | else if (Info.env.magiskVersionCode < Info.remote.magisk.versionCode) 24 | Notifications.magiskUpdate(applicationContext) 25 | ListenableWorker.Result.success() 26 | }.getOrElse { 27 | ListenableWorker.Result.failure() 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui 2 | 3 | import android.view.MenuItem 4 | import com.topjohnwu.magisk.R 5 | import com.topjohnwu.magisk.base.viewmodel.BaseViewModel 6 | import com.topjohnwu.magisk.model.navigation.Navigation 7 | 8 | 9 | class MainViewModel : BaseViewModel() { 10 | 11 | var shownUnsupportedDialog = false 12 | 13 | fun navPressed() = Navigation.Main.OPEN_NAV.publish() 14 | 15 | fun navigationItemPressed(item: MenuItem): Boolean { 16 | when (item.itemId) { 17 | R.id.magiskFragment -> Navigation.home() 18 | R.id.superuserFragment -> Navigation.superuser() 19 | R.id.magiskHideFragment -> Navigation.hide() 20 | R.id.modulesFragment -> Navigation.modules() 21 | R.id.reposFragment -> Navigation.repos() 22 | R.id.logFragment -> Navigation.log() 23 | R.id.settings -> Navigation.settings() 24 | else -> null 25 | }?.publish()?.let { return@navigationItemPressed true } 26 | return false 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/superuser/SuperuserFragment.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.superuser 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.base.BaseFragment 5 | import com.topjohnwu.magisk.databinding.FragmentSuperuserBinding 6 | import org.koin.androidx.viewmodel.ext.android.viewModel 7 | 8 | class SuperuserFragment : 9 | BaseFragment() { 10 | 11 | override val layoutRes: Int = R.layout.fragment_superuser 12 | override val viewModel: SuperuserViewModel by viewModel() 13 | 14 | override fun onStart() { 15 | super.onStart() 16 | setHasOptionsMenu(true) 17 | requireActivity().setTitle(R.string.superuser) 18 | } 19 | 20 | override fun onResume() { 21 | super.onResume() 22 | viewModel.updatePolicies() 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/CachedValue.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | class CachedValue(private val factory: () -> T) : Lazy { 4 | 5 | private var _val : T? = null 6 | 7 | override val value: T 8 | get() { 9 | val local = _val 10 | return local ?: synchronized(this) { 11 | _val ?: factory().also { _val = it } 12 | } 13 | } 14 | 15 | override fun isInitialized() = _val != null 16 | 17 | fun invalidate() { 18 | synchronized(this) { 19 | _val = null 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/KObservableField.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import androidx.databinding.Observable 4 | import androidx.databinding.ObservableField 5 | import java.io.Serializable 6 | 7 | /** 8 | * Kotlin version of [ObservableField]. 9 | * You can define if wrapped type is Nullable or not. 10 | * You can use kotlin get/set syntax for value 11 | */ 12 | open class KObservableField : ObservableField, Serializable { 13 | 14 | var value: T 15 | get() = get() 16 | set(value) { set(value) } 17 | 18 | constructor(init: T) { 19 | value = init 20 | } 21 | 22 | constructor(init: T, vararg dependencies: Observable) : super(*dependencies) { 23 | value = init 24 | } 25 | 26 | @Suppress("UNCHECKED_CAST") 27 | override fun get(): T { 28 | return super.get() as T 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/ProgressInputStream.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import com.topjohnwu.superuser.internal.UiThreadHandler 4 | import java.io.FilterInputStream 5 | import java.io.InputStream 6 | 7 | class ProgressInputStream( 8 | base: InputStream, 9 | val progressEmitter: (Long) -> Unit = {} 10 | ) : FilterInputStream(base) { 11 | 12 | private var bytesRead = 0L 13 | private var lastUpdate = 0L 14 | 15 | private fun emitProgress() { 16 | val cur = System.currentTimeMillis() 17 | if (cur - lastUpdate > 1000) { 18 | lastUpdate = cur 19 | UiThreadHandler.run { progressEmitter(bytesRead) } 20 | } 21 | } 22 | 23 | override fun read(): Int { 24 | val b = read() 25 | if (b >= 0) { 26 | bytesRead++ 27 | emitProgress() 28 | } 29 | return b 30 | } 31 | 32 | override fun read(b: ByteArray): Int { 33 | return read(b, 0, b.size) 34 | } 35 | 36 | override fun read(b: ByteArray, off: Int, len: Int): Int { 37 | val sz = super.read(b, off, len) 38 | if (sz > 0) { 39 | bytesRead += sz 40 | emitProgress() 41 | } 42 | return sz 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/RootInit.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import android.content.Context 4 | import com.topjohnwu.magisk.Const 5 | import com.topjohnwu.magisk.Info 6 | import com.topjohnwu.magisk.R 7 | import com.topjohnwu.magisk.extensions.rawResource 8 | import com.topjohnwu.magisk.wrap 9 | import com.topjohnwu.superuser.Shell 10 | import com.topjohnwu.superuser.ShellUtils 11 | import com.topjohnwu.superuser.io.SuFile 12 | 13 | class RootInit : Shell.Initializer() { 14 | 15 | override fun onInit(context: Context, shell: Shell): Boolean { 16 | return init(context.wrap(), shell) 17 | } 18 | 19 | fun init(context: Context, shell: Shell): Boolean { 20 | // Invalidate env state if shell is recreated 21 | Info.envRef.invalidate() 22 | 23 | val job = shell.newJob() 24 | if (shell.isRoot) { 25 | job.add(context.rawResource(R.raw.util_functions)) 26 | .add(context.rawResource(R.raw.utils)) 27 | Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk") 28 | } else { 29 | job.add(context.rawResource(R.raw.nonroot_utils)) 30 | } 31 | 32 | job.add("mount_partitions", 33 | "get_flags", 34 | "run_migrations", 35 | "export BOOTMODE=true") 36 | .exec() 37 | 38 | Info.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean() 39 | Info.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean() 40 | Info.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean() 41 | 42 | return true 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/RxBus.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import io.reactivex.Observable 4 | import io.reactivex.subjects.PublishSubject 5 | 6 | class RxBus { 7 | 8 | private val _bus = PublishSubject.create() 9 | 10 | val bus: Observable get() = _bus 11 | 12 | fun post(event: Event) { 13 | _bus.onNext(event) 14 | } 15 | 16 | fun post(event: Int) { 17 | _bus.onNext(SimpleEvent(event)) 18 | } 19 | 20 | inline fun register(noinline predicate: (T) -> Boolean = { true }): Observable { 21 | return bus 22 | .ofType(T::class.java) 23 | .filter(predicate) 24 | } 25 | 26 | fun register(eventId: Int): Observable { 27 | return bus 28 | .ofType(SimpleEvent::class.java) 29 | .map { it.eventId } 30 | .filter { it == eventId } 31 | } 32 | 33 | interface Event 34 | 35 | private class SimpleEvent(val eventId: Int) : Event 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/SafetyNetHelper.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | interface SafetyNetHelper { 4 | 5 | val version: Int 6 | 7 | fun attest() 8 | 9 | interface Callback { 10 | fun onResponse(responseCode: Int) 11 | } 12 | 13 | companion object { 14 | 15 | val RESPONSE_ERR = 0x01 16 | val CONNECTION_FAIL = 0x02 17 | 18 | val BASIC_PASS = 0x10 19 | val CTS_PASS = 0x20 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/SuConnector.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import android.net.LocalSocket 4 | import android.net.LocalSocketAddress 5 | import androidx.collection.ArrayMap 6 | import timber.log.Timber 7 | import java.io.* 8 | 9 | abstract class SuConnector @Throws(IOException::class) 10 | protected constructor(name: String) { 11 | 12 | private val socket: LocalSocket = LocalSocket() 13 | protected var out: DataOutputStream 14 | protected var input: DataInputStream 15 | 16 | init { 17 | socket.connect(LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT)) 18 | out = DataOutputStream(BufferedOutputStream(socket.outputStream)) 19 | input = DataInputStream(BufferedInputStream(socket.inputStream)) 20 | } 21 | 22 | private fun readString(): String { 23 | val len = input.readInt() 24 | val buf = ByteArray(len) 25 | input.readFully(buf) 26 | return String(buf, Charsets.UTF_8) 27 | } 28 | 29 | @Throws(IOException::class) 30 | fun readRequest(): Map { 31 | val ret = ArrayMap() 32 | while (true) { 33 | val name = readString() 34 | if (name == "eof") 35 | break 36 | ret[name] = readString() 37 | } 38 | return ret 39 | } 40 | 41 | fun response() { 42 | runCatching { 43 | onResponse() 44 | out.flush() 45 | }.onFailure { Timber.e(it) } 46 | 47 | runCatching { 48 | input.close() 49 | out.close() 50 | socket.close() 51 | } 52 | } 53 | 54 | @Throws(IOException::class) 55 | protected abstract fun onResponse() 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import com.topjohnwu.superuser.io.SuFile 4 | import com.topjohnwu.superuser.io.SuFileOutputStream 5 | import java.io.File 6 | import java.io.IOException 7 | import java.io.InputStream 8 | import java.util.zip.ZipEntry 9 | import java.util.zip.ZipInputStream 10 | 11 | @Throws(IOException::class) 12 | fun File.unzip(folder: File, path: String = "", junkPath: Boolean = false) { 13 | inputStream().buffered().use { 14 | it.unzip(folder, path, junkPath) 15 | } 16 | } 17 | 18 | @Throws(IOException::class) 19 | fun InputStream.unzip(folder: File, path: String, junkPath: Boolean) { 20 | try { 21 | val zin = ZipInputStream(this) 22 | var entry: ZipEntry 23 | while (true) { 24 | entry = zin.nextEntry ?: break 25 | if (!entry.name.startsWith(path) || entry.isDirectory) { 26 | // Ignore directories, only create files 27 | continue 28 | } 29 | val name = if (junkPath) 30 | entry.name.substring(entry.name.lastIndexOf('/') + 1) 31 | else 32 | entry.name 33 | 34 | var dest = File(folder, name) 35 | if (!dest.parentFile!!.exists() && !dest.parentFile!!.mkdirs()) { 36 | dest = SuFile(folder, name) 37 | dest.parentFile!!.mkdirs() 38 | } 39 | SuFileOutputStream(dest).use { out -> zin.copyTo(out) } 40 | } 41 | } catch (e: IOException) { 42 | e.printStackTrace() 43 | throw e 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/view/dialogs/ManagerInstallDialog.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.view.dialogs 2 | 3 | import android.app.Activity 4 | import com.topjohnwu.magisk.Info 5 | import com.topjohnwu.magisk.R 6 | import com.topjohnwu.magisk.model.download.DownloadService 7 | import com.topjohnwu.magisk.model.entity.internal.Configuration 8 | import com.topjohnwu.magisk.model.entity.internal.DownloadSubject 9 | import com.topjohnwu.magisk.view.MarkDownWindow 10 | 11 | class ManagerInstallDialog(a: Activity) : CustomAlertDialog(a) { 12 | 13 | init { 14 | val subject = DownloadSubject.Manager(Configuration.APK.Upgrade) 15 | setTitle(a.getString(R.string.repo_install_title, a.getString(R.string.app_name))) 16 | setMessage(a.getString(R.string.repo_install_msg, subject.title)) 17 | setCancelable(true) 18 | setPositiveButton(R.string.install) { _, _ -> 19 | DownloadService(a) { this.subject = subject } 20 | } 21 | if (Info.remote.app.note.isNotEmpty()) { 22 | setNeutralButton(R.string.app_changelog) { _, _ -> 23 | MarkDownWindow.show(a, null, Info.remote.app.note) } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/view/dialogs/UninstallDialog.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.view.dialogs 2 | 3 | import android.app.Activity 4 | import android.app.ProgressDialog 5 | import android.widget.Toast 6 | import com.topjohnwu.magisk.Info 7 | import com.topjohnwu.magisk.R 8 | import com.topjohnwu.magisk.model.download.DownloadService 9 | import com.topjohnwu.magisk.model.entity.internal.Configuration 10 | import com.topjohnwu.magisk.model.entity.internal.DownloadSubject 11 | import com.topjohnwu.magisk.utils.Utils 12 | import com.topjohnwu.superuser.Shell 13 | 14 | class UninstallDialog(activity: Activity) : CustomAlertDialog(activity) { 15 | 16 | init { 17 | setTitle(R.string.uninstall_magisk_title) 18 | setMessage(R.string.uninstall_magisk_msg) 19 | setNeutralButton(R.string.restore_img) { _, _ -> 20 | val dialog = ProgressDialog.show(activity, 21 | activity.getString(R.string.restore_img), 22 | activity.getString(R.string.restore_img_msg)) 23 | Shell.su("restore_imgs").submit { result -> 24 | dialog.cancel() 25 | if (result.isSuccess) { 26 | Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT) 27 | } else { 28 | Utils.toast(R.string.restore_fail, Toast.LENGTH_LONG) 29 | } 30 | } 31 | } 32 | if (Info.remote.uninstaller.link.isNotEmpty()) { 33 | setPositiveButton(R.string.complete_uninstall) { _, _ -> 34 | DownloadService(activity) { 35 | subject = DownloadSubject.Magisk(Configuration.Uninstall) 36 | } 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-hdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-mdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-nodpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v26/sc_cloud_download.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v26/sc_extension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v26/sc_magiskhide.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v26/sc_superuser.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-xhdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-xxhdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RikkaW/Magisk/d19f65ce4a04352ea5f2de969098b0d0b8ddb6f4/app/src/main/res/drawable-xxxhdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bug_report.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cancel.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_checked.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_download.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_extension.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_file_download_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_help.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_indeterminate.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_patreon.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_paypal.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_restart.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_safetynet.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_save.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_save_compat.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sort.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_splash_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_superuser.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_twitter.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_unchecked.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_undelete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_update.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_warning.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_xda.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sc_cloud_download.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sc_extension.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sc_magiskhide.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sc_superuser.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 20 | 21 | 25 | 26 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/custom_channel_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_log.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 16 | 17 | 25 | 26 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_magisk_hide.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 19 | 20 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_console.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_page_log.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | 18 | 21 | 22 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_section.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_spinner.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/markdown_window.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_log.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_magiskhide.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_reboot.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_repo.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/raw/.gitignore: -------------------------------------------------------------------------------- 1 | /util_functions.sh 2 | -------------------------------------------------------------------------------- /app/src/main/res/raw/changelog.md: -------------------------------------------------------------------------------- 1 | # v7.4.0 2 | - Hide Magisk Manager with stub APKs on Android 9.0+. Not all devices will be supported, please refer to Magisk v20.1 release notes. 3 | - Allow customizing app name when hiding Magisk Manager 4 | - Generate random keys to sign the hidden Magisk Manager to prevent signature detections 5 | - Fix fingerprint UI infinite loop 6 | -------------------------------------------------------------------------------- /app/src/main/res/raw/nonroot_utils.sh: -------------------------------------------------------------------------------- 1 | mount_partitions() { 2 | [ "`getprop ro.build.ab_update`" = "true" ] && SLOT=`getprop ro.boot.slot_suffix` 3 | [ "`getprop ro.build.system_root_image`" = "true" ] && SYSTEM_ROOT=true || SYSTEM_ROOT=false 4 | } 5 | 6 | get_flags() { 7 | $SYSTEM_ROOT && KEEPVERITY=true || KEEPVERITY=false 8 | [ "`getprop ro.crypto.state`" = "encrypted" ] && KEEPFORCEENCRYPT=true || KEEPFORCEENCRYPT=false 9 | RECOVERYMODE=false 10 | } 11 | 12 | run_migrations() { return; } 13 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFC107 4 | #AD8305 5 | 6 | #FFFFFF 7 | #ADADAD 8 | #1D1D1D 9 | 10 | #FFFFFF 11 | #2AE4E4E4 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24dp 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-v19/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 13 | 14 | 17 | 18 | 21 | 22 | 25 | 26 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_button_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_card_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_dialog_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_divider_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_image_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 17 | 18 | 21 | 22 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_progress_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/view_styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 12 | 13 |