├── .idea ├── .name ├── .gitignore ├── vcs.xml ├── compiler.xml ├── migrations.xml ├── misc.xml ├── deploymentTargetSelector.xml ├── gradle.xml └── inspectionProfiles │ └── Project_Default.xml ├── app ├── .gitignore ├── jks │ └── King丶枫岚.jks ├── src │ ├── main │ │ ├── assets │ │ │ ├── stub.apk │ │ │ └── magiskboot │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── ic_copy.png │ │ │ │ ├── icon_method.png │ │ │ │ ├── ic_launcher_background.png │ │ │ │ ├── ic_launcher_foreground.png │ │ │ │ ├── ic_content_cut_white_24dp.png │ │ │ │ ├── ic_select_all_white_24dp.png │ │ │ │ ├── ic_content_copy_white_24dp.png │ │ │ │ ├── ic_content_paste_white_24dp.png │ │ │ │ ├── shape_flash_button.xml │ │ │ │ ├── shape_indicator_select.xml │ │ │ │ ├── shape_shadow_layout.xml │ │ │ │ ├── shape_indicator.xml │ │ │ │ ├── kernelsu.xml │ │ │ │ └── magisk.xml │ │ │ ├── mipmap-mdpi │ │ │ │ ├── icon.png │ │ │ │ ├── ic_blog.png │ │ │ │ ├── ic_file.png │ │ │ │ ├── ic_home.png │ │ │ │ ├── ic_idea.png │ │ │ │ ├── ic_log.png │ │ │ │ ├── ic_mail.png │ │ │ │ ├── ic_root.png │ │ │ │ ├── ic_tips.png │ │ │ │ ├── qq_yege.jpg │ │ │ │ ├── ic_alipay.png │ │ │ │ ├── ic_folder.png │ │ │ │ ├── ic_github.png │ │ │ │ ├── ic_start.png │ │ │ │ ├── ic_update.png │ │ │ │ ├── ic_waring.png │ │ │ │ ├── qq_meizu.png │ │ │ │ ├── git_lumyuan.png │ │ │ │ ├── ic_developer.png │ │ │ │ ├── ic_download.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_settings.png │ │ │ │ ├── qq_xiaoqian.png │ │ │ │ ├── ic_notification.png │ │ │ │ ├── ic_logo_white_24dp.png │ │ │ │ └── ic_sub_notification.png │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-hdpi │ │ │ │ └── ic_file_sort.png │ │ │ ├── drawable-mdpi │ │ │ │ └── ic_file_sort.png │ │ │ ├── drawable-xhdpi │ │ │ │ └── ic_file_sort.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xxhdpi │ │ │ │ └── ic_file_sort.png │ │ │ ├── xml │ │ │ │ ├── network_security_config.xml │ │ │ │ └── update_path.xml │ │ │ ├── drawable-anydpi │ │ │ │ └── ic_file_sort.xml │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── themes.xml │ │ │ │ └── colors.xml │ │ │ ├── animator │ │ │ │ ├── fragment_slide_show.xml │ │ │ │ └── fragment_slide_hide.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── dialog_full_base.xml │ │ │ │ ├── layout_navigation_item.xml │ │ │ │ ├── fragment_navigation.xml │ │ │ │ ├── activity_log.xml │ │ │ │ ├── dialog_file_select.xml │ │ │ │ ├── dialog_permissin_write.xml │ │ │ │ └── fragment_home.xml │ │ │ └── raw │ │ │ │ ├── apatch.sh │ │ │ │ └── manager.sh │ │ ├── aidl │ │ │ ├── com │ │ │ │ └── flyme │ │ │ │ │ └── update │ │ │ │ │ └── helper │ │ │ │ │ ├── bean │ │ │ │ │ └── UpdateInfo.aidl │ │ │ │ │ ├── interfaces │ │ │ │ │ └── IUpdateCallback.aidl │ │ │ │ │ └── service │ │ │ │ │ └── IUpdateService.aidl │ │ │ └── android │ │ │ │ └── os │ │ │ │ ├── IUpdateEngineCallback.aidl │ │ │ │ └── IUpdateEngine.aidl │ │ ├── java │ │ │ └── com │ │ │ │ └── flyme │ │ │ │ └── update │ │ │ │ └── helper │ │ │ │ ├── bean │ │ │ │ ├── FileBean.java │ │ │ │ └── UpdateInfo.java │ │ │ │ ├── interfaces │ │ │ │ ├── OnNavigationStateListener.java │ │ │ │ └── UpdateCallback.java │ │ │ │ ├── utils │ │ │ │ ├── Natives.java │ │ │ │ ├── Config.java │ │ │ │ ├── LogUtils.java │ │ │ │ ├── NotificationUtils.java │ │ │ │ ├── Preconditions.java │ │ │ │ ├── Utils.java │ │ │ │ ├── ColorChangeUtils.java │ │ │ │ ├── CrashHandlerUtil.java │ │ │ │ ├── UpdateParser.java │ │ │ │ └── Reflection.java │ │ │ │ ├── manager │ │ │ │ ├── ActivityManger.java │ │ │ │ ├── SuFileManager.java │ │ │ │ └── UpdateServiceManager.java │ │ │ │ ├── proxy │ │ │ │ ├── ServiceManagerProxy.java │ │ │ │ └── UpdateEngineProxy.java │ │ │ │ ├── application │ │ │ │ └── App.java │ │ │ │ ├── activity │ │ │ │ ├── LogActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── BaseActivity.java │ │ │ │ ├── shell │ │ │ │ └── ShellInit.java │ │ │ │ ├── fragment │ │ │ │ └── AboutFragment.java │ │ │ │ ├── widget │ │ │ │ ├── TouchFeedback.java │ │ │ │ └── NavigationBar.java │ │ │ │ ├── service │ │ │ │ └── UpdateService.java │ │ │ │ └── adapter │ │ │ │ └── FileAdapter.java │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── jni.cc │ │ │ ├── ksu.h │ │ │ └── ksu.cc │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── flyme │ │ │ └── update │ │ │ └── helper │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── flyme │ │ └── update │ │ └── helper │ │ └── ExampleInstrumentedTest.java ├── libs │ ├── SystemUpdateManager.jar │ └── UpdateEngineService.jar ├── build.gradle.kts └── proguard-rules.pro ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── .gitignore ├── settings.gradle.kts ├── gradle.properties ├── gradlew.bat ├── main.py └── gradlew /.idea/.name: -------------------------------------------------------------------------------- 1 | 系统更新助手 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /app/jks/King丶枫岚.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/jks/King丶枫岚.jks -------------------------------------------------------------------------------- /app/src/main/assets/stub.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/assets/stub.apk -------------------------------------------------------------------------------- /app/src/main/assets/magiskboot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/assets/magiskboot -------------------------------------------------------------------------------- /app/libs/SystemUpdateManager.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/libs/SystemUpdateManager.jar -------------------------------------------------------------------------------- /app/libs/UpdateEngineService.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/libs/UpdateEngineService.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_copy.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/icon.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_blog.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_file.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_home.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_idea.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_log.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_mail.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_root.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_tips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_tips.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/qq_yege.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/qq_yege.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/icon_method.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_alipay.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_folder.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_start.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_update.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_waring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_waring.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/qq_meizu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/qq_meizu.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/git_lumyuan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/git_lumyuan.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_developer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_developer.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_download.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_settings.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/qq_xiaoqian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/qq_xiaoqian.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_file_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable-hdpi/ic_file_sort.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_file_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable-mdpi/ic_file_sort.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_file_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable-xhdpi/ic_file_sort.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_file_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable-xxhdpi/ic_file_sort.png -------------------------------------------------------------------------------- /app/src/main/aidl/com/flyme/update/helper/bean/UpdateInfo.aidl: -------------------------------------------------------------------------------- 1 | // UpdateInfo.aidl 2 | package com.flyme.update.helper.bean; 3 | 4 | parcelable UpdateInfo; 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_logo_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_logo_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_sub_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/mipmap-mdpi/ic_sub_notification.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_cut_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_content_cut_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_select_all_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_select_all_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_copy_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_content_copy_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_paste_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/King-Maple/V-ABHelper/HEAD/app/src/main/res/drawable/ic_content_paste_white_24dp.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # V-ABHelper 2 | 最开始我主要是为了给自己的魅族20Pro设备更新方便点,代码参考了 "一叶孤舟" 3 | 一叶孤舟是使用Shell的方式,我是使用libsu中的RootService去调用UpdataEngine,本质是没有区别的。 4 | 由于都是调用系统的服务去更新,所以,系统的有什么限制,这个就有啥限制。程序是在原开源基础上,修改而来,代码肯定是乱的, 5 | 6 | 请勿用于非法用途,只供学习参考 -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/bean/FileBean.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.bean; 2 | 3 | public class FileBean { 4 | public String Name; 5 | public long lastModified; 6 | public long length; 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/interfaces/OnNavigationStateListener.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.interfaces; 2 | 3 | public interface OnNavigationStateListener { 4 | void onNavigationState(boolean b, int i); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/interfaces/UpdateCallback.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.interfaces; 2 | 3 | public interface UpdateCallback { 4 | void onPayloadApplicationComplete(int errorCode); 5 | 6 | void onStatusUpdate(int status, float percent); 7 | } 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue May 09 08:35:59 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/Natives.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | public class Natives { 4 | public static native int getVersion(); 5 | 6 | public static native boolean isSafeMode(); 7 | 8 | public static native boolean isLkmMode(); 9 | } 10 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_flash_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/xml/update_path.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/aidl/com/flyme/update/helper/interfaces/IUpdateCallback.aidl: -------------------------------------------------------------------------------- 1 | // IUpdateCallback.aidl 2 | package com.flyme.update.helper.interfaces; 3 | 4 | import com.flyme.update.helper.interfaces.IUpdateCallback; 5 | 6 | interface IUpdateCallback { 7 | void onPayloadApplicationComplete(int errorCode); 8 | 9 | void onStatusUpdate(int status, float percent); 10 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_indicator_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /app/src/test/java/com/flyme/update/helper/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_file_sort.xml: -------------------------------------------------------------------------------- 1 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/cpp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # For more information about using CMake with Android Studio, read the 3 | # documentation: https://d.android.com/studio/projects/add-native-code.html 4 | 5 | # Sets the minimum version of CMake required to build the native library. 6 | cmake_minimum_required(VERSION 3.18.1) 7 | 8 | project("kernelsu") 9 | 10 | add_library(kernelsu 11 | SHARED 12 | jni.cc 13 | ksu.cc 14 | ) 15 | 16 | find_library(log-lib log) 17 | 18 | target_link_libraries(kernelsu ${log-lib}) -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/Config.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | public class Config { 4 | public static boolean isSAR = false; 5 | 6 | public static boolean recovery = false; 7 | 8 | public static boolean keepVerity = false; 9 | 10 | public static boolean keepEnc = false; 11 | 12 | public static boolean patchVbmeta = false; 13 | 14 | public static boolean isVab; 15 | 16 | public static String currentSlot; 17 | 18 | public static String flymemodel; 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_shadow_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | V-AB更新助手 3 | 1. 设备获取Root权限且以授予本软件权限; 4 |
2. 设备支持V-AB系统分区; 5 |

6 | 可能出现的问题: 7 |
8 | 1. 选择ROM路径时一定要从“本机存储”中选择,否则可能会无法选择ROM; 9 |
10 | 2. 由于Android对后台服务的限制,请在更新时尽量保证本软件为前台应用; 11 |
12 | 3. 如果出现卡进度的情况(一般是挂后台出现),可以直接结束应用,重新选择相同的ROM更新即可(进度不会重置); 13 |
14 | 4. 如果提示更新失败,请重启手机并等候一段时间再进行刷写操作]]>
15 |
-------------------------------------------------------------------------------- /app/src/main/aidl/com/flyme/update/helper/service/IUpdateService.aidl: -------------------------------------------------------------------------------- 1 | // IUpdateService.aidl 2 | package com.flyme.update.helper.service; 3 | 4 | import com.flyme.update.helper.interfaces.IUpdateCallback; 5 | import com.flyme.update.helper.bean.UpdateInfo; 6 | 7 | interface IUpdateService { 8 | 9 | boolean isValid(); 10 | 11 | boolean startUpdateSystem(in UpdateInfo info, IUpdateCallback callback); 12 | 13 | boolean closeAssetFileDescriptor(); 14 | 15 | void cancel(); 16 | 17 | IBinder getFileSystemService(); 18 | 19 | int GetKsuVersion(); 20 | 21 | boolean KsuisSafeMode(); 22 | 23 | boolean KsuIsLkmMode(); 24 | } -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | mavenLocal() 14 | maven(url="https://maven.aliyun.com/repository/public/") 15 | maven(url="https://jitpack.io") 16 | maven(url="https://dl.bintray.com/kotlin/kotlin-eap") 17 | maven(url="https://api.xposed.info/") 18 | maven(url="https://s01.oss.sonatype.org/content/repositories/releases") 19 | } 20 | } 21 | 22 | rootProject.name = "系统更新助手" 23 | include(":app") 24 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/animator/fragment_slide_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/animator/fragment_slide_hide.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 17 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/manager/ActivityManger.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.manager; 2 | 3 | import android.app.Activity; 4 | 5 | import java.util.LinkedList; 6 | 7 | public class ActivityManger { 8 | public static LinkedList activities = new LinkedList(); 9 | 10 | public static void addActivity(Activity activity) { 11 | activities.add(activity); 12 | } 13 | 14 | public static void removeActivity(Activity activity) { 15 | activities.remove(activity); 16 | } 17 | 18 | public static void finishAll() { 19 | for (Activity activity : activities) { 20 | if (!activity.isFinishing()) { 21 | activity.finish(); 22 | activities.remove(activity); 23 | } 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/proxy/ServiceManagerProxy.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.proxy; 2 | 3 | import android.os.IBinder; 4 | 5 | import com.flyme.update.helper.utils.Reflection; 6 | 7 | public class ServiceManagerProxy { 8 | private static final Reflection Ref = Reflection.on("android.os.ServiceManager"); 9 | private static final Reflection.MethodWrapper METHOD_GET_SERVICE = Ref.method("getService", String.class); 10 | private static final Reflection.MethodWrapper METHOD_GET_SERVICE_OR_THROW = Ref.method("getServiceOrThrow", String.class); 11 | 12 | public static IBinder getService(String name) { 13 | return METHOD_GET_SERVICE.callStatic(name); 14 | } 15 | 16 | public static IBinder getServiceOrThrow(String name) { 17 | return METHOD_GET_SERVICE_OR_THROW.callStatic(name); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/kernelsu.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 14 | 17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/LogUtils.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import android.util.Log; 4 | 5 | public class LogUtils { 6 | private static boolean enableLog = true; 7 | 8 | public static void d(String tag, String msg) { 9 | if (enableLog) 10 | Log.d(tag, msg); 11 | } 12 | 13 | public static void e(String tag, String msg) { 14 | if (enableLog) 15 | Log.e(tag, msg); 16 | } 17 | 18 | public static void w(String tag, String msg) { 19 | if (enableLog) 20 | Log.w(tag, msg); 21 | } 22 | 23 | public static void i(String tag, String msg) { 24 | if (enableLog) 25 | Log.i(tag, msg); 26 | } 27 | 28 | public static void v(String tag, String msg) { 29 | if (enableLog) 30 | Log.v(tag, msg); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/manager/SuFileManager.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.manager; 2 | 3 | import com.topjohnwu.superuser.nio.FileSystemManager; 4 | 5 | public class SuFileManager { 6 | private static volatile SuFileManager instance; 7 | private FileSystemManager uFileSystemManager; 8 | 9 | public static synchronized SuFileManager getInstance() { 10 | if (instance == null) { 11 | synchronized (SuFileManager.class) { 12 | if (instance == null) { 13 | instance = new SuFileManager(); 14 | } 15 | } 16 | } 17 | return instance; 18 | } 19 | 20 | public void init(FileSystemManager manager) { 21 | uFileSystemManager = manager; 22 | } 23 | 24 | public FileSystemManager getRemote() { 25 | return uFileSystemManager; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/flyme/update/helper/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.flyme.update.helper", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | -------------------------------------------------------------------------------- /app/src/main/aidl/android/os/IUpdateEngineCallback.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.os; 18 | 19 | /** @hide */ 20 | oneway interface IUpdateEngineCallback { 21 | /** @hide */ 22 | void onStatusUpdate(int status_code, float percentage); 23 | /** @hide */ 24 | void onPayloadApplicationComplete(int error_code); 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_full_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true 22 | 23 | android.enableJetifier=true -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import android.app.Notification; 4 | import android.content.Context; 5 | 6 | import androidx.core.app.NotificationCompat; 7 | 8 | import com.flyme.update.helper.R; 9 | 10 | public class NotificationUtils { 11 | 12 | public static Notification notifyMsg(Context context, String ContentText, String ContentTitle) { 13 | return new NotificationCompat.Builder(context, "new_version_push") 14 | .setContentText(ContentText) 15 | .setContentTitle(ContentTitle) 16 | .setAutoCancel(true) 17 | .setPriority(2) 18 | .setShowWhen(false) 19 | .setSmallIcon(R.mipmap.icon) 20 | .build(); 21 | } 22 | 23 | public static Notification notifyProgress(Context context, String ContentText, String ContentTitle, int maxprogress, int progress, boolean indeterminate) { 24 | return new NotificationCompat.Builder(context,"new_version_push") 25 | .setContentText(ContentText) 26 | .setContentTitle(ContentTitle) 27 | .setAutoCancel(false) 28 | .setOngoing(true) 29 | .setPriority(2) 30 | .setShowWhen(false) 31 | .setProgress(maxprogress, progress,indeterminate) 32 | .setSmallIcon(R.mipmap.icon) 33 | .build(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/application/App.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.application; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Application; 5 | import android.content.Context; 6 | 7 | import com.flyme.update.helper.activity.LogActivity; 8 | import com.flyme.update.helper.utils.Config; 9 | import com.flyme.update.helper.utils.CrashHandlerUtil; 10 | import com.flyme.update.helper.utils.Utils; 11 | import com.kongzue.dialogx.DialogX; 12 | 13 | import org.lsposed.hiddenapibypass.HiddenApiBypass; 14 | 15 | public class App extends Application { 16 | 17 | public static int StatusBarHeight; 18 | 19 | static { 20 | HiddenApiBypass.addHiddenApiExemptions(""); 21 | } 22 | 23 | @Override 24 | public void onCreate() { 25 | super.onCreate(); 26 | DialogX.init(this); 27 | CrashHandlerUtil.getInstance().init(this, LogActivity.class); 28 | Config.isVab = Utils.getprop("ro.build.ab_update").equals("true"); 29 | Config.currentSlot = Utils.getprop("ro.boot.slot_suffix"); 30 | Config.flymemodel = Utils.getprop("ro.product.flyme.model"); 31 | @SuppressLint({"DiscouragedApi", "InternalInsetResource"}) 32 | int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); 33 | if (resourceId > 0) 34 | StatusBarHeight = getResources().getDimensionPixelSize(resourceId); 35 | } 36 | 37 | @Override 38 | protected void attachBaseContext(Context base) { 39 | super.attachBaseContext(base); 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_navigation_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 22 | 23 | 31 | 32 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/Preconditions.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | public final class Preconditions { 6 | private Preconditions() {} 7 | 8 | public static T checkNotNull(T value) { 9 | if(value == null) { 10 | throw new NullPointerException(); 11 | } 12 | return value; 13 | } 14 | 15 | public static T checkNotNull(T value, String errorMsg) { 16 | if(value == null) { 17 | throw new NullPointerException(errorMsg); 18 | } 19 | return value; 20 | } 21 | 22 | public static void checkArgument(boolean valid) { 23 | if(!valid) { 24 | throw new IllegalArgumentException(); 25 | } 26 | } 27 | 28 | public static void checkArgument(boolean valid,String errorMsg) { 29 | if(!valid) { 30 | throw new IllegalArgumentException(errorMsg); 31 | } 32 | } 33 | 34 | public static void checkState(boolean valid) { 35 | if(!valid) { 36 | throw new IllegalStateException(); 37 | } 38 | } 39 | 40 | public static void checkState(boolean valid,String errorMsg) { 41 | if(!valid) { 42 | throw new IllegalStateException(errorMsg); 43 | } 44 | } 45 | 46 | public static T checkNotEmpty(T value) { 47 | if(TextUtils.isEmpty(value)) { 48 | throw new IllegalArgumentException(); 49 | } 50 | return value; 51 | } 52 | 53 | public static T checkNotEmpty(T value, String errorMsg) { 54 | if(TextUtils.isEmpty(value)) { 55 | throw new IllegalArgumentException(errorMsg); 56 | } 57 | return value; 58 | } 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/activity/LogActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.activity; 2 | 3 | import android.content.ClipboardManager; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.LinearLayout; 8 | import android.widget.TextView; 9 | 10 | import com.flyme.update.helper.application.App; 11 | import com.flyme.update.helper.R; 12 | import com.kongzue.dialogx.dialogs.PopTip; 13 | 14 | public class LogActivity extends BaseActivity { 15 | 16 | private void defaultOkHandler(final TextView logView, View okButton) { 17 | logView.setText(getIntent().getStringExtra("CashStr")); 18 | okButton.setOnClickListener(v -> { 19 | String log = logView.getText().toString(); 20 | copyLog2Author(log); 21 | }); 22 | } 23 | 24 | private void copyLog2Author(String log) { 25 | ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); 26 | cm.setText(log); 27 | String str = cm.getText().toString(); 28 | if (log.equals(str)) { 29 | PopTip.build().setMessage("复制成功!赶快把神秘代码给作者吧!").iconSuccess().show(); 30 | } else { 31 | PopTip.build().setMessage("复制失败!").iconError().show(); 32 | } 33 | } 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_log); 39 | LinearLayout status_bar = findViewById(R.id.status_bar); 40 | LinearLayout.LayoutParams Params = (LinearLayout.LayoutParams) status_bar.getLayoutParams(); 41 | Params.height = App.StatusBarHeight; 42 | status_bar.setLayoutParams(Params); 43 | defaultOkHandler(findViewById(R.id.tv_log), findViewById(R.id.bt_copy)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/cpp/jni.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "ksu.h" 9 | 10 | #define LOG_TAG "KernelSU" 11 | #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) 12 | 13 | 14 | extern "C" { 15 | JNICALL jint GetVersion(JNIEnv * env , jclass clazz ) { 16 | return get_version(); 17 | } 18 | 19 | JNICALL jboolean isSafeMode(JNIEnv *env, jclass clazz ) { 20 | return is_safe_mode(); 21 | } 22 | 23 | JNICALL jboolean isLkmMode(JNIEnv *env, jclass clazz ) { 24 | return is_lkm_mode(); 25 | } 26 | } 27 | 28 | 29 | static JNINativeMethod getMethods[] = { 30 | {"getVersion", "()I", (void *) GetVersion}, 31 | {"isSafeMode", "()Z", (void *) isSafeMode}, 32 | {"isLkmMode", "()Z", (void *) isLkmMode}, 33 | }; 34 | 35 | //此函数通过调用JNI中 RegisterNatives 方法来注册我们的函数 36 | static int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *Methods, int methodsNum) { 37 | //找到声明native方法的类 38 | jclass clazz = env->FindClass(className); 39 | if (clazz == nullptr) { 40 | return JNI_FALSE; 41 | } 42 | //注册函数 参数:java类 所要注册的函数数组 注册函数的个数 43 | if (env->RegisterNatives(clazz, Methods, methodsNum) < 0) { 44 | return JNI_FALSE; 45 | } 46 | return JNI_TRUE; 47 | } 48 | 49 | static int registerNatives(JNIEnv *env) { 50 | //指定类的路径,通过FindClass 方法来找到对应的类 51 | return registerNativeMethods(env, "com/flyme/update/helper/utils/Natives", getMethods, sizeof(getMethods) / sizeof(getMethods[0])); 52 | } 53 | 54 | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { 55 | JNIEnv *env = nullptr; 56 | jint result = -1; 57 | if (jvm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { 58 | LOGD("GetEnv Fail"); 59 | return -1; 60 | } 61 | if (!registerNatives(env)) { 62 | LOGD("registerNatives Fail"); 63 | return -1; 64 | } 65 | result = JNI_VERSION_1_4; 66 | return result; 67 | } -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #FFEF5361 11 | #FFFD6D4B 12 | #FFFFCF47 13 | #FF9FD661 14 | #FF3FD1AD 15 | #FF2CBDF4 16 | #FFAD8FEF 17 | #FFEE85C0 18 | #8CFFFFFF 19 | #88CE9C 20 | #8CDCF7E8 21 | #AADCF7E8 22 | #D9D9D9 23 | #FFF5F5F5 24 | #FF474658 25 | #60000000 26 | #ff5f7c89 27 | #8c5f7c89 28 | #eceff2 29 | #fff2df 30 | #e8f6e9 31 | #e4f2fd 32 | #dff7f9 33 | #e8eaf6 34 | #EFEBE8 35 | #795547 36 | #ffebed 37 | #f3e5f6 38 | #9d27b1 39 | #3DDC84 40 | #8Ceceff2 41 | #FF119EFF 42 | #CC000000 43 | -------------------------------------------------------------------------------- /app/src/main/cpp/ksu.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by weishu on 2022/12/9. 3 | // 4 | 5 | #ifndef KERNELSU_KSU_H 6 | #define KERNELSU_KSU_H 7 | 8 | #include 9 | 10 | bool become_manager(const char *); 11 | 12 | int get_version(); 13 | 14 | bool get_allow_list(int *uids, int *size); 15 | 16 | bool uid_should_umount(int uid); 17 | 18 | bool is_safe_mode(); 19 | 20 | bool is_lkm_mode(); 21 | 22 | #define KSU_APP_PROFILE_VER 2 23 | #define KSU_MAX_PACKAGE_NAME 256 24 | // NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. 25 | #define KSU_MAX_GROUPS 32 26 | #define KSU_SELINUX_DOMAIN 64 27 | 28 | using p_key_t = char[KSU_MAX_PACKAGE_NAME]; 29 | 30 | struct root_profile { 31 | int32_t uid; 32 | int32_t gid; 33 | 34 | int32_t groups_count; 35 | int32_t groups[KSU_MAX_GROUPS]; 36 | 37 | // kernel_cap_t is u32[2] for capabilities v3 38 | struct { 39 | uint64_t effective; 40 | uint64_t permitted; 41 | uint64_t inheritable; 42 | } capabilities; 43 | 44 | char selinux_domain[KSU_SELINUX_DOMAIN]; 45 | 46 | int32_t namespaces; 47 | }; 48 | 49 | struct non_root_profile { 50 | bool umount_modules; 51 | }; 52 | 53 | struct app_profile { 54 | // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. 55 | uint32_t version; 56 | 57 | // this is usually the package of the app, but can be other value for special apps 58 | char key[KSU_MAX_PACKAGE_NAME]; 59 | int32_t current_uid; 60 | bool allow_su; 61 | 62 | union { 63 | struct { 64 | bool use_default; 65 | char template_name[KSU_MAX_PACKAGE_NAME]; 66 | 67 | struct root_profile profile; 68 | } rp_config; 69 | 70 | struct { 71 | bool use_default; 72 | 73 | struct non_root_profile profile; 74 | } nrp_config; 75 | }; 76 | }; 77 | 78 | bool set_app_profile(const app_profile *profile); 79 | 80 | bool get_app_profile(p_key_t key, app_profile *profile); 81 | 82 | #endif //KERNELSU_KSU_H 83 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 24 | 25 | 32 | 33 | 38 | 39 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/shell/ShellInit.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.shell; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.flyme.update.helper.R; 7 | import com.flyme.update.helper.utils.Config; 8 | import com.topjohnwu.superuser.Shell; 9 | import com.topjohnwu.superuser.ShellUtils; 10 | 11 | import org.apache.commons.io.IOUtils; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | import java.util.stream.Collectors; 18 | 19 | public class ShellInit extends Shell.Initializer { 20 | 21 | private String fastCmd(Shell shell,String cmd) { 22 | return ShellUtils.fastCmd(shell, cmd); 23 | } 24 | 25 | private boolean getBool(Shell shell,String name) { 26 | return fastCmd(shell,"echo $" + name).equals("true"); 27 | } 28 | 29 | @Override 30 | public boolean onInit(Context context, Shell shell) { 31 | InputStream bashrc = context.getResources().openRawResource(R.raw.manager); 32 | try { 33 | String result = new BufferedReader(new InputStreamReader(bashrc)) 34 | .lines().collect(Collectors.joining("\n")); 35 | shell.newJob() 36 | .add(result) 37 | .add("app_init") 38 | .exec(); 39 | Config.isSAR = getBool(shell, "SYSTEM_ROOT"); 40 | Log.d("ShellInit","isSAR = " + Config.isSAR); 41 | Config.recovery = getBool(shell, "RECOVERYMODE"); 42 | Log.d("ShellInit","recovery = " + Config.recovery); 43 | Config.keepVerity = getBool(shell, "KEEPVERITY"); 44 | Log.d("ShellInit","keepVerity = " + Config.keepVerity); 45 | Config.keepEnc = getBool(shell, "KEEPFORCEENCRYPT"); 46 | Log.d("ShellInit","keepEnc = " + Config.keepEnc); 47 | Config.patchVbmeta = getBool(shell, "PATCHVBMETAFLAG"); 48 | Log.d("ShellInit","patchVbmeta = " + Config.patchVbmeta); 49 | IOUtils.close(bashrc); 50 | return true; 51 | } catch (IOException e) { 52 | Log.e("ShellInit","onInit Error = " + e.getMessage()); 53 | return false; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/cpp/ksu.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Created by weishu on 2022/12/9. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "ksu.h" 12 | 13 | #define KERNEL_SU_OPTION 0xDEADBEEF 14 | 15 | #define CMD_GRANT_ROOT 0 16 | 17 | #define CMD_BECOME_MANAGER 1 18 | #define CMD_GET_VERSION 2 19 | #define CMD_ALLOW_SU 3 20 | #define CMD_DENY_SU 4 21 | #define CMD_GET_SU_LIST 5 22 | #define CMD_GET_DENY_LIST 6 23 | #define CMD_CHECK_SAFEMODE 9 24 | 25 | #define CMD_GET_APP_PROFILE 10 26 | #define CMD_SET_APP_PROFILE 11 27 | 28 | #define CMD_IS_UID_GRANTED_ROOT 12 29 | #define CMD_IS_UID_SHOULD_UMOUNT 13 30 | 31 | static bool ksuctl(int cmd, void* arg1, void* arg2) { 32 | int32_t result = 0; 33 | prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); 34 | return result == KERNEL_SU_OPTION; 35 | } 36 | 37 | bool become_manager(const char* pkg) { 38 | char param[128]; 39 | uid_t uid = getuid(); 40 | uint32_t userId = uid / 100000; 41 | if (userId == 0) { 42 | sprintf(param, "/data/data/%s", pkg); 43 | } else { 44 | snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg); 45 | } 46 | 47 | return ksuctl(CMD_BECOME_MANAGER, param, nullptr); 48 | } 49 | 50 | // cache the result to avoid unnecessary syscall 51 | static bool is_lkm; 52 | int get_version() { 53 | int32_t version = -1; 54 | int32_t lkm = 0; 55 | ksuctl(CMD_GET_VERSION, &version, &lkm); 56 | if (!is_lkm && lkm != 0) { 57 | is_lkm = true; 58 | } 59 | return version; 60 | } 61 | 62 | bool get_allow_list(int *uids, int *size) { 63 | return ksuctl(CMD_GET_SU_LIST, uids, size); 64 | } 65 | 66 | bool is_safe_mode() { 67 | return ksuctl(CMD_CHECK_SAFEMODE, nullptr, nullptr); 68 | } 69 | 70 | bool is_lkm_mode() { 71 | // you should call get_version first! 72 | return is_lkm; 73 | } 74 | 75 | bool uid_should_umount(int uid) { 76 | bool should; 77 | return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, reinterpret_cast(uid), &should) && should; 78 | } 79 | 80 | bool set_app_profile(const app_profile *profile) { 81 | return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, nullptr); 82 | } 83 | 84 | bool get_app_profile(p_key_t key, app_profile *profile) { 85 | return ksuctl(CMD_GET_APP_PROFILE, (void*) profile, nullptr); 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/raw/apatch.sh: -------------------------------------------------------------------------------- 1 | #!/system/bin/sh 2 | ####################################################################################### 3 | # APatch Boot Image Patcher 4 | ####################################################################################### 5 | # 6 | # Usage: boot_patch.sh [ARGS_PASS_TO_KPTOOLS] 7 | # 8 | # This script should be placed in a directory with the following files: 9 | # 10 | # File name Type Description 11 | # 12 | # boot_patch.sh script A script to patch boot image for APatch. 13 | # (this file) The script will use files in its same 14 | # directory to complete the patching process. 15 | # bootimg binary The target boot image 16 | # kpimg binary KernelPatch core Image 17 | # kptools executable The KernelPatch tools binary to inject kpimg to kernel Image 18 | # magiskboot executable Magisk tool to unpack boot.img. 19 | # 20 | ####################################################################################### 21 | 22 | ARCH=$(getprop ro.product.cpu.abi) 23 | 24 | echo "****************************" 25 | echo " APatch Boot Image Patcher" 26 | echo "****************************" 27 | 28 | SUPERKEY=$1 29 | BOOTIMAGE=$2 30 | shift 2 31 | 32 | [ -z "$SUPERKEY" ] && { echo "- SuperKey empty!"; exit 1; } 33 | [ -e "$BOOTIMAGE" ] || { echo "- $BOOTIMAGE does not exist!"; exit 1; } 34 | 35 | # Check for dependencies 36 | command -v ./magiskboot >/dev/null 2>&1 || { echo "- Command magiskboot not found!"; exit 1; } 37 | command -v ./kptools >/dev/null 2>&1 || { echo "- Command kptools not found!"; exit 1; } 38 | 39 | if [ ! -f kernel ]; then 40 | echo "- Unpacking boot image" 41 | ./magiskboot unpack "$BOOTIMAGE" >/dev/null 2>&1 42 | if [ $? -ne 0 ]; then 43 | echo "- Unpack error: $?" 44 | exit $? 45 | fi 46 | fi 47 | 48 | mv kernel kernel.ori 49 | 50 | echo "- Patching kernel" 51 | 52 | ./kptools -p -i kernel.ori -s "$SUPERKEY" -k kpimg -o kernel "$@" 53 | 54 | if [ $? -ne 0 ]; then 55 | echo "- Patch kernel error: $?" 56 | exit $? 57 | fi 58 | 59 | echo "- Repacking boot image" 60 | ./magiskboot repack "$BOOTIMAGE" >/dev/null 2>&1 61 | 62 | if [ $? -ne 0 ]; then 63 | echo "- Repack error: $?" 64 | exit $? 65 | fi 66 | 67 | echo "- Cleaning up" 68 | ./magiskboot cleanup >/dev/null 2>&1 69 | rm -f kernel.ori 70 | 71 | echo "- Patching kernel Success" -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.topjohnwu.superuser.ShellUtils; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.Arrays; 9 | import java.util.stream.Collectors; 10 | 11 | public class Utils { 12 | public static boolean dd(String ifile, String ofile) { 13 | return ShellUtils.fastCmdResult("dd if=" + ifile + " of=" + ofile); 14 | } 15 | 16 | 17 | public static void reboot() { 18 | ShellUtils.fastCmd("/system/bin/svc power reboot || /system/bin/reboot"); 19 | } 20 | 21 | public static String getprop(String key) { 22 | try{ 23 | Class c = Class.forName("android.os.SystemProperties"); 24 | Method get = c.getMethod("get", String.class, String.class); 25 | return (String) (get.invoke(c, key, "")); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | } 29 | return ""; 30 | } 31 | 32 | public static boolean isAbDevice() { 33 | return Utils.getprop("ro.build.ab_update").equals("true"); 34 | } 35 | 36 | public static String bytesToHex(byte[] byteArray) { 37 | if (byteArray == null) { 38 | return null; 39 | } 40 | char[] hexArray = "0123456789ABCDEF".toCharArray(); 41 | char[] hexChars = new char[byteArray.length * 2]; 42 | for (int j = 0; j < byteArray.length; j++) { 43 | int v = byteArray[j] & 0xFF; 44 | hexChars[j * 2] = hexArray[v >>> 4]; 45 | hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 46 | } 47 | return new String(hexChars); 48 | } 49 | 50 | public static byte[] hexTobytes(String hexString) { 51 | if (!TextUtils.isEmpty(hexString)) { 52 | return null; 53 | } 54 | hexString = hexString.toLowerCase().trim(); 55 | final byte[] byteArray = new byte[hexString.length() >> 1]; 56 | int index = 0; 57 | for (int i = 0; i < hexString.length(); i++) { 58 | if (index > hexString.length() - 1) { 59 | return byteArray; 60 | } 61 | byte highDit = (byte) (Character.digit(hexString.charAt(index), 16) & 0xFF); 62 | byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1), 16) & 0xFF); 63 | byteArray[i] = (byte) (highDit << 4 | lowDit); 64 | index += 2; 65 | } 66 | return byteArray; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/ColorChangeUtils.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ArgbEvaluator; 6 | import android.animation.ObjectAnimator; 7 | import android.annotation.SuppressLint; 8 | import android.os.Handler; 9 | import android.view.View; 10 | 11 | import com.flyme.update.helper.activity.BaseActivity; 12 | 13 | 14 | public class ColorChangeUtils { 15 | 16 | private final int[] colors; 17 | private long duration = 2000; 18 | private long delay = 5000; 19 | private int index = -1; 20 | 21 | private final Handler mHandler; 22 | private final Runnable mRunnable; 23 | 24 | public ColorChangeUtils(BaseActivity activity, int[] colors, View mView) { 25 | this.colors = colors; 26 | mHandler = activity.getMainHandler(); 27 | mRunnable = new Runnable() { 28 | @Override 29 | public void run() { 30 | @SuppressLint("ObjectAnimatorBinding") 31 | ObjectAnimator backgroundColor = ObjectAnimator.ofInt(mView, "backgroundColor", getColorTemp(), nextColor()); 32 | backgroundColor.setDuration(duration); 33 | backgroundColor.setEvaluator(new ArgbEvaluator()); 34 | backgroundColor.addListener(new AnimatorListenerAdapter() { 35 | @Override 36 | public void onAnimationEnd(Animator animation) { 37 | super.onAnimationEnd(animation); 38 | mHandler.postDelayed(mRunnable, delay); 39 | } 40 | }); 41 | backgroundColor.start(); 42 | } 43 | }; 44 | ObjectAnimator backgroundColor = ObjectAnimator.ofInt(mView, "backgroundColor", getColorTemp(), nextColor()); 45 | backgroundColor.setDuration(0); 46 | backgroundColor.start(); 47 | } 48 | 49 | 50 | private int nextColor(){ 51 | int i = index + 1; 52 | if (i >= colors.length) i = 0; 53 | return colors[i]; 54 | } 55 | 56 | private int getColorTemp(){ 57 | index++; 58 | if (index >= colors.length) index = 0; 59 | return colors[index]; 60 | } 61 | 62 | public long getDuration() { 63 | return duration; 64 | } 65 | 66 | public void setDuration(long duration) { 67 | this.duration = duration; 68 | } 69 | 70 | public long getDelay() { 71 | return delay; 72 | } 73 | 74 | public void setDelay(long delay) { 75 | this.delay = delay; 76 | } 77 | 78 | public void startAnimation(){ 79 | mHandler.postDelayed(mRunnable, delay); 80 | } 81 | 82 | public void stopAnimation(){ 83 | mHandler.removeCallbacks(mRunnable); 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import android.annotation.SuppressLint 2 | 3 | plugins { 4 | id("com.android.application") 5 | } 6 | 7 | android { 8 | namespace = "com.flyme.update.helper" 9 | compileSdk = 33 10 | 11 | defaultConfig { 12 | applicationId = "com.flyme.update.helper" 13 | minSdk = 30 14 | @SuppressLint("ExpiredTargetSdkVersion") 15 | targetSdk = 30 16 | versionCode = 225 17 | versionName = "2.2.5" 18 | 19 | renderscriptTargetApi = 21 20 | renderscriptSupportModeEnabled = true 21 | 22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 23 | } 24 | 25 | 26 | buildTypes { 27 | release { 28 | proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") 29 | multiDexEnabled = true 30 | signingConfig = signingConfigs.getByName("debug") 31 | isMinifyEnabled = true 32 | } 33 | } 34 | compileOptions { 35 | sourceCompatibility = JavaVersion.VERSION_1_8 36 | targetCompatibility = JavaVersion.VERSION_1_8 37 | } 38 | 39 | buildFeatures { 40 | aidl = true 41 | buildConfig = true 42 | compose = true 43 | } 44 | 45 | externalNativeBuild { 46 | cmake { 47 | path("src/main/cpp/CMakeLists.txt") 48 | } 49 | } 50 | 51 | } 52 | 53 | dependencies { 54 | compileOnly(fileTree("libs").include("*.jar")) 55 | 56 | implementation("androidx.appcompat:appcompat:1.3.0") 57 | implementation("com.google.android.material:material:1.4.0") 58 | 59 | 60 | testImplementation("junit:junit:4.13.2") 61 | androidTestImplementation("androidx.test.ext:junit:1.1.5") 62 | androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") 63 | 64 | compileOnly("de.robv.android.xposed:api:82") 65 | implementation("org.luckypray:DexKit:1.1.6") 66 | 67 | val libsuVersion = "6.0.0" 68 | implementation("com.github.topjohnwu.libsu:core:${libsuVersion}") 69 | implementation("com.github.topjohnwu.libsu:service:${libsuVersion}") 70 | implementation("com.github.topjohnwu.libsu:nio:${libsuVersion}") 71 | 72 | //全面屏适配 73 | implementation("com.geyifeng.immersionbar:immersionbar:3.2.2") 74 | 75 | //高斯模糊控件 76 | implementation("com.github.mmin18:realtimeblurview:1.2.1") 77 | 78 | implementation("me.itangqi.waveloadingview:library:0.3.5") 79 | 80 | implementation("org.lsposed.hiddenapibypass:hiddenapibypass:4.3") 81 | 82 | val dialogx_version = "0.0.50.beta17.1" 83 | implementation("com.github.kongzue.DialogX:DialogX:${dialogx_version}") 84 | implementation("com.github.kongzue.DialogX:DialogXKongzueStyle:${dialogx_version}") 85 | implementation("com.github.kongzue.DialogX:DialogXMaterialYou:${dialogx_version}") 86 | implementation("com.github.kongzue.DialogXSample:FileDialog:0.0.14") 87 | 88 | implementation("com.ejlchina:okhttps:3.5.3") 89 | 90 | implementation("commons-io:commons-io:2.11.0") 91 | } -------------------------------------------------------------------------------- /app/src/main/aidl/android/os/IUpdateEngine.aidl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.os; 18 | 19 | import android.os.IUpdateEngineCallback; 20 | import android.os.ParcelFileDescriptor; 21 | 22 | /** @hide */ 23 | interface IUpdateEngine { 24 | /** @hide */ 25 | void applyPayload(String url, 26 | in long payload_offset, 27 | in long payload_size, 28 | in String[] headerKeyValuePairs); 29 | /** @hide */ 30 | void applyPayloadFd(in ParcelFileDescriptor pfd, 31 | in long payload_offset, 32 | in long payload_size, 33 | in String[] headerKeyValuePairs); 34 | /** @hide */ 35 | boolean bind(IUpdateEngineCallback callback); 36 | /** @hide */ 37 | boolean unbind(IUpdateEngineCallback callback); 38 | /** @hide */ 39 | void suspend(); 40 | /** @hide */ 41 | void resume(); 42 | /** @hide */ 43 | void cancel(); 44 | /** @hide */ 45 | void resetStatus(); 46 | /** @hide */ 47 | void setShouldSwitchSlotOnReboot(in String metadataFilename); 48 | /** @hide */ 49 | void resetShouldSwitchSlotOnReboot(); 50 | 51 | /** @hide */ 52 | boolean verifyPayloadApplicable(in String metadataFilename); 53 | /** 54 | * Allocate space on userdata partition. 55 | * 56 | * @return 0 indicates allocation is successful. 57 | * Non-zero indicates space is insufficient. The returned value is the 58 | * total required space (in bytes) on userdata partition. 59 | * 60 | * @throws ServiceSpecificException for other errors. 61 | * 62 | * @hide 63 | */ 64 | long allocateSpaceForPayload(in String metadataFilename, 65 | in String[] headerKeyValuePairs); 66 | /** @hide 67 | * 68 | * Wait for merge to finish, and clean up necessary files. 69 | * 70 | * @param callback Report status updates in callback (not the one previously 71 | * bound with {@link #bind()}). 72 | * {@link IUpdateEngineCallback#onStatusUpdate} is called with 73 | * CLEANUP_PREVIOUS_UPDATE and a progress value during the cleanup. 74 | * {@link IUpdateEngineCallback#onPayloadApplicationComplete} is called at 75 | * the end with SUCCESS if successful. ERROR if transient errors (e.g. merged 76 | * but needs reboot). DEVICE_CORRUPTED for permanent errors. 77 | */ 78 | void cleanupSuccessfulUpdate(IUpdateEngineCallback callback); 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/manager/UpdateServiceManager.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.manager; 2 | 3 | import android.os.IBinder; 4 | import android.os.RemoteException; 5 | 6 | import com.flyme.update.helper.bean.UpdateInfo; 7 | import com.flyme.update.helper.interfaces.IUpdateCallback; 8 | import com.flyme.update.helper.service.IUpdateService; 9 | import com.topjohnwu.superuser.nio.FileSystemManager; 10 | 11 | public class UpdateServiceManager { 12 | 13 | private static volatile UpdateServiceManager instance; 14 | private IUpdateService uNativeService = null; 15 | 16 | public static synchronized UpdateServiceManager getInstance() { 17 | if (instance == null) { 18 | synchronized (UpdateServiceManager.class) { 19 | if (instance == null) { 20 | instance = new UpdateServiceManager(); 21 | } 22 | } 23 | } 24 | return instance; 25 | } 26 | 27 | public void init(IUpdateService service) { 28 | uNativeService = service; 29 | } 30 | 31 | public IUpdateService getService() { 32 | return uNativeService; 33 | } 34 | 35 | public boolean startUpdateSystem(UpdateInfo info, IUpdateCallback listener) { 36 | try { 37 | return this.uNativeService.startUpdateSystem(info, listener); 38 | } catch (RemoteException e) { 39 | e.printStackTrace(); 40 | return false; 41 | } 42 | } 43 | 44 | public boolean isValid() { 45 | try { 46 | return this.uNativeService.isValid(); 47 | } catch (RemoteException e) { 48 | e.printStackTrace(); 49 | return false; 50 | } 51 | } 52 | 53 | public boolean cancel() { 54 | try { 55 | this.uNativeService.cancel(); 56 | return true; 57 | } catch (RemoteException e) { 58 | e.printStackTrace(); 59 | return false; 60 | } 61 | } 62 | 63 | public boolean closeAssetFileDescriptor() { 64 | try { 65 | this.uNativeService.closeAssetFileDescriptor(); 66 | return true; 67 | } catch (RemoteException e) { 68 | e.printStackTrace(); 69 | return false; 70 | } 71 | } 72 | 73 | public FileSystemManager getFileSystemManager() { 74 | try { 75 | IBinder binder = this.uNativeService.getFileSystemService(); 76 | return FileSystemManager.getRemote(binder); 77 | } catch (RemoteException e) { 78 | return null; 79 | } 80 | } 81 | 82 | public int GetKsuVersion() { 83 | try { 84 | return this.uNativeService.GetKsuVersion(); 85 | } catch (RemoteException e) { 86 | return -1; 87 | } 88 | } 89 | 90 | public boolean KsuIsLkmMode() { 91 | try { 92 | return this.uNativeService.KsuIsLkmMode(); 93 | } catch (RemoteException e) { 94 | return false; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_log.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 19 | 20 | 29 | 30 | 31 | 32 | 37 | 38 | 41 | 42 | 50 | 51 | 52 | 53 | 63 | 64 | 71 | 72 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_file_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 28 | 29 | 40 | 41 | 50 | 51 | 52 | 53 | 61 | 62 | 73 | 74 | 75 | 76 | 79 | 80 | 89 | 90 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/bean/UpdateInfo.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.bean; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | public class UpdateInfo implements Parcelable { 9 | private String url; 10 | private long offset; 11 | private long size; 12 | private String[] headerKeyValuePairs; 13 | private String displayid; 14 | private String maskid; 15 | private int type; 16 | private String buildInfo; 17 | private String flymeid; 18 | 19 | public UpdateInfo() { 20 | } 21 | 22 | protected UpdateInfo(Parcel in) { 23 | url = in.readString(); 24 | offset = in.readLong(); 25 | size = in.readLong(); 26 | headerKeyValuePairs = in.createStringArray(); 27 | displayid = in.readString(); 28 | maskid = in.readString(); 29 | type = in.readInt(); 30 | buildInfo = in.readString(); 31 | flymeid= in.readString(); 32 | } 33 | 34 | public static final Creator CREATOR = new Creator() { 35 | @Override 36 | public UpdateInfo createFromParcel(Parcel in) { 37 | return new UpdateInfo(in); 38 | } 39 | 40 | @Override 41 | public UpdateInfo[] newArray(int size) { 42 | return new UpdateInfo[size]; 43 | } 44 | }; 45 | 46 | @Override 47 | public int describeContents() { 48 | return 0; 49 | } 50 | 51 | @Override 52 | public void writeToParcel(@NonNull Parcel dest, int flags) { 53 | dest.writeString(url); 54 | dest.writeLong(offset); 55 | dest.writeLong(size); 56 | dest.writeStringArray(headerKeyValuePairs); 57 | dest.writeString(displayid); 58 | dest.writeString(maskid); 59 | dest.writeInt(type); 60 | dest.writeString(buildInfo); 61 | dest.writeString(flymeid); 62 | } 63 | 64 | public int getType() { 65 | return type; 66 | } 67 | 68 | public long getOffset() { 69 | return offset; 70 | } 71 | 72 | public long getSize() { 73 | return size; 74 | } 75 | 76 | public String getDisplayid() { 77 | return displayid; 78 | } 79 | 80 | public String getMaskid() { 81 | return maskid; 82 | } 83 | 84 | public String getUrl() { 85 | return url; 86 | } 87 | 88 | public String[] getHeaderKeyValuePairs() { 89 | return headerKeyValuePairs; 90 | } 91 | 92 | 93 | public void setDisplayid(String displayid) { 94 | this.displayid = displayid; 95 | } 96 | 97 | public void setHeaderKeyValuePairs(String[] headerKeyValuePairs) { 98 | this.headerKeyValuePairs = headerKeyValuePairs; 99 | } 100 | 101 | public void setMaskid(String maskid) { 102 | this.maskid = maskid; 103 | } 104 | 105 | public void setOffset(long offset) { 106 | this.offset = offset; 107 | } 108 | 109 | public void setSize(long size) { 110 | this.size = size; 111 | } 112 | 113 | public void setType(int type) { 114 | this.type = type; 115 | } 116 | 117 | public void setUrl(String url) { 118 | this.url = url; 119 | } 120 | 121 | public String getBuildInfo() { 122 | return buildInfo; 123 | } 124 | 125 | public void setBuildInfo(String buildInfo) { 126 | this.buildInfo = buildInfo; 127 | } 128 | 129 | public void setFlymeid(String flymeid) { 130 | this.flymeid = flymeid; 131 | } 132 | 133 | public String getFlymeid() { 134 | return this.flymeid; 135 | } 136 | 137 | @NonNull 138 | @Override 139 | public String toString() { 140 | return "UpdateInfo {" + 141 | "url=" + url + 142 | ", offset=" + offset + 143 | ", size=" + size + 144 | ", headerKeyValuePairs=" + headerKeyValuePairs.length + 145 | ", displayid=" + displayid + 146 | ", maskid=" + maskid + 147 | ", type=" + type + 148 | '}'; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/proxy/UpdateEngineProxy.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.proxy; 2 | 3 | import android.content.res.AssetFileDescriptor; 4 | import android.os.IBinder; 5 | import android.os.IUpdateEngineCallback; 6 | import android.os.UpdateEngine; 7 | import android.os.UpdateEngineCallback; 8 | import android.util.Log; 9 | 10 | public class UpdateEngineProxy { 11 | private static final String TAG = "UpdateEngineProxy"; 12 | private static final String UPDATE_ENGINE_SERVICE = "android.os.UpdateEngineService"; 13 | private UpdateEngine mUpdateEngine; 14 | 15 | public UpdateEngineProxy() { 16 | IBinder mIBinder = ServiceManagerProxy.getService(UPDATE_ENGINE_SERVICE); 17 | if (mIBinder == null) { 18 | Log.e(TAG, "Failed to find update_engine"); 19 | return; 20 | } 21 | mUpdateEngine = new UpdateEngine(); 22 | } 23 | 24 | public boolean isValid() { 25 | return mUpdateEngine != null; 26 | } 27 | 28 | 29 | public boolean bind(UpdateEngineCallback callback) { 30 | return this.mUpdateEngine.bind(callback); 31 | } 32 | 33 | public void applyPayload(String url, long payload_offset, long payload_size, String[] headerKeyValuePairs) { 34 | this.mUpdateEngine.applyPayload(url, payload_offset, payload_size, headerKeyValuePairs); 35 | } 36 | 37 | public void applyPayload(AssetFileDescriptor fileDescriptor, String[] headerKeyValuePairs) { 38 | this.mUpdateEngine.applyPayload(fileDescriptor, headerKeyValuePairs); 39 | } 40 | 41 | public boolean unbind(IUpdateEngineCallback callback) { 42 | return this.mUpdateEngine.unbind(); 43 | } 44 | 45 | public void cancel() { 46 | this.mUpdateEngine.cancel(); 47 | } 48 | 49 | public void resetShouldSwitchSlotOnReboot() { 50 | this.mUpdateEngine.resetShouldSwitchSlotOnReboot(); 51 | } 52 | 53 | public void resetStatus() { 54 | this.mUpdateEngine.resetStatus(); 55 | } 56 | 57 | public void resume() { 58 | this.mUpdateEngine.resume(); 59 | } 60 | 61 | public void setShouldSwitchSlotOnReboot(String metadataFilename) { 62 | this.mUpdateEngine.setShouldSwitchSlotOnReboot(metadataFilename); 63 | } 64 | 65 | public void suspend() { 66 | this.mUpdateEngine.suspend(); 67 | } 68 | 69 | public boolean verifyPayloadMetadata(String metadataFilename) { 70 | return this.mUpdateEngine.verifyPayloadMetadata(metadataFilename); 71 | } 72 | 73 | 74 | 75 | public static final class ErrorCodeConstants { 76 | public static final int DEVICE_CORRUPTED = 61; 77 | public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12; 78 | public static final int DOWNLOAD_TRANSFER_ERROR = 9; 79 | public static final int ERROR = 1; 80 | public static final int FILESYSTEM_COPIER_ERROR = 4; 81 | public static final int INSTALL_DEVICE_OPEN_ERROR = 7; 82 | public static final int KERNEL_DEVICE_OPEN_ERROR = 8; 83 | public static final int NOT_ENOUGH_SPACE = 60; 84 | public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; 85 | public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; 86 | public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; 87 | public static final int PAYLOAD_TIMESTAMP_ERROR = 51; 88 | public static final int POST_INSTALL_RUNNER_ERROR = 5; 89 | public static final int SUCCESS = 0; 90 | public static final int UPDATED_BUT_NOT_ACTIVE = 52; 91 | 92 | public ErrorCodeConstants() { 93 | } 94 | } 95 | 96 | public static final class UpdateStatusConstants { 97 | public static final int ATTEMPTING_ROLLBACK = 8; 98 | public static final int CHECKING_FOR_UPDATE = 1; 99 | public static final int DISABLED = 9; 100 | public static final int DOWNLOADING = 3; 101 | public static final int FINALIZING = 5; 102 | public static final int IDLE = 0; 103 | public static final int REPORTING_ERROR_EVENT = 7; 104 | public static final int UPDATED_NEED_REBOOT = 6; 105 | public static final int UPDATE_AVAILABLE = 2; 106 | public static final int VERIFYING = 4; 107 | 108 | public UpdateStatusConstants() { 109 | } 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/magisk.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | 19 | 22 | 25 | 28 | 31 | 34 | 37 | 40 | 43 | 46 | 49 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/CrashHandlerUtil.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.pm.PackageInfo; 7 | import android.content.pm.PackageManager; 8 | import android.os.Build; 9 | 10 | import com.flyme.update.helper.manager.ActivityManger; 11 | 12 | import java.io.PrintWriter; 13 | import java.io.StringWriter; 14 | import java.lang.reflect.Field; 15 | import java.util.Arrays; 16 | 17 | public class CrashHandlerUtil implements Thread.UncaughtExceptionHandler { 18 | 19 | //CrashHandler实例 20 | @SuppressLint("StaticFieldLeak") 21 | private static volatile CrashHandlerUtil instance = null; 22 | //程序的Context对象 23 | private Context mContext; 24 | 25 | private Class mActivity; 26 | 27 | //来存储设备信息和异常信息用 28 | public static String logstr; 29 | 30 | private Thread.UncaughtExceptionHandler mDefaultHandler; 31 | 32 | public static CrashHandlerUtil getInstance() { 33 | if (instance == null) { 34 | synchronized (CrashHandlerUtil.class) { 35 | if (instance == null) { 36 | instance = new CrashHandlerUtil(); 37 | } 38 | } 39 | } 40 | return instance; 41 | } 42 | 43 | 44 | public void init(Context context, Class a) { 45 | mContext = context; 46 | mActivity = a; 47 | mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); 48 | Thread.setDefaultUncaughtExceptionHandler(this); 49 | } 50 | 51 | @Override 52 | public void uncaughtException(Thread thread, Throwable ex) { 53 | if (!handleException(ex) && mDefaultHandler != null) { 54 | mDefaultHandler.uncaughtException(thread, ex); 55 | } else { 56 | ActivityManger.finishAll(); 57 | android.os.Process.killProcess(android.os.Process.myPid()); 58 | System.exit(1); 59 | } 60 | 61 | } 62 | 63 | private static String getAndroidName() { 64 | String str = ""; 65 | for (Field field : Build.VERSION_CODES.class.getFields()) { 66 | try { 67 | if (field.getInt(new Object()) == Build.VERSION.SDK_INT) { 68 | str = field.getName(); 69 | } 70 | } catch (IllegalArgumentException | IllegalAccessException e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | return str; 75 | } 76 | 77 | @SuppressLint("WrongConstant") 78 | private boolean handleException(Throwable ex) { 79 | if (ex == null) { 80 | return false; 81 | } 82 | try { 83 | @SuppressLint("WrongConstant") 84 | PackageInfo packageInfo = mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 1); 85 | StringBuilder a = new StringBuilder("应用版本: "); 86 | a.append(packageInfo.versionName).append("\n"); 87 | a.append("Android版本: Android "); 88 | a.append(getAndroidName()); 89 | a.append(" / "); 90 | a.append(Build.VERSION.RELEASE); 91 | a.append(" API "); 92 | a.append(Build.VERSION.SDK_INT).append("\n"); 93 | a.append("手机厂家: "); 94 | a.append(Build.MANUFACTURER).append("\n"); 95 | a.append("手机型号: "); 96 | a.append(Build.MODEL).append("\n"); 97 | a.append("CPU指令集: "); 98 | a.append(Arrays.toString(Build.SUPPORTED_ABIS)); 99 | a.append("\n"); 100 | int i = 0; 101 | String result = getStackTrace(ex); 102 | a.append("\n具体错误:\n"); 103 | a.append(result); 104 | logstr = a.toString(); 105 | } catch (PackageManager.NameNotFoundException e) { 106 | e.printStackTrace(); 107 | } 108 | mContext.startActivity(new Intent(mContext, mActivity).addFlags(0x10000000).putExtra("CashStr", logstr)); 109 | return true; 110 | } 111 | 112 | private String getStackTrace(Throwable th) { 113 | StringWriter stringWriter = new StringWriter(); 114 | th.printStackTrace(new PrintWriter(stringWriter)); 115 | return stringWriter.toString(); 116 | } 117 | 118 | } 119 | 120 | 121 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # coding: utf-8 3 | import requests 4 | import os 5 | import sys 6 | import oss2 7 | from oss2.exceptions import NoSuchKey, OssError, NotFound 8 | import time 9 | 10 | api_url = "https://api.github.com/repos/bmax121/KernelPatch/releases/latest" 11 | download_url = "https://mirror.ghproxy.com/https://github.com/bmax121/KernelPatch/releases/download/" 12 | 13 | # 阿里云OSS 配置 14 | access_id = "************************" 15 | access_key = "************************" 16 | bucket_name = "kpatch" 17 | endpoint = "https://oss-cn-shenzhen.aliyuncs.com" 18 | 19 | # 测试号 配置 20 | weixin_appID = "wxe3e5f9013782a443" 21 | weixin_appsecret = "afd1d76471981ed812aa051281cd6ac1" 22 | weixin_user_id = "oWCnq6Jf5-hdozP0aN-C5dQzigUU" 23 | weixin_template_id = "ULyapScH_7ubuQHBLSyoLWMesG_s-lalUdocqpObXiA" 24 | 25 | weixin_push_data = dict() 26 | 27 | if sys.version_info[0] == 2: 28 | reload(sys) 29 | sys.setdefaultencoding('utf8') 30 | 31 | try: 32 | auth = oss2.Auth(access_id, access_key) 33 | bucket = oss2.Bucket(auth, endpoint, bucket_name) 34 | except OssError: 35 | print("无法连接到阿里云OSS服务器,请检查[AccessKeyId/AccessKeySecret/Endpoint]设置是否正确!") 36 | except OsError: 37 | print("无法连接到阿里云OSS服务器,请检查[AccessKeyId/AccessKeySecret/Endpoint]设置是否正确!") 38 | except Exception as e: 39 | print("无法连接到阿里云OSS服务器" + str(e)) 40 | 41 | if not bucket: 42 | sys.exit(1) 43 | 44 | def download_file(tag, fliename): 45 | print("download_file: " + fliename) 46 | try: 47 | r = requests.get(download_url + tag + "/" + fliename) 48 | if r.status_code == 200: 49 | return r.content 50 | else: 51 | return False 52 | except Exception as e: 53 | print("download_file fail: " + str(e)) 54 | return False 55 | 56 | 57 | def check_file(tag, fliename): 58 | try: 59 | file_info = bucket.get_object(tag + "/" + fliename).read() 60 | return True 61 | except Exception as e: 62 | return False 63 | 64 | def upload_file(path, local_path): 65 | print("upload_file: " + path) 66 | if len(local_path) < 100: 67 | return False 68 | result = bucket.put_object(path, local_path) 69 | if result.status == 200: 70 | return True 71 | 72 | r = requests.get(api_url) 73 | all_info = r.json() 74 | if not all_info: 75 | print("request Api fail, please try again") 76 | sys.exit(1) 77 | last_tag = all_info['tag_name'] 78 | 79 | if not last_tag: 80 | print("get tag_name fail") 81 | sys.exit(1) 82 | 83 | print("Check Tag:" + last_tag) 84 | weixin_push_data["tag"] = {"value": last_tag} 85 | weixin_push_data["time"] = {"value": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 86 | 87 | upload_file("latest.json", r.content) 88 | 89 | 90 | if not check_file(last_tag, "kpimg"): 91 | kpimg_data = download_file(last_tag, "kpimg-android") 92 | if kpimg_data: 93 | if upload_file(last_tag + "/kpimg", kpimg_data): 94 | weixin_push_data["kpimg"] = {"value": "success"} 95 | else: 96 | weixin_push_data["kpimg"] = {"value": "fail"} 97 | 98 | if not check_file(last_tag, "kptools"): 99 | kptools_data = download_file(last_tag, "kptools-android") 100 | if kptools_data: 101 | if upload_file(last_tag + "/kptools", kptools_data): 102 | weixin_push_data["kptools"] = {"value": "success"} 103 | else: 104 | weixin_push_data["kptools"] = {"value": "fail"} 105 | 106 | if not check_file(last_tag, "kpatch"): 107 | kpatch_data = download_file(last_tag, "kpatch-android") 108 | if kpatch_data: 109 | if upload_file(last_tag + "/kpatch", kpatch_data): 110 | weixin_push_data["kpatch"] = {"value": "success"} 111 | else: 112 | weixin_push_data["kpatch"] = {"value": "fail"} 113 | 114 | if weixin_push_data.get("kpatch"): 115 | post_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}".format(weixin_appID, weixin_appsecret) 116 | access_token = requests.get(post_url).json()['access_token'] 117 | url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={}".format(access_token) 118 | data = { 119 | "touser": weixin_user_id, 120 | "template_id": weixin_template_id, 121 | "url": "http://weixin.qq.com/download", 122 | "topcolor": "#FF0000", 123 | "data": weixin_push_data 124 | } 125 | headers = { 126 | 'Content-Type': 'application/json', 127 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36' 128 | } 129 | requests.post(url, headers=headers, json=data) 130 | 131 | print("Done") -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # 代码混淆压缩比,在0~7之间,默认为5,一般不做修改 2 | -optimizationpasses 5 3 | 4 | # 混合时不使用大小写混合,混合后的类名为小写 5 | -dontusemixedcaseclassnames 6 | 7 | # 指定不去忽略非公共库的类 8 | -dontskipnonpubliclibraryclasses 9 | 10 | # 这句话能够使我们的项目混淆后产生映射文件 11 | # 包含有类名->混淆后类名的映射关系 12 | -verbose 13 | 14 | # 指定不去忽略非公共库的类成员 15 | -dontskipnonpubliclibraryclassmembers 16 | 17 | # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。 18 | -dontpreverify 19 | 20 | # 保留Annotation不混淆 21 | -keepattributes *Annotation*,InnerClasses 22 | 23 | # 避免混淆泛型 24 | -keepattributes Signature 25 | 26 | # 抛出异常时保留代码行号 27 | -keepattributes SourceFile,LineNumberTable 28 | 29 | # 指定混淆是采用的算法,后面的参数是一个过滤器 30 | # 这个过滤器是谷歌推荐的算法,一般不做更改 31 | -optimizations !code/simplification/cast,!field/*,!class/merging/* 32 | 33 | # 忽略警告 34 | #-ignorewarning 35 | 36 | # 保留我们使用的四大组件,自定义的Application等等这些类不被混淆 37 | # 因为这些子类都有可能被外部调用 38 | -keep public class * extends android.app.Activity 39 | #-keep public class * extends android.app.Appliction 40 | -keep public class * extends android.app.Service 41 | -keep public class * extends android.content.BroadcastReceiver 42 | -keep public class * extends android.content.ContentProvider 43 | -keep public class * extends android.app.backup.BackupAgentHelper 44 | -keep public class * extends android.preference.Preference 45 | -keep public class * extends android.view.View 46 | #-keep public class com.android.vending.licensing.ILicensingService 47 | 48 | # Gson 49 | -dontwarn com.google.** 50 | -keep class com.google.gson.** {*;} 51 | -keep class com.google.protobuf.** {*;} 52 | #okhttp 53 | -dontwarn okhttp3.** 54 | -keep class okhttp3.**{*;} 55 | #okio 56 | -dontwarn okio.** 57 | -keep class okio.**{*;} 58 | 59 | #OSS 60 | -keep class com.alibaba.sdk.android.oss.** { *; } 61 | -dontwarn okio.** 62 | -dontwarn org.apache.commons.codec.binary.** 63 | 64 | -dontwarn android.os.** 65 | # 保留support下的所有类及其内部类 66 | -keep class android.support.** {*;} 67 | 68 | # 保留继承的 69 | -keep public class * extends android.support.v4.** 70 | -keep public class * extends android.support.v7.** 71 | -keep public class * extends android.support.annotation.** 72 | 73 | # 保留R下面的资源 74 | #-keep class **.R$* {*;} 75 | 76 | # 保留本地native方法不被混淆 77 | -keepclasseswithmembernames class * { 78 | native ; 79 | } 80 | 81 | # 保留在Activity中的方法参数是view的方法, 82 | # 这样以来我们在layout中写的onClick就不会被影响 83 | -keepclassmembers class * extends android.app.Activity{ 84 | public void *(android.view.View); 85 | } 86 | 87 | # 保留枚举类不被混淆 88 | -keepclassmembers enum * { 89 | public static **[] values(); 90 | public static ** valueOf(java.lang.String); 91 | } 92 | 93 | # 保留我们自定义控件(继承自View)不被混淆 94 | -keep public class * extends android.view.View{ 95 | *** get*(); 96 | void set*(***); 97 | public (android.content.Context); 98 | public (android.content.Context, android.util.AttributeSet); 99 | public (android.content.Context, android.util.AttributeSet, int); 100 | } 101 | 102 | # 保留Parcelable序列化类不被混淆 103 | -keep class * implements android.os.Parcelable { 104 | public static final android.os.Parcelable$Creator *; 105 | } 106 | 107 | # 保留Serializable序列化的类不被混淆 108 | -keepclassmembers class * implements java.io.Serializable { 109 | static final long serialVersionUID; 110 | private static final java.io.ObjectStreamField[] serialPersistentFields; 111 | !static !transient ; 112 | !private ; 113 | !private ; 114 | private void writeObject(java.io.ObjectOutputStream); 115 | private void readObject(java.io.ObjectInputStream); 116 | java.lang.Object writeReplace(); 117 | java.lang.Object readResolve(); 118 | } 119 | 120 | # 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆 121 | -keepclassmembers class * { 122 | void *(**On*Event); 123 | void *(**On*Listener); 124 | } 125 | 126 | # webView处理,项目中没有使用到webView忽略即可 127 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 128 | # public *; 129 | #} 130 | #-keepclassmembers class * extends android.webkit.webViewClient { 131 | # public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap); 132 | # public boolean *(android.webkit.WebView, java.lang.String); 133 | #} 134 | #-keepclassmembers class * extends android.webkit.webViewClient { 135 | # public void *(android.webkit.webView, jav.lang.String); 136 | #} 137 | 138 | # 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用 139 | # 记得proguard-android.txt中一定不要加-dontoptimize才起作用 140 | # 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制 141 | #-assumenosideeffects class android.util.Log { 142 | # public static int v(...); 143 | # public static int i(...); 144 | # public static int w(...); 145 | # public static int d(...); 146 | # public static int e(...); 147 | #} 148 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/UpdateParser.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import com.flyme.update.helper.bean.UpdateInfo; 4 | 5 | import org.apache.commons.io.IOUtils; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | import java.util.Enumeration; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | import java.util.zip.ZipEntry; 15 | import java.util.zip.ZipFile; 16 | 17 | public class UpdateParser { 18 | private static final String FILE_URL_PREFIX = "file://"; 19 | private final File mFile; 20 | 21 | public UpdateParser(String file) { 22 | this.mFile = new File(file); 23 | } 24 | 25 | private long getOffset(ZipEntry zipEntry, String str) { 26 | return (zipEntry.getMethod() != 0 ? 16 : 0) + 30 + str.length() + zipEntry.getCompressedSize() + (zipEntry.getExtra() != null ? zipEntry.getExtra().length : 0); 27 | } 28 | 29 | public UpdateInfo parse() { 30 | long payloadSize = 0,payloadOffset = 0,offset = 0; 31 | ZipFile zipFile = null; 32 | UpdateInfo mUpdateInfo = new UpdateInfo(); 33 | mUpdateInfo.setUrl(FILE_URL_PREFIX + this.mFile.getAbsolutePath()); 34 | mUpdateInfo.setType(-1); 35 | try { 36 | zipFile = new ZipFile(this.mFile); 37 | Enumeration entries = zipFile.entries(); 38 | while (entries.hasMoreElements()) { 39 | ZipEntry nextElement = entries.nextElement(); 40 | String name = nextElement.getName(); 41 | offset += getOffset(nextElement, name); 42 | 43 | if (nextElement.isDirectory()) { 44 | offset -= nextElement.getCompressedSize(); 45 | } else if ("payload.bin".equals(name)) { 46 | if (nextElement.getMethod() != 0) { 47 | break; 48 | } 49 | payloadSize = nextElement.getCompressedSize(); 50 | payloadOffset = offset - nextElement.getCompressedSize(); 51 | } else if ("payload_properties.txt".equals(name)) { 52 | BufferedReader buffer = new BufferedReader(new InputStreamReader(zipFile.getInputStream(nextElement))); 53 | mUpdateInfo.setHeaderKeyValuePairs(buffer.lines().toArray(String[]::new)); 54 | } else if ("META-INF/build.prop".equals(name)) { 55 | BufferedReader buffer = new BufferedReader(new InputStreamReader(zipFile.getInputStream(nextElement))); 56 | String[] example = buffer.lines().toArray(String[]::new); 57 | for (String str : example) { 58 | if (str.contains("ro.product.flyme.model")) { 59 | Pattern pattern = Pattern.compile("ro.product.flyme.model=(.*)\\n"); 60 | Matcher matcher = pattern.matcher(str + "\n"); 61 | if (matcher.find()) 62 | mUpdateInfo.setFlymeid(matcher.group(1)); 63 | } else if (str.contains("ro.build.display.id")) { 64 | Pattern pattern = Pattern.compile("ro.build.display.id=(.*)\\n"); 65 | Matcher matcher = pattern.matcher(str + "\n"); 66 | if (matcher.find()) 67 | mUpdateInfo.setDisplayid(matcher.group(1)); 68 | } else if (str.contains("ro.build.mask.id")) { 69 | Pattern pattern = Pattern.compile("ro.build.mask.id=(.*)\\n"); 70 | Matcher matcher = pattern.matcher(str + "\n"); 71 | if (matcher.find()) 72 | mUpdateInfo.setMaskid(matcher.group(1)); 73 | } else if (str.contains("ro.product.system.model")) { 74 | Pattern pattern = Pattern.compile("ro.product.system.model=(.*)\\n"); 75 | Matcher matcher = pattern.matcher(str + "\n"); 76 | if (matcher.find()) 77 | mUpdateInfo.setBuildInfo(matcher.group(1)); 78 | } 79 | } 80 | } else if ("type.txt".equals(name)) { 81 | String example = IOUtils.toString(zipFile.getInputStream(nextElement)); 82 | mUpdateInfo.setType(Integer.parseInt(example)); 83 | } 84 | } 85 | } catch (IOException e) { 86 | e.printStackTrace(); 87 | } finally { 88 | if (zipFile != null) { 89 | try { 90 | zipFile.close(); 91 | } catch (IOException e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | } 96 | mUpdateInfo.setOffset(payloadOffset); 97 | mUpdateInfo.setSize(payloadSize); 98 | return mUpdateInfo; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/fragment/AboutFragment.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.fragment; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import androidx.fragment.app.Fragment; 13 | 14 | import com.flyme.update.helper.R; 15 | import com.flyme.update.helper.utils.AndroidInfo; 16 | import com.flyme.update.helper.widget.TouchFeedback; 17 | 18 | 19 | public class AboutFragment extends Fragment implements TouchFeedback.OnFeedBackListener { 20 | 21 | @Override 22 | public void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | } 25 | 26 | @SuppressLint("SetTextI18n") 27 | @Override 28 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 29 | View inflate = inflater.inflate(R.layout.fragment_about, container, false); 30 | TouchFeedback touchFeedback = TouchFeedback.newInstance(getContext()); 31 | //初始化标题 32 | AndroidInfo androidInfo = new AndroidInfo(getContext()); 33 | inflate.findViewById(R.id.about_app_info).setText(getContext().getString(R.string.app_name) + " V " + androidInfo.getVerName()); 34 | inflate.findViewById(R.id.tv_author).setText("你好,我是King丶枫岚"); 35 | inflate.findViewById(R.id.tv_email).setText("i@yowal.cn"); 36 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.group)); 37 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.email)); 38 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.developer)); 39 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.xiaoqian)); 40 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.meizu)); 41 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.lumyuan)); 42 | touchFeedback.setOnFeedBackListener(this, inflate.findViewById(R.id.yege)); 43 | return inflate; 44 | } 45 | 46 | @Override 47 | public void onClick(View view) { 48 | int id = view.getId(); 49 | if (id == R.id.group) { 50 | String qqUrl = "https://www.yowal.cn"; 51 | Intent qqIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(qqUrl)); 52 | qqIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 53 | startActivity(qqIntent); 54 | } else if (id == R.id.email) { 55 | String[] mailto = { "i@yowal.cn" }; 56 | Intent sendIntent = new Intent(Intent.ACTION_SEND); 57 | String emailBody = ""; 58 | sendIntent.setType("message/rfc822"); 59 | sendIntent.putExtra(Intent.EXTRA_EMAIL, mailto); 60 | sendIntent.putExtra(Intent.EXTRA_SUBJECT, ""); 61 | sendIntent.putExtra(Intent.EXTRA_TEXT, emailBody); 62 | startActivity(sendIntent); 63 | } else if (id == R.id.developer) { 64 | Intent coolapkIntent = new Intent(); 65 | coolapkIntent.setClassName("com.coolapk.market", "com.coolapk.market.view.AppLinkActivity"); 66 | coolapkIntent.setAction("android.intent.action.VIEW"); 67 | coolapkIntent.setData(Uri.parse("http://www.coolapk.com/u/1010404")); 68 | startActivity(coolapkIntent); 69 | } else if (id == R.id.xiaoqian) { 70 | Intent coolapkIntent = new Intent(); 71 | coolapkIntent.setClassName("com.coolapk.market", "com.coolapk.market.view.AppLinkActivity"); 72 | coolapkIntent.setAction("android.intent.action.VIEW"); 73 | coolapkIntent.setData(Uri.parse("http://www.coolapk.com/u/621284")); 74 | startActivity(coolapkIntent); 75 | } else if (id == R.id.meizu) { 76 | Intent coolapkIntent = new Intent(); 77 | coolapkIntent.setClassName("com.coolapk.market", "com.coolapk.market.view.AppLinkActivity"); 78 | coolapkIntent.setAction("android.intent.action.VIEW"); 79 | coolapkIntent.setData(Uri.parse("http://www.coolapk.com/u/27248277")); 80 | startActivity(coolapkIntent); 81 | } else if (id == R.id.lumyuan) { 82 | Intent coolapkIntent = new Intent(); 83 | coolapkIntent.setClassName("com.coolapk.market", "com.coolapk.market.view.AppLinkActivity"); 84 | coolapkIntent.setAction("android.intent.action.VIEW"); 85 | coolapkIntent.setData(Uri.parse("http://www.coolapk.com/u/2073264")); 86 | startActivity(coolapkIntent); 87 | } else if (id == R.id.yege) { 88 | Intent coolapkIntent = new Intent(); 89 | coolapkIntent.setClassName("com.coolapk.market", "com.coolapk.market.view.AppLinkActivity"); 90 | coolapkIntent.setAction("android.intent.action.VIEW"); 91 | coolapkIntent.setData(Uri.parse("http://www.coolapk.com/u/377020")); 92 | startActivity(coolapkIntent); 93 | } 94 | 95 | } 96 | 97 | @Override 98 | public void onLongClick(View view) { 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.activity; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.os.Bundle; 8 | import android.os.Handler; 9 | import android.os.IBinder; 10 | import android.os.Looper; 11 | import android.os.Message; 12 | import android.util.Log; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.fragment.app.Fragment; 16 | import androidx.fragment.app.FragmentTransaction; 17 | 18 | import com.flyme.update.helper.BuildConfig; 19 | import com.flyme.update.helper.R; 20 | import com.flyme.update.helper.fragment.AboutFragment; 21 | import com.flyme.update.helper.fragment.HomeFragment; 22 | import com.flyme.update.helper.service.IUpdateService; 23 | import com.flyme.update.helper.service.UpdateService; 24 | import com.flyme.update.helper.shell.ShellInit; 25 | import com.flyme.update.helper.manager.SuFileManager; 26 | import com.flyme.update.helper.manager.UpdateServiceManager; 27 | import com.flyme.update.helper.widget.NavigationBar; 28 | import com.kongzue.dialogx.dialogs.TipDialog; 29 | import com.kongzue.dialogx.dialogs.WaitDialog; 30 | import com.topjohnwu.superuser.Shell; 31 | import com.topjohnwu.superuser.ipc.RootService; 32 | 33 | import org.apache.commons.io.FileUtils; 34 | 35 | import java.io.File; 36 | import java.io.IOException; 37 | import java.util.ArrayList; 38 | import java.util.List; 39 | 40 | public class MainActivity extends BaseActivity { 41 | 42 | private final List fragments = new ArrayList<>(); 43 | private static final int MSG_WHAT_REQUEST_ROOT_PERMISSION = 0; 44 | private static final int MSG_WHAT_NO_ROOT_PERMISSON = 1; 45 | 46 | static { 47 | Shell.enableVerboseLogging = BuildConfig.DEBUG; 48 | Shell.setDefaultBuilder(Shell.Builder.create() 49 | .setInitializers(ShellInit.class) 50 | .setTimeout(2)); 51 | } 52 | 53 | @SuppressLint("HandlerLeak") 54 | private final Handler mHandler = new Handler(Looper.getMainLooper()) { 55 | @Override 56 | public void handleMessage(@NonNull Message msg) { 57 | super.handleMessage(msg); 58 | if (msg.what == MSG_WHAT_REQUEST_ROOT_PERMISSION) { 59 | openService(); 60 | } else if (msg.what == MSG_WHAT_NO_ROOT_PERMISSON) { 61 | TipDialog.show(MainActivity.this,"请授予超级权限后使用!", WaitDialog.TYPE.ERROR); 62 | } 63 | } 64 | }; 65 | 66 | @SuppressLint("WrongConstant") 67 | @Override 68 | protected void onCreate(Bundle savedInstanceState) { 69 | super.onCreate(savedInstanceState); 70 | setContentView(R.layout.activity_main); 71 | setDoubleTouchClose(true); 72 | fragments.add(new HomeFragment()); 73 | fragments.add(new AboutFragment()); 74 | replaceFragment(fragments.get(0)); 75 | NavigationBar navigationBar = findViewById(R.id.main_navigation); 76 | navigationBar.bindData(new String[]{ "开始", "关于" }, new int[] { R.mipmap.ic_home, R.mipmap.ic_settings }); 77 | navigationBar.setPositionListener((view, position) -> replaceFragment(fragments.get(position))); 78 | WaitDialog.show(MainActivity.this,"正在检测超级权限..."); 79 | 80 | getASynHandler().postDelayed(() -> { 81 | Log.d("MainActivity_onCreate", "isRoot = " + Shell.getShell().isRoot()); 82 | if (!Shell.getShell().isRoot()) { 83 | mHandler.sendEmptyMessage(MSG_WHAT_NO_ROOT_PERMISSON); 84 | } else { 85 | try { 86 | Shell.cmd("rm -r " + getFilesDir().toString()).exec(); 87 | Shell.cmd("mkdir " + getFilesDir().toString()).exec(); 88 | FileUtils.copyToFile(getAssets().open("magiskboot"), new File(getFilesDir(),"magiskboot")); 89 | FileUtils.copyToFile(getResources().openRawResource(R.raw.apatch), new File(getFilesDir(),"apatch.sh")); 90 | Shell.cmd("chmod -R 777 " + getFilesDir().toString()).exec(); 91 | } catch (IOException e) { 92 | e.printStackTrace(); 93 | } 94 | mHandler.sendEmptyMessage(MSG_WHAT_REQUEST_ROOT_PERMISSION); 95 | } 96 | },2000); 97 | } 98 | 99 | 100 | private void replaceFragment(Fragment fragment){ 101 | FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); 102 | fragmentTransaction.setCustomAnimations(R.animator.fragment_slide_show, R.animator.fragment_slide_hide); 103 | fragmentTransaction.replace(R.id.main_frame_layout, fragment); 104 | fragmentTransaction.commit(); 105 | } 106 | 107 | private void openService() { 108 | WaitDialog.show(MainActivity.this,"正常启动服务..."); 109 | Intent intent = new Intent(this, UpdateService.class); 110 | RootService.bind(intent,new RootConnect()); 111 | } 112 | 113 | class RootConnect implements ServiceConnection { 114 | @Override 115 | public void onServiceConnected(ComponentName name, IBinder service) { 116 | IUpdateService mUpdateService = IUpdateService.Stub.asInterface(service); 117 | if (mUpdateService != null) { 118 | UpdateServiceManager.getInstance().init(mUpdateService); 119 | SuFileManager.getInstance().init(UpdateServiceManager.getInstance().getFileSystemManager()); 120 | WaitDialog.dismiss(MainActivity.this); 121 | } else { 122 | TipDialog.show(MainActivity.this,"启动服务失败!", WaitDialog.TYPE.ERROR); 123 | } 124 | } 125 | 126 | @Override 127 | public void onServiceDisconnected(ComponentName name) { 128 | 129 | } 130 | } 131 | 132 | 133 | 134 | 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_permissin_write.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 26 | 27 | 37 | 38 | 45 | 46 | 52 | 53 | 58 | 59 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 84 | 85 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 105 | 111 | 112 | 121 | 122 | 129 | 130 | 131 | 132 | 141 | 142 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/widget/TouchFeedback.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.widget; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ObjectAnimator; 6 | import android.annotation.SuppressLint; 7 | import android.content.Context; 8 | import android.os.Build; 9 | import android.os.Handler; 10 | import android.view.HapticFeedbackConstants; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.animation.DecelerateInterpolator; 14 | 15 | /** 16 | * 实现Android组件点击事件、长按事件触发时的视觉与触感效果 17 | */ 18 | public class TouchFeedback { 19 | 20 | private float scaling = 0.9f; 21 | private long duration = 150L; 22 | private final Handler longTouchHandler; 23 | private final Runnable longTouchRunnable; 24 | private static final int FEEDBACK_CANCEL = 0; 25 | private static final int FEEDBACK_CLICK = 1; 26 | private static final int FEEDBACK_LONG_CLICK = 2; 27 | /** 28 | * 私有构造函数 29 | */ 30 | private TouchFeedback(Context context){ 31 | this.longTouchHandler = new Handler(context.getMainLooper()); 32 | this.longTouchRunnable = ()->{ 33 | this.isLongFeed = true; 34 | }; 35 | } 36 | 37 | public float getScaling() { 38 | return scaling; 39 | } 40 | 41 | public void setScaling(float scaling) { 42 | this.scaling = scaling; 43 | } 44 | 45 | public long getDuration() { 46 | return duration; 47 | } 48 | 49 | public void setDuration(long duration) { 50 | this.duration = duration; 51 | } 52 | 53 | /** 54 | * 单例模式 55 | */ 56 | private static TouchFeedback touchFeedback; 57 | public static TouchFeedback newInstance(Context context){ 58 | if (touchFeedback == null) { 59 | synchronized (TouchFeedback.class) { 60 | if (touchFeedback == null) { 61 | touchFeedback = new TouchFeedback(context); 62 | } 63 | } 64 | } 65 | return touchFeedback; 66 | } 67 | 68 | private void vibrationPress(View view){ 69 | int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R 70 | ? HapticFeedbackConstants.GESTURE_START : Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 71 | ? HapticFeedbackConstants.KEYBOARD_PRESS : HapticFeedbackConstants.VIRTUAL_KEY; 72 | view.performHapticFeedback(flag); 73 | } 74 | 75 | private void vibrationUp(View view){ 76 | int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R 77 | ? HapticFeedbackConstants.GESTURE_END : Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 78 | ? HapticFeedbackConstants.KEYBOARD_RELEASE : HapticFeedbackConstants.VIRTUAL_KEY; 79 | view.performHapticFeedback(flag); 80 | } 81 | 82 | private void viewPress(View view){ 83 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", scaling); 84 | scaleX.setDuration(duration); 85 | scaleX.setInterpolator(new DecelerateInterpolator()); 86 | scaleX.start(); 87 | 88 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", scaling); 89 | scaleY.setDuration(duration); 90 | scaleY.setInterpolator(new DecelerateInterpolator()); 91 | scaleY.start(); 92 | 93 | vibrationPress(view); 94 | } 95 | 96 | public boolean isUp = false; 97 | private void viewUp(OnFeedBackListener onFeedBackListener, View view, int type){ 98 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1); 99 | scaleX.setDuration((long) (duration * 0.9)); 100 | scaleX.setInterpolator(new DecelerateInterpolator()); 101 | scaleX.start(); 102 | 103 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1); 104 | scaleY.setDuration((long) (duration * 0.9)); 105 | scaleY.setInterpolator(new DecelerateInterpolator()); 106 | scaleY.start(); 107 | 108 | vibrationUp(view); 109 | if (!isUp) isUp = true; 110 | 111 | scaleY.addListener(new AnimatorListenerAdapter() { 112 | @Override 113 | public void onAnimationEnd(Animator animation) { 114 | super.onAnimationEnd(animation); 115 | if (isUp){ 116 | if (type == FEEDBACK_CLICK){ 117 | onFeedBackListener.onClick(view); 118 | }else if (type == FEEDBACK_LONG_CLICK){ 119 | onFeedBackListener.onLongClick(view); 120 | } 121 | } 122 | isUp = false; 123 | } 124 | }); 125 | } 126 | 127 | public interface OnFeedBackListener{ 128 | void onClick(View view); 129 | void onLongClick(View view); 130 | } 131 | 132 | @SuppressLint("ClickableViewAccessibility") 133 | public void setOnFeedBackListener(OnFeedBackListener onFeedBackListener, View view){ 134 | view.setOnTouchListener((v, event) -> { 135 | int action = event.getAction(); 136 | switch (action){ 137 | case MotionEvent.ACTION_DOWN: 138 | viewPress(v); 139 | break; 140 | case MotionEvent.ACTION_CANCEL: 141 | viewUp(onFeedBackListener, v, FEEDBACK_CANCEL); 142 | break; 143 | case MotionEvent.ACTION_UP: 144 | viewUp(onFeedBackListener, v, FEEDBACK_CLICK); 145 | break; 146 | } 147 | return true; 148 | }); 149 | } 150 | 151 | private boolean isLongFeed = false; 152 | @SuppressLint("ClickableViewAccessibility") 153 | public void setOnFeedBackListener(OnFeedBackListener onFeedBackListener, View view, boolean isLongFeed){ 154 | if (isLongFeed){ 155 | view.setOnTouchListener((v, event) -> { 156 | int action = event.getAction(); 157 | switch (action){ 158 | case MotionEvent.ACTION_DOWN: 159 | viewPress(v); 160 | this.isLongFeed = false; 161 | longTouchHandler.removeCallbacks(longTouchRunnable); 162 | longTouchHandler.postDelayed(longTouchRunnable, 200); 163 | break; 164 | case MotionEvent.ACTION_CANCEL: 165 | viewUp(onFeedBackListener, v, FEEDBACK_CANCEL); 166 | break; 167 | case MotionEvent.ACTION_UP: 168 | viewUp(onFeedBackListener, v, this.isLongFeed ? FEEDBACK_LONG_CLICK : FEEDBACK_CLICK); 169 | break; 170 | } 171 | return false; 172 | }); 173 | }else { 174 | setOnFeedBackListener(onFeedBackListener, view); 175 | } 176 | } 177 | } 178 | 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/service/UpdateService.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.service; 2 | 3 | import static android.system.Os.prctl; 4 | 5 | import android.content.Intent; 6 | import android.content.res.AssetFileDescriptor; 7 | import android.net.Uri; 8 | import android.os.IBinder; 9 | import android.os.ParcelFileDescriptor; 10 | import android.os.Process; 11 | import android.os.RemoteException; 12 | import android.os.UpdateEngineCallback; 13 | import android.system.ErrnoException; 14 | import android.system.Os; 15 | 16 | import androidx.annotation.NonNull; 17 | 18 | import com.flyme.update.helper.interfaces.IUpdateCallback; 19 | import com.flyme.update.helper.utils.LogUtils; 20 | import com.flyme.update.helper.utils.Natives; 21 | import com.flyme.update.helper.proxy.UpdateEngineProxy; 22 | import com.flyme.update.helper.bean.UpdateInfo; 23 | import com.topjohnwu.superuser.ipc.RootService; 24 | import com.topjohnwu.superuser.nio.FileSystemManager; 25 | 26 | import java.io.File; 27 | import java.io.IOException; 28 | 29 | public class UpdateService extends RootService { 30 | 31 | static { 32 | if (Process.myUid() == 0) 33 | System.loadLibrary("kernelsu"); 34 | } 35 | 36 | static final String TAG = "UpdateService"; 37 | 38 | 39 | @Override 40 | public void onCreate() { 41 | /*Cursor cursor = null; 42 | try { 43 | cursor = getApplicationContext().getContentResolver().query(Uri.parse("content://com.flyme.secureservice.open.DataProvider"), null, null, new String[]{"5"}, null); 44 | if (cursor == null) { 45 | LogUtils.e(TAG, "ContentResolver = null"); 46 | return; 47 | } 48 | if (cursor.getCount() > 0) { 49 | cursor.moveToFirst(); 50 | int index = cursor.getColumnIndex("value"); 51 | if (index >= 0) { 52 | String dsid = cursor.getString(index); 53 | LogUtils.d(TAG, "dsid = " + dsid); 54 | cursor.close(); 55 | return; 56 | } 57 | LogUtils.e(TAG, "no value in cursor"); 58 | } 59 | cursor.close(); 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | LogUtils.e(TAG, e.getMessage()); 63 | }*/ 64 | 65 | } 66 | 67 | 68 | @Override 69 | public IBinder onBind(@NonNull Intent intent) { 70 | return new UpdateServiceIPC(); 71 | } 72 | 73 | static class UpdateServiceIPC extends IUpdateService.Stub { 74 | private final UpdateEngineProxy mUpdateEngine = new UpdateEngineProxy(); 75 | private AssetFileDescriptor mAssetFileDescriptor; 76 | 77 | @Override 78 | public boolean startUpdateSystem(UpdateInfo info, IUpdateCallback listener) { 79 | try { 80 | if (info.getHeaderKeyValuePairs() == null || info.getHeaderKeyValuePairs().length == 0) 81 | return false; 82 | boolean bind_status = mUpdateEngine.bind(new UpdateEngineCallback() { 83 | @Override 84 | public void onStatusUpdate(int status, float percent) { 85 | if (listener != null) { 86 | try { 87 | listener.onStatusUpdate(status, percent); 88 | } catch (RemoteException e) { 89 | e.printStackTrace(); 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public void onPayloadApplicationComplete(int errorCode) { 96 | if (listener != null) { 97 | try { 98 | listener.onPayloadApplicationComplete(errorCode); 99 | } catch (RemoteException e) { 100 | e.printStackTrace(); 101 | } 102 | } 103 | if (mAssetFileDescriptor != null) { 104 | try { 105 | mAssetFileDescriptor.close(); 106 | } catch (IOException e) { 107 | e.printStackTrace(); 108 | } 109 | } 110 | } 111 | }); 112 | if (!bind_status) 113 | return false; 114 | //mUpdateEngine.resetStatus(); 115 | if (info.getUrl().startsWith("file:///data/ota_package")) { 116 | mUpdateEngine.applyPayload(info.getUrl(), info.getOffset(), info.getSize(), info.getHeaderKeyValuePairs()); 117 | return true; 118 | } 119 | Uri uriFile = Uri.parse(info.getUrl()); 120 | if (uriFile.getPath() == null) 121 | return false; 122 | mAssetFileDescriptor = new AssetFileDescriptor(ParcelFileDescriptor.open(new File(uriFile.getPath()), ParcelFileDescriptor.parseMode("r")), info.getOffset(), info.getSize()); 123 | if (!mAssetFileDescriptor.getFileDescriptor().valid()) 124 | return false; 125 | mUpdateEngine.applyPayload(mAssetFileDescriptor, info.getHeaderKeyValuePairs()); 126 | return true; 127 | } catch (Exception e) { 128 | String ExceptionString = e.getLocalizedMessage(); 129 | LogUtils.e(TAG, ExceptionString); 130 | return ExceptionString.contains("Already processing an update") || ExceptionString.contains("waiting for reboot"); 131 | } 132 | } 133 | 134 | @Override 135 | public void cancel() { 136 | this.mUpdateEngine.cancel(); 137 | } 138 | 139 | @Override 140 | public boolean isValid() { 141 | return this.mUpdateEngine.isValid(); 142 | } 143 | 144 | @Override 145 | public boolean closeAssetFileDescriptor() { 146 | if (mAssetFileDescriptor != null) { 147 | try { 148 | mAssetFileDescriptor.close(); 149 | } catch (IOException e) { 150 | e.printStackTrace(); 151 | } 152 | } 153 | return true; 154 | } 155 | 156 | @Override 157 | public IBinder getFileSystemService() { 158 | return FileSystemManager.getService(); 159 | } 160 | 161 | @Override 162 | public int GetKsuVersion() { 163 | /* int version = -1; 164 | int lkm = -1; 165 | try { 166 | Os.prctl( 0xDEADBEEF, 2, version, lkm,0); 167 | } catch (ErrnoException e) { 168 | e.printStackTrace(); 169 | }*/ 170 | return Natives.getVersion(); 171 | } 172 | 173 | @Override 174 | public boolean KsuisSafeMode() { 175 | return Natives.isSafeMode(); 176 | } 177 | 178 | @Override 179 | public boolean KsuIsLkmMode() { 180 | return Natives.isLkmMode(); 181 | } 182 | 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/res/raw/manager.sh: -------------------------------------------------------------------------------- 1 | ################################## 2 | # Magisk app internal scripts 3 | ################################## 4 | 5 | run_delay() { 6 | (sleep $1; $2)& 7 | } 8 | 9 | env_check() { 10 | for file in busybox magiskboot magiskinit util_functions.sh boot_patch.sh; do 11 | [ -f "$MAGISKBIN/$file" ] || return 1 12 | done 13 | if [ "$2" -ge 25000 ]; then 14 | [ -f "$MAGISKBIN/magiskpolicy" ] || return 1 15 | fi 16 | if [ "$2" -ge 25210 ]; then 17 | [ -b "$MAGISKTMP/.magisk/block/preinit" ] || return 2 18 | fi 19 | grep -xqF "MAGISK_VER='$1'" "$MAGISKBIN/util_functions.sh" || return 3 20 | grep -xqF "MAGISK_VER_CODE=$2" "$MAGISKBIN/util_functions.sh" || return 3 21 | return 0 22 | } 23 | 24 | cp_readlink() { 25 | if [ -z $2 ]; then 26 | cd $1 27 | else 28 | cp -af $1/. $2 29 | cd $2 30 | fi 31 | for file in *; do 32 | if [ -L $file ]; then 33 | local full=$(readlink -f $file) 34 | rm $file 35 | cp -af $full $file 36 | fi 37 | done 38 | chmod -R 755 . 39 | cd / 40 | } 41 | 42 | fix_env() { 43 | # Cleanup and make dirs 44 | rm -rf $MAGISKBIN/* 45 | mkdir -p $MAGISKBIN 2>/dev/null 46 | chmod 700 $NVBASE 47 | cp_readlink $1 $MAGISKBIN 48 | rm -rf $1 49 | chown -R 0:0 $MAGISKBIN 50 | } 51 | 52 | direct_install() { 53 | echo "- Flashing new boot image" 54 | flash_image $1/new-boot.img $2 55 | case $? in 56 | 1) 57 | echo "! Insufficient partition size" 58 | return 1 59 | ;; 60 | 2) 61 | echo "! $2 is read only" 62 | return 2 63 | ;; 64 | esac 65 | 66 | rm -f $1/new-boot.img 67 | fix_env $1 68 | run_migrations 69 | copy_preinit_files 70 | 71 | return 0 72 | } 73 | 74 | run_uninstaller() { 75 | rm -rf /dev/tmp 76 | mkdir -p /dev/tmp/install 77 | unzip -o "$1" "assets/*" "lib/*" -d /dev/tmp/install 78 | INSTALLER=/dev/tmp/install sh /dev/tmp/install/assets/uninstaller.sh dummy 1 "$1" 79 | } 80 | 81 | restore_imgs() { 82 | [ -z $SHA1 ] && return 1 83 | local BACKUPDIR=/data/magisk_backup_$SHA1 84 | [ -d $BACKUPDIR ] || return 1 85 | 86 | get_flags 87 | find_boot_image 88 | 89 | for name in dtb dtbo; do 90 | [ -f $BACKUPDIR/${name}.img.gz ] || continue 91 | local IMAGE=$(find_block $name$SLOT) 92 | [ -z $IMAGE ] && continue 93 | flash_image $BACKUPDIR/${name}.img.gz $IMAGE 94 | done 95 | [ -f $BACKUPDIR/boot.img.gz ] || return 1 96 | flash_image $BACKUPDIR/boot.img.gz $BOOTIMAGE 97 | } 98 | 99 | post_ota() { 100 | cd $NVBASE 101 | cp -f $1 bootctl 102 | rm -f $1 103 | chmod 755 bootctl 104 | ./bootctl hal-info || return 105 | SLOT_NUM=0 106 | [ $(./bootctl get-current-slot) -eq 0 ] && SLOT_NUM=1 107 | ./bootctl set-active-boot-slot $SLOT_NUM 108 | cat << EOF > post-fs-data.d/post_ota.sh 109 | /data/adb/bootctl mark-boot-successful 110 | rm -f /data/adb/bootctl 111 | rm -f /data/adb/post-fs-data.d/post_ota.sh 112 | EOF 113 | chmod 755 post-fs-data.d/post_ota.sh 114 | cd / 115 | } 116 | 117 | add_hosts_module() { 118 | # Do not touch existing hosts module 119 | [ -d $NVBASE/modules/hosts ] && return 120 | cd $NVBASE/modules 121 | mkdir -p hosts/system/etc 122 | cat << EOF > hosts/module.prop 123 | id=hosts 124 | name=Systemless Hosts 125 | version=1.0 126 | versionCode=1 127 | author=Magisk 128 | description=Magisk app built-in systemless hosts module 129 | EOF 130 | magisk --clone /system/etc/hosts hosts/system/etc/hosts 131 | touch hosts/update 132 | cd / 133 | } 134 | 135 | adb_pm_install() { 136 | local tmp=/data/local/tmp/temp.apk 137 | cp -f "$1" $tmp 138 | chmod 644 $tmp 139 | su 2000 -c pm install -g $tmp || pm install -g $tmp || su 1000 -c pm install -g $tmp 140 | local res=$? 141 | rm -f $tmp 142 | if [ $res = 0 ]; then 143 | appops set "$2" REQUEST_INSTALL_PACKAGES allow 144 | fi 145 | return $res 146 | } 147 | 148 | check_boot_ramdisk() { 149 | # Create boolean ISAB 150 | ISAB=true 151 | [ -z $SLOT ] && ISAB=false 152 | 153 | # If we are A/B, then we must have ramdisk 154 | $ISAB && return 0 155 | 156 | # If we are using legacy SAR, but not A/B, assume we do not have ramdisk 157 | if grep ' / ' /proc/mounts | grep -q '/dev/root'; then 158 | # Override recovery mode to true 159 | RECOVERYMODE=true 160 | return 1 161 | fi 162 | 163 | return 0 164 | } 165 | 166 | check_encryption() { 167 | if $ISENCRYPTED; then 168 | if [ $SDK_INT -lt 24 ]; then 169 | CRYPTOTYPE="block" 170 | else 171 | # First see what the system tells us 172 | CRYPTOTYPE=$(getprop ro.crypto.type) 173 | if [ -z $CRYPTOTYPE ]; then 174 | # If not mounting through device mapper, we are FBE 175 | if grep ' /data ' /proc/mounts | grep -qv 'dm-'; then 176 | CRYPTOTYPE="file" 177 | else 178 | # We are either FDE or metadata encryption (which is also FBE) 179 | CRYPTOTYPE="block" 180 | grep -q ' /metadata ' /proc/mounts && CRYPTOTYPE="file" 181 | fi 182 | fi 183 | fi 184 | else 185 | CRYPTOTYPE="N/A" 186 | fi 187 | } 188 | 189 | ########################## 190 | # Non-root util_functions 191 | ########################## 192 | 193 | mount_partitions() { 194 | [ "$(getprop ro.build.ab_update)" = "true" ] && SLOT=$(getprop ro.boot.slot_suffix) 195 | # Check whether non rootfs root dir exists 196 | SYSTEM_ROOT=false 197 | grep ' / ' /proc/mounts | grep -qv 'rootfs' && SYSTEM_ROOT=true 198 | } 199 | 200 | get_flags() { 201 | KEEPVERITY=$SYSTEM_ROOT 202 | ISENCRYPTED=false 203 | [ "$(getprop ro.crypto.state)" = "encrypted" ] && ISENCRYPTED=true 204 | KEEPFORCEENCRYPT=$ISENCRYPTED 205 | # Although this most certainly won't work without root, keep it just in case 206 | if [ -e /dev/block/by-name/vbmeta_a ] || [ -e /dev/block/by-name/vbmeta ]; then 207 | VBMETAEXIST=true 208 | else 209 | VBMETAEXIST=false 210 | fi 211 | # Preset PATCHVBMETAFLAG to false in the non-root case 212 | PATCHVBMETAFLAG=false 213 | # Make sure RECOVERYMODE has value 214 | [ -z $RECOVERYMODE ] && RECOVERYMODE=false 215 | } 216 | 217 | run_migrations() { return; } 218 | 219 | grep_prop() { 220 | local REGEX="s/^$1=//p" 221 | shift 222 | local FILES=$@ 223 | [ -z "$FILES" ] && FILES='/system/build.prop' 224 | cat $FILES 2>/dev/null | dos2unix | sed -n "$REGEX" | head -n 1 225 | } 226 | 227 | ############# 228 | # Initialize 229 | ############# 230 | 231 | app_init() { 232 | mount_partitions 233 | RAMDISKEXIST=false 234 | check_boot_ramdisk && RAMDISKEXIST=true 235 | get_flags 236 | run_migrations 237 | SHA1=$(grep_prop SHA1 $MAGISKTMP/.magisk/config) 238 | check_encryption 239 | } 240 | 241 | 242 | #From Magisk 243 | flash_image() { 244 | case "$1" in 245 | *.gz) CMD1="gzip -d < '$1' 2>/dev/null";; 246 | *) CMD1="cat '$1'";; 247 | esac 248 | CMD2="cat -" 249 | if [ -b "$2" ]; then 250 | local img_sz=$(stat -c '%s' "$1") 251 | local blk_sz=$(blockdev --getsize64 "$2") 252 | [ "$img_sz" -gt "$blk_sz" ] && return 1 253 | blockdev --setrw "$2" 254 | local blk_ro=$(blockdev --getro "$2") 255 | [ "$blk_ro" -eq 1 ] && return 2 256 | eval "$CMD1" | eval "$CMD2" | cat - /dev/zero > "$2" 2>/dev/null 257 | elif [ -c "$2" ]; then 258 | flash_eraseall "$2" >&2 259 | eval "$CMD1" | eval "$CMD2" | nandwrite -p "$2" - >&2 260 | else 261 | echo "- Not block or char device, storing image" 262 | eval "$CMD1" | eval "$CMD2" > "$2" 2>/dev/null 263 | fi 264 | return 0 265 | } 266 | 267 | export BOOTMODE=true 268 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/activity/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.activity; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.annotation.SuppressLint; 5 | import android.app.Activity; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.res.Configuration; 9 | import android.content.res.Resources; 10 | import android.os.Bundle; 11 | import android.os.Handler; 12 | import android.os.HandlerThread; 13 | import android.os.Looper; 14 | import android.view.View; 15 | import android.widget.ImageView; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | import androidx.appcompat.app.AppCompatActivity; 20 | 21 | import com.flyme.update.helper.R; 22 | import com.flyme.update.helper.interfaces.OnNavigationStateListener; 23 | import com.flyme.update.helper.manager.ActivityManger; 24 | import com.flyme.update.helper.widget.TouchFeedback; 25 | import com.github.mmin18.widget.RealtimeBlurView; 26 | import com.gyf.immersionbar.ImmersionBar; 27 | import com.kongzue.dialogx.dialogs.CustomDialog; 28 | import com.kongzue.dialogx.dialogs.PopTip; 29 | import com.kongzue.dialogx.interfaces.OnBindView; 30 | 31 | import java.util.ArrayList; 32 | 33 | public abstract class BaseActivity extends AppCompatActivity{ 34 | 35 | @SuppressLint("StaticFieldLeak") 36 | public static Activity mContext; 37 | 38 | public ArrayList feedViews = new ArrayList<>(); 39 | 40 | private TouchFeedback touchFeedback; 41 | 42 | private Handler mainHandler; 43 | 44 | private Handler aSynHandler; 45 | 46 | @Override 47 | protected void onCreate(Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | mContext = this; 50 | touchFeedback = TouchFeedback.newInstance(this); 51 | mainHandler = new Handler(Looper.getMainLooper()); 52 | ActivityManger.addActivity(this); 53 | 54 | } 55 | 56 | @Override 57 | protected void onStart() { 58 | super.onStart(); 59 | ImmersionBar.with(this) 60 | .transparentStatusBar() 61 | .statusBarDarkFont(true) 62 | .init(); 63 | } 64 | 65 | @Override 66 | protected void onDestroy() { 67 | super.onDestroy(); 68 | if (null != mainHandler) { 69 | mainHandler.removeCallbacksAndMessages(null); 70 | } 71 | if (null != aSynHandler) { 72 | aSynHandler.removeCallbacksAndMessages(null); 73 | } 74 | ActivityManger.removeActivity(this); 75 | } 76 | 77 | public boolean isNightMode(){ 78 | return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; 79 | } 80 | 81 | public Context getApp() { 82 | return getApplication(); 83 | } 84 | 85 | 86 | @Override 87 | public void onBackPressed() { 88 | if (isOpenDoubleTouchClose){ 89 | if (System.currentTimeMillis() - time > 2000) { 90 | PopTip.build().setMessage("再按一次退出软件").iconWarning().show(); 91 | time = System.currentTimeMillis(); 92 | } else { 93 | finish(); 94 | super.onBackPressed(); 95 | } 96 | }else { 97 | finish(); 98 | super.onBackPressed(); 99 | } 100 | } 101 | 102 | private boolean isOpenDoubleTouchClose = false; 103 | private long time; 104 | 105 | public void setDoubleTouchClose(boolean isOpenDoubleTouchClose){ 106 | this.isOpenDoubleTouchClose = isOpenDoubleTouchClose; 107 | } 108 | 109 | private void isNavigationBarExist(Activity activity, final OnNavigationStateListener onNavigationStateListener) { 110 | if (activity == null) { 111 | return; 112 | } 113 | final int height = getNavigationHeight(activity); 114 | activity.getWindow().getDecorView().setOnApplyWindowInsetsListener((v, windowInsets) -> { 115 | boolean isShowing = false; 116 | int b = 0; 117 | if (windowInsets != null) { 118 | b = windowInsets.getSystemWindowInsetBottom(); 119 | isShowing = (b == height); 120 | } 121 | if (onNavigationStateListener != null && b <= height) { 122 | onNavigationStateListener.onNavigationState(isShowing, b); 123 | } 124 | return windowInsets; 125 | }); 126 | } 127 | 128 | private int getNavigationHeight(Context activity) { 129 | if (activity == null) { 130 | return 0; 131 | } 132 | Resources resources = activity.getResources(); 133 | int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android"); 134 | int height = 0; 135 | if (resourceId > 0) { 136 | //获取NavigationBar的高度 137 | height = resources.getDimensionPixelSize(resourceId); 138 | } 139 | return height; 140 | } 141 | 142 | public Handler getASynHandler() { 143 | if (null == aSynHandler) { 144 | HandlerThread handlerThread = new HandlerThread("FlymeUpdate"); 145 | handlerThread.start(); 146 | aSynHandler = new Handler(handlerThread.getLooper()); 147 | } 148 | return aSynHandler; 149 | } 150 | 151 | public Handler getMainHandler() { 152 | return mainHandler; 153 | } 154 | 155 | public void showNotificationPermissinDialog() { 156 | CustomDialog.build() 157 | .setAutoUnsafePlacePadding(false) 158 | .setCancelable(false) 159 | .setCustomView(new OnBindView(R.layout.dialog_full_base) { 160 | @SuppressLint("SetTextI18n") 161 | @Override 162 | public void onBind(CustomDialog dialog, View v) { 163 | LinearLayout relativeLayout = v.findViewById(R.id.root_view); 164 | View inflate = View.inflate(mContext, R.layout.dialog_permissin_write, null); 165 | relativeLayout.removeAllViews(); 166 | relativeLayout.addView(inflate); 167 | 168 | RealtimeBlurView blurView = v.findViewById(R.id.blur_view); 169 | translation(blurView); 170 | 171 | inflate.findViewById(R.id.cancel_button).setOnClickListener((view)->{ 172 | dialog.dismiss(); 173 | }); 174 | 175 | inflate.findViewById(R.id.confirm_button).setOnClickListener((view)->{ 176 | Intent intent = new Intent(); 177 | intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 178 | intent.putExtra("android.provider.extra.APP_PACKAGE", getPackageName()); 179 | startActivity(intent); 180 | dialog.dismiss(); 181 | }); 182 | 183 | inflate.findViewById(R.id.icon).setImageResource(R.mipmap.ic_notification); 184 | inflate.findViewById(R.id.sub_icon).setImageResource(R.mipmap.ic_sub_notification); 185 | inflate.findViewById(R.id.name).setText("通知权限申请"); 186 | inflate.findViewById(R.id.content).setText(getString(R.string.app_name) + "在刷机过程中会在通知栏发送刷机进度通知,但这需要系统已允许" + getString(R.string.app_name) + "发送通知。"); 187 | inflate.findViewById(R.id.sub_content).setText("用于" + getString(R.string.app_name) + "的发送刷机进度功能"); 188 | inflate.findViewById(R.id.sub_tips).setText("授予该权限后,软件可以在通知栏发送通知"); 189 | } 190 | }) 191 | .show(); 192 | } 193 | 194 | private void translation(View view){ 195 | ObjectAnimator translationY = ObjectAnimator.ofFloat(view, "translationY", 0, 0.1f); 196 | translationY.setDuration(100); 197 | translationY.start(); 198 | getMainHandler().postDelayed(()->{ 199 | translation(view); 200 | }, 200); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/adapter/FileAdapter.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.adapter; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.webkit.MimeTypeMap; 9 | import android.widget.BaseAdapter; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import com.flyme.update.helper.R; 14 | import com.flyme.update.helper.bean.FileBean; 15 | import com.flyme.update.helper.utils.FileDialogUtils; 16 | 17 | import java.io.File; 18 | import java.text.SimpleDateFormat; 19 | import java.util.ArrayList; 20 | import java.util.Calendar; 21 | import java.util.Date; 22 | import java.util.Locale; 23 | 24 | public class FileAdapter extends BaseAdapter { 25 | 26 | private ArrayList fileList; 27 | private LayoutInflater layoutInflater; 28 | private Activity activity; 29 | private FileDialogUtils dialog; 30 | 31 | public FileAdapter(FileDialogUtils dialog, Activity activity, ArrayList fileList) { 32 | super(); 33 | layoutInflater = activity.getLayoutInflater(); 34 | this.dialog = dialog; 35 | this.activity = activity; 36 | this.fileList = fileList; 37 | } 38 | 39 | @Override 40 | public int getCount() { 41 | return fileList.size(); 42 | } 43 | 44 | public void setFileList(ArrayList fileList) { 45 | this.fileList = fileList; 46 | } 47 | 48 | public ArrayList getFileList() { 49 | return this.fileList; 50 | } 51 | 52 | @Override 53 | public Object getItem(int position) { 54 | return fileList.get(position); 55 | } 56 | 57 | @Override 58 | public long getItemId(int position) { 59 | return position; 60 | } 61 | 62 | @Override 63 | public View getView(int position, View convertView, ViewGroup parent) { 64 | ViewHolder viewHolder; 65 | if (convertView == null) { 66 | convertView = layoutInflater.inflate(com.kongzue.filedialog.R.layout.item_dialogx_file_list, null); 67 | } 68 | if (convertView.getTag() instanceof ViewHolder) { 69 | viewHolder = (ViewHolder) convertView.getTag(); 70 | } else { 71 | viewHolder = new ViewHolder(convertView); 72 | convertView.setTag(viewHolder); 73 | } 74 | String fileName = fileList.get(position).Name; 75 | boolean isFolder = fileName.startsWith("/"); 76 | String itemPath = dialog.getPath() + (isFolder ? "" : "/") + fileName; 77 | 78 | viewHolder.txtFileName.getPaint().setFakeBoldText(false); 79 | if ("...".equals(fileName)) { 80 | viewHolder.txtFileName.setText("..."); 81 | viewHolder.imgIcon.setImageResource(com.kongzue.filedialog.R.drawable.ic_dialogx_filedialog_back); 82 | viewHolder.btnHaveChild.setVisibility(View.GONE); 83 | } else { 84 | if (fileName.startsWith("/")) { 85 | viewHolder.txtFileName.setText(fileName.substring(1)); 86 | viewHolder.imgIcon.setImageResource(com.kongzue.filedialog.R.drawable.ic_dialogx_filedialog_folder); 87 | viewHolder.btnHaveChild.setVisibility(View.VISIBLE); 88 | } else { 89 | viewHolder.txtFileName.setText(fileName); 90 | if (dialog.getSelectPathList() != null && dialog.getSelectPathList().contains(itemPath)) { 91 | viewHolder.imgIcon.setImageResource(com.kongzue.filedialog.R.drawable.ic_dialogx_filedialog_select); 92 | viewHolder.txtFileName.getPaint().setFakeBoldText(true); 93 | } else { 94 | viewHolder.imgIcon.setImageResource(com.kongzue.filedialog.R.drawable.ic_dialogx_filedialog_file); 95 | } 96 | viewHolder.btnHaveChild.setVisibility(View.GONE); 97 | } 98 | } 99 | 100 | if ((dialog.getMimeTypes() != null && dialog.getMimeTypes().length != 0) || 101 | dialog.getSuffixArray() != null && dialog.getSuffixArray().length != 0) { 102 | if ("...".equals(fileName) || isFolder) { 103 | convertView.setAlpha(1f); 104 | } else { 105 | if (isSelectMimeType(itemPath) || isSelectSuffix(itemPath)) { 106 | convertView.setAlpha(1f); 107 | } else { 108 | convertView.setAlpha(0.3f); 109 | } 110 | } 111 | } 112 | 113 | if (dialog.isShowFileDate() && !"...".equals(fileName)){ 114 | viewHolder.txtDate.setText(getFileDate(activity,new File(itemPath))); 115 | viewHolder.txtDate.setVisibility(View.VISIBLE); 116 | }else{ 117 | viewHolder.txtDate.setVisibility(View.GONE); 118 | } 119 | 120 | return convertView; 121 | } 122 | 123 | private boolean isSelectSuffix(String itemPath) { 124 | if (dialog.getSuffixArray() != null && dialog.getSuffixArray().length != 0) { 125 | for (String suffix : dialog.getSuffixArray()) { 126 | if (itemPath.toLowerCase().endsWith(suffix.toLowerCase())) { 127 | return true; 128 | } 129 | } 130 | } 131 | return false; 132 | } 133 | 134 | private boolean isSelectMimeType(String itemPath) { 135 | if (dialog.getMimeTypes() != null && dialog.getMimeTypes().length != 0) { 136 | for (String mime : dialog.getMimeTypes()) { 137 | if (mime.toUpperCase().equals(getMimeType(new File(itemPath)).toUpperCase())) { 138 | return true; 139 | } 140 | } 141 | } 142 | return false; 143 | } 144 | 145 | private String getFileDate(Context context, File file) { 146 | Date lastModified = new Date(file.lastModified()); 147 | Calendar fileCal = Calendar.getInstance(); 148 | fileCal.setTime(lastModified); 149 | 150 | Calendar today = Calendar.getInstance(); 151 | Calendar yesterday = Calendar.getInstance(); 152 | yesterday.add(Calendar.DATE, -1); 153 | 154 | SimpleDateFormat dateFormat = new SimpleDateFormat(context.getString(com.kongzue.filedialog.R.string.dialogx_filedialog_default_date_format)); 155 | 156 | if (fileCal.get(Calendar.YEAR) == today.get(Calendar.YEAR) 157 | && fileCal.get(Calendar.DAY_OF_YEAR) == today.get(Calendar.DAY_OF_YEAR)) { 158 | SimpleDateFormat timeFormat = new SimpleDateFormat(context.getString(com.kongzue.filedialog.R.string.dialogx_filedialog_default_date_format_today)); 159 | return timeFormat.format(lastModified); 160 | } else if (fileCal.get(Calendar.YEAR) == yesterday.get(Calendar.YEAR) 161 | && fileCal.get(Calendar.DAY_OF_YEAR) == yesterday.get(Calendar.DAY_OF_YEAR)) { 162 | SimpleDateFormat timeFormat = new SimpleDateFormat(context.getString(com.kongzue.filedialog.R.string.dialogx_filedialog_default_date_format_yesterday)); 163 | return timeFormat.format(lastModified); 164 | } else { 165 | return dateFormat.format(lastModified); 166 | } 167 | } 168 | 169 | private String getMimeType(File file) { 170 | String suffix = getSuffix(file); 171 | if (suffix == null) { 172 | return "file/*"; 173 | } 174 | String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(suffix); 175 | if (type != null && !type.isEmpty()) { 176 | return type; 177 | } 178 | return "file/*"; 179 | } 180 | 181 | private String getSuffix(File file) { 182 | if (file == null || !file.exists() || file.isDirectory()) { 183 | return null; 184 | } 185 | String fileName = file.getName(); 186 | if (fileName.equals("") || fileName.endsWith(".")) { 187 | return null; 188 | } 189 | int index = fileName.lastIndexOf("."); 190 | if (index != -1) { 191 | return fileName.substring(index + 1).toLowerCase(Locale.US); 192 | } else { 193 | return null; 194 | } 195 | } 196 | 197 | class ViewHolder { 198 | 199 | ImageView imgIcon; 200 | TextView txtFileName; 201 | TextView txtDate; 202 | ImageView btnHaveChild; 203 | 204 | public ViewHolder(View convertView) { 205 | imgIcon = convertView.findViewById(com.kongzue.filedialog.R.id.img_icon); 206 | txtFileName = convertView.findViewById(com.kongzue.filedialog.R.id.txt_fileName); 207 | txtDate = convertView.findViewById(com.kongzue.filedialog.R.id.txt_date); 208 | btnHaveChild = convertView.findViewById(com.kongzue.filedialog.R.id.btn_have_child); 209 | } 210 | } 211 | } 212 | 213 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 19 | 20 | 26 | 27 | 33 | 34 | 42 | 43 | 44 | 45 | 60 | 61 | 62 | 63 | 70 | 71 | 79 | 80 | 85 | 86 | 93 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 113 | 114 | 119 | 120 | 127 | 128 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 148 | 149 | 156 | 157 | 162 | 163 | 170 | 171 | 179 | 180 | 181 | 182 | 183 | 184 | 193 | 194 | 199 | 200 | 206 | 207 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 226 | 227 | 231 | 232 | 240 | 241 | 246 | 247 | 252 | 253 | 258 | 259 | 265 | 266 | 267 | 268 | 275 | 276 | 277 | 278 | 279 | 280 | 283 | 284 | 285 | 286 | 287 | 288 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/widget/NavigationBar.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.widget; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.os.Build; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.view.animation.AnticipateOvershootInterpolator; 11 | import android.widget.ImageView; 12 | import android.widget.LinearLayout; 13 | import android.widget.TextView; 14 | 15 | import androidx.annotation.Nullable; 16 | import androidx.viewpager.widget.ViewPager; 17 | 18 | import com.flyme.update.helper.R; 19 | import com.flyme.update.helper.utils.AndroidInfo; 20 | import com.google.android.material.tabs.TabLayout; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class NavigationBar extends LinearLayout{ 26 | 27 | private static ViewPager pager; 28 | private static String[] itemTitles; 29 | private static int[] itemIds; 30 | private static List tabs = new ArrayList<>(); 31 | private OnClickPositionListener positionListener; 32 | 33 | public NavigationBar(Context context) { 34 | this(context, null); 35 | } 36 | 37 | public NavigationBar(Context context, @Nullable AttributeSet attrs) { 38 | this(context, attrs, 0); 39 | } 40 | 41 | public NavigationBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 42 | super(context, attrs, defStyleAttr); 43 | positionListener = (view, position) -> { 44 | 45 | }; 46 | } 47 | 48 | public void bindData(ViewPager viewPager, String[] titles, int[] ids) { 49 | pager = viewPager; 50 | itemIds = ids; 51 | itemTitles = titles; 52 | init(getContext()); 53 | } 54 | 55 | public void bindData(String[] titles, int[] ids) { 56 | itemIds = ids; 57 | itemTitles = titles; 58 | init(getContext()); 59 | } 60 | 61 | private TabLayout tabLayout; 62 | private void init(Context context){ 63 | setOrientation(VERTICAL); 64 | View rootView = View.inflate(context, R.layout.fragment_navigation, null); 65 | tabLayout = rootView.findViewById(R.id.tabLayout); 66 | LinearLayout box = rootView.findViewById(R.id.navigation_box); 67 | if (pager != null) tabLayout.setupWithViewPager(pager); 68 | for (int i = 0; i < itemTitles.length; i++) { 69 | View item = View.inflate(getContext(), R.layout.layout_navigation_item, null); 70 | ImageView icon = item.findViewById(R.id.item_icon); 71 | TextView title = item.findViewById(R.id.item_title); 72 | icon.setImageResource(itemIds[i]); 73 | title.setText(itemTitles[i]); 74 | LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); 75 | layoutParams.weight = 1f; 76 | item.setLayoutParams(layoutParams); 77 | TabLayout.Tab tab = tabLayout.newTab(); 78 | tabs.add(tab); 79 | tabLayout.addTab(tab); 80 | clickViews.add(new Item(0, item)); 81 | int finalI = i; 82 | item.setOnClickListener((view)->{ 83 | if (pager != null) pager.setCurrentItem(finalI); 84 | else tabLayout.selectTab(tabs.get(finalI), true); 85 | positionListener.onChanged(view, finalI); 86 | }); 87 | if (i == 2){ 88 | { 89 | ObjectAnimator scaleX = ObjectAnimator.ofFloat(icon, "scaleX", 1.15f); 90 | scaleX.setDuration(0); 91 | scaleX.start(); 92 | } 93 | { 94 | ObjectAnimator scaleY = ObjectAnimator.ofFloat(icon, "scaleY", 1.15f); 95 | scaleY.setDuration(0); 96 | scaleY.start(); 97 | } 98 | } 99 | box.addView(item); 100 | } 101 | 102 | tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { 103 | @Override 104 | public void onTabSelected(TabLayout.Tab tab) { 105 | setCurrentItem(tab.getPosition()); 106 | positionListener.onChanged(box.getChildAt(tab.getPosition()), tab.getPosition()); 107 | } 108 | 109 | @Override 110 | public void onTabUnselected(TabLayout.Tab tab) { 111 | 112 | } 113 | 114 | @Override 115 | public void onTabReselected(TabLayout.Tab tab) { 116 | 117 | } 118 | }); 119 | 120 | postDelayed(()->{ 121 | initView(); 122 | setCurrentItem(mPosition); 123 | },0L); 124 | 125 | addView(rootView); 126 | 127 | AndroidInfo androidInfo = new AndroidInfo(context); 128 | int width = androidInfo.width(); 129 | int height = androidInfo.height(); 130 | 131 | if ((16 / 9) > (height / width)) tabLayout.setVisibility(View.GONE); 132 | } 133 | 134 | private final List clickViews = new ArrayList<>(); 135 | 136 | public void initView(){ 137 | for (int i = 1; i < clickViews.size(); i++) { 138 | defaultedView(i); 139 | } 140 | } 141 | 142 | private int mPosition = 0; 143 | public void setCurrentItem(int position){ 144 | postDelayed(()->{ 145 | defaulted(mPosition); 146 | selected(position); 147 | mPosition = position; 148 | }, 0L); 149 | } 150 | 151 | private final int duration = 750; 152 | private void selected(int position){ 153 | Item item = clickViews.get(position); 154 | View view = item.getItem(); 155 | View box = view.findViewById(R.id.shadow_layout); 156 | ImageView icon = view.findViewById(R.id.item_icon); 157 | TextView title = view.findViewById(R.id.item_title); 158 | 159 | title.setTextColor(getColor(R.color.indicator_color)); 160 | icon.setColorFilter(getColor(R.color.indicator_color)); 161 | ObjectAnimator alpha = ObjectAnimator.ofFloat(box, "alpha", 0.8f); 162 | alpha.setDuration(duration); 163 | alpha.start(); 164 | 165 | ObjectAnimator alpha1 = ObjectAnimator.ofFloat(title, "alpha", 1); 166 | alpha1.setDuration(duration); 167 | alpha1.start(); 168 | 169 | { 170 | ObjectAnimator translationY = ObjectAnimator.ofFloat(title, "translationY", item.getTranslationX(), 0); 171 | translationY.setDuration(duration); 172 | translationY.addUpdateListener(animation -> { 173 | float f = (float) animation.getAnimatedValue(); 174 | item.setTranslationX(f); 175 | }); 176 | translationY.setInterpolator(new AnticipateOvershootInterpolator()); 177 | translationY.start(); 178 | } 179 | 180 | { 181 | ObjectAnimator translationY = ObjectAnimator.ofFloat(icon, "translationY", item.getTranslationX(), 0); 182 | translationY.setDuration(duration); 183 | translationY.addUpdateListener(animation -> { 184 | float f = (float) animation.getAnimatedValue(); 185 | item.setTranslationX(f); 186 | }); 187 | translationY.setInterpolator(new AnticipateOvershootInterpolator()); 188 | translationY.start(); 189 | } 190 | } 191 | 192 | private void defaulted(int position){ 193 | Item item = clickViews.get(position); 194 | View view = item.getItem(); 195 | View box = view.findViewById(R.id.shadow_layout); 196 | ImageView icon = view.findViewById(R.id.item_icon); 197 | TextView title = view.findViewById(R.id.item_title); 198 | 199 | title.setTextColor(getColor(R.color.indicator_default)); 200 | icon.setColorFilter(getColor(R.color.indicator_default)); 201 | ObjectAnimator alpha = ObjectAnimator.ofFloat(box, "alpha", 0); 202 | alpha.setDuration(duration); 203 | alpha.start(); 204 | 205 | ObjectAnimator alpha1 = ObjectAnimator.ofFloat(title, "alpha", 0); 206 | alpha1.setDuration(duration); 207 | alpha1.start(); 208 | 209 | { 210 | ObjectAnimator translationY = ObjectAnimator.ofFloat(title, "translationY", item.getTranslationX(), title.getHeight() * 0.5f); 211 | translationY.setDuration(duration); 212 | translationY.addUpdateListener(animation -> { 213 | float f = (float) animation.getAnimatedValue(); 214 | item.setTranslationX(f); 215 | }); 216 | translationY.setInterpolator(new AnticipateOvershootInterpolator()); 217 | translationY.start(); 218 | } 219 | 220 | { 221 | ObjectAnimator translationY = ObjectAnimator.ofFloat(icon, "translationY", item.getTranslationX(), title.getHeight() * 0.5f); 222 | translationY.setDuration(duration); 223 | translationY.addUpdateListener(animation -> { 224 | float f = (float) animation.getAnimatedValue(); 225 | item.setTranslationX(f); 226 | }); 227 | translationY.setInterpolator(new AnticipateOvershootInterpolator()); 228 | translationY.start(); 229 | } 230 | } 231 | 232 | private void defaultedView(int position){ 233 | Item item = clickViews.get(position); 234 | View view = item.getItem(); 235 | View box = view.findViewById(R.id.shadow_layout); 236 | ImageView icon = view.findViewById(R.id.item_icon); 237 | TextView title = view.findViewById(R.id.item_title); 238 | 239 | title.setTextColor(getColor(R.color.indicator_default)); 240 | icon.setColorFilter(getColor(R.color.indicator_default)); 241 | ObjectAnimator alpha = ObjectAnimator.ofFloat(box, "alpha", 0); 242 | alpha.setDuration(0); 243 | alpha.start(); 244 | 245 | ObjectAnimator alpha1 = ObjectAnimator.ofFloat(title, "alpha", 0); 246 | alpha1.setDuration(0); 247 | alpha1.start(); 248 | 249 | { 250 | ObjectAnimator translationY = ObjectAnimator.ofFloat(title, "translationY", item.getTranslationX(), title.getHeight() * 0.5f); 251 | translationY.setDuration(0); 252 | translationY.addUpdateListener(animation -> { 253 | float f = (float) animation.getAnimatedValue(); 254 | item.setTranslationX(f); 255 | }); 256 | translationY.setInterpolator(new AnticipateOvershootInterpolator()); 257 | translationY.start(); 258 | } 259 | 260 | { 261 | ObjectAnimator translationY = ObjectAnimator.ofFloat(icon, "translationY", item.getTranslationX(), title.getHeight() * 0.5f); 262 | translationY.setDuration(0); 263 | translationY.addUpdateListener(animation -> { 264 | float f = (float) animation.getAnimatedValue(); 265 | item.setTranslationX(f); 266 | }); 267 | translationY.setInterpolator(new AnticipateOvershootInterpolator()); 268 | translationY.start(); 269 | } 270 | } 271 | 272 | private int getColor(int id){ 273 | return Color.parseColor(getContext().getString(id)); 274 | } 275 | 276 | public OnClickPositionListener getPositionListener() { 277 | return positionListener; 278 | } 279 | 280 | public void setPositionListener(OnClickPositionListener positionListener) { 281 | this.positionListener = positionListener; 282 | } 283 | 284 | private static class Item { 285 | private float translationX; 286 | private View item; 287 | 288 | public Item(float translationX, View item) { 289 | this.translationX = translationX; 290 | this.item = item; 291 | } 292 | 293 | public float getTranslationX() { 294 | return translationX; 295 | } 296 | 297 | public void setTranslationX(float translationX) { 298 | this.translationX = translationX; 299 | } 300 | 301 | public View getItem() { 302 | return item; 303 | } 304 | 305 | public void setItem(View item) { 306 | this.item = item; 307 | } 308 | } 309 | 310 | public static interface OnClickPositionListener{ 311 | void onChanged(View view, int position); 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /app/src/main/java/com/flyme/update/helper/utils/Reflection.java: -------------------------------------------------------------------------------- 1 | package com.flyme.update.helper.utils; 2 | 3 | import java.lang.reflect.AccessibleObject; 4 | import java.lang.reflect.Array; 5 | import java.lang.reflect.Constructor; 6 | import java.lang.reflect.Field; 7 | import java.lang.reflect.InvocationHandler; 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Member; 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.Proxy; 12 | 13 | /** 14 | * Created by canyie on 2019/10/24. 15 | */ 16 | @SuppressWarnings({"unchecked", "unused", "WeakerAccess"}) 17 | public final class Reflection { 18 | private Class clazz; 19 | 20 | private Reflection(Class clazz) { 21 | this.clazz = clazz; 22 | } 23 | 24 | public static Reflection on(String name) { 25 | return new Reflection(findClass(name)); 26 | } 27 | 28 | public static Reflection on(String name, ClassLoader loader) { 29 | return new Reflection(findClass(name, loader)); 30 | } 31 | 32 | public static Reflection on(Class clazz) { 33 | Preconditions.checkNotNull(clazz, "clazz == null"); 34 | return new Reflection(clazz); 35 | } 36 | 37 | 38 | public Class unwrap() { 39 | return clazz; 40 | } 41 | 42 | public static MethodWrapper wrap(Method method) { 43 | Preconditions.checkNotNull(method, "method == null"); 44 | return new MethodWrapper(method); 45 | } 46 | 47 | public MethodWrapper method(String name, Class... parameterTypes) { 48 | return method(clazz, name, parameterTypes); 49 | } 50 | 51 | public static MethodWrapper method(String className, String name, Class... parameterTypes) { 52 | return method(findClass(className), name, parameterTypes); 53 | } 54 | 55 | public static MethodWrapper method(Class clazz, String name, Class... parameterTypes) { 56 | return wrap(getMethod(clazz, name, parameterTypes)); 57 | } 58 | 59 | public static FieldWrapper wrap(Field field) { 60 | Preconditions.checkNotNull(field, "field == null"); 61 | return new FieldWrapper(field); 62 | } 63 | 64 | public FieldWrapper field(String name) { 65 | return field(clazz, name); 66 | } 67 | 68 | public static FieldWrapper field(String className, String name) { 69 | return field(findClass(className), name); 70 | } 71 | 72 | public static FieldWrapper field(Class clazz, String name) { 73 | return wrap(getField(clazz, name)); 74 | } 75 | 76 | public static ConstructorWrapper wrap(Constructor constructor) { 77 | Preconditions.checkNotNull(constructor, "constructor == null"); 78 | return new ConstructorWrapper<>(constructor); 79 | } 80 | 81 | public ConstructorWrapper constructor(Class... parameterTypes) { 82 | return wrap(getConstructor(clazz, parameterTypes)); 83 | } 84 | 85 | public T[] array(int length) { 86 | return (T[]) Array.newInstance(clazz, length); 87 | } 88 | 89 | 90 | public static Class findClassOrNull(String name) { 91 | try { 92 | return Class.forName(name); 93 | } catch (ClassNotFoundException ignored) { 94 | return null; 95 | } 96 | } 97 | 98 | 99 | public static Class findClassOrNull(String name, ClassLoader loader) { 100 | try { 101 | return Class.forName(name, true, loader); 102 | } catch (ClassNotFoundException ignored) { 103 | return null; 104 | } 105 | } 106 | 107 | 108 | public static Class findClass(String name) { 109 | try { 110 | return Class.forName(name); 111 | } catch (ClassNotFoundException e) { 112 | throw new RuntimeException("Class " + name + " not found", e); 113 | } 114 | } 115 | 116 | 117 | public static Class findClass(String name, ClassLoader loader) { 118 | try { 119 | return Class.forName(name, true, loader); 120 | } catch (ClassNotFoundException e) { 121 | throw new RuntimeException("No class " + name + " found in classloader " + loader, e); 122 | } 123 | } 124 | 125 | 126 | public static Method getMethod(Class clazz, String name, Class... parameterTypes) { 127 | Method method = findMethod(clazz, name, parameterTypes); 128 | if (method == null) { 129 | throw new RuntimeException("No method '" + name + getParameterTypesMessage(parameterTypes) + "' found in class " + clazz.getName()); 130 | } 131 | return method; 132 | } 133 | 134 | private static String getParameterTypesMessage(Class[] parameterTypes) { 135 | if (parameterTypes == null || parameterTypes.length == 0) { 136 | return "()"; 137 | } 138 | StringBuilder sb = new StringBuilder("("); 139 | boolean isFirst = true; 140 | for (Class type : parameterTypes) { 141 | if (isFirst) { 142 | isFirst = false; 143 | } else { 144 | sb.append(","); 145 | } 146 | sb.append(type.getName()); 147 | } 148 | return sb.append(')').toString(); 149 | } 150 | 151 | 152 | public static Method findMethod(Class clazz, String name, Class... parameterTypes) { 153 | checkForFindMethod(clazz, name, parameterTypes); 154 | return findMethodNoChecks(clazz, name, parameterTypes); 155 | } 156 | 157 | 158 | public static Method findMethodNoChecks(Class clazz, String name, Class... parameterTypes) { 159 | while (clazz != null) { 160 | try { 161 | Method method = clazz.getDeclaredMethod(name, parameterTypes); 162 | method.setAccessible(true); 163 | return method; 164 | } catch (NoSuchMethodException ignored) { 165 | } 166 | clazz = clazz.getSuperclass(); 167 | } 168 | return null; 169 | } 170 | 171 | private static void checkForFindMethod(Class clazz, String name, Class... parameterTypes) { 172 | Preconditions.checkNotNull(clazz, "clazz == null"); 173 | Preconditions.checkNotEmpty(name, "name is null or empty"); 174 | if (parameterTypes != null) { 175 | for (int i = 0; i < parameterTypes.length; i++) { 176 | if (parameterTypes[i] == null) { 177 | throw new NullPointerException("parameterTypes[" + i + "] == null"); 178 | } 179 | } 180 | } 181 | 182 | } 183 | 184 | 185 | public static Field getField(Class clazz, String name) { 186 | Field field = findField(clazz, name); 187 | if (field == null) { 188 | throw new RuntimeException("No field '" + name + "' found in class " + clazz.getName()); 189 | } 190 | return field; 191 | } 192 | 193 | 194 | public static Field findField(Class clazz, String name) { 195 | checkForFindField(clazz, name); 196 | return findFieldNoChecks(clazz, name); 197 | } 198 | 199 | 200 | public static Field findFieldNoChecks(Class clazz, String name) { 201 | while (clazz != null) { 202 | try { 203 | Field field = clazz.getDeclaredField(name); 204 | field.setAccessible(true); 205 | return field; 206 | } catch (NoSuchFieldException ignored) { 207 | } 208 | clazz = clazz.getSuperclass(); 209 | } 210 | return null; 211 | } 212 | 213 | private static void checkForFindField(Class clazz, String name) { 214 | Preconditions.checkNotNull(clazz, "clazz == null"); 215 | Preconditions.checkNotEmpty(name, "name is null or empty"); 216 | } 217 | 218 | 219 | public static Constructor getConstructor(Class clazz, Class... parameterTypes) { 220 | Constructor c = findConstructor(clazz, parameterTypes); 221 | if (c == null) { 222 | throw new RuntimeException("No constructor '" + clazz.getName() + getParameterTypesMessage(parameterTypes) + "' found in class " + clazz.getName()); 223 | } 224 | return c; 225 | } 226 | 227 | 228 | public static Constructor findConstructor(Class clazz, Class... parameterTypes) { 229 | checkForFindConstructor(clazz, parameterTypes); 230 | return findConstructorNoChecks(clazz, parameterTypes); 231 | } 232 | 233 | 234 | public static Constructor findConstructorNoChecks(Class clazz, Class... parameterTypes) { 235 | try { 236 | Constructor constructor = clazz.getDeclaredConstructor(parameterTypes); 237 | constructor.setAccessible(true); 238 | return constructor; 239 | } catch (NoSuchMethodException ignored) { 240 | } 241 | return null; 242 | } 243 | 244 | private static void checkForFindConstructor(Class clazz, Class... parameterTypes) { 245 | Preconditions.checkNotNull(clazz, "clazz == null"); 246 | if (parameterTypes != null) { 247 | for (int i = 0; i < parameterTypes.length; i++) { 248 | if (parameterTypes[i] == null) { 249 | throw new NullPointerException("parameterTypes[" + i + "] == null"); 250 | } 251 | } 252 | } 253 | } 254 | 255 | public boolean isInstance(Object instance) { 256 | return clazz.isInstance(instance); 257 | } 258 | 259 | public int getModifiers() { 260 | return clazz.getModifiers(); 261 | } 262 | 263 | public boolean isLambdaClass() { 264 | return isLambdaClass(clazz); 265 | } 266 | 267 | public boolean isProxyClass() { 268 | return isProxyClass(clazz); 269 | } 270 | 271 | public T proxy(InvocationHandler h) { 272 | return (T) Proxy.newProxyInstance(h.getClass().getClassLoader(), new Class[]{clazz}, h); 273 | } 274 | 275 | public static boolean isLambdaClass(Class clazz) { 276 | return clazz.getName().contains("$$Lambda$"); 277 | } 278 | 279 | public static boolean isProxyClass(Class clazz) { 280 | return Proxy.isProxyClass(clazz); 281 | } 282 | 283 | public static void throwUnchecked(Throwable e) throws T { 284 | throw (T) e; 285 | } 286 | 287 | public static class MemberWrapper { 288 | M member; 289 | MemberWrapper(M member) { 290 | member.setAccessible(true); 291 | this.member = member; 292 | } 293 | 294 | public final M unwrap() { 295 | return member; 296 | } 297 | 298 | public final int getModifiers() { 299 | return member.getModifiers(); 300 | } 301 | 302 | public final Class getDeclaringClass() { 303 | return member.getDeclaringClass(); 304 | } 305 | } 306 | 307 | public static class MethodWrapper extends MemberWrapper { 308 | MethodWrapper(Method method) { 309 | super(method); 310 | } 311 | 312 | public T call(Object instance, Object... args) { 313 | try { 314 | return (T) member.invoke(instance, args); 315 | } catch (IllegalAccessException e) { 316 | throw new RuntimeException(e); 317 | } catch (InvocationTargetException e) { 318 | throw new RuntimeException(e); 319 | } 320 | } 321 | 322 | public T callStatic(Object... args) { 323 | return call(null, args); 324 | } 325 | } 326 | 327 | public static class FieldWrapper extends MemberWrapper { 328 | FieldWrapper(Field field) { 329 | super(field); 330 | } 331 | 332 | public T getValue(Object instance) { 333 | try { 334 | return (T) member.get(instance); 335 | } catch (IllegalAccessException e) { 336 | throw new RuntimeException(e); 337 | } 338 | } 339 | 340 | public T getStaticValue() { 341 | return getValue(null); 342 | } 343 | 344 | public void setValue(Object instance, Object value) { 345 | try { 346 | member.set(instance, value); 347 | } catch (IllegalAccessException e) { 348 | throw new RuntimeException(e); 349 | } 350 | } 351 | 352 | public void setStaticValue(Object value) { 353 | setValue(null, value); 354 | } 355 | 356 | public Class getType() { 357 | return member.getType(); 358 | } 359 | } 360 | 361 | public static class ConstructorWrapper extends MemberWrapper> { 362 | ConstructorWrapper(Constructor constructor) { 363 | super(constructor); 364 | } 365 | 366 | public T newInstance(Object... args) { 367 | try { 368 | return member.newInstance(args); 369 | } catch (IllegalAccessException e) { 370 | throw new RuntimeException(e); 371 | } catch (InvocationTargetException e) { 372 | throw new RuntimeException(e); 373 | } catch (InstantiationException e) { 374 | throw new RuntimeException(e); 375 | } 376 | } 377 | } 378 | } 379 | 380 | 381 | --------------------------------------------------------------------------------