├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.MD ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro ├── shared │ ├── .gitignore │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ ├── a │ │ └── p.java │ │ └── com │ │ └── topjohnwu │ │ └── magisk │ │ ├── DynAPK.java │ │ ├── FileProvider.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 ├── signing │ ├── .gitignore │ ├── build.gradle.kts │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── topjohnwu │ │ │ └── signing │ │ │ ├── ApkSignerV2.java │ │ │ ├── BootSigner.java │ │ │ ├── ByteArrayStream.java │ │ │ ├── CryptoUtils.java │ │ │ ├── JarMap.java │ │ │ ├── SignApk.java │ │ │ ├── SignBoot.java │ │ │ ├── ZipSigner.java │ │ │ └── ZipUtils.java │ │ └── resources │ │ └── keys │ │ ├── testkey.pk8 │ │ ├── testkey.x509.pem │ │ ├── verity.pk8 │ │ └── verity.x509.pem └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── a │ │ └── stubs.kt │ └── com │ │ └── topjohnwu │ │ └── magisk │ │ ├── arch │ │ ├── BaseUIActivity.kt │ │ ├── BaseUIComponent.kt │ │ ├── BaseUIFragment.kt │ │ ├── BaseViewModel.kt │ │ ├── Helpers.kt │ │ ├── Queryable.kt │ │ └── ViewEvent.kt │ │ ├── core │ │ ├── App.kt │ │ ├── Config.kt │ │ ├── Const.kt │ │ ├── GeneralReceiver.kt │ │ ├── Hacks.kt │ │ ├── Info.kt │ │ ├── SplashActivity.kt │ │ ├── UpdateCheckService.kt │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseReceiver.kt │ │ │ ├── BaseService.kt │ │ │ ├── BaseWorkerWrapper.kt │ │ │ └── PermissionRequestBuilder.kt │ │ ├── download │ │ │ ├── Action.kt │ │ │ ├── BaseDownloader.kt │ │ │ ├── DownloadService.kt │ │ │ ├── ManagerHandler.kt │ │ │ ├── ModuleProcessor.kt │ │ │ └── Subject.kt │ │ ├── magiskdb │ │ │ ├── BaseDao.kt │ │ │ ├── PolicyDao.kt │ │ │ ├── Query.kt │ │ │ ├── SettingsDao.kt │ │ │ └── StringDao.kt │ │ ├── model │ │ │ ├── UpdateInfo.kt │ │ │ ├── module │ │ │ │ ├── BaseModule.kt │ │ │ │ ├── Module.kt │ │ │ │ └── Repo.kt │ │ │ └── su │ │ │ │ ├── SuLog.kt │ │ │ │ └── SuPolicy.kt │ │ ├── su │ │ │ ├── SuCallbackHandler.kt │ │ │ └── SuRequestHandler.kt │ │ ├── tasks │ │ │ ├── FlashZip.kt │ │ │ ├── MagiskInstaller.kt │ │ │ ├── PatchAPK.kt │ │ │ └── RepoUpdater.kt │ │ └── utils │ │ │ ├── AXML.kt │ │ │ ├── BiometricHelper.kt │ │ │ ├── IODispatcherExecutor.kt │ │ │ ├── Keygen.kt │ │ │ ├── Locales.kt │ │ │ ├── MediaStoreUtils.kt │ │ │ ├── ProgressInputStream.kt │ │ │ ├── RootInit.kt │ │ │ ├── ZipUtils.kt │ │ │ └── net │ │ │ ├── LollipopNetworkObserver.kt │ │ │ ├── MarshmallowNetworkObserver.kt │ │ │ ├── NetworkObserver.kt │ │ │ └── PreLollipopNetworkObserver.kt │ │ ├── data │ │ ├── database │ │ │ ├── Repo.kt │ │ │ ├── RepoDao.kt │ │ │ └── SuLogDao.kt │ │ ├── network │ │ │ └── NetworkServices.kt │ │ ├── preference │ │ │ ├── BooleanProperty.kt │ │ │ ├── FloatProperty.kt │ │ │ ├── IntProperty.kt │ │ │ ├── LongProperty.kt │ │ │ ├── PreferenceModel.kt │ │ │ ├── Property.kt │ │ │ ├── StringProperty.kt │ │ │ └── StringSetProperty.kt │ │ └── repository │ │ │ ├── DBConfig.kt │ │ │ ├── LogRepository.kt │ │ │ └── NetworkService.kt │ │ ├── databinding │ │ ├── AdaptersRecycler.kt │ │ ├── BindingBoundAdapter.kt │ │ ├── DataBindingAdapters.kt │ │ ├── RecyclerViewItems.kt │ │ └── RvBindingAdapter.kt │ │ ├── di │ │ ├── ApplicationModule.kt │ │ ├── DatabaseModule.kt │ │ ├── Modules.kt │ │ ├── NetworkingModule.kt │ │ ├── RepositoryModule.kt │ │ └── ViewModelsModule.kt │ │ ├── events │ │ ├── OpenInappLinkEvent.kt │ │ ├── RebootEvent.kt │ │ ├── SnackbarEvent.kt │ │ ├── ViewEvents.kt │ │ └── dialog │ │ │ ├── BiometricEvent.kt │ │ │ ├── DarkThemeDialog.kt │ │ │ ├── DialogEvent.kt │ │ │ ├── EnvFixDialog.kt │ │ │ ├── ManagerInstallDialog.kt │ │ │ ├── ModuleInstallDialog.kt │ │ │ ├── SecondSlotWarningDialog.kt │ │ │ ├── SuperuserRevokeDialog.kt │ │ │ └── UninstallDialog.kt │ │ ├── ktx │ │ ├── Dimens.kt │ │ ├── XAndroid.kt │ │ ├── XBinding.kt │ │ ├── XJava.kt │ │ ├── XKoin.kt │ │ ├── XSU.kt │ │ ├── XString.kt │ │ └── XTime.kt │ │ ├── ui │ │ ├── MainActivity.kt │ │ ├── flash │ │ │ ├── ConsoleItem.kt │ │ │ ├── FlashFragment.kt │ │ │ └── FlashViewModel.kt │ │ ├── hide │ │ │ ├── HideApp.kt │ │ │ ├── HideFragment.kt │ │ │ ├── HideRvItems.kt │ │ │ └── HideViewModel.kt │ │ ├── home │ │ │ ├── DeveloperItem.kt │ │ │ ├── HomeFragment.kt │ │ │ └── HomeViewModel.kt │ │ ├── install │ │ │ ├── InstallFragment.kt │ │ │ └── InstallViewModel.kt │ │ ├── log │ │ │ ├── LogFragment.kt │ │ │ ├── LogRvItem.kt │ │ │ └── LogViewModel.kt │ │ ├── module │ │ │ ├── ModuleFragment.kt │ │ │ ├── ModuleRvItem.kt │ │ │ └── ModuleViewModel.kt │ │ ├── safetynet │ │ │ ├── CheckSafetyNetEvent.kt │ │ │ ├── SafetyNetHelper.kt │ │ │ ├── SafetynetFragment.kt │ │ │ └── SafetynetViewModel.kt │ │ ├── settings │ │ │ ├── BaseSettingsItem.kt │ │ │ ├── SettingsFragment.kt │ │ │ ├── SettingsItems.kt │ │ │ └── SettingsViewModel.kt │ │ ├── superuser │ │ │ ├── PolicyRvItem.kt │ │ │ ├── SpinnerRvItem.kt │ │ │ ├── SuperuserFragment.kt │ │ │ └── SuperuserViewModel.kt │ │ ├── surequest │ │ │ ├── SuRequestActivity.kt │ │ │ └── SuRequestViewModel.kt │ │ └── theme │ │ │ ├── Theme.kt │ │ │ ├── ThemeFragment.kt │ │ │ └── ThemeViewModel.kt │ │ ├── utils │ │ ├── CachedValue.kt │ │ ├── DiffObservableList.kt │ │ ├── EndlessRecyclerScrollListener.kt │ │ ├── FilterableDiffObservableList.kt │ │ ├── HideBottomViewOnScrollBehavior.kt │ │ ├── HideTopViewOnScrollBehavior.kt │ │ ├── HideableBehavior.kt │ │ ├── KItemDecoration.kt │ │ ├── MarkwonImagePlugin.kt │ │ ├── MotionRevealHelper.kt │ │ ├── ObservableHost.kt │ │ ├── TransitiveText.kt │ │ └── Utils.kt │ │ └── view │ │ ├── MagiskDialog.kt │ │ ├── MarkDownWindow.kt │ │ ├── Notifications.kt │ │ ├── Shortcuts.kt │ │ ├── TappableHeadlineItem.kt │ │ └── TextItem.kt │ └── res │ ├── anim │ ├── fragment_enter.xml │ ├── fragment_enter_pop.xml │ ├── fragment_exit.xml │ └── fragment_exit_pop.xml │ ├── color │ ├── color_error_transient.xml │ ├── color_menu_tint.xml │ ├── color_on_primary_transient.xml │ ├── color_primary_error_transient.xml │ ├── color_primary_transient.xml │ ├── color_secondary_transient.xml │ ├── color_state_primary_transient.xml │ └── color_text_transient.xml │ ├── drawable-anydpi-v21 │ └── ic_magisk_outline.xml │ ├── drawable-anydpi-v23 │ └── ic_splash_activity.xml │ ├── drawable-anydpi-v26 │ └── ic_launcher.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_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 │ ├── avd_bug_from_filled.xml │ ├── avd_bug_to_filled.xml │ ├── avd_circle_check_from_filled.xml │ ├── avd_circle_check_to_filled.xml │ ├── avd_delete_magisk.xml │ ├── avd_home_from_filled.xml │ ├── avd_home_to_filled.xml │ ├── avd_magisk_delete.xml │ ├── avd_module_from_filled.xml │ ├── avd_module_to_filled.xml │ ├── avd_settings_from_filled.xml │ ├── avd_settings_to_filled.xml │ ├── avd_superuser_from_filled.xml │ ├── avd_superuser_to_filled.xml │ ├── bg_checkbox.xml │ ├── bg_divider_rounded_on_primary.xml │ ├── bg_line_bottom_rounded.xml │ ├── bg_line_top_rounded.xml │ ├── bg_selection_circle_green.xml │ ├── bg_shadow.xml │ ├── divider_l1.xml │ ├── divider_l_50.xml │ ├── ic_back_md2.xml │ ├── ic_bug_filled_md2.xml │ ├── ic_bug_md2.xml │ ├── ic_bug_outlined_md2.xml │ ├── ic_check_circle_checked_md2.xml │ ├── ic_check_circle_md2.xml │ ├── ic_check_circle_unchecked_md2.xml │ ├── ic_check_md2.xml │ ├── ic_checkbox.xml │ ├── ic_close_md2.xml │ ├── ic_compound_checkbox.xml │ ├── ic_day.xml │ ├── ic_day_night.xml │ ├── ic_delete_md2.xml │ ├── ic_device.xml │ ├── ic_download_md2.xml │ ├── ic_extension.xml │ ├── ic_fingerprint.xml │ ├── ic_folder_list.xml │ ├── ic_forth_md2.xml │ ├── ic_github.xml │ ├── ic_hide_md2.xml │ ├── ic_hide_select_md2.xml │ ├── ic_home_filled_md2.xml │ ├── ic_home_md2.xml │ ├── ic_home_outlined_md2.xml │ ├── ic_info.xml │ ├── ic_install.xml │ ├── ic_logo.xml │ ├── ic_magisk.xml │ ├── ic_magisk_delete.xml │ ├── ic_magisk_padded.xml │ ├── ic_magiskhide.xml │ ├── ic_manager.xml │ ├── ic_module_filled_md2.xml │ ├── ic_module_md2.xml │ ├── ic_module_outlined_md2.xml │ ├── ic_module_storage_md2.xml │ ├── ic_more.xml │ ├── ic_night.xml │ ├── ic_notifications_md2.xml │ ├── ic_order_date.xml │ ├── ic_order_name.xml │ ├── ic_paint.xml │ ├── ic_patreon.xml │ ├── ic_paypal.xml │ ├── ic_refresh_data_md2.xml │ ├── ic_refresh_safetynet_md2.xml │ ├── ic_restart.xml │ ├── ic_safetynet_md2.xml │ ├── ic_save_md2.xml │ ├── ic_search_md2.xml │ ├── ic_settings_filled_md2.xml │ ├── ic_settings_md2.xml │ ├── ic_settings_outlined_md2.xml │ ├── ic_show_md2.xml │ ├── ic_splash_activity.xml │ ├── ic_superuser.xml │ ├── ic_superuser_filled_md2.xml │ ├── ic_superuser_md2.xml │ ├── ic_superuser_outlined_md2.xml │ ├── ic_twitter.xml │ ├── ic_up_md2.xml │ ├── ic_update_md2.xml │ ├── sc_extension.xml │ ├── sc_magiskhide.xml │ └── sc_superuser.xml │ ├── layout │ ├── activity_main_md2.xml │ ├── activity_request.xml │ ├── dialog_magisk_base.xml │ ├── dialog_settings_app_name.xml │ ├── dialog_settings_download_path.xml │ ├── dialog_settings_update_channel.xml │ ├── fragment_flash_md2.xml │ ├── fragment_hide_md2.xml │ ├── fragment_home_md2.xml │ ├── fragment_install_md2.xml │ ├── fragment_log_md2.xml │ ├── fragment_module_md2.xml │ ├── fragment_safetynet_md2.xml │ ├── fragment_settings_md2.xml │ ├── fragment_superuser_md2.xml │ ├── fragment_theme_md2.xml │ ├── include_hide_filter.xml │ ├── include_home_magisk.xml │ ├── include_home_manager.xml │ ├── include_log_magisk.xml │ ├── include_log_superuser.xml │ ├── include_module_filter.xml │ ├── item_console_md2.xml │ ├── item_developer.xml │ ├── item_hide_md2.xml │ ├── item_hide_process_md2.xml │ ├── item_icon_link.xml │ ├── item_list_single_line.xml │ ├── item_log_access_md2.xml │ ├── item_log_track_md2.xml │ ├── item_module_download.xml │ ├── item_module_md2.xml │ ├── item_policy_md2.xml │ ├── item_repo_md2.xml │ ├── item_section_md2.xml │ ├── item_settings.xml │ ├── item_settings_section.xml │ ├── item_spinner.xml │ ├── item_tappable_headline.xml │ ├── item_text.xml │ ├── item_theme.xml │ ├── item_theme_container.xml │ └── markdown_window_md2.xml │ ├── menu │ ├── menu_bottom_nav.xml │ ├── menu_flash.xml │ ├── menu_hide_md2.xml │ ├── menu_home_md2.xml │ ├── menu_log_md2.xml │ ├── menu_module_md2.xml │ └── menu_reboot.xml │ ├── navigation │ └── main.xml │ ├── raw │ ├── .gitignore │ ├── changelog.md │ └── manager.sh │ ├── values-anydpi-v21 │ └── drawable.xml │ ├── values-ar │ └── strings.xml │ ├── values-az │ └── strings.xml │ ├── values-be │ └── 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-fa │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-hi │ └── strings.xml │ ├── values-hr │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-iw │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-ka │ └── strings.xml │ ├── values-ko │ └── strings.xml │ ├── values-lt │ └── strings.xml │ ├── values-mk │ └── strings.xml │ ├── values-nb │ └── strings.xml │ ├── values-night │ ├── colors.xml │ └── themes_md2.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-v19 │ └── styles_md2.xml │ ├── values-v21 │ ├── styles_md2.xml │ └── styles_md2_impl.xml │ ├── values-vi │ └── strings.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values-zh-rTW │ └── strings.xml │ └── values │ ├── arrays.xml │ ├── attrs.xml │ ├── colors.xml │ ├── default_color.xml │ ├── dimens.xml │ ├── ids.xml │ ├── resources.xml │ ├── strings.xml │ ├── styles_md2.xml │ ├── styles_md2_appearance.xml │ ├── styles_md2_impl.xml │ ├── styles_view_md2.xml │ └── themes_md2.xml ├── build.gradle.kts ├── build.py ├── buildSrc ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── java │ └── BuildSource.kt ├── config.prop.sample ├── docs ├── README.md ├── app_changes.md ├── boot.md ├── changes.md ├── deploy.md ├── details.md ├── faq.md ├── guides.md ├── images │ ├── device_info.png │ ├── disable_auto_ota.png │ ├── install_inactive_slot.png │ ├── logo.png │ ├── manager_reboot.png │ ├── ota_done.png │ └── restore_img.png ├── install.md ├── ota.md ├── procedures.html ├── releases │ ├── 18000.md │ ├── 18100.md │ ├── 19000.md │ ├── 19100.md │ ├── 19200.md │ ├── 19300.md │ ├── 19400.md │ ├── 20000.md │ ├── 20100.md │ ├── 20200.md │ ├── 20300.md │ ├── 20400.md │ ├── 21000.md │ └── index.md └── tools.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── native ├── .gitignore ├── build.gradle.kts ├── jni │ ├── .gitignore │ ├── Android.mk │ ├── Application.mk │ ├── core │ │ ├── applet_stub.cpp │ │ ├── applets.cpp │ │ ├── bootstages.cpp │ │ ├── daemon.cpp │ │ ├── db.cpp │ │ ├── magisk.cpp │ │ ├── module.cpp │ │ ├── restorecon.cpp │ │ ├── scripting.cpp │ │ └── socket.cpp │ ├── external │ │ ├── Android.mk │ │ ├── xz-embedded │ │ │ ├── xz.h │ │ │ ├── xz_config.h │ │ │ ├── xz_crc32.c │ │ │ ├── xz_dec_lzma2.c │ │ │ ├── xz_dec_stream.c │ │ │ ├── xz_lzma2.h │ │ │ ├── xz_private.h │ │ │ └── xz_stream.h │ │ └── xz_config │ │ │ └── config.h │ ├── include │ │ ├── daemon.hpp │ │ ├── db.hpp │ │ ├── flags.hpp │ │ ├── magisk.hpp │ │ ├── magiskpolicy.hpp │ │ ├── resetprop.hpp │ │ └── socket.hpp │ ├── init │ │ ├── getinfo.cpp │ │ ├── init.cpp │ │ ├── init.hpp │ │ ├── magiskrc.inc │ │ ├── mount.cpp │ │ ├── rootdir.cpp │ │ └── twostage.cpp │ ├── magiskboot │ │ ├── bootimg.cpp │ │ ├── bootimg.hpp │ │ ├── compress.cpp │ │ ├── compress.hpp │ │ ├── dtb.cpp │ │ ├── dtb.hpp │ │ ├── format.cpp │ │ ├── format.hpp │ │ ├── hexpatch.cpp │ │ ├── magiskboot.hpp │ │ ├── main.cpp │ │ ├── pattern.cpp │ │ └── ramdisk.cpp │ ├── magiskhide │ │ ├── hide_policy.cpp │ │ ├── hide_utils.cpp │ │ ├── magiskhide.cpp │ │ ├── magiskhide.hpp │ │ └── proc_monitor.cpp │ ├── magiskpolicy │ │ ├── magiskpolicy.cpp │ │ ├── policydb.cpp │ │ ├── rules.cpp │ │ ├── sepolicy.cpp │ │ ├── sepolicy.hpp │ │ └── statement.cpp │ ├── resetprop │ │ ├── _resetprop.hpp │ │ ├── persist_properties.cpp │ │ └── resetprop.cpp │ ├── su │ │ ├── connect.cpp │ │ ├── pts.cpp │ │ ├── pts.hpp │ │ ├── su.cpp │ │ ├── su.hpp │ │ └── su_daemon.cpp │ ├── systemproperties │ │ ├── Android.mk │ │ ├── context_node.cpp │ │ ├── contexts_serialized.cpp │ │ ├── contexts_split.cpp │ │ ├── include │ │ │ ├── _system_properties.h │ │ │ ├── async_safe │ │ │ │ └── log.h │ │ │ ├── private │ │ │ │ ├── ErrnoRestorer.h │ │ │ │ ├── bionic_defs.h │ │ │ │ ├── bionic_futex.h │ │ │ │ ├── bionic_lock.h │ │ │ │ ├── bionic_macros.h │ │ │ │ └── hacks.h │ │ │ ├── property_info_parser │ │ │ │ └── property_info_parser.h │ │ │ ├── system_properties.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 │ │ ├── system_property_api.cpp │ │ └── system_property_set.cpp │ └── utils │ │ ├── Android.mk │ │ ├── cpio.cpp │ │ ├── files.cpp │ │ ├── files.hpp │ │ ├── include │ │ ├── cpio.hpp │ │ ├── selinux.hpp │ │ ├── stream.hpp │ │ └── utils.hpp │ │ ├── logging.cpp │ │ ├── logging.hpp │ │ ├── misc.cpp │ │ ├── misc.hpp │ │ ├── missing.cpp │ │ ├── missing.hpp │ │ ├── new.cpp │ │ ├── selinux.cpp │ │ ├── stream.cpp │ │ ├── xwrap.cpp │ │ └── xwrap.hpp └── 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.kts ├── stub ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ ├── a │ │ └── a.java │ └── com │ │ └── topjohnwu │ │ └── magisk │ │ └── MainActivity.java │ └── res │ ├── values-ar │ └── strings.xml │ ├── values-az │ └── strings.xml │ ├── values-be │ └── 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-fa │ └── strings.xml │ ├── values-fr │ └── strings.xml │ ├── values-hi │ └── strings.xml │ ├── values-hr │ └── strings.xml │ ├── values-in │ └── strings.xml │ ├── values-it │ └── strings.xml │ ├── values-iw │ └── strings.xml │ ├── values-ja │ └── strings.xml │ ├── values-ka │ └── 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-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 └── ndk-bins ├── README.md ├── arm ├── crtbegin_dynamic.o ├── crtbegin_so.o ├── crtbegin_static.o ├── crtend_android.o ├── crtend_so.o ├── libc.a ├── libm.a ├── libm_hard.a └── libstdc++.a └── i686 ├── crtbegin_dynamic.o ├── crtbegin_so.o ├── crtbegin_static.o ├── crtend_android.o ├── crtend_so.o ├── libc.a ├── libm.a ├── libstdc++.a └── libz.a /.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 | *.ttf binary 21 | 22 | # Help GitHub detect languages 23 | native/jni/external/** linguist-vendored 24 | native/jni/systemproperties/** linguist-language=C++ 25 | -------------------------------------------------------------------------------- /.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 "pcre"] 26 | path = native/jni/external/pcre 27 | url = https://android.googlesource.com/platform/external/pcre 28 | [submodule "termux-elf-cleaner"] 29 | path = tools/termux-elf-cleaner 30 | url = https://github.com/termux/termux-elf-cleaner.git 31 | -------------------------------------------------------------------------------- /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/shared/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/shared/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.library") 3 | } 4 | 5 | android { 6 | defaultConfig { 7 | vectorDrawables.useSupportLibrary = true 8 | consumerProguardFiles("proguard-rules.pro") 9 | } 10 | } 11 | 12 | dependencies { 13 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) 14 | } 15 | -------------------------------------------------------------------------------- /app/shared/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -keepclassmembers class * extends javax.net.ssl.SSLSocketFactory { 24 | ** delegate; 25 | } 26 | -------------------------------------------------------------------------------- /app/shared/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/shared/src/main/java/a/p.java: -------------------------------------------------------------------------------- 1 | package a; 2 | 3 | import com.topjohnwu.magisk.FileProvider; 4 | 5 | public class p extends FileProvider { 6 | /* Stub */ 7 | } 8 | -------------------------------------------------------------------------------- /app/shared/src/main/java/com/topjohnwu/magisk/ProviderCallHandler.java: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | 6 | public interface ProviderCallHandler { 7 | Bundle call(Context context, String method, String arg, Bundle extras); 8 | } 9 | -------------------------------------------------------------------------------- /app/shared/src/main/java/com/topjohnwu/magisk/net/ErrorHandler.java: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.net; 2 | 3 | import java.net.HttpURLConnection; 4 | 5 | public interface ErrorHandler { 6 | void onError(HttpURLConnection conn, Exception e); 7 | } 8 | -------------------------------------------------------------------------------- /app/shared/src/main/java/com/topjohnwu/magisk/net/ResponseListener.java: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.net; 2 | 3 | public interface ResponseListener { 4 | void onResponse(T response); 5 | } 6 | -------------------------------------------------------------------------------- /app/shared/src/main/java/com/topjohnwu/magisk/utils/APKInstall.java: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Build; 7 | 8 | import com.topjohnwu.magisk.FileProvider; 9 | 10 | import java.io.File; 11 | 12 | public class APKInstall { 13 | public static void install(Context c, File apk) { 14 | c.startActivity(installIntent(c, apk)); 15 | } 16 | 17 | public static Intent installIntent(Context c, File apk) { 18 | Intent install = new Intent(Intent.ACTION_INSTALL_PACKAGE); 19 | install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 20 | install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 21 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 22 | install.setData(FileProvider.getUriForFile(c, c.getPackageName() + ".provider", apk)); 23 | } else { 24 | apk.setReadable(true, false); 25 | install.setData(Uri.fromFile(apk)); 26 | } 27 | return install; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/shared/src/main/java/com/topjohnwu/magisk/utils/CompoundEnumeration.java: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils; 2 | 3 | import java.util.Enumeration; 4 | import java.util.NoSuchElementException; 5 | 6 | public class CompoundEnumeration implements Enumeration { 7 | private Enumeration[] enums; 8 | private int index = 0; 9 | 10 | @SafeVarargs 11 | public CompoundEnumeration(Enumeration ...enums) { 12 | this.enums = enums; 13 | } 14 | 15 | private boolean next() { 16 | while (index < enums.length) { 17 | if (enums[index] != null && enums[index].hasMoreElements()) { 18 | return true; 19 | } 20 | index++; 21 | } 22 | return false; 23 | } 24 | 25 | public boolean hasMoreElements() { 26 | return next(); 27 | } 28 | 29 | public E nextElement() { 30 | if (!next()) { 31 | throw new NoSuchElementException(); 32 | } 33 | return enums[index].nextElement(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/signing/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/signing/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar 2 | 3 | plugins { 4 | id("java-library") 5 | id("java") 6 | id("com.github.johnrengelman.shadow") version "6.0.0" 7 | } 8 | 9 | java { 10 | sourceCompatibility = JavaVersion.VERSION_1_8 11 | targetCompatibility = JavaVersion.VERSION_1_8 12 | } 13 | 14 | val jar by tasks.getting(Jar::class) { 15 | manifest { 16 | attributes["Main-Class"] = "com.topjohnwu.signing.ZipSigner" 17 | } 18 | } 19 | 20 | val shadowJar by tasks.getting(ShadowJar::class) { 21 | archiveBaseName.set("zipsigner") 22 | archiveClassifier.set(null as String?) 23 | archiveVersion.set("4.0") 24 | } 25 | 26 | repositories { 27 | jcenter() 28 | } 29 | 30 | dependencies { 31 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) 32 | 33 | api("org.bouncycastle:bcprov-jdk15on:1.66") 34 | api("org.bouncycastle:bcpkix-jdk15on:1.66") 35 | } 36 | -------------------------------------------------------------------------------- /app/signing/src/main/java/com/topjohnwu/signing/ByteArrayStream.java: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.signing; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | public class ByteArrayStream extends ByteArrayOutputStream { 9 | 10 | public synchronized void readFrom(InputStream is) { 11 | readFrom(is, Integer.MAX_VALUE); 12 | } 13 | 14 | public synchronized void readFrom(InputStream is, int len) { 15 | int read; 16 | byte buffer[] = new byte[4096]; 17 | try { 18 | while ((read = is.read(buffer, 0, Math.min(len, buffer.length))) > 0) { 19 | write(buffer, 0, read); 20 | len -= read; 21 | } 22 | } catch (IOException e) { 23 | e.printStackTrace(); 24 | } 25 | } 26 | 27 | public ByteArrayInputStream getInputStream() { 28 | return new ByteArrayInputStream(buf, 0, count); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/signing/src/main/resources/keys/testkey.pk8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/signing/src/main/resources/keys/testkey.pk8 -------------------------------------------------------------------------------- /app/signing/src/main/resources/keys/verity.pk8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/signing/src/main/resources/keys/verity.pk8 -------------------------------------------------------------------------------- /app/src/main/java/a/stubs.kt: -------------------------------------------------------------------------------- 1 | @file:JvmName("a") 2 | package a 3 | 4 | import com.topjohnwu.magisk.core.App 5 | import com.topjohnwu.magisk.core.GeneralReceiver 6 | import com.topjohnwu.magisk.core.SplashActivity 7 | import com.topjohnwu.magisk.core.download.DownloadService 8 | import com.topjohnwu.magisk.ui.MainActivity 9 | import com.topjohnwu.magisk.ui.surequest.SuRequestActivity 10 | import com.topjohnwu.signing.BootSigner 11 | 12 | fun main(args: Array) { 13 | BootSigner.main(args) 14 | } 15 | 16 | class b : MainActivity() 17 | 18 | class c : SplashActivity() 19 | 20 | class e : App { 21 | constructor() : super() 22 | constructor(o: Any) : super(o) 23 | } 24 | 25 | class h : GeneralReceiver() 26 | 27 | class j : DownloadService() 28 | 29 | class m : SuRequestActivity() 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/arch/Queryable.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.arch 2 | 3 | import android.os.Handler 4 | import androidx.core.os.postDelayed 5 | import com.topjohnwu.superuser.internal.UiThreadHandler 6 | 7 | interface Queryable { 8 | 9 | val queryDelay: Long 10 | val queryHandler: Handler get() = UiThreadHandler.handler 11 | 12 | fun submitQuery() { 13 | queryHandler.postDelayed(queryDelay) { query() } 14 | } 15 | 16 | fun query() 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/arch/ViewEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.arch 2 | 3 | import android.content.Context 4 | import kotlinx.coroutines.CoroutineScope 5 | 6 | /** 7 | * Class for passing events from ViewModels to Activities/Fragments 8 | * (see https://medium.com/google-developers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150) 9 | */ 10 | abstract class ViewEvent 11 | 12 | abstract class ViewEventWithScope: ViewEvent() { 13 | lateinit var scope: CoroutineScope 14 | } 15 | 16 | interface ContextExecutor { 17 | operator fun invoke(context: Context) 18 | } 19 | 20 | interface ActivityExecutor { 21 | operator fun invoke(activity: BaseUIActivity<*, *>) 22 | } 23 | 24 | interface FragmentExecutor { 25 | operator fun invoke(fragment: BaseUIFragment<*, *>) 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/base/BaseReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.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.core.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/core/base/BaseService.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.base 2 | 3 | import android.app.Service 4 | import android.content.Context 5 | import com.topjohnwu.magisk.core.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/core/base/PermissionRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.base 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 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/download/Action.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.download 2 | 3 | import android.net.Uri 4 | import android.os.Parcelable 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | sealed class Action : Parcelable { 8 | 9 | sealed class Flash : Action() { 10 | 11 | @Parcelize 12 | object Primary : Flash() 13 | 14 | @Parcelize 15 | object Secondary : Flash() 16 | 17 | } 18 | 19 | sealed class APK : Action() { 20 | 21 | @Parcelize 22 | object Upgrade : APK() 23 | 24 | @Parcelize 25 | object Restore : APK() 26 | } 27 | 28 | @Parcelize 29 | object Download : Action() 30 | 31 | @Parcelize 32 | object Uninstall : Action() 33 | 34 | @Parcelize 35 | object EnvFix : Action() 36 | 37 | @Parcelize 38 | data class Patch(val fileUri: Uri) : Action() 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/magiskdb/BaseDao.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.magiskdb 2 | 3 | import androidx.annotation.StringDef 4 | 5 | abstract class BaseDao { 6 | 7 | object Table { 8 | const val POLICY = "policies" 9 | const val LOG = "logs" 10 | const val SETTINGS = "settings" 11 | const val STRINGS = "strings" 12 | } 13 | 14 | @StringDef(Table.POLICY, Table.LOG, Table.SETTINGS, Table.STRINGS) 15 | @Retention(AnnotationRetention.SOURCE) 16 | annotation class TableStrict 17 | 18 | @TableStrict 19 | abstract val table: String 20 | 21 | inline fun buildQuery(builder: Builder.() -> Unit = {}) = 22 | Builder::class.java.newInstance() 23 | .apply { table = this@BaseDao.table } 24 | .apply(builder) 25 | .toString() 26 | .let { Query(it) } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/magiskdb/SettingsDao.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.magiskdb 2 | 3 | class SettingsDao : BaseDao() { 4 | 5 | override val table = Table.SETTINGS 6 | 7 | suspend fun delete(key: String) = buildQuery { 8 | condition { equals("key", key) } 9 | }.commit() 10 | 11 | suspend fun put(key: String, value: Int) = buildQuery { 12 | values("key" to key, "value" to value) 13 | }.commit() 14 | 15 | suspend fun fetch(key: String, default: Int = -1) = buildQuery { 16 | fields("value") 17 | condition { equals("key", key) } 18 | }.query { 19 | it["value"] 20 | }.firstOrNull() ?: default 21 | 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/model/UpdateInfo.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.model 2 | 3 | import android.os.Parcelable 4 | import com.squareup.moshi.JsonClass 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @JsonClass(generateAdapter = true) 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 | @JsonClass(generateAdapter = true) 16 | data class UninstallerJson( 17 | val link: String = "" 18 | ) 19 | 20 | @JsonClass(generateAdapter = true) 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 | @JsonClass(generateAdapter = true) 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 | @Parcelize 39 | @JsonClass(generateAdapter = true) 40 | data class StubJson( 41 | val versionCode: Int = -1, 42 | val link: String = "" 43 | ) : Parcelable 44 | 45 | @JsonClass(generateAdapter = true) 46 | data class CommitInfo( 47 | val sha: String 48 | ) 49 | 50 | @JsonClass(generateAdapter = true) 51 | data class BranchInfo( 52 | val commit: CommitInfo 53 | ) 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/model/module/BaseModule.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.model.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/core/model/su/SuLog.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.model.su 2 | 3 | import androidx.room.Entity 4 | import androidx.room.Ignore 5 | import androidx.room.PrimaryKey 6 | import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.ALLOW 7 | import com.topjohnwu.magisk.ktx.now 8 | import com.topjohnwu.magisk.ktx.timeFormatTime 9 | import com.topjohnwu.magisk.ktx.toTime 10 | 11 | @Entity(tableName = "logs") 12 | data class SuLog( 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 | fun SuPolicy.toLog( 27 | toUid: Int, 28 | fromPid: Int, 29 | command: String 30 | ) = SuLog(uid, toUid, fromPid, packageName, appName, command, policy == ALLOW, now) 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/utils/IODispatcherExecutor.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.utils 2 | 3 | import kotlinx.coroutines.* 4 | import java.util.concurrent.* 5 | 6 | class IODispatcherExecutor : AbstractExecutorService() { 7 | 8 | private val job = SupervisorJob().apply { invokeOnCompletion { future.run() } } 9 | private val scope = CoroutineScope(job + Dispatchers.IO) 10 | private val future = FutureTask(Callable { true }) 11 | 12 | override fun execute(command: Runnable) { 13 | scope.launch { 14 | command.run() 15 | } 16 | } 17 | 18 | override fun shutdown() = job.cancel() 19 | 20 | override fun shutdownNow(): List { 21 | job.cancel() 22 | return emptyList() 23 | } 24 | 25 | override fun isShutdown() = job.isCancelled 26 | 27 | override fun isTerminated() = job.isCancelled && job.isCompleted 28 | 29 | override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean { 30 | return try { 31 | future.get(timeout, unit) 32 | } catch (e: TimeoutException) { 33 | false 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/utils/ProgressInputStream.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.utils 2 | 3 | import java.io.FilterInputStream 4 | import java.io.InputStream 5 | 6 | class ProgressInputStream( 7 | base: InputStream, 8 | val progressEmitter: (Long) -> Unit = {} 9 | ) : FilterInputStream(base) { 10 | 11 | private var bytesRead = 0L 12 | private var lastUpdate = 0L 13 | 14 | private fun emitProgress() { 15 | val cur = System.currentTimeMillis() 16 | if (cur - lastUpdate > 1000) { 17 | lastUpdate = cur 18 | progressEmitter(bytesRead) 19 | } 20 | } 21 | 22 | override fun read(): Int { 23 | val b = read() 24 | if (b >= 0) { 25 | bytesRead++ 26 | emitProgress() 27 | } 28 | return b 29 | } 30 | 31 | override fun read(b: ByteArray): Int { 32 | return read(b, 0, b.size) 33 | } 34 | 35 | override fun read(b: ByteArray, off: Int, len: Int): Int { 36 | val sz = super.read(b, off, len) 37 | if (sz > 0) { 38 | bytesRead += sz 39 | emitProgress() 40 | } 41 | return sz 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/utils/net/NetworkObserver.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.core.utils.net 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.os.Build 6 | import androidx.core.content.getSystemService 7 | 8 | typealias ConnectionCallback = (Boolean) -> Unit 9 | 10 | abstract class NetworkObserver( 11 | context: Context, 12 | protected val callback: ConnectionCallback 13 | ) { 14 | 15 | protected val app: Context = context.applicationContext 16 | protected val manager = context.getSystemService()!! 17 | 18 | protected abstract fun stopObserving() 19 | protected abstract fun getCurrentState() 20 | 21 | companion object { 22 | fun observe(context: Context, callback: ConnectionCallback): NetworkObserver { 23 | return when (Build.VERSION.SDK_INT) { 24 | in 23 until Int.MAX_VALUE -> MarshmallowNetworkObserver(context, callback) 25 | in 21 until 23 -> LollipopNetworkObserver(context, callback) 26 | else -> PreLollipopNetworkObserver(context, callback) 27 | }.apply { getCurrentState() } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/core/utils/net/PreLollipopNetworkObserver.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("DEPRECATION") 2 | 3 | package com.topjohnwu.magisk.core.utils.net 4 | 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.content.Intent 8 | import android.content.IntentFilter 9 | import android.net.ConnectivityManager 10 | import androidx.core.net.ConnectivityManagerCompat 11 | 12 | class PreLollipopNetworkObserver( 13 | context: Context, 14 | callback: ConnectionCallback 15 | ): NetworkObserver(context, callback) { 16 | 17 | private val receiver = ConnectivityBroadcastReceiver() 18 | 19 | init { 20 | val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) 21 | app.registerReceiver(receiver, filter) 22 | } 23 | 24 | override fun stopObserving() { 25 | app.unregisterReceiver(receiver) 26 | } 27 | 28 | override fun getCurrentState() { 29 | callback(manager.activeNetworkInfo?.isConnected ?: false) 30 | } 31 | 32 | private inner class ConnectivityBroadcastReceiver: BroadcastReceiver() { 33 | override fun onReceive(context: Context?, intent: Intent) { 34 | val info = ConnectivityManagerCompat.getNetworkInfoFromBroadcast(manager, intent) 35 | callback(info?.isConnected ?: false) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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.core.model.su.SuLog 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.withContext 7 | import java.util.* 8 | 9 | @Database(version = 1, entities = [SuLog::class], exportSchema = false) 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 | suspend fun deleteAll() = withContext(Dispatchers.IO) { db.clearAllTables() } 22 | 23 | suspend fun fetchAll(): MutableList { 24 | deleteOutdated() 25 | return fetch() 26 | } 27 | 28 | @Query("SELECT * FROM logs ORDER BY time DESC") 29 | protected abstract suspend fun fetch(): MutableList 30 | 31 | @Query("DELETE FROM logs WHERE time < :timeout") 32 | protected abstract suspend fun deleteOutdated(timeout: Long = twoWeeksAgo) 33 | 34 | @Insert 35 | abstract suspend fun insert(log: SuLog) 36 | 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/BooleanProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.ktx.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 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/FloatProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.ktx.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 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/IntProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.ktx.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 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/LongProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.ktx.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 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/Property.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.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 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/StringProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.ktx.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 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/preference/StringSetProperty.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.preference 2 | 3 | import androidx.core.content.edit 4 | import com.topjohnwu.magisk.ktx.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 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/data/repository/LogRepository.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.data.repository 2 | 3 | import com.topjohnwu.magisk.core.Const 4 | import com.topjohnwu.magisk.core.model.su.SuLog 5 | import com.topjohnwu.magisk.data.database.SuLogDao 6 | import com.topjohnwu.magisk.ktx.await 7 | import com.topjohnwu.superuser.Shell 8 | 9 | 10 | class LogRepository( 11 | private val logDao: SuLogDao 12 | ) { 13 | 14 | suspend fun fetchSuLogs() = logDao.fetchAll() 15 | 16 | suspend fun fetchMagiskLogs(): String { 17 | val list = object : AbstractMutableList() { 18 | val buf = StringBuilder() 19 | override val size get() = 0 20 | override fun get(index: Int): String = "" 21 | override fun removeAt(index: Int): String = "" 22 | override fun set(index: Int, element: String): String = "" 23 | override fun add(index: Int, element: String) { 24 | if (element.isNotEmpty()) { 25 | buf.append(element) 26 | buf.append('\n') 27 | } 28 | } 29 | } 30 | Shell.su("cat ${Const.MAGISK_LOG}").to(list).await() 31 | return list.buf.toString() 32 | } 33 | 34 | suspend fun clearLogs() = logDao.deleteAll() 35 | 36 | fun clearMagiskLogs(cb: (Shell.Result) -> Unit) = 37 | Shell.su("echo -n > ${Const.MAGISK_LOG}").submit(cb) 38 | 39 | suspend fun insert(log: SuLog) = logDao.insert(log) 40 | 41 | } 42 | -------------------------------------------------------------------------------- /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/databinding/RvBindingAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.databinding 2 | 3 | import androidx.databinding.ViewDataBinding 4 | import androidx.recyclerview.widget.RecyclerView 5 | import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapter 6 | 7 | class RvBindingAdapter : BindingRecyclerViewAdapter() { 8 | 9 | private var recyclerView: RecyclerView? = null 10 | 11 | override fun onBindBinding( 12 | binding: ViewDataBinding, 13 | variableId: Int, 14 | layoutRes: Int, 15 | position: Int, 16 | item: T 17 | ) { 18 | super.onBindBinding(binding, variableId, layoutRes, position, item) 19 | 20 | when (item) { 21 | is LenientRvItem -> { 22 | val recycler = recyclerView ?: return 23 | item.onBindingBound(binding) 24 | item.onBindingBound(binding, recycler) 25 | } 26 | else -> item.onBindingBound(binding) 27 | } 28 | } 29 | 30 | override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { 31 | super.onAttachedToRecyclerView(recyclerView) 32 | this.recyclerView = recyclerView 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/di/ApplicationModule.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.di 2 | 3 | import android.content.Context 4 | import android.os.Build 5 | import androidx.preference.PreferenceManager 6 | import com.topjohnwu.magisk.core.ResMgr 7 | import org.koin.core.qualifier.named 8 | import org.koin.dsl.module 9 | 10 | val SUTimeout = named("su_timeout") 11 | val Protected = named("protected") 12 | 13 | val applicationModule = module { 14 | factory { ResMgr.resource } 15 | factory { get().packageManager } 16 | factory(Protected) { createDEContext(get()) } 17 | single(SUTimeout) { get(Protected).getSharedPreferences("su_timeout", 0) } 18 | single { PreferenceManager.getDefaultSharedPreferences(get(Protected)) } 19 | } 20 | 21 | private fun createDEContext(context: Context): Context { 22 | return if (Build.VERSION.SDK_INT >= 24) 23 | context.createDeviceProtectedStorageContext() 24 | else context 25 | } 26 | -------------------------------------------------------------------------------- /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.core.magiskdb.PolicyDao 6 | import com.topjohnwu.magisk.core.magiskdb.SettingsDao 7 | import com.topjohnwu.magisk.core.magiskdb.StringDao 8 | import com.topjohnwu.magisk.core.tasks.RepoUpdater 9 | import com.topjohnwu.magisk.data.database.RepoDatabase 10 | import com.topjohnwu.magisk.data.database.SuLogDatabase 11 | import org.koin.dsl.module 12 | 13 | 14 | val databaseModule = module { 15 | single { PolicyDao(get()) } 16 | single { SettingsDao() } 17 | single { StringDao() } 18 | single { createRepoDatabase(get()) } 19 | single { get().repoDao() } 20 | single { get().repoByNameDao() } 21 | single { get().repoByUpdatedDao() } 22 | single { createSuLogDatabase(get(Protected)).suLogDao() } 23 | single { RepoUpdater(get(), get()) } 24 | } 25 | 26 | fun createRepoDatabase(context: Context) = 27 | Room.databaseBuilder(context, RepoDatabase::class.java, "repo.db") 28 | .fallbackToDestructiveMigration() 29 | .build() 30 | 31 | fun createSuLogDatabase(context: Context) = 32 | Room.databaseBuilder(context, SuLogDatabase::class.java, "sulogs.db") 33 | .fallbackToDestructiveMigration() 34 | .build() 35 | -------------------------------------------------------------------------------- /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.NetworkService 5 | import org.koin.dsl.module 6 | 7 | 8 | val repositoryModule = module { 9 | single { LogRepository(get()) } 10 | single { NetworkService(get(), get(), get(), get()) } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/events/OpenInappLinkEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.events 2 | 3 | import android.content.Context 4 | import android.content.res.Resources 5 | import android.util.TypedValue 6 | import androidx.annotation.AttrRes 7 | import androidx.browser.customtabs.CustomTabsIntent 8 | import androidx.core.net.toUri 9 | import com.topjohnwu.magisk.R 10 | import com.topjohnwu.magisk.arch.ContextExecutor 11 | import com.topjohnwu.magisk.arch.ViewEvent 12 | 13 | data class OpenInappLinkEvent( 14 | private val link: String 15 | ) : ViewEvent(), ContextExecutor { 16 | 17 | // todo find app that can open the link and as a fallback open custom tabs! it shouldn't be the default 18 | override fun invoke(context: Context) = CustomTabsIntent.Builder() 19 | .setShowTitle(true) 20 | .setToolbarColor(context.themedColor(R.attr.colorSurface)) 21 | .enableUrlBarHiding() 22 | .build() 23 | .launchUrl(context, link.toUri()) 24 | 25 | private fun Context.themedColor(@AttrRes attribute: Int) = theme 26 | .resolveAttribute(attribute).data 27 | 28 | private fun Resources.Theme.resolveAttribute( 29 | @AttrRes attribute: Int, 30 | resolveRefs: Boolean = true 31 | ) = TypedValue().also { resolveAttribute(attribute, it, resolveRefs) } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/events/RebootEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.events 2 | 3 | import android.view.ContextThemeWrapper 4 | import android.view.MenuItem 5 | import android.widget.PopupMenu 6 | import com.topjohnwu.magisk.R 7 | import com.topjohnwu.magisk.core.base.BaseActivity 8 | import com.topjohnwu.superuser.Shell 9 | import com.topjohnwu.magisk.ktx.reboot as systemReboot 10 | 11 | object RebootEvent { 12 | 13 | private fun reboot(item: MenuItem): Boolean { 14 | when (item.itemId) { 15 | R.id.action_reboot_normal -> systemReboot() 16 | R.id.action_reboot_bootloader -> systemReboot("bootloader") 17 | R.id.action_reboot_download -> systemReboot("download") 18 | R.id.action_reboot_edl -> systemReboot("edl") 19 | R.id.action_reboot_recovery -> Shell.su("/system/bin/reboot recovery").submit() 20 | else -> Unit 21 | } 22 | return true 23 | } 24 | 25 | fun inflateMenu(activity: BaseActivity): PopupMenu { 26 | val themeWrapper = ContextThemeWrapper(activity, R.style.Foundation_PopupMenu) 27 | val menu = PopupMenu(themeWrapper, activity.findViewById(R.id.action_reboot)) 28 | activity.menuInflater.inflate(R.menu.menu_reboot, menu.menu) 29 | menu.setOnMenuItemClickListener(::reboot) 30 | return menu 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/events/dialog/BiometricEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.events.dialog 2 | 3 | import com.topjohnwu.magisk.arch.ActivityExecutor 4 | import com.topjohnwu.magisk.arch.BaseUIActivity 5 | import com.topjohnwu.magisk.arch.ViewEvent 6 | import com.topjohnwu.magisk.core.utils.BiometricHelper 7 | 8 | class BiometricEvent( 9 | builder: Builder.() -> Unit 10 | ) : ViewEvent(), ActivityExecutor { 11 | 12 | private var listenerOnFailure: GenericDialogListener = {} 13 | private var listenerOnSuccess: GenericDialogListener = {} 14 | 15 | init { 16 | builder(Builder()) 17 | } 18 | 19 | override fun invoke(activity: BaseUIActivity<*, *>) { 20 | BiometricHelper.authenticate( 21 | activity, 22 | onError = listenerOnFailure, 23 | onSuccess = listenerOnSuccess 24 | ) 25 | } 26 | 27 | inner class Builder internal constructor() { 28 | 29 | fun onFailure(listener: GenericDialogListener) { 30 | listenerOnFailure = listener 31 | } 32 | 33 | fun onSuccess(listener: GenericDialogListener) { 34 | listenerOnSuccess = listener 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/events/dialog/DialogEvent.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.events.dialog 2 | 3 | import com.topjohnwu.magisk.arch.ActivityExecutor 4 | import com.topjohnwu.magisk.arch.BaseUIActivity 5 | import com.topjohnwu.magisk.arch.ViewEvent 6 | import com.topjohnwu.magisk.view.MagiskDialog 7 | 8 | abstract class DialogEvent : ViewEvent(), ActivityExecutor { 9 | 10 | protected lateinit var dialog: MagiskDialog 11 | 12 | override fun invoke(activity: BaseUIActivity<*, *>) { 13 | dialog = MagiskDialog(activity).apply(this::build).reveal() 14 | } 15 | 16 | abstract fun build(dialog: MagiskDialog) 17 | 18 | } 19 | 20 | typealias GenericDialogListener = () -> Unit 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/events/dialog/SecondSlotWarningDialog.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.events.dialog 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.view.MagiskDialog 5 | 6 | class SecondSlotWarningDialog : DialogEvent() { 7 | 8 | override fun build(dialog: MagiskDialog) { 9 | dialog.applyTitle(android.R.string.dialog_alert_title) 10 | .applyMessage(R.string.install_inactive_slot_msg) 11 | .applyButton(MagiskDialog.ButtonType.POSITIVE) { 12 | titleRes = android.R.string.ok 13 | } 14 | .cancellable(true) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/events/dialog/SuperuserRevokeDialog.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.events.dialog 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.view.MagiskDialog 5 | 6 | class SuperuserRevokeDialog( 7 | builder: Builder.() -> Unit 8 | ) : DialogEvent() { 9 | 10 | private val callbacks = Builder().apply(builder) 11 | 12 | override fun build(dialog: MagiskDialog) { 13 | dialog.applyTitle(R.string.su_revoke_title) 14 | .applyMessage(R.string.su_revoke_msg, callbacks.appName) 15 | .applyButton(MagiskDialog.ButtonType.POSITIVE) { 16 | titleRes = android.R.string.ok 17 | onClick { callbacks.listenerOnSuccess() } 18 | } 19 | .applyButton(MagiskDialog.ButtonType.NEGATIVE) { 20 | titleRes = android.R.string.cancel 21 | } 22 | } 23 | 24 | inner class Builder internal constructor() { 25 | var appName: String = "" 26 | 27 | internal var listenerOnSuccess: GenericDialogListener = {} 28 | 29 | fun onSuccess(listener: GenericDialogListener) { 30 | listenerOnSuccess = listener 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ktx/Dimens.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ktx 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/ktx/XKoin.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ktx 2 | 3 | import org.koin.core.context.KoinContextHandler 4 | import org.koin.core.parameter.ParametersDefinition 5 | import org.koin.core.qualifier.Qualifier 6 | 7 | fun getKoin() = KoinContextHandler.get() 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) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ktx/XSU.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ktx 2 | 3 | import android.content.Context 4 | import com.topjohnwu.magisk.core.Config 5 | import com.topjohnwu.magisk.core.Const 6 | import com.topjohnwu.superuser.Shell 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.withContext 9 | 10 | fun reboot(reason: String = if (Config.recovery) "recovery" else "") { 11 | Shell.su("/system/bin/svc power reboot $reason || /system/bin/reboot $reason").submit() 12 | } 13 | 14 | fun relaunchApp(context: Context) { 15 | val intent = context.packageManager.getLaunchIntentForPackage(context.packageName) ?: return 16 | val args = mutableListOf("am", "start", "--user", Const.USER_ID.toString()) 17 | val cmd = intent.toCommand(args).joinToString(separator = " ") 18 | Shell.su("run_delay 1 \"$cmd\"").exec() 19 | Runtime.getRuntime().exit(0) 20 | } 21 | 22 | suspend fun Shell.Job.await() = withContext(Dispatchers.IO) { exec() } 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ktx/XTime.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ktx 2 | 3 | import com.topjohnwu.magisk.core.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", 18 | currentLocale 19 | ) } 20 | val timeFormatStandard by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", 21 | currentLocale 22 | ) } 23 | val timeFormatMedium by lazy { DateFormat.getDateInstance(DateFormat.MEDIUM, 24 | currentLocale 25 | ) } 26 | val timeFormatTime by lazy { SimpleDateFormat("h:mm a", 27 | currentLocale 28 | ) } 29 | val timeDateFormat by lazy { 30 | DateFormat.getDateTimeInstance( 31 | DateFormat.DEFAULT, 32 | DateFormat.DEFAULT, 33 | currentLocale 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/flash/ConsoleItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.flash 2 | 3 | import android.view.View 4 | import android.widget.TextView 5 | import androidx.core.view.updateLayoutParams 6 | import androidx.databinding.ViewDataBinding 7 | import androidx.recyclerview.widget.RecyclerView 8 | import com.topjohnwu.magisk.R 9 | import com.topjohnwu.magisk.databinding.ComparableRvItem 10 | import com.topjohnwu.magisk.databinding.LenientRvItem 11 | import kotlin.math.max 12 | 13 | class ConsoleItem(val item: String) : ComparableRvItem(), LenientRvItem { 14 | override val layoutRes = R.layout.item_console_md2 15 | 16 | private var parentWidth = -1 17 | 18 | override fun onBindingBound(binding: ViewDataBinding, recyclerView: RecyclerView) { 19 | if (parentWidth < 0) 20 | parentWidth = (recyclerView.parent as View).width 21 | 22 | val view = binding.root as TextView 23 | view.measure(0, 0) 24 | 25 | // We want our recyclerView at least as wide as screen 26 | val desiredWidth = max(view.measuredWidth, parentWidth) 27 | 28 | view.updateLayoutParams { width = desiredWidth } 29 | 30 | if (recyclerView.width < desiredWidth) { 31 | recyclerView.requestLayout() 32 | } 33 | } 34 | 35 | override fun contentSameAs(other: ConsoleItem) = itemSameAs(other) 36 | override fun itemSameAs(other: ConsoleItem) = item == other.item 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/hide/HideApp.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.hide 2 | 3 | import android.content.pm.ApplicationInfo 4 | import android.content.pm.PackageManager 5 | import android.graphics.drawable.Drawable 6 | import com.topjohnwu.magisk.core.utils.currentLocale 7 | import com.topjohnwu.magisk.ktx.getLabel 8 | 9 | class HideTarget(line: String) { 10 | val packageName: String 11 | val process: String 12 | 13 | init { 14 | val split = line.split(Regex("\\|"), 2) 15 | packageName = split[0] 16 | process = split.getOrElse(1) { packageName } 17 | } 18 | } 19 | 20 | class HideAppInfo(info: ApplicationInfo, pm: PackageManager) 21 | : ApplicationInfo(info), Comparable { 22 | 23 | val label = info.getLabel(pm) 24 | val iconImage: Drawable = info.loadIcon(pm) 25 | 26 | override fun compareTo(other: HideAppInfo) = comparator.compare(this, other) 27 | 28 | companion object { 29 | private val comparator = compareBy( 30 | { it.label.toLowerCase(currentLocale) }, 31 | { it.packageName } 32 | ) 33 | } 34 | } 35 | 36 | data class HideProcessInfo( 37 | val name: String, 38 | val packageName: String, 39 | val isHidden: Boolean 40 | ) 41 | 42 | class HideAppTarget( 43 | val info: HideAppInfo, 44 | val processes: List 45 | ) : Comparable { 46 | override fun compareTo(other: HideAppTarget) = compareValuesBy(this, other) { it.info } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/log/LogRvItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.log 2 | 3 | import androidx.databinding.Bindable 4 | import com.topjohnwu.magisk.BR 5 | import com.topjohnwu.magisk.R 6 | import com.topjohnwu.magisk.core.model.su.SuLog 7 | import com.topjohnwu.magisk.databinding.ObservableItem 8 | import com.topjohnwu.magisk.ktx.timeDateFormat 9 | import com.topjohnwu.magisk.ktx.toTime 10 | import com.topjohnwu.magisk.utils.set 11 | 12 | class LogRvItem(val item: SuLog) : ObservableItem() { 13 | 14 | override val layoutRes = R.layout.item_log_access_md2 15 | 16 | val date = item.time.toTime(timeDateFormat) 17 | 18 | @get:Bindable 19 | var isTop = false 20 | set(value) = set(value, field, { field = it }, BR.top) 21 | 22 | @get:Bindable 23 | var isBottom = false 24 | set(value) = set(value, field, { field = it }, BR.bottom) 25 | 26 | override fun itemSameAs(other: LogRvItem) = item.appName == other.item.appName 27 | 28 | override fun contentSameAs(other: LogRvItem) = item.fromUid == other.item.fromUid && 29 | item.toUid == other.item.toUid && 30 | item.fromPid == other.item.fromPid && 31 | item.packageName == other.item.packageName && 32 | item.command == other.item.command && 33 | item.action == other.item.action && 34 | item.time == other.item.time && 35 | isTop == other.isTop && 36 | isBottom == other.isBottom 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/safetynet/SafetyNetHelper.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.safetynet 2 | 3 | import org.json.JSONObject 4 | 5 | interface SafetyNetHelper { 6 | 7 | val version: Int 8 | 9 | fun attest() 10 | 11 | interface Callback { 12 | fun onResponse(response: JSONObject?) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/safetynet/SafetynetFragment.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.safetynet 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import com.topjohnwu.magisk.R 8 | import com.topjohnwu.magisk.arch.BaseUIFragment 9 | import com.topjohnwu.magisk.databinding.FragmentSafetynetMd2Binding 10 | import org.koin.androidx.viewmodel.ext.android.viewModel 11 | 12 | class SafetynetFragment : BaseUIFragment() { 13 | 14 | override val layoutRes = R.layout.fragment_safetynet_md2 15 | override val viewModel by viewModel() 16 | 17 | override fun onStart() { 18 | super.onStart() 19 | activity.setTitle(R.string.safetynet) 20 | } 21 | 22 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 23 | super.onCreateView(inflater, container, savedInstanceState) 24 | 25 | // Set barrier reference IDs in code, since resource IDs will be stripped in release mode 26 | binding.snetBarrier.referencedIds = intArrayOf(R.id.basic_text, R.id.cts_text) 27 | 28 | return binding.root 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsFragment.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.settings 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import com.topjohnwu.magisk.R 6 | import com.topjohnwu.magisk.arch.BaseUIFragment 7 | import com.topjohnwu.magisk.databinding.FragmentSettingsMd2Binding 8 | import com.topjohnwu.magisk.ktx.setOnViewReadyListener 9 | import org.koin.androidx.viewmodel.ext.android.viewModel 10 | 11 | class SettingsFragment : BaseUIFragment() { 12 | 13 | override val layoutRes = R.layout.fragment_settings_md2 14 | override val viewModel by viewModel() 15 | 16 | override fun onStart() { 17 | super.onStart() 18 | 19 | activity.title = resources.getString(R.string.settings) 20 | } 21 | 22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 23 | super.onViewCreated(view, savedInstanceState) 24 | binding.settingsList.setOnViewReadyListener { 25 | binding.settingsList.scrollToPosition(0) 26 | } 27 | } 28 | 29 | override fun onResume() { 30 | super.onResume() 31 | viewModel.items.forEach { it.refresh() } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/superuser/SpinnerRvItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.superuser 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 | } 14 | -------------------------------------------------------------------------------- /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.arch.BaseUIFragment 5 | import com.topjohnwu.magisk.databinding.FragmentSuperuserMd2Binding 6 | import org.koin.androidx.viewmodel.ext.android.viewModel 7 | 8 | class SuperuserFragment : BaseUIFragment() { 9 | 10 | override val layoutRes = R.layout.fragment_superuser_md2 11 | override val viewModel by viewModel() 12 | 13 | override fun onStart() { 14 | super.onStart() 15 | activity.title = resources.getString(R.string.superuser) 16 | } 17 | 18 | override fun onPreBind(binding: FragmentSuperuserMd2Binding) {} 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/ui/theme/ThemeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.ui.theme 2 | 3 | import com.topjohnwu.magisk.arch.BaseViewModel 4 | import com.topjohnwu.magisk.events.RecreateEvent 5 | import com.topjohnwu.magisk.events.dialog.DarkThemeDialog 6 | import com.topjohnwu.magisk.view.TappableHeadlineItem 7 | 8 | class ThemeViewModel : BaseViewModel(), TappableHeadlineItem.Listener { 9 | 10 | val themeHeadline = TappableHeadlineItem.ThemeMode 11 | 12 | override fun onItemPressed(item: TappableHeadlineItem) = when (item) { 13 | is TappableHeadlineItem.ThemeMode -> darkModePressed() 14 | else -> Unit 15 | } 16 | 17 | fun saveTheme(theme: Theme) { 18 | theme.select() 19 | RecreateEvent().publish() 20 | } 21 | 22 | private fun darkModePressed() = DarkThemeDialog().publish() 23 | 24 | } 25 | -------------------------------------------------------------------------------- /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/HideableBehavior.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import android.view.View 4 | 5 | interface HideableBehavior { 6 | 7 | fun setHidden(view: V, hide: Boolean, lockState: Boolean = false) 8 | 9 | } -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.utils 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.net.Uri 6 | import android.widget.Toast 7 | import com.topjohnwu.magisk.R 8 | import com.topjohnwu.magisk.core.Config 9 | import com.topjohnwu.magisk.core.Const 10 | import com.topjohnwu.magisk.core.Info 11 | import com.topjohnwu.magisk.ktx.get 12 | import com.topjohnwu.superuser.internal.UiThreadHandler 13 | 14 | object Utils { 15 | 16 | fun toast(msg: CharSequence, duration: Int) { 17 | UiThreadHandler.run { Toast.makeText(get(), msg, duration).show() } 18 | } 19 | 20 | fun toast(resId: Int, duration: Int) { 21 | UiThreadHandler.run { Toast.makeText(get(), resId, duration).show() } 22 | } 23 | 24 | fun showSuperUser(): Boolean { 25 | return Info.env.isActive && (Const.USER_ID == 0 26 | || Config.suMultiuserMode == Config.Value.MULTIUSER_MODE_USER) 27 | } 28 | 29 | fun openLink(context: Context, link: Uri) { 30 | val intent = Intent(Intent.ACTION_VIEW, link) 31 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 32 | if (intent.resolveActivity(context.packageManager) != null) { 33 | context.startActivity(intent) 34 | } else { 35 | toast( 36 | R.string.open_link_failed_toast, 37 | Toast.LENGTH_SHORT 38 | ) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/view/TappableHeadlineItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.view 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.databinding.ComparableRvItem 5 | 6 | sealed class TappableHeadlineItem : ComparableRvItem() { 7 | 8 | abstract val title: Int 9 | abstract val icon: Int 10 | 11 | override val layoutRes = R.layout.item_tappable_headline 12 | 13 | override fun itemSameAs(other: TappableHeadlineItem) = 14 | this === other 15 | 16 | override fun contentSameAs(other: TappableHeadlineItem) = 17 | title == other.title && icon == other.icon 18 | 19 | // --- listener 20 | 21 | interface Listener { 22 | 23 | fun onItemPressed(item: TappableHeadlineItem) 24 | 25 | } 26 | 27 | // --- objects 28 | 29 | object Hide : TappableHeadlineItem() { 30 | override val title = R.string.magiskhide 31 | override val icon = R.drawable.ic_hide_md2 32 | } 33 | 34 | object ThemeMode : TappableHeadlineItem() { 35 | override val title = R.string.settings_dark_mode_title 36 | override val icon = R.drawable.ic_day_night 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/topjohnwu/magisk/view/TextItem.kt: -------------------------------------------------------------------------------- 1 | package com.topjohnwu.magisk.view 2 | 3 | import com.topjohnwu.magisk.R 4 | import com.topjohnwu.magisk.databinding.ComparableRvItem 5 | 6 | class TextItem(val text: Int) : ComparableRvItem() { 7 | override val layoutRes = R.layout.item_text 8 | 9 | override fun contentSameAs(other: TextItem) = text == other.text 10 | override fun itemSameAs(other: TextItem) = contentSameAs(other) 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_enter_pop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fragment_exit_pop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_error_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_menu_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_on_primary_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_primary_error_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_primary_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_secondary_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_state_primary_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/color/color_text_transient.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi-v23/ic_splash_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-hdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-mdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-nodpi/logo.png -------------------------------------------------------------------------------- /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/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-xhdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-xxhdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_magisk_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ianmacd/Magisk/3c0c7a45e7d6d4268fe837fff58357527dece787/app/src/main/res/drawable-xxxhdpi/ic_magisk_outline.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_divider_rounded_on_primary.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_line_bottom_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_line_top_rounded.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_selection_circle_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_shadow.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider_l1.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider_l_50.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_back_md2.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bug_filled_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bug_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 17 | 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_bug_outlined_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_circle_checked_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_circle_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 10 | 11 | 14 | 15 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_circle_unchecked_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_compound_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_day.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_day_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete_md2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_device.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_download_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_extension.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_folder_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_forth_md2.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_github.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hide_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hide_select_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_filled_md2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_outlined_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_install.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_magisk_delete.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_manager.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_module_filled_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_module_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_module_outlined_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_module_storage_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_more.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_night.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_order_name.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_paint.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 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_data_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh_safetynet_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_restart.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_safetynet_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_save_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_filled_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_settings_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_show_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 12 | -------------------------------------------------------------------------------- /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_superuser_filled_md2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_superuser_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 12 | 13 | 17 | 18 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_superuser_outlined_md2.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_twitter.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_up_md2.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_update_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /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/fragment_theme_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 14 | 23 | 24 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_console_md2.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_icon_link.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 15 | 16 | 17 | 18 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_list_single_line.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | 17 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_module_download.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | 17 |