├── .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 |
8 |
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 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
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 extends ZipEntry> 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 |
--------------------------------------------------------------------------------