├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── copyright │ ├── ayst_shen_foxmail_com.xml │ └── profiles_settings.xml ├── dbnavigator.xml ├── encodings.xml ├── gradle.xml ├── jarRepositories.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── build.properties ├── config.ini ├── libs │ └── otasdk(3.2.0.12)_ota_release.aar ├── proguard-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── aidl │ │ └── com │ │ │ └── ayst │ │ │ └── romupgrade │ │ │ └── IRomUpgradeService.aidl │ │ ├── cpp │ │ └── native-lib.cpp │ │ ├── java │ │ └── com │ │ │ └── ayst │ │ │ └── romupgrade │ │ │ ├── App.java │ │ │ ├── adapter │ │ │ └── DownloadAdapter.java │ │ │ ├── baidu │ │ │ ├── ErrorString.java │ │ │ ├── NewVersionBean.java │ │ │ └── SystemInfo.java │ │ │ ├── config │ │ │ └── UsbConfigManager.java │ │ │ ├── entity │ │ │ ├── InstallProgress.java │ │ │ └── LocalPackage.java │ │ │ ├── receiver │ │ │ └── UpdateReceiver.java │ │ │ ├── service │ │ │ └── UpdateService.java │ │ │ └── util │ │ │ ├── AppUtils.java │ │ │ ├── DataEncryptUtil.java │ │ │ ├── EncryptUtil.java │ │ │ ├── FileUtils.java │ │ │ ├── InstallUtil.java │ │ │ ├── MD5.java │ │ │ ├── SPUtils.java │ │ │ └── filecopy │ │ │ ├── FileCopyTask.java │ │ │ ├── FileCopyTaskParam.java │ │ │ └── IFileCopyListener.java │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bg_dialog.xml │ │ ├── btn_default_disable.xml │ │ ├── btn_default_normal.xml │ │ ├── btn_default_pressed.xml │ │ ├── btn_default_selected.xml │ │ ├── btn_default_selector.xml │ │ ├── btn_default_text_selector.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── layout_download.xml │ │ └── layout_download_item.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-zh │ │ └── strings.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── file_paths.xml └── system.keystore ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── 1585270225.jpg ├── 1585270231.jpg ├── 1585270300.jpg ├── 1585270318.jpg ├── device-2020-01-18-150533.png ├── device-2020-01-18-150622.png └── device-2020-01-18-150651.png ├── settings.gradle └── utils.gradle /.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 | app/build/ 15 | app/release/ 16 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/copyright/ayst_shen_foxmail_com.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 37 | 38 | 39 | 40 | 41 | 42 | 44 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android 系统 OTA 升级应用 2 | 一个负责 Android 系统 ota 升级的后台应用,开机后自动运行后台 Service,支持系统升级、应用升级,支持本地升级(tf卡、u盘)、在线升级(百度),支持推荐升级、静默升级。 3 | 4 | 已知兼容版本: 5 | - Android 5.1 6 | - Android 6.0 7 | - Android 7.1 8 | - Android 8.1 9 | 10 | ## 预览 11 | ### 本地升级(tf卡、u盘) 12 | ![](screenshots/device-2020-01-18-150651.png) 13 | 14 | ### 在线升级([百度](https://ota.baidu.com/)) 15 | ![](screenshots/device-2020-01-18-150533.png) 16 | 17 | ![](screenshots/device-2020-01-18-150622.png) 18 | 19 | ## API 20 | 此固件升级应用也对外提供下面 API,供第三方应用调用 **安装、验证、删除** 升级包。 21 | ```java 22 | // IRomUpgradeService.aidl 23 | package com.ayst.romupgrade; 24 | 25 | // Declare any non-default types here with import statements 26 | 27 | interface IRomUpgradeService { 28 | boolean installPackage(String packagePath); 29 | boolean verifyPackage(String packagePath); 30 | void deletePackage(String packagePath); 31 | } 32 | ``` 33 | 34 | ### API使用 35 | 1. 在 APP 源码 **aidl/com/ayst/romupgrade/** 目录下新建 **IRomUpgradeService.aidl**,如下: 36 | ```java 37 | // IRomUpgradeService.aidl 38 | package com.ayst.romupgrade; 39 | 40 | // Declare any non-default types here with import statements 41 | 42 | interface IRomUpgradeService { 43 | boolean installPackage(String packagePath); 44 | boolean verifyPackage(String packagePath); 45 | void deletePackage(String packagePath); 46 | } 47 | ``` 48 | 2. 实现下面代码: 49 | ```java 50 | Intent intent = new Intent(); 51 | intent.setPackage("com.ayst.romupgrade"); 52 | intent.setAction("com.ayst.romupgrade.UPGRADE_SERVICE"); 53 | mContext.bindService(intent, mRomUpgradeServiceConnection, Context.BIND_AUTO_CREATE); 54 | 55 | private ServiceConnection mRomUpgradeServiceConnection = new ServiceConnection() { 56 | @Override 57 | public void onServiceConnected(ComponentName name, IBinder service) { 58 | Log.d(TAG, "IRomUpgradeService, onServiceConnected..."); 59 | mRomUpgradeService = IRomUpgradeService.Stub.asInterface(service); 60 | } 61 | 62 | @Override 63 | public void onServiceDisconnected(ComponentName name) { 64 | Log.d(TAG, "IRomUpgradeService, onServiceDisconnected..."); 65 | mRomUpgradeService = null; 66 | } 67 | }; 68 | 69 | /** 70 | * 安装升级 71 | * 72 | * @param packagePath ota升级包 73 | * @return 74 | */ 75 | public boolean installPackage(String packagePath) { 76 | if (null != mRomUpgradeService) { 77 | try { 78 | return mRomUpgradeService.installPackage(packagePath); 79 | } catch (RemoteException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | 84 | return false; 85 | } 86 | 87 | /** 88 | * 验证升级包 89 | * 90 | * @param packagePath ota升级包 91 | * @return 92 | */ 93 | public boolean verifyPackage(String packagePath) { 94 | if (null != mRomUpgradeService) { 95 | try { 96 | return mRomUpgradeService.verifyPackage(packagePath); 97 | } catch (RemoteException e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | return false; 103 | } 104 | 105 | /** 106 | * 删除升级包 107 | * 108 | * @param packagePath ota升级包 109 | */ 110 | public void deletePackage(String packagePath) { 111 | if (null != mRomUpgradeService) { 112 | try { 113 | mRomUpgradeService.deletePackage(packagePath); 114 | } catch (RemoteException e) { 115 | e.printStackTrace(); 116 | } 117 | } 118 | } 119 | ``` 120 | 121 | ## 集成 122 | 这里讲述如何将此升级应用内置到您定制的系统固件中。 123 | 124 | ### 前提条件 125 | - 系统签名 126 | - root权限(应用升级默认采用静默安装,因此需要root权限) 127 | 128 | ### 内置 129 | 1. 编译release版本apk文件(或者直接下载已发布的release版本)。 130 | 2. 在Android源码vendor/xxx/common/apps/路径下新建“RomUpgrade”目录。 131 | 3. 将升级应用apk文件复制到“RomUpgrade”目录,并重命名为“RomUpgrade.apk。 132 | 4. 将升级应用apk文件中的so库提取出来,复制到“RomUpgrade/lib/arm/”(如果是64系统请提取64库到“RomUpgrade/lib/arm64/”)。 133 | 5. 新建Android.mk文件,内容如下: 134 | ``` 135 | LOCAL_PATH := $(call my-dir) 136 | include $(CLEAR_VARS) 137 | LOCAL_MODULE := RomUpgrade 138 | LOCAL_MODULE_CLASS := APPS 139 | LOCAL_MODULE_TAGS := optional 140 | LOCAL_BUILT_MODULE_STEM := package.apk 141 | LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) 142 | #LOCAL_PRIVILEGED_MODULE := 143 | LOCAL_CERTIFICATE := PRESIGNED 144 | #LOCAL_OVERRIDES_PACKAGES := 145 | LOCAL_SRC_FILES := $(LOCAL_MODULE).apk 146 | #LOCAL_REQUIRED_MODULES := 147 | LOCAL_PREBUILT_JNI_LIBS := \ 148 | lib/arm/libotaso.so \ 149 | lib/arm/libnative-lib.so 150 | include $(BUILD_PREBUILT) 151 | ``` 152 | 6. 修改vendor/xxx/common/apps/apps.mk,如下: 153 | ``` 154 | PRODUCT_PACKAGES += \ 155 | RomUpgrade 156 | 157 | ``` 158 | 159 | ### 配置属性 160 | 将下面属性配置到系统build.prop中: 161 | ``` 162 | # 百度ota平台产品线id 163 | ro.baidu.product.id=10254 164 | 165 | # 百度ota平台产品线密钥 166 | ro.baidu.product.secret=NTUyOGFhOTVjODRlZjFmOA== 167 | 168 | # 可升级的内置应用包名(多个包名通过逗号分隔,无应用升级可不配) 169 | ro.baidu.presetapp=com.ayst.sample1,com.ayst.sample2 170 | 171 | # 系统固件版本号 172 | ro.fw.version=1.0.0 173 | ``` 174 | 175 | ## 使用 176 | 177 | ### 本地升级(tf卡、u盘) 178 | #### 本地应用升级 179 | 1. 在tf卡或u盘根目录新建“exupdate”目录。 180 | 2. 将待安装apk文件复制到“exupdate”目录下。 181 | 3. 插入tf卡或u盘插入Android设备。 182 | 4. 等待5秒左右,会弹出升级提示对话框,请根据提示完成升级。 183 | 184 | #### 本地系统升级 185 | 1. 将待升级系统ota包复制到tf卡或u盘根目录下,并重命名为“update.zip”。 186 | 2. 插入tf卡或u盘插入Android设备。 187 | 3. 等待5秒左右,会弹出升级提示对话框,请根据提示完成升级。 188 | 189 | #### 本地应用与系统同时升级 190 | 1. 在tf卡或u盘根目录新建“exupdate”目录。 191 | 2. 将待安装apk文件复制到“exupdate”目录下。 192 | 3. 将待升级系统ota包复制到tf卡或u盘目录下,并重命名为“update.zip”。 193 | 4. 插入tf卡或u盘插入Android设备。 194 | 5. 等待5秒左右,会弹出升级提示对话框,请根据提示完成升级。 195 | 196 | #### 配置推荐升级或静默升级 197 | 1. 在tf卡或u盘根目录新建“exupdate”目录。 198 | 2. 在“exupdate”目录下新建“config.ini”文件,文件内容如下: 199 | ``` 200 | #升级类型,1:推荐升级,2:静默升级 201 | UPDATE_TYPE=2 202 | ``` 203 | #### 配置升级包版本号 204 | 此版本号可以配可以不配。如果配置了该版本号则当前系统版本小于此版本时才会升级,否则不升级。如果没有配置该版本号则一律升级。 205 | ``` 206 | #OTA升级包版本号,如:1.0.0 207 | PACKAGE_VERSION=1.0.0 208 | ``` 209 | 注意 如果配置了静默升级,请一定要配置此版本号,否则升级完成后又会重复升级。 210 | 211 | ### 在线升级(百度) 212 | #### 注册百度OTA平台账号 213 | 注册 [百度安全 OTA 平台](https://ota.baidu.com/) 账号,申请开通产品线,将**『产品线ID』**和**『产品线秘钥』**通过属性配置到系统。 214 | 215 | #### 上传升级包 216 | 进入对应 『**产品线**』配置页面,进入『**升级资源库**』,点击『**上传升级包**』。 217 | ![](screenshots/1585270300.jpg) 218 | 219 | 参数说明: 220 | 221 | - **上传文件**     选择 OTA 升级包文件(应用升级请上传 apk 文件)。 222 | - **类型** 勾选『**系统升级**』『**整包**』。 223 | - **版本号**       **版本号格式:1.0.0.0,不足4段补0**(系统固件的版本号默认从『**ro.topband.sw.version**』属性读取)。 224 | - **标签**         忽略。 225 | 226 | #### 配置测试任务 227 | ##### 添加测试设备分组 228 | 进入『**设备分组管理->测试设备分组**』,点击『**添加测试设备分组**』,勾选『**填写设备ID**』。 229 | ![](screenshots/1585270318.jpg) 230 | 231 | 参数说明: 232 | 233 | - **设备ID**        CPU 序号。可以通过命令『**cat /proc/cpuinfo**』查看。 234 | 235 | ##### 创建测试任务 236 | 进入『**测试任务列表**』,点击『**创建测试任务**』。 237 | 参数说明: 238 | 239 | - **任务名称**          自定义。 240 | - **升级范围**          勾选『**指定测试设备分组**』,点击『**选择设备分组**』,选择上一步添加的『**测试设备**』分组。 241 | - **升级类型**          选择『**系统升级**』『**整包升级**』。 242 | - **系统升级资源**    点击『**资源库选择**』,选择上一步上传的升级包。 243 | - **升级方式**          勾选『**提示升级**』。如果不希望 Android 弹出升级提示,而是直接安装升级包,可以勾选『**静默升级**』。 244 | - **升级提示**          一般填写此次升级的修改点,当 Android 弹出升级提示时,会显示这些内容。 245 | - **优先级**            默认值。 246 | 247 | ##### 设备端检查升级 248 | 确保设备端网络连接正常,重启设备后会立即检查升级(稍后会每30分钟检查一次),当检查到新版本升级时,会弹出下面升级提示框: 249 | ![](screenshots/1585270225.jpg) 250 | 251 | 点击『**立即升级**』开始下载,下载完成后将自动重启安装(**安装过程中切勿断电**)。 252 | ![](screenshots/1585270231.jpg) 253 | 254 | #### 配置正式任务 255 | 测试任务验证正常后,按照同样方法配置正式任务即可。 256 | 257 | ## 开发者 258 | * ayst.shen@foxmail.com 259 | 260 | ## License 261 | ``` 262 | Copyright 2019 Bob Shen 263 | 264 | Licensed under the Apache License, Version 2.0 (the "License"); you may 265 | not use this file except in compliance with the License. You may obtain 266 | a copy of the License at 267 | 268 | http://www.apache.org/licenses/LICENSE-2.0 269 | 270 | Unless required by applicable law or agreed to in writing, software 271 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 272 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 273 | License for the specific language governing permissions and limitations 274 | under the License. 275 | ``` 276 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # For more information about using CMake with Android Studio, read the 2 | # documentation: https://d.android.com/studio/projects/add-native-code.html 3 | 4 | # Sets the minimum version of CMake required to build the native library. 5 | 6 | cmake_minimum_required(VERSION 3.4.1) 7 | 8 | # Creates and names a library, sets it as either STATIC 9 | # or SHARED, and provides the relative paths to its source code. 10 | # You can define multiple libraries, and CMake builds them for you. 11 | # Gradle automatically packages shared libraries with your APK. 12 | 13 | add_library( # Sets the name of the library. 14 | native-lib 15 | 16 | # Sets the library as a shared library. 17 | SHARED 18 | 19 | # Provides a relative path to your source file(s). 20 | src/main/cpp/native-lib.cpp ) 21 | 22 | # Searches for a specified prebuilt library and stores the path as a 23 | # variable. Because CMake includes system libraries in the search path by 24 | # default, you only need to specify the name of the public NDK library 25 | # you want to add. CMake verifies that the library exists before 26 | # completing its build. 27 | 28 | find_library( # Sets the name of the path variable. 29 | log-lib 30 | 31 | # Specifies the name of the NDK library that 32 | # you want CMake to locate. 33 | log ) 34 | 35 | # Specifies libraries CMake should link to your target library. You 36 | # can link multiple libraries, such as libraries you define in this 37 | # build script, prebuilt third-party libraries, or system libraries. 38 | 39 | target_link_libraries( # Specifies the target library. 40 | native-lib 41 | 42 | # Links the target library to the log library 43 | # included in the NDK. 44 | ${log-lib} ) -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 | apply plugin: 'com.android.application' 18 | 19 | android { 20 | compileSdkVersion rootProject.ext.compileSdkVersion 21 | defaultConfig { 22 | applicationId "com.ayst.romupgrade" 23 | minSdkVersion rootProject.ext.minSdkVersion 24 | targetSdkVersion rootProject.ext.targetSdkVersion 25 | versionCode 26 | versionName 27 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 28 | externalNativeBuild { 29 | cmake { 30 | cppFlags "" 31 | } 32 | } 33 | } 34 | 35 | signingConfigs { 36 | release { 37 | File strFile = new File(rootProject.ext.keyStorePath) 38 | storeFile file(strFile) 39 | keyAlias rootProject.ext.keyStoreAlias 40 | keyPassword rootProject.ext.keyStoreKeyPassword 41 | storePassword rootProject.ext.keyStorePassword 42 | } 43 | } 44 | 45 | buildTypes { 46 | release { 47 | minifyEnabled true 48 | shrinkResources false 49 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 50 | signingConfig signingConfigs.release 51 | 52 | applicationVariants.all { variant -> 53 | variant.outputs.all { 54 | def fileName = "AutoUpgrade_${variant.buildType.name}_v${variant.versionName}_${releaseTime()}.apk" 55 | outputFileName = fileName 56 | } 57 | } 58 | } 59 | 60 | debug { 61 | //applicationIdSuffix ".BETA" 62 | versionNameSuffix "-BETA" 63 | minifyEnabled false 64 | shrinkResources false 65 | debuggable true 66 | testCoverageEnabled true 67 | signingConfig signingConfigs.release 68 | } 69 | } 70 | 71 | lintOptions { 72 | abortOnError false 73 | } 74 | 75 | compileOptions { 76 | sourceCompatibility JavaVersion.VERSION_1_8 77 | targetCompatibility JavaVersion.VERSION_1_8 78 | } 79 | 80 | externalNativeBuild { 81 | cmake { 82 | path "CMakeLists.txt" 83 | } 84 | } 85 | } 86 | 87 | dependencies { 88 | implementation fileTree(dir: 'libs', include: ['*.jar']) 89 | implementation fileTree(include: ['*.aar'], dir: 'libs') 90 | 91 | //noinspection GradleCompatible 92 | implementation 'androidx.cardview:cardview:1.0.0' 93 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 94 | implementation 'androidx.appcompat:appcompat:1.1.0' 95 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 96 | 97 | // Butterknife 98 | implementation 'com.jakewharton:butterknife:10.1.0' 99 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0' 100 | 101 | // Retrofit2 102 | implementation 'com.squareup.retrofit2:retrofit:2.7.0' 103 | implementation('com.squareup.retrofit2:converter-gson:2.7.0') { 104 | exclude module: 'gson' 105 | } 106 | 107 | // FileDownloader 108 | implementation 'com.liulishuo.filedownloader:library:1.7.7' 109 | 110 | // Gson 111 | implementation 'com.google.code.gson:gson:2.8.5' 112 | 113 | // rxjava 114 | implementation "io.reactivex.rxjava2:rxjava:2.2.4" 115 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' 116 | 117 | implementation 'cn.trinea.android.common:trinea-android-common:4.2.15' 118 | } 119 | 120 | def releaseTime() { 121 | return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC")) 122 | } 123 | 124 | updateVersion() 125 | 126 | def updateVersion() { 127 | def propertiesFile = file('build.properties') 128 | Properties properties = readProperties(propertiesFile) 129 | 130 | def versionMajor = properties['version_major'].toString().toInteger() 131 | def versionMinor = properties['version_minor'].toString().toInteger() 132 | def versionPatch = properties['version_patch'].toString().toInteger() 133 | def versionBuild = properties['version_build'].toString().toInteger() 134 | def versionStore = properties['version_store'].toString().toInteger() 135 | 136 | if (isReleaseTask()) { 137 | System.out.println(">>> Building Release...") 138 | versionPatch++ 139 | versionStore++ 140 | versionBuild = 0 141 | properties['version_patch'] = versionPatch.toString() 142 | properties['version_store'] = versionStore.toString() 143 | properties['version_build'] = versionBuild.toString() 144 | writeProperties(propertiesFile, properties) 145 | } 146 | if (isCompileTask()) { 147 | System.out.println(">>> Compiling Source...") 148 | versionBuild++ 149 | properties['version_build'] = versionBuild.toString() 150 | writeProperties(propertiesFile, properties) 151 | } 152 | 153 | def versionName = "${versionMajor}.${versionMinor}.${versionPatch}" 154 | def applicationId = android.defaultConfig.applicationId 155 | if (!isReleaseTask()) { 156 | versionName = versionName + ".${versionBuild}" 157 | applicationId = applicationId + android.buildTypes.debug.applicationIdSuffix 158 | } 159 | System.out.println(">>> " + project.parent.name + " " + versionName + " (" + versionStore + ") '" + applicationId + "'") 160 | android.defaultConfig.versionCode = versionStore 161 | android.defaultConfig.versionName = versionName 162 | } 163 | 164 | def isCompileTask() { 165 | def tasks = gradle.getStartParameter().getTaskNames() 166 | return ':app:generateDebugSources' in tasks || ':app:generateReleaseSources' in tasks || 167 | 'generateDebugSources' in tasks || 'generateReleaseSources' in tasks 168 | } 169 | 170 | def isReleaseTask() { 171 | def tasks = gradle.getStartParameter().getTaskNames() 172 | return ':app:assembleRelease' in tasks || 'assembleRelease' in tasks 173 | } 174 | 175 | static Properties readProperties(propertiesFile) { 176 | if (propertiesFile.canRead()) { 177 | Properties properties = new Properties() 178 | def inputStream = new FileInputStream(propertiesFile) 179 | properties.load(inputStream) 180 | inputStream.close() 181 | return properties 182 | } else { 183 | def message = ">>> Could not read " + propertiesFile.name + " file!" 184 | System.err.println(message) 185 | throw new GradleException(message) 186 | } 187 | } 188 | 189 | def static writeProperties(propertiesFile, properties) { 190 | def writer = propertiesFile.newWriter() 191 | properties.store(writer, "Build Properties") 192 | writer.close() 193 | } 194 | -------------------------------------------------------------------------------- /app/build.properties: -------------------------------------------------------------------------------- 1 | #Build Properties 2 | #Thu Apr 29 18:01:03 CST 2021 3 | version_minor=0 4 | version_build=0 5 | version_patch=11 6 | version_store=111 7 | version_major=2 8 | -------------------------------------------------------------------------------- /app/config.ini: -------------------------------------------------------------------------------- 1 | #升级类型,1:推荐升级,2:静默升级 2 | UPDATE_TYPE=2 3 | 4 | #OTA升级包版本号,如:1.0.0 5 | PACKAGE_VERSION=1.0.0 -------------------------------------------------------------------------------- /app/libs/otasdk(3.2.0.12)_ota_release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/libs/otasdk(3.2.0.12)_ota_release.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # Platform calls Class.forName on types which do not exist on Android to determine platform. 24 | -dontnote retrofit2.Platform 25 | # Platform used when running on RoboVM on iOS. Will not be used at runtime. 26 | -dontnote retrofit2.Platform$IOS$MainThreadExecutor 27 | # Platform used when running on Java 8 VMs. Will not be used at runtime. 28 | -dontwarn retrofit2.Platform$Java8 29 | # Retain generic type information for use by reflection by converters and adapters. 30 | -keepattributes Signature 31 | # Retain declared checked exceptions for use by a Proxy instance. 32 | -keepattributes Exceptions 33 | 34 | # Retrofit 35 | -dontnote retrofit2.Platform 36 | -dontnote retrofit2.Platform$IOS$MainThreadExecutor 37 | -dontwarn retrofit2.Platform$Java8 38 | -keepattributes Signature 39 | -keepattributes Exceptions 40 | -dontwarn retrofit2.** 41 | -keep class retrofit2.** { *; } 42 | -dontwarn javax.annotation.** 43 | -dontwarn javax.inject.** 44 | 45 | # okhttp 46 | -dontwarn okio.** 47 | -dontwarn okhttp3.** 48 | 49 | # Gson 50 | -keep class com.topband.autoupgrade.http.**{*;} 51 | -keep class com.topband.autoupgrade.baidu.**{*;} 52 | 53 | -keepattributes *Annotation* 54 | 55 | # EventBus 56 | -keepattributes *Annotation* 57 | -keepclassmembers class * { 58 | @org.greenrobot.eventbus.Subscribe ; 59 | } 60 | -keep enum org.greenrobot.eventbus.ThreadMode { *; } 61 | 62 | # Only required if you use AsyncExecutor 63 | -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent { 64 | (java.lang.Throwable); 65 | } 66 | 67 | # For Baidu otasdk 68 | -dontwarn com.baidu.commonlib.interfaces.** 69 | -keep class com.baidu.commonlib.interfaces.** { *; } 70 | 71 | -dontwarn com.baidu.eventbus.lib.** 72 | -keep class com.baidu.eventbus.lib.** { *; } 73 | 74 | -keepattributes *Annotation* 75 | -keepclassmembers class ** { 76 | @com.baidu.eventbus.lib.Event ; 77 | } 78 | 79 | # RXJava 80 | -dontwarn sun.misc.** 81 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 82 | long producerIndex; 83 | long consumerIndex; 84 | } 85 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 86 | rx.internal.util.atomic.LinkedQueueNode producerNode; 87 | } 88 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { 89 | rx.internal.util.atomic.LinkedQueueNode consumerNode; 90 | } 91 | 92 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 50 | 51 | 55 | 56 | 57 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 75 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/aidl/com/ayst/romupgrade/IRomUpgradeService.aidl: -------------------------------------------------------------------------------- 1 | // IRomUpgradeService.aidl 2 | package com.ayst.romupgrade; 3 | 4 | // Declare any non-default types here with import statements 5 | 6 | interface IRomUpgradeService { 7 | void checkUpdate(); 8 | boolean installPackage(String packagePath); 9 | boolean verifyPackage(String packagePath); 10 | void deletePackage(String packagePath); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/cpp/native-lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" 5 | JNIEXPORT jstring 6 | 7 | JNICALL 8 | Java_com_topband_autoupgrade_MainActivity_stringFromJNI( 9 | JNIEnv *env, 10 | jobject /* this */) { 11 | std::string hello = "Hello from C++"; 12 | return env->NewStringUTF(hello.c_str()); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade; 18 | 19 | import android.util.Log; 20 | 21 | import com.baidu.commonlib.interfaces.IOtaAgent; 22 | import com.baidu.commonlib.interfaces.IOtaSdkHelper; 23 | import com.baidu.otasdk.ota.OtaApplication; 24 | import com.liulishuo.filedownloader.FileDownloader; 25 | import com.ayst.romupgrade.baidu.SystemInfo; 26 | import com.ayst.romupgrade.util.AppUtils; 27 | 28 | import java.util.Arrays; 29 | import java.util.List; 30 | 31 | /** 32 | * Created by ayst.shen@foxmail.com on 2017/12/13. 33 | */ 34 | public class App extends OtaApplication { 35 | private static final String TAG = "App"; 36 | 37 | /** 38 | * Baidu otask product id and secret 39 | */ 40 | private static final String DEFAULT_PRODUCT_ID = "9732"; 41 | private static final String DEFAULT_PRODUCT_SECRET = "ZWMzZjQ0M2Q0Y2IyZjg2NA=="; 42 | 43 | private static IOtaAgent sOtaAgent; 44 | private static String sProductId = DEFAULT_PRODUCT_ID; 45 | private static String sProductSecret = DEFAULT_PRODUCT_SECRET; 46 | 47 | @Override 48 | public void onCreate() { 49 | super.onCreate(); 50 | 51 | Log.i(TAG, "onCreate..."); 52 | 53 | FileDownloader.setup(this); 54 | } 55 | 56 | /** 57 | * Initialize Baidu otasdk 58 | * 59 | * @param otaSdkHelper 60 | */ 61 | @Override 62 | protected void initService(IOtaSdkHelper otaSdkHelper) { 63 | otaSdkHelper.init(AppUtils.getDeviceId(), new SystemInfo()); 64 | otaSdkHelper.setUpgradePath(AppUtils.getExternalDir(this, "upgrade")); 65 | otaSdkHelper.setExtOption(16, AppUtils.getExternalDir(App.this, "apks")); 66 | otaSdkHelper.setAutoCheck(true); 67 | otaSdkHelper.setSilentUpgradeTime("00:00", "24:00"); 68 | 69 | // Initialize preset app array. 70 | String presetStr = AppUtils.getProperty("ro.baidu.presetapp", ""); 71 | List presetList = Arrays.asList(presetStr.split(",")); 72 | otaSdkHelper.presetAppNames(presetList); 73 | 74 | for(String app : presetList) { 75 | Log.i(TAG, "initService, preset app: " + app); 76 | } 77 | 78 | // Read the id and secret of different products from the property. 79 | sProductId = AppUtils.getProperty("ro.baidu.product.id", sProductId); 80 | sProductSecret = AppUtils.getProperty("ro.baidu.product.secret", sProductSecret); 81 | 82 | sOtaAgent = otaSdkHelper.getInst(sProductId, sProductSecret); 83 | 84 | Log.i(TAG, "initService, product id: " + sProductId 85 | + " device id: " + AppUtils.getDeviceId()); 86 | } 87 | 88 | public static IOtaAgent getOtaAgent() { 89 | return sOtaAgent; 90 | } 91 | 92 | public static String getProductId() { 93 | return sProductId; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/adapter/DownloadAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.adapter; 18 | 19 | import android.annotation.SuppressLint; 20 | import android.content.Context; 21 | import android.view.LayoutInflater; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.widget.BaseAdapter; 25 | import android.widget.ProgressBar; 26 | import android.widget.TextView; 27 | 28 | import com.ayst.romupgrade.R; 29 | import com.ayst.romupgrade.entity.InstallProgress; 30 | 31 | import java.util.List; 32 | 33 | public class DownloadAdapter extends BaseAdapter { 34 | private LayoutInflater mInflater = null; 35 | private List mData = null; 36 | 37 | public DownloadAdapter(Context context, List data) { 38 | mInflater = LayoutInflater.from(context); 39 | mData = data; 40 | } 41 | 42 | @Override 43 | public int getCount() { 44 | return mData.size(); 45 | } 46 | 47 | @Override 48 | public Object getItem(int position) { 49 | if (position < mData.size()) { 50 | return mData.get(position); 51 | } 52 | return null; 53 | } 54 | 55 | @Override 56 | public long getItemId(int position) { 57 | return 0; 58 | } 59 | 60 | @SuppressLint("SetTextI18n") 61 | @Override 62 | public View getView(int position, View view, ViewGroup parent) { 63 | ViewHolder holder; 64 | if (view == null) { 65 | view = mInflater.inflate(R.layout.layout_download_item, null); 66 | holder = new ViewHolder(); 67 | holder.mTitleTv = (TextView) view.findViewById(R.id.title); 68 | holder.mDownloadPgr = (ProgressBar) view.findViewById(R.id.progress); 69 | view.setTag(holder); 70 | } else { 71 | holder = (ViewHolder) view.getTag(); 72 | } 73 | 74 | holder.mTitleTv.setText(mData.get(position).getInfo().getPackageName() + 75 | " (" + mData.get(position).getInfo().getVersion() + ")"); 76 | holder.mDownloadPgr.setProgress(mData.get(position).getProgress()); 77 | 78 | return view; 79 | } 80 | 81 | public void update(List data) { 82 | mData = data; 83 | notifyDataSetChanged(); 84 | } 85 | 86 | private final class ViewHolder { 87 | private TextView mTitleTv = null; 88 | private ProgressBar mDownloadPgr = null; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/baidu/ErrorString.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.baidu; 18 | 19 | import android.annotation.SuppressLint; 20 | import android.content.Context; 21 | 22 | import com.ayst.romupgrade.R; 23 | import com.baidu.commonlib.interfaces.ErrorCode; 24 | 25 | import java.util.HashMap; 26 | 27 | public class ErrorString { 28 | @SuppressLint("UseSparseArrays") 29 | private static HashMap sErrStrMap = new HashMap<>(); 30 | 31 | static { 32 | // checkUpdate回调错误码 33 | sErrStrMap.put(ErrorCode.CHECK_PARAM_ERROR, R.string.check_param_error); 34 | sErrStrMap.put(ErrorCode.CHECK_NET_ERROR, R.string.check_net_error); 35 | sErrStrMap.put(ErrorCode.CHECK_NO_UPDATE, R.string.check_no_update); 36 | 37 | // download回调错误码 38 | sErrStrMap.put(ErrorCode.DOWNLOAD_PARAM_ERROR, R.string.download_param_error); 39 | sErrStrMap.put(ErrorCode.DOWNLOAD_NET_ERROR, R.string.download_net_error); 40 | sErrStrMap.put(ErrorCode.DOWNLOAD_NO_SPACE, R.string.download_no_space); 41 | sErrStrMap.put(ErrorCode.DOWNLOAD_APP_INSTALLED_ERROR, R.string.download_app_installed_error); 42 | sErrStrMap.put(ErrorCode.DOWNLOAD_APP_NOT_INSTALL_ERROR, R.string.download_app_not_install_error); 43 | sErrStrMap.put(ErrorCode.DOWNLOAD_LOW_VERSION_ERROR, R.string.download_low_version_error); 44 | sErrStrMap.put(ErrorCode.DOWNLOAD_SHA1_VERIFY_ERROR, R.string.download_sha1_verify_error); 45 | 46 | // upgrade回调错误码 47 | sErrStrMap.put(ErrorCode.UPGRADE_PARAM_ERROR, R.string.upgrade_param_error); 48 | sErrStrMap.put(ErrorCode.UPGRADE_SHA1_VERIFY_ERROR, R.string.upgrade_sha1_verify_error); 49 | sErrStrMap.put(ErrorCode.UPGRADE_SIGN_VERIFY_ERROR, R.string.upgrade_sign_verify_error); 50 | sErrStrMap.put(ErrorCode.UPGRADE_FILE_NOT_EXIST_ERROR, R.string.upgrade_file_not_exist_error); 51 | sErrStrMap.put(ErrorCode.UPGRADE_NOT_INSTALLED_ERROR, R.string.upgrade_not_installed_error); 52 | sErrStrMap.put(ErrorCode.UPGRADE_INSTALL_ERROR, R.string.upgrade_install_error); 53 | sErrStrMap.put(ErrorCode.UPGRADE_UNINSTALL_ERROR, R.string.upgrade_uninstall_error); 54 | sErrStrMap.put(ErrorCode.UPGRADE_CUSTOM_ERROR, R.string.upgrade_custom_error); 55 | sErrStrMap.put(ErrorCode.UPGRADE_USER_NOT_CONFIRM, R.string.upgrade_user_not_confirm); 56 | } 57 | 58 | public static String get(Context context, int errorCode) { 59 | Integer strId = sErrStrMap.get(errorCode); 60 | if (strId != null) { 61 | return context.getString(strId); 62 | } 63 | return ""; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/baidu/NewVersionBean.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.baidu; 18 | 19 | import android.text.TextUtils; 20 | 21 | import com.google.gson.annotations.SerializedName; 22 | 23 | import org.json.JSONException; 24 | import org.json.JSONObject; 25 | 26 | import java.io.Serializable; 27 | 28 | public class NewVersionBean implements Serializable { 29 | 30 | /** 31 | * 安装完成后动作 32 | */ 33 | public static final int AFTER_NONE = 0; // 什么也不做 34 | public static final int AFTER_REBOOT = 1; // 安装后重启 35 | public static final int AFTER_START_APP = 2; // 安装后启动APP 36 | 37 | /** 38 | * package : com.android.system 39 | * version : 1.0.51.0 40 | * info : infos 41 | * taskid : 60900 42 | * detail : {"after":"2"} 43 | * filesize : 514044607 44 | * updtype : 1 45 | * silent:true 46 | */ 47 | 48 | @SerializedName("package") 49 | private String packageName; 50 | @SerializedName("version") 51 | private String version; 52 | @SerializedName("info") 53 | private String info; 54 | @SerializedName("taskid") 55 | private String taskId; 56 | @SerializedName("detail") 57 | private String detail; 58 | @SerializedName("filesize") 59 | private int fileSize; 60 | @SerializedName("updtype") 61 | private int updateType; 62 | @SerializedName("silent") 63 | private boolean silent; 64 | 65 | public String getPackageName() { 66 | return packageName; 67 | } 68 | 69 | public void setPackageName(String packageName) { 70 | this.packageName = packageName; 71 | } 72 | 73 | public String getVersion() { 74 | return version; 75 | } 76 | 77 | public void setVersion(String version) { 78 | this.version = version; 79 | } 80 | 81 | public String getInfo() { 82 | return info; 83 | } 84 | 85 | public void setInfo(String info) { 86 | this.info = info; 87 | } 88 | 89 | public String getTaskId() { 90 | return taskId; 91 | } 92 | 93 | public void setTaskId(String taskId) { 94 | this.taskId = taskId; 95 | } 96 | 97 | public String getDetail() { 98 | return detail; 99 | } 100 | 101 | public void setDetail(String detail) { 102 | this.detail = detail; 103 | } 104 | 105 | public int getFileSize() { 106 | return fileSize; 107 | } 108 | 109 | public void setFileSize(int fileSize) { 110 | this.fileSize = fileSize; 111 | } 112 | 113 | public int getUpdateType() { 114 | return updateType; 115 | } 116 | 117 | public void setUpdateType(int updateType) { 118 | this.updateType = updateType; 119 | } 120 | 121 | public boolean isSilent() { 122 | return silent; 123 | } 124 | 125 | public void setSilent(boolean silent) { 126 | this.silent = silent; 127 | } 128 | 129 | public int getAfter() { 130 | if (!TextUtils.isEmpty(detail)) { 131 | try { 132 | JSONObject detailObject = new JSONObject(detail); 133 | return detailObject.getInt("after"); 134 | } catch (JSONException e) { 135 | e.printStackTrace(); 136 | } 137 | } 138 | return AFTER_NONE; 139 | } 140 | 141 | @Override 142 | public String toString() { 143 | return "NewVersionBean{" + 144 | "packageName='" + packageName + '\'' + 145 | ", version='" + version + '\'' + 146 | ", info='" + info + '\'' + 147 | ", taskId='" + taskId + '\'' + 148 | ", detail='" + detail + '\'' + 149 | ", fileSize=" + fileSize + 150 | ", updateType=" + updateType + 151 | ", silent=" + silent + 152 | '}'; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/baidu/SystemInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.baidu; 18 | 19 | import android.os.Build; 20 | 21 | import com.baidu.commonlib.interfaces.ISystemInfo; 22 | import com.ayst.romupgrade.util.AppUtils; 23 | 24 | public class SystemInfo implements ISystemInfo { 25 | @Override 26 | public String getVersion() { 27 | return AppUtils.getFwVersion(); 28 | } 29 | 30 | @Override 31 | public String getModel() { 32 | return Build.MODEL; 33 | } 34 | 35 | @Override 36 | public String getCPU() { 37 | return Build.BOARD; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/config/UsbConfigManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.config; 18 | 19 | import android.text.TextUtils; 20 | import android.util.Log; 21 | 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.InputStream; 25 | import java.util.Properties; 26 | 27 | /** 28 | * Parse the config.ini configuration file in the U disk 29 | * Created by ayst.shen@foxmail.com on 2018/5/21. 30 | */ 31 | public class UsbConfigManager { 32 | private final static String TAG = "UsbConfigManager"; 33 | 34 | /** 35 | * 升级类型,1:推荐升级,2:静默升级 36 | */ 37 | public final static String UPDATE_TYPE = "UPDATE_TYPE"; 38 | 39 | /** 40 | * OTA升级包版本号,如:1.0.0 41 | */ 42 | public final static String PACKAGE_VERSION = "PACKAGE_VERSION"; 43 | 44 | private File mConfigFile; 45 | private Properties mCfgProperties = null; 46 | private int mUpdateType = -1; 47 | private String mPackageVersion = ""; 48 | 49 | public UsbConfigManager(File configFile) { 50 | mConfigFile = configFile; 51 | if (mConfigFile.exists()) { 52 | getProperties(); 53 | } 54 | } 55 | 56 | private Properties getProperties() { 57 | if (mCfgProperties != null) { 58 | return mCfgProperties; 59 | } 60 | mCfgProperties = new Properties(); 61 | try { 62 | InputStream is = new FileInputStream(mConfigFile); 63 | mCfgProperties.load(is); 64 | } catch (Exception e) { 65 | e.printStackTrace(); 66 | } 67 | return mCfgProperties; 68 | } 69 | 70 | /** 71 | * 获取升级类型 72 | * 73 | * @return 1:推荐升级,2:静默升级 74 | */ 75 | public int getUpdateType() { 76 | if (-1 == mUpdateType) { 77 | Properties properties = getProperties(); 78 | String updateTypeStr = properties.getProperty(UPDATE_TYPE, "1"); 79 | try { 80 | mUpdateType = Integer.parseInt(updateTypeStr); 81 | } catch (NumberFormatException e) { 82 | e.printStackTrace(); 83 | } 84 | Log.i(TAG, "getUpdateType, value:" + mUpdateType); 85 | } 86 | 87 | return mUpdateType; 88 | } 89 | 90 | /** 91 | * 获取升级包版本号 92 | * 93 | * @return 版本号 94 | */ 95 | public String getPackageVersion() { 96 | if (TextUtils.isEmpty(mPackageVersion)) { 97 | Properties properties = getProperties(); 98 | mPackageVersion = properties.getProperty(PACKAGE_VERSION, "1.0.0"); 99 | Log.i(TAG, "getPackageVersion, value:" + mPackageVersion); 100 | } 101 | 102 | return mPackageVersion; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/entity/InstallProgress.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.entity; 18 | 19 | import com.ayst.romupgrade.baidu.NewVersionBean; 20 | 21 | public class InstallProgress { 22 | private NewVersionBean info; 23 | private int progress; 24 | private boolean isDownloaded; 25 | private boolean isInstalled; 26 | private boolean isAfterExecuted; 27 | 28 | public InstallProgress(NewVersionBean info) { 29 | this.info = info; 30 | this.progress = 0; 31 | this.isDownloaded = false; 32 | this.isInstalled = false; 33 | this.isAfterExecuted = false; 34 | } 35 | 36 | public NewVersionBean getInfo() { 37 | return info; 38 | } 39 | 40 | public void setInfo(NewVersionBean info) { 41 | this.info = info; 42 | } 43 | 44 | public int getProgress() { 45 | return progress; 46 | } 47 | 48 | public void setProgress(int progress) { 49 | this.progress = progress; 50 | } 51 | 52 | public boolean isDownloaded() { 53 | return isDownloaded; 54 | } 55 | 56 | public void setDownloaded(boolean downloaded) { 57 | isDownloaded = downloaded; 58 | } 59 | 60 | public boolean isInstalled() { 61 | return isInstalled; 62 | } 63 | 64 | public void setInstalled(boolean installed) { 65 | isInstalled = installed; 66 | } 67 | 68 | public boolean isAfterExecuted() { 69 | return isAfterExecuted; 70 | } 71 | 72 | public void setAfterExecuted(boolean afterExecuted) { 73 | isAfterExecuted = afterExecuted; 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return "InstallProgress{" + 79 | "info=" + info.toString() + 80 | ", progress=" + progress + 81 | ", isDownloaded=" + isDownloaded + 82 | ", isInstalled=" + isInstalled + 83 | ", isAfterExecuted=" + isAfterExecuted + 84 | '}'; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/entity/LocalPackage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.entity; 18 | 19 | import java.io.File; 20 | 21 | public class LocalPackage { 22 | public static final int TYPE_APP = 0; 23 | public static final int TYPE_ROM = 1; 24 | 25 | private int type; 26 | private File file; 27 | 28 | public LocalPackage(int type, File file) { 29 | this.type = type; 30 | this.file = file; 31 | } 32 | 33 | public int getType() { 34 | return type; 35 | } 36 | 37 | public void setType(int type) { 38 | this.type = type; 39 | } 40 | 41 | public File getFile() { 42 | return file; 43 | } 44 | 45 | public void setFile(File file) { 46 | this.file = file; 47 | } 48 | 49 | @Override 50 | public String toString() { 51 | return "LocalPackage{" + 52 | "type=" + type + 53 | ", file=" + file.getAbsolutePath() + 54 | '}'; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/receiver/UpdateReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.receiver; 18 | 19 | import android.content.Context; 20 | import android.content.Intent; 21 | import android.content.BroadcastReceiver; 22 | import android.os.Bundle; 23 | import android.text.TextUtils; 24 | import android.util.Log; 25 | 26 | import com.baidu.otasdk.ota.Constants; 27 | import com.ayst.romupgrade.App; 28 | import com.ayst.romupgrade.service.UpdateService; 29 | 30 | /** 31 | * Listen to the broadcast, start the UpdateService 32 | * to perform the upgrade action. 33 | *

34 | * Intent.ACTION_BOOT_COMPLETED 35 | * ConnectivityManager.CONNECTIVITY_ACTION 36 | * Intent.ACTION_MEDIA_MOUNTED 37 | * UsbManager.ACTION_USB_STATE 38 | * VolumeInfo.ACTION_VOLUME_STATE_CHANGED 39 | * Constants.BROADCAST_NEWVERSION 40 | *

41 | * Created by ayst.shen@foxmail.com on 2018/11/6. 42 | */ 43 | public class UpdateReceiver extends BroadcastReceiver { 44 | private static final String TAG = "UpdateReceiver"; 45 | 46 | private static final String ACTION_START = "com.ayst.romupgrade.action.START"; 47 | 48 | private static int sVolumeState = -1; 49 | 50 | @Override 51 | public void onReceive(Context context, Intent intent) { 52 | String action = intent.getAction(); 53 | Log.i(TAG, "onReceive, action = " + action); 54 | 55 | if (TextUtils.equals(Intent.ACTION_BOOT_COMPLETED, action) 56 | || TextUtils.equals(ACTION_START, action)) { 57 | /* 58 | Check local and remote upgrade after power-on. 59 | */ 60 | Log.i(TAG, "onReceive, Boot completed. To check local and remote update."); 61 | context.startService(buildIntent(context, 62 | UpdateService.COMMAND_INITIAL, 63 | 5000)); 64 | 65 | context.startService(buildIntent(context, 66 | UpdateService.COMMAND_CHECK_LOCAL_UPDATE, 67 | 10000)); 68 | 69 | // context.startService(buildIntent(context, 70 | // UpdateService.COMMAND_CHECK_REMOTE_UPDATE, 71 | // 25000)); 72 | 73 | } else if (TextUtils.equals(Intent.ACTION_MEDIA_MOUNTED, action)) { 74 | /* 75 | U-disk insert check local upgrade. 76 | */ 77 | Log.i(TAG, "onReceive, Media is mounted. To check local update."); 78 | context.startService(buildIntent(context, 79 | UpdateService.COMMAND_CHECK_LOCAL_UPDATE, 80 | 5000)); 81 | 82 | } else if (TextUtils.equals(UsbManager.ACTION_USB_STATE, action)) { 83 | /* 84 | U-disk insert check local upgrade. 85 | */ 86 | Bundle extras = intent.getExtras(); 87 | if (null != extras) { 88 | boolean connected = extras.getBoolean(UsbManager.USB_CONNECTED); 89 | boolean configured = extras.getBoolean(UsbManager.USB_CONFIGURED); 90 | boolean mtpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_MTP); 91 | boolean ptpEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_PTP); 92 | 93 | if (!connected && mtpEnabled && !configured) { 94 | Log.i(TAG, "onReceive, mtp is enabled. To check local update."); 95 | context.startService(buildIntent(context, 96 | UpdateService.COMMAND_CHECK_LOCAL_UPDATE, 97 | 5000)); 98 | } 99 | } 100 | 101 | } else if (TextUtils.equals(VolumeInfo.ACTION_VOLUME_STATE_CHANGED, action)) { 102 | /* 103 | U-disk insert check local upgrade. 104 | */ 105 | int state = intent.getIntExtra(VolumeInfo.EXTRA_VOLUME_STATE, VolumeInfo.STATE_UNMOUNTED); 106 | if (sVolumeState == VolumeInfo.STATE_UNMOUNTED && state == VolumeInfo.STATE_MOUNTED) { 107 | Log.i(TAG, "onReceive, Volume is mounted. To check local update."); 108 | context.startService(buildIntent(context, 109 | UpdateService.COMMAND_CHECK_LOCAL_UPDATE, 110 | 5000)); 111 | } 112 | sVolumeState = state; 113 | 114 | } else if (TextUtils.equals(Constants.BROADCAST_NEWVERSION, action)) { 115 | /* 116 | Baidu otasdk automatically checks for upgrade notifications. 117 | */ 118 | String pid = intent.getStringExtra(Constants.BROADCAST_KEY_PID); 119 | if (TextUtils.equals(App.getProductId(), pid)) { 120 | String infos = intent.getStringExtra(Constants.BROADCAST_KEY_INFOS); 121 | 122 | Log.i(TAG, "onReceive, new version infos=" + infos); 123 | if (!TextUtils.isEmpty(infos)) { 124 | Intent serviceIntent = buildIntent(context, UpdateService.COMMAND_NEW_VERSION, 0); 125 | Bundle bundle = new Bundle(); 126 | bundle.putString("infos", infos); 127 | serviceIntent.putExtra("bundle", bundle); 128 | context.startService(serviceIntent); 129 | } 130 | } 131 | } 132 | } 133 | 134 | private Intent buildIntent(Context context, int command, int delay) { 135 | Intent intent = new Intent(context, UpdateService.class); 136 | intent.putExtra("command", command); 137 | intent.putExtra("delay", delay); 138 | return intent; 139 | } 140 | 141 | public class VolumeInfo { 142 | /** 143 | * Unmounted 144 | */ 145 | public static final int STATE_UNMOUNTED = 0; 146 | 147 | /** 148 | * Checking 149 | */ 150 | public static final int STATE_CHECKING = 1; 151 | 152 | /** 153 | * Mounted 154 | */ 155 | public static final int STATE_MOUNTED = 2; 156 | 157 | /** 158 | * Read only 159 | */ 160 | public static final int STATE_MOUNTED_READ_ONLY = 3; 161 | 162 | /** 163 | * Formatting 164 | */ 165 | public static final int STATE_FORMATTING = 4; 166 | 167 | /** 168 | * Ejecting 169 | */ 170 | public static final int STATE_EJECTING = 5; 171 | /** 172 | * Not mountable 173 | */ 174 | public static final int STATE_UNMOUNTABLE = 6; 175 | 176 | /** 177 | * Removed 178 | */ 179 | public static final int STATE_REMOVED = 7; 180 | 181 | /** 182 | * Remove fail 183 | */ 184 | public static final int STATE_BAD_REMOVAL = 8; 185 | 186 | /** 187 | * Volume state changed broadcast action. 188 | */ 189 | public static final String ACTION_VOLUME_STATE_CHANGED = 190 | "android.os.storage.action.VOLUME_STATE_CHANGED"; 191 | 192 | public static final String EXTRA_VOLUME_ID = 193 | "android.os.storage.extra.VOLUME_ID"; 194 | 195 | public static final String EXTRA_VOLUME_STATE = 196 | "android.os.storage.extra.VOLUME_STATE"; 197 | } 198 | 199 | public class UsbManager { 200 | /** 201 | * Broadcast Action: A sticky broadcast for USB state change events when in device mode. 202 | */ 203 | public static final String ACTION_USB_STATE = 204 | "android.hardware.usb.action.USB_STATE"; 205 | 206 | /** 207 | * Boolean extra indicating whether USB is connected or disconnected. 208 | * Used in extras for the {@link #ACTION_USB_STATE} broadcast. 209 | *

210 | * {@hide} 211 | */ 212 | public static final String USB_CONNECTED = "connected"; 213 | 214 | /** 215 | * Boolean extra indicating whether USB is configured. 216 | * Used in extras for the {@link #ACTION_USB_STATE} broadcast. 217 | */ 218 | public static final String USB_CONFIGURED = "configured"; 219 | 220 | /** 221 | * Name of the MTP USB function. 222 | * Used in extras for the {@link #ACTION_USB_STATE} broadcast 223 | */ 224 | public static final String USB_FUNCTION_MTP = "mtp"; 225 | 226 | /** 227 | * Name of the PTP USB function. 228 | * Used in extras for the {@link #ACTION_USB_STATE} broadcast 229 | */ 230 | public static final String USB_FUNCTION_PTP = "ptp"; 231 | } 232 | } 233 | 234 | 235 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/AppUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | import android.annotation.SuppressLint; 20 | import android.app.Activity; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.content.pm.PackageInfo; 24 | import android.content.pm.PackageManager; 25 | import android.net.ConnectivityManager; 26 | import android.net.NetworkInfo; 27 | import android.net.wifi.WifiInfo; 28 | import android.net.wifi.WifiManager; 29 | import android.os.Build; 30 | import android.os.Environment; 31 | import android.os.storage.StorageManager; 32 | import android.os.storage.StorageVolume; 33 | import android.text.TextUtils; 34 | import android.util.Log; 35 | 36 | import androidx.annotation.NonNull; 37 | import androidx.annotation.RequiresApi; 38 | 39 | import java.io.BufferedInputStream; 40 | import java.io.BufferedReader; 41 | import java.io.File; 42 | import java.io.FileReader; 43 | import java.io.IOException; 44 | import java.io.InputStreamReader; 45 | import java.io.LineNumberReader; 46 | import java.lang.reflect.Method; 47 | import java.util.ArrayList; 48 | import java.util.List; 49 | import java.util.Locale; 50 | 51 | /** 52 | * Created by ayst.shen@foxmail.com on 2016/4/6. 53 | */ 54 | public class AppUtils { 55 | private final static String TAG = "AppUtils"; 56 | 57 | private static final String KEY_IS_FIRST = "is_first_run"; 58 | 59 | // Application version 60 | private static String mVersionName = ""; 61 | private static int mVersionCode = -1; 62 | 63 | // Firmware version 64 | private static String mFwVersion = ""; 65 | 66 | // MAC 67 | private static String mEth0Mac = ""; 68 | private static String mWifiMac = ""; 69 | private static String mMac = ""; 70 | private static String mMacNoColon = ""; 71 | 72 | // Screen 73 | private static int mScreenWidth = -1; 74 | private static int mScreenHeight = -1; 75 | 76 | // Storage 77 | private static String sRootDir = ""; 78 | 79 | // Device id 80 | private static String sDeviceId = ""; 81 | 82 | /** 83 | * Is first run 84 | * 85 | * @param context Context 86 | * @return true: First run, false: Not the first time 87 | */ 88 | public static boolean isFirstRun(Context context) { 89 | boolean isFirst = SPUtils.getInstance(context).getData(KEY_IS_FIRST, true); 90 | if (isFirst) { 91 | SPUtils.getInstance(context).saveData(KEY_IS_FIRST, false); 92 | } 93 | return isFirst; 94 | } 95 | 96 | /** 97 | * Get application version name 98 | * 99 | * @param context Context 100 | * @return version name 101 | */ 102 | public static String getVersionName(Context context) { 103 | if (TextUtils.isEmpty(mVersionName)) { 104 | try { 105 | PackageInfo info = context.getPackageManager().getPackageInfo( 106 | context.getPackageName(), 0); 107 | mVersionName = info.versionName; 108 | mVersionCode = info.versionCode; 109 | } catch (PackageManager.NameNotFoundException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | return mVersionName; 114 | } 115 | 116 | /** 117 | * Get application version code 118 | * 119 | * @param context Context 120 | * @return version code 121 | */ 122 | public static int getVersionCode(Context context) { 123 | if (-1 == mVersionCode) { 124 | try { 125 | PackageInfo info = context.getPackageManager().getPackageInfo( 126 | context.getPackageName(), 0); 127 | mVersionName = info.versionName; 128 | mVersionCode = info.versionCode; 129 | } catch (PackageManager.NameNotFoundException e) { 130 | e.printStackTrace(); 131 | } 132 | } 133 | return mVersionCode; 134 | } 135 | 136 | /** 137 | * Get firmware version 138 | * 139 | * @return version 140 | */ 141 | public static String getFwVersion() { 142 | if (TextUtils.isEmpty(mFwVersion)) { 143 | mFwVersion = getProperty("ro.fw.version", 144 | getProperty("ro.topband.sw.version", "1.0.0")); 145 | } 146 | return mFwVersion; 147 | } 148 | 149 | /** 150 | * Get serial number 151 | * 152 | * @return serial number 153 | */ 154 | @SuppressLint("HardwareIds") 155 | public static String getSerialNo() { 156 | String sn = android.os.Build.SERIAL; 157 | if (TextUtils.isEmpty(sn)) { 158 | sn = getProperty("ro.serialno", ""); 159 | if (TextUtils.isEmpty(sn)) { 160 | sn = getProperty("ro.boot.serialno", ""); 161 | if (TextUtils.isEmpty(sn)) { 162 | sn = getCPUSerial(); 163 | } 164 | } 165 | } 166 | 167 | return sn; 168 | } 169 | 170 | /** 171 | * Get cpu serial 172 | * 173 | * @return success: cpu serial, failed: "0000000000000000" 174 | */ 175 | public static String getCPUSerial() { 176 | String cpuAddress = "0000000000000000"; 177 | 178 | try { 179 | Process process = Runtime.getRuntime().exec("cat /proc/cpuinfo"); 180 | InputStreamReader is = new InputStreamReader(process.getInputStream()); 181 | LineNumberReader input = new LineNumberReader(is); 182 | 183 | String str; 184 | while ((str = input.readLine()) != null) { 185 | if (!TextUtils.isEmpty(str)) { 186 | if (str.contains("Serial")) { 187 | String cpuStr = str.substring(str.indexOf(":") + 1); 188 | cpuAddress = cpuStr.trim(); 189 | break; 190 | } 191 | } 192 | } 193 | } catch (IOException e) { 194 | Log.e(TAG, "getCPUSerial, " + e.getMessage()); 195 | } 196 | 197 | return cpuAddress; 198 | } 199 | 200 | /** 201 | * Get device id 202 | * 203 | * @return device id 204 | */ 205 | public static String getDeviceId() { 206 | if (TextUtils.isEmpty(sDeviceId)) { 207 | sDeviceId = getSerialNo(); 208 | } 209 | 210 | return sDeviceId; 211 | } 212 | 213 | /** 214 | * Get current country 215 | * 216 | * @return country 217 | */ 218 | public static String getCountry() { 219 | return Locale.getDefault().getCountry(); 220 | } 221 | 222 | /** 223 | * Get current language 224 | * 225 | * @return language 226 | */ 227 | public static String getLanguage() { 228 | return Locale.getDefault().getLanguage(); 229 | } 230 | 231 | /** 232 | * Whether the network is connected 233 | * 234 | * @param context Context 235 | * @return true: connected, false: disconnected 236 | */ 237 | public static boolean isConnNetWork(Context context) { 238 | ConnectivityManager conManager = (ConnectivityManager) context. 239 | getSystemService(Context.CONNECTIVITY_SERVICE); 240 | NetworkInfo networkInfo = conManager.getActiveNetworkInfo(); 241 | return ((networkInfo != null) && networkInfo.isConnected()); 242 | } 243 | 244 | /** 245 | * Whether WiFi is connected 246 | * 247 | * @param context Context 248 | * @return true: connected, false: disconnected 249 | */ 250 | public static boolean isWifiConnected(Context context) { 251 | ConnectivityManager conManager = (ConnectivityManager) context. 252 | getSystemService(Context.CONNECTIVITY_SERVICE); 253 | NetworkInfo wifiNetworkInfo = conManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 254 | return ((wifiNetworkInfo != null) && wifiNetworkInfo.isConnected()); 255 | } 256 | 257 | /** 258 | * Get Ethernet MAC 259 | * 260 | * @param context Context 261 | * @return Mac 262 | */ 263 | public static String getEth0Mac(Context context) { 264 | if (TextUtils.isEmpty(mEth0Mac)) { 265 | try { 266 | int numRead = 0; 267 | char[] buf = new char[1024]; 268 | StringBuffer strBuf = new StringBuffer(1000); 269 | BufferedReader reader = new BufferedReader(new FileReader( 270 | "/sys/class/net/eth0/address")); 271 | while ((numRead = reader.read(buf)) != -1) { 272 | String readData = String.valueOf(buf, 0, numRead); 273 | strBuf.append(readData); 274 | } 275 | mEth0Mac = strBuf.toString().replaceAll("\r|\n", ""); 276 | reader.close(); 277 | } catch (IOException ex) { 278 | Log.w(TAG, "eth0 mac not exist"); 279 | } 280 | } 281 | return mEth0Mac; 282 | } 283 | 284 | /** 285 | * Get WiFi MAC 286 | * 287 | * @param context Context 288 | * @return Mac 289 | */ 290 | @SuppressLint("HardwareIds") 291 | public static String getWifiMac(Context context) { 292 | if (TextUtils.isEmpty(mWifiMac)) { 293 | WifiManager wifiManager = (WifiManager) context.getApplicationContext() 294 | .getSystemService(Context.WIFI_SERVICE); 295 | WifiInfo wifiInfo = wifiManager.getConnectionInfo(); 296 | mWifiMac = wifiInfo.getMacAddress(); 297 | } 298 | return mWifiMac; 299 | } 300 | 301 | /** 302 | * Get MAC, get the Ethernet MAC first, then get the WiFi MAC if it is empty. 303 | * 304 | * @param context Context 305 | * @return Mac 306 | */ 307 | public static String getMac(Context context) { 308 | if (TextUtils.isEmpty(mMac)) { 309 | mMac = getEth0Mac(context); 310 | if (TextUtils.isEmpty(mMac)) { 311 | mMac = getWifiMac(context); 312 | } 313 | } 314 | return mMac; 315 | } 316 | 317 | /** 318 | * Get the MAC with the colon removed 319 | * 320 | * @param context Context 321 | * @return Mac 322 | */ 323 | public static String getMacNoColon(Context context) { 324 | if (TextUtils.isEmpty(mMacNoColon)) { 325 | String mac = getMac(context); 326 | if (!TextUtils.isEmpty(mac)) { 327 | mMacNoColon = mac.replace(":", ""); 328 | } 329 | } 330 | return mMacNoColon; 331 | } 332 | 333 | /** 334 | * Get screen width 335 | * 336 | * @param context Activity 337 | * @return screen width 338 | */ 339 | public static int getScreenWidth(Activity context) { 340 | if (-1 == mScreenWidth) { 341 | mScreenWidth = context.getWindowManager().getDefaultDisplay().getWidth(); 342 | } 343 | return mScreenWidth; 344 | } 345 | 346 | /** 347 | * Get screen height 348 | * 349 | * @param context Activity 350 | * @return screen height 351 | */ 352 | public static int getScreenHeight(Activity context) { 353 | if (-1 == mScreenHeight) { 354 | mScreenHeight = context.getWindowManager().getDefaultDisplay().getHeight(); 355 | } 356 | return mScreenHeight; 357 | } 358 | 359 | /** 360 | * Get property 361 | * 362 | * @param key property key 363 | * @param defaultValue default value 364 | * @return property value 365 | */ 366 | @SuppressLint("PrivateApi") 367 | public static String getProperty(String key, String defaultValue) { 368 | String value = defaultValue; 369 | try { 370 | Class c = Class.forName("android.os.SystemProperties"); 371 | Method get = c.getMethod("get", String.class, String.class); 372 | value = (String) (get.invoke(c, key, defaultValue)); 373 | } catch (Exception e) { 374 | e.printStackTrace(); 375 | } 376 | 377 | return value; 378 | } 379 | 380 | /** 381 | * Set property 382 | * 383 | * @param key property key 384 | * @param value property value 385 | */ 386 | @SuppressLint("PrivateApi") 387 | public static void setProperty(String key, String value) { 388 | try { 389 | Class c = Class.forName("android.os.SystemProperties"); 390 | Method set = c.getMethod("set", String.class, String.class); 391 | set.invoke(c, key, value); 392 | } catch (Exception e) { 393 | e.printStackTrace(); 394 | } 395 | } 396 | 397 | private static boolean isExternalStorageMounted() { 398 | return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); 399 | } 400 | 401 | /** 402 | * /storage/emulated/0/"packagename" 403 | * 404 | * @param context Context 405 | * @return path 406 | */ 407 | public static String getExternalRootDir(Context context) { 408 | if (sRootDir.isEmpty()) { 409 | File sdcardDir = null; 410 | try { 411 | if (isExternalStorageMounted()) { 412 | sdcardDir = Environment.getExternalStorageDirectory(); 413 | Log.i(TAG, "Environment.MEDIA_MOUNTED :" + sdcardDir.getAbsolutePath() 414 | + " R:" + sdcardDir.canRead() + " W:" + sdcardDir.canWrite()); 415 | 416 | if (sdcardDir.canWrite()) { 417 | String dir = sdcardDir.getAbsolutePath() + File.separator + context.getPackageName(); 418 | File file = new File(dir); 419 | if (!file.exists()) { 420 | Log.i(TAG, "getExternalRootDir, dir not exist and make dir"); 421 | file.mkdirs(); 422 | } 423 | sRootDir = dir; 424 | return sRootDir; 425 | } 426 | } 427 | } catch (Exception e) { 428 | e.printStackTrace(); 429 | } 430 | } 431 | return sRootDir; 432 | } 433 | 434 | /** 435 | * /storage/emulated/0/"packagename"/"dirName" 436 | * 437 | * @param context Context 438 | * @param dirName relative path 439 | * @return full path 440 | */ 441 | public static String getExternalDir(Context context, String dirName) { 442 | String dir = getExternalRootDir(context) + File.separator + dirName; 443 | File file = new File(dir); 444 | if (!file.exists()) { 445 | Log.i(TAG, "getDir, dir not exist and make dir"); 446 | file.mkdirs(); 447 | } 448 | return dir; 449 | } 450 | 451 | /** 452 | * /storage/emulated/0/Android/data/"packagename"/cache/"dirName" 453 | * 454 | * @param context Context 455 | * @param dirName relative path 456 | * @return full path 457 | */ 458 | public static String getExternalCacheDir(Context context, String dirName) { 459 | String dir = ""; 460 | if (isExternalStorageMounted()) { 461 | dir = context.getExternalCacheDir().getAbsolutePath() + File.separator + dirName; 462 | File file = new File(dir); 463 | if (!file.exists()) { 464 | Log.i(TAG, "getExternalCacheDir, dir not exist and make dir"); 465 | file.mkdirs(); 466 | } 467 | } 468 | return dir; 469 | } 470 | 471 | /** 472 | * /data/user/0/"packagename"/cache/"dirName" 473 | * 474 | * @param context Context 475 | * @param dirName relative path 476 | * @return full path 477 | */ 478 | public static String getCacheDir(Context context, String dirName) { 479 | String dir = context.getCacheDir().getAbsolutePath() + File.separator + dirName; 480 | File file = new File(dir); 481 | if (!file.exists()) { 482 | Log.i(TAG, "getCacheDir, dir not exist and make dir"); 483 | file.mkdirs(); 484 | } 485 | return dir; 486 | } 487 | 488 | /** 489 | * Get all external storage paths 490 | * 491 | * @param context context 492 | * @return storage paths 493 | */ 494 | public static List getStorageList(Context context) { 495 | List paths; 496 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 497 | paths = getStorageVolumeList(context); 498 | } else { 499 | paths = getMountPathList(); 500 | } 501 | 502 | if (paths.isEmpty() && isExternalStorageMounted()) { 503 | paths.add(Environment.getExternalStorageDirectory() 504 | .getAbsolutePath()); 505 | } 506 | return paths; 507 | } 508 | 509 | /** 510 | * Get all external storage paths, for lower than Android N 511 | * 512 | * @return storage paths 513 | */ 514 | private static List getMountPathList() { 515 | List paths = new ArrayList(); 516 | 517 | try { 518 | Process p = Runtime.getRuntime().exec("cat /proc/mounts"); 519 | BufferedInputStream inputStream = new BufferedInputStream(p.getInputStream()); 520 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); 521 | 522 | String line; 523 | while ((line = bufferedReader.readLine()) != null) { 524 | Log.i(TAG, "getMountPathList, " + line); 525 | 526 | // /data/media /storage/emulated/0 sdcardfs rw,nosuid,nodev,relatime,uid=1023,gid=1023 0 0 527 | String[] temp = TextUtils.split(line, " "); 528 | String result = temp[1]; 529 | File file = new File(result); 530 | if (file.isDirectory() && file.canRead() && file.canWrite()) { 531 | Log.d(TAG, "getMountPathList, add --> " + file.getAbsolutePath()); 532 | paths.add(result); 533 | } 534 | 535 | if (p.waitFor() != 0 && p.exitValue() == 1) { 536 | Log.e(TAG, "getMountPathList, cmd execute failed!"); 537 | } 538 | } 539 | bufferedReader.close(); 540 | inputStream.close(); 541 | 542 | } catch (Exception e) { 543 | Log.e(TAG, "getMountPathList, failed, " + e.toString()); 544 | } 545 | 546 | return paths; 547 | } 548 | 549 | /** 550 | * Get all external storage paths, for higher than Android N 551 | * 552 | * @param context context 553 | * @return storage paths 554 | */ 555 | @RequiresApi(api = Build.VERSION_CODES.N) 556 | private static List getStorageVolumeList(Context context) { 557 | List paths = new ArrayList(); 558 | StorageManager storageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); 559 | List volumes = storageManager.getStorageVolumes(); 560 | 561 | try { 562 | Class storageVolumeClazz = Class.forName("android.os.storage.StorageVolume"); 563 | Method getPath = storageVolumeClazz.getMethod("getPath"); 564 | Method isRemovable = storageVolumeClazz.getMethod("isRemovable"); 565 | 566 | for (StorageVolume storageVolume : volumes) { 567 | String storagePath = (String) getPath.invoke(storageVolume); 568 | boolean isRemovableResult = (boolean) isRemovable.invoke(storageVolume); 569 | String description = storageVolume.getDescription(context); 570 | paths.add(storagePath); 571 | 572 | Log.d(TAG, "getStorageVolumeList, storagePath=" + storagePath 573 | + ", isRemovableResult=" + isRemovableResult + ", description=" + description); 574 | } 575 | } catch (Exception e) { 576 | Log.e(TAG, "getStorageVolumeList, failed, " + e); 577 | } 578 | 579 | return paths; 580 | } 581 | 582 | /** 583 | * reboot 584 | * 585 | * @param context Context 586 | */ 587 | public static void reboot(Context context) { 588 | Intent intent = new Intent(Intent.ACTION_REBOOT); 589 | intent.putExtra("nowait", 1); 590 | intent.putExtra("interval", 1); 591 | intent.putExtra("window", 0); 592 | context.sendBroadcast(intent); 593 | } 594 | 595 | /** 596 | * shutdown 597 | * 598 | * @param context Context 599 | */ 600 | public static void shutdown(Context context) { 601 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 602 | Intent intent = new Intent("com.android.internal.intent.action.REQUEST_SHUTDOWN"); 603 | intent.putExtra("android.intent.extra.KEY_CONFIRM", false); 604 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 605 | context.startActivity(intent); 606 | } else { 607 | Intent intent = new Intent("android.intent.action.ACTION_REQUEST_SHUTDOWN"); 608 | intent.putExtra("android.intent.extra.KEY_CONFIRM", false); 609 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 610 | context.startActivity(intent); 611 | } 612 | } 613 | 614 | /** 615 | * start app 616 | * 617 | * @param context Context 618 | * @param packageName PackageName 619 | */ 620 | public static void startApp(@NonNull Context context, @NonNull String packageName) { 621 | Intent intent = context.getPackageManager() 622 | .getLaunchIntentForPackage(packageName); 623 | if (intent != null) { 624 | context.startActivity(intent); 625 | } else { 626 | Log.e(TAG, "startApp, Package does not exist."); 627 | } 628 | } 629 | } 630 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/DataEncryptUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | import android.text.TextUtils; 20 | import android.util.Log; 21 | 22 | import com.google.gson.Gson; 23 | import org.json.JSONObject; 24 | 25 | /** 26 | * Created by ayst.shen@foxmail.com on 2017/12/12. 27 | */ 28 | public class DataEncryptUtil { 29 | private static final String TAG = "DataEncryptUtil"; 30 | private static Gson gson = new Gson(); 31 | 32 | public static String encryptData(T value, String key) { 33 | if (value == null) { 34 | Log.e(TAG, "encryptData, value is null"); 35 | return ""; 36 | } 37 | try { 38 | JSONObject json = new JSONObject(gson.toJson(value)); 39 | Log.d(TAG, "encryptData, request data: " + json.toString()); 40 | return EncryptUtil.encryptAES(key, json.toString()); 41 | } catch (Exception e) { 42 | Log.e(TAG, "encryptData, " + e.getMessage()); 43 | } 44 | return ""; 45 | } 46 | 47 | public static T decryptData(String value, Class tClass, String key) { 48 | if (!TextUtils.isEmpty(value)) { 49 | try { 50 | String decryptString = EncryptUtil.decryptAES(key, value); 51 | if (!TextUtils.isEmpty(decryptString)) { 52 | return gson.fromJson(decryptString, tClass); 53 | } 54 | } catch (Exception e) { 55 | Log.e(TAG, "decryptData, " + e.getMessage()); 56 | } 57 | } 58 | return null; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/EncryptUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | import android.annotation.SuppressLint; 20 | 21 | import java.math.BigInteger; 22 | import java.nio.charset.StandardCharsets; 23 | import java.security.MessageDigest; 24 | import java.security.NoSuchAlgorithmException; 25 | 26 | import javax.crypto.Cipher; 27 | import javax.crypto.spec.SecretKeySpec; 28 | 29 | /** 30 | * Created by ayst.shen@foxmail.com on 17/8/15. 31 | */ 32 | public class EncryptUtil { 33 | private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding"; 34 | private static final String ASE_ALGORITHM = "AES"; 35 | private final static String HEX = "0123456789abcdef"; 36 | 37 | public static String getMD5HexMsg(String data) throws NoSuchAlgorithmException { 38 | MessageDigest md5Digest = MessageDigest.getInstance("MD5"); 39 | md5Digest.update(data.getBytes()); 40 | return new BigInteger(1, md5Digest.digest()).toString(16); 41 | } 42 | 43 | public static String encryptAES(String seed, String cleartext) throws Exception { 44 | byte[] rawKey = seed.getBytes(StandardCharsets.UTF_8); 45 | byte[] result = cleartext.getBytes(StandardCharsets.UTF_8); 46 | return toHex(encodeAES(rawKey, result)); 47 | } 48 | 49 | public static String decryptAES(String seed, String encrypted) throws Exception { 50 | byte[] rawKey = seed.getBytes(StandardCharsets.UTF_8); 51 | byte[] enc = toByte(encrypted); 52 | byte[] result = decodeAES(rawKey, enc); 53 | return new String(result); 54 | } 55 | 56 | @SuppressLint("GetInstance") 57 | public static byte[] encodeAES(byte[] keyBytes, byte[] data) throws Exception { 58 | Cipher encodeCipher = Cipher.getInstance(AES_TRANSFORMATION); 59 | SecretKeySpec key = new SecretKeySpec(keyBytes, ASE_ALGORITHM); 60 | encodeCipher.init(Cipher.ENCRYPT_MODE, key); 61 | return encodeCipher.doFinal(data); 62 | } 63 | 64 | @SuppressLint("GetInstance") 65 | public static byte[] decodeAES(byte[] keyBytes, byte[] data) throws Exception { 66 | Cipher encodeCipher = Cipher.getInstance(AES_TRANSFORMATION); 67 | SecretKeySpec key = new SecretKeySpec(keyBytes, ASE_ALGORITHM); 68 | encodeCipher.init(Cipher.DECRYPT_MODE, key); 69 | return encodeCipher.doFinal(data); 70 | } 71 | 72 | private static String toHex(byte[] buf) { 73 | if (buf == null) 74 | return ""; 75 | StringBuffer result = new StringBuffer(2 * buf.length); 76 | for (int i = 0; i < buf.length; i++) { 77 | appendHex(result, buf[i]); 78 | } 79 | return result.toString(); 80 | } 81 | 82 | public static String toHex(String txt) { 83 | return toHex(txt.getBytes()); 84 | } 85 | 86 | public static String fromHex(String hex) { 87 | return new String(toByte(hex)); 88 | } 89 | 90 | public static byte[] toByte(String hexString) { 91 | int len = hexString.length() / 2; 92 | byte[] result = new byte[len]; 93 | for (int i = 0; i < len; i++) 94 | result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue(); 95 | return result; 96 | } 97 | 98 | private static void appendHex(StringBuffer sb, byte b) { 99 | sb.append(HEX.charAt((b >> 4) & 0x0f)).append(HEX.charAt(b & 0x0f)); 100 | } 101 | 102 | } 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/FileUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | import android.content.ContentResolver; 20 | import android.content.ContentUris; 21 | import android.content.Context; 22 | import android.database.Cursor; 23 | import android.net.Uri; 24 | import android.os.Build; 25 | import android.os.Environment; 26 | import android.provider.DocumentsContract; 27 | import android.provider.MediaStore; 28 | import android.text.TextUtils; 29 | 30 | import java.io.*; 31 | 32 | /** 33 | * Created by ayst.shen@foxmail.com on 17/8/15. 34 | */ 35 | public class FileUtils { 36 | 37 | /** 38 | * Write file 39 | * 40 | * @param file Write file 41 | * @param message Written content 42 | * @throws IOException 43 | */ 44 | public static void writeFile(File file, String message) 45 | throws IOException { 46 | 47 | FileWriter fWriter = new FileWriter(file); 48 | try { 49 | fWriter.write(message); 50 | } finally { 51 | fWriter.close(); 52 | } 53 | } 54 | 55 | /** 56 | * Read file 57 | * 58 | * @param file Read file 59 | * @return Read content 60 | * @throws IOException 61 | */ 62 | public static String readFile(File file) 63 | throws IOException { 64 | 65 | FileReader fRead = new FileReader(file); 66 | try { 67 | BufferedReader buffer = new BufferedReader(fRead); 68 | StringBuilder sb = new StringBuilder(); 69 | String str; 70 | while ((str = buffer.readLine()) != null) { 71 | sb.append(str); 72 | } 73 | return sb.toString(); 74 | } finally { 75 | fRead.close(); 76 | } 77 | } 78 | 79 | /** 80 | * Get file suffix 81 | * 82 | * @param fileName file name 83 | * @return suffix 84 | */ 85 | public static String getFileSuffix(String fileName) { 86 | if (!TextUtils.isEmpty(fileName) && fileName.length() > 3) { 87 | int dot = fileName.lastIndexOf("."); 88 | if (dot > 0) { 89 | return fileName.substring(dot + 1); 90 | } else { 91 | return ""; 92 | } 93 | } 94 | return ""; 95 | } 96 | 97 | /** 98 | * Get the file path from uri 99 | * 100 | * @param context Context 101 | * @param uri Uri 102 | * @return File path 103 | */ 104 | public static String getFilePathByUri(Context context, Uri uri) { 105 | String path = null; 106 | // Starting with file:// 107 | if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { 108 | path = uri.getPath(); 109 | return path; 110 | } 111 | 112 | // Starting with content://, example: content://media/extenral/images/media/17766 113 | if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) 114 | && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 115 | Cursor cursor = context.getContentResolver().query(uri, 116 | new String[]{MediaStore.Images.Media.DATA}, 117 | null, null, null); 118 | if (cursor != null) { 119 | if (cursor.moveToFirst()) { 120 | int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); 121 | if (columnIndex > -1) { 122 | path = cursor.getString(columnIndex); 123 | } 124 | } 125 | cursor.close(); 126 | } 127 | return path; 128 | } 129 | 130 | // Starting with content://, example: content://media/extenral/images/media/17766, 131 | // >= Build.VERSION_CODES.KITKAT 132 | if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) 133 | && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 134 | if (DocumentsContract.isDocumentUri(context, uri)) { 135 | if (isExternalStorageDocument(uri)) { 136 | // ExternalStorageProvider 137 | final String docId = DocumentsContract.getDocumentId(uri); 138 | final String[] split = docId.split(":"); 139 | final String type = split[0]; 140 | if ("primary".equalsIgnoreCase(type)) { 141 | path = Environment.getExternalStorageDirectory() + "/" + split[1]; 142 | return path; 143 | } 144 | } else if (isDownloadsDocument(uri)) { 145 | // DownloadsProvider 146 | final String id = DocumentsContract.getDocumentId(uri); 147 | final Uri contentUri = ContentUris.withAppendedId( 148 | Uri.parse("content://downloads/public_downloads"), 149 | Long.valueOf(id)); 150 | path = getDataColumn(context, contentUri, null, null); 151 | return path; 152 | } else if (isMediaDocument(uri)) { 153 | // MediaProvider 154 | final String docId = DocumentsContract.getDocumentId(uri); 155 | final String[] split = docId.split(":"); 156 | final String type = split[0]; 157 | Uri contentUri = null; 158 | if ("image".equals(type)) { 159 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 160 | } else if ("video".equals(type)) { 161 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 162 | } else if ("audio".equals(type)) { 163 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; 164 | } 165 | final String selection = "_id=?"; 166 | final String[] selectionArgs = new String[]{split[1]}; 167 | path = getDataColumn(context, contentUri, selection, selectionArgs); 168 | return path; 169 | } 170 | } 171 | } 172 | return null; 173 | } 174 | 175 | private static String getDataColumn(Context context, Uri uri, String selection, 176 | String[] selectionArgs) { 177 | Cursor cursor = null; 178 | final String column = "_data"; 179 | final String[] projection = {column}; 180 | try { 181 | cursor = context.getContentResolver().query(uri, projection, selection, 182 | selectionArgs, null); 183 | if (cursor != null && cursor.moveToFirst()) { 184 | final int column_index = cursor.getColumnIndexOrThrow(column); 185 | return cursor.getString(column_index); 186 | } 187 | } finally { 188 | if (cursor != null) 189 | cursor.close(); 190 | } 191 | return null; 192 | } 193 | 194 | private static boolean isExternalStorageDocument(Uri uri) { 195 | return "com.android.externalstorage.documents".equals(uri.getAuthority()); 196 | } 197 | 198 | private static boolean isDownloadsDocument(Uri uri) { 199 | return "com.android.providers.downloads.documents".equals(uri.getAuthority()); 200 | } 201 | 202 | private static boolean isMediaDocument(Uri uri) { 203 | return "com.android.providers.media.documents".equals(uri.getAuthority()); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/InstallUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.content.Context; 22 | import android.content.DialogInterface; 23 | import android.content.Intent; 24 | import android.net.Uri; 25 | import android.os.Build; 26 | import android.provider.Settings; 27 | import android.util.Log; 28 | import android.view.WindowManager; 29 | 30 | import androidx.annotation.RequiresApi; 31 | import androidx.core.content.FileProvider; 32 | 33 | import com.ayst.romupgrade.R; 34 | 35 | import java.io.BufferedReader; 36 | import java.io.DataOutputStream; 37 | import java.io.File; 38 | import java.io.IOException; 39 | import java.io.InputStreamReader; 40 | import java.nio.charset.Charset; 41 | 42 | public class InstallUtil { 43 | private static final String TAG = "InstallUtil"; 44 | private static final String AUTHORITY = "com.ayst.romupgrade.fileProvider"; 45 | 46 | private static final String EXTRA_SILENT_INSTALL = "silent_install"; 47 | 48 | public static void install(Context context, String path) { 49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 50 | installO(context, path); 51 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 52 | installN(context, path); 53 | } else { 54 | installOther(context, path); 55 | } 56 | } 57 | 58 | /** 59 | * android1.x-6.x 60 | * 61 | * @param context Context 62 | * @param path Package 63 | */ 64 | private static void installOther(Context context, String path) { 65 | Intent install = new Intent(Intent.ACTION_VIEW); 66 | install.setDataAndType(Uri.parse("file://" + path), 67 | "application/vnd.android.package-archive"); 68 | install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 69 | install.putExtra(EXTRA_SILENT_INSTALL, true); // 静默安装 70 | context.startActivity(install); 71 | } 72 | 73 | /** 74 | * android7.x 75 | * 76 | * @param context Context 77 | * @param path Package 78 | */ 79 | private static void installN(Context context, String path) { 80 | Uri apkUri = FileProvider.getUriForFile(context, AUTHORITY, new File(path)); 81 | Intent install = new Intent(Intent.ACTION_VIEW); 82 | install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 83 | install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 84 | install.setDataAndType(apkUri, "application/vnd.android.package-archive"); 85 | install.putExtra(EXTRA_SILENT_INSTALL, true); // 静默安装 86 | context.startActivity(install); 87 | } 88 | 89 | /** 90 | * android8.x 91 | * 92 | * @param context Context 93 | * @param path Package 94 | */ 95 | @RequiresApi(api = Build.VERSION_CODES.O) 96 | private static void installO(Context context, String path) { 97 | boolean isGranted = context.getPackageManager().canRequestPackageInstalls(); 98 | if (isGranted) { 99 | installN(context, path); 100 | } else { 101 | Dialog dialog = new AlertDialog.Builder(context.getApplicationContext()) 102 | .setTitle(R.string.upgrade_unknown_sources_package) 103 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 104 | public void onClick(DialogInterface d, int w) { 105 | Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES); 106 | context.startActivity(intent); 107 | } 108 | }).create(); 109 | dialog.setCancelable(false); 110 | dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 111 | dialog.show(); 112 | } 113 | } 114 | 115 | /** 116 | * Silent install 117 | * 118 | * @param path Package 119 | * @return true: success false: failed 120 | */ 121 | public static boolean installSilent(String path) { 122 | boolean result = false; 123 | BufferedReader es = null; 124 | DataOutputStream os = null; 125 | 126 | try { 127 | Process process = Runtime.getRuntime().exec("su"); 128 | os = new DataOutputStream(process.getOutputStream()); 129 | 130 | String command = "pm install -r " + path + "\n"; 131 | os.write(command.getBytes(Charset.forName("utf-8"))); 132 | os.flush(); 133 | os.writeBytes("exit\n"); 134 | os.flush(); 135 | 136 | process.waitFor(); 137 | es = new BufferedReader(new InputStreamReader(process.getErrorStream())); 138 | 139 | String line; 140 | StringBuilder builder = new StringBuilder(); 141 | while ((line = es.readLine()) != null) { 142 | builder.append(line); 143 | } 144 | Log.d(TAG, "install msg is " + builder.toString()); 145 | 146 | /* Installation is considered a Failure if the result contains 147 | the Failure character, or a success if it is not. 148 | */ 149 | if (!builder.toString().contains("Failure")) { 150 | result = true; 151 | } 152 | } catch (Exception e) { 153 | Log.e(TAG, e.getMessage(), e); 154 | } finally { 155 | try { 156 | if (os != null) { 157 | os.close(); 158 | } 159 | if (es != null) { 160 | es.close(); 161 | } 162 | } catch (IOException e) { 163 | Log.e(TAG, e.getMessage(), e); 164 | } 165 | } 166 | 167 | return result; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/MD5.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | import java.io.FileInputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.UnsupportedEncodingException; 23 | import java.security.MessageDigest; 24 | import java.security.NoSuchAlgorithmException; 25 | 26 | import android.util.Log; 27 | 28 | /** 29 | * Created by ayst.shen@foxmail.com on 17/8/15. 30 | */ 31 | public class MD5 { 32 | 33 | private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 34 | 'A', 'B', 'C', 'D', 'E', 'F'}; 35 | 36 | public static String toHexString(byte[] b) { 37 | StringBuilder sb = new StringBuilder(b.length * 2); 38 | for (int i = 0; i < b.length; i++) { 39 | sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]); 40 | sb.append(HEX_DIGITS[b[i] & 0x0f]); 41 | } 42 | return sb.toString(); 43 | } 44 | 45 | public static String md5ForFile(String filename) { 46 | InputStream fis = null; 47 | byte[] buffer = new byte[1024]; 48 | int numRead = 0; 49 | MessageDigest md5; 50 | try{ 51 | fis = new FileInputStream(filename); 52 | md5 = MessageDigest.getInstance("MD5"); 53 | long total = 0; 54 | while((numRead=fis.read(buffer)) > 0) { 55 | md5.update(buffer,0,numRead); 56 | total+=numRead; 57 | } 58 | Log.d("MD5", "md5sum.total="+total); 59 | fis.close(); 60 | return toHexString(md5.digest()); 61 | } catch (Exception e) { 62 | Log.e("MD5", "md5sum.exception="+e.getMessage()); 63 | if (fis != null) { 64 | try { 65 | fis.close(); 66 | } catch (IOException e1) { 67 | e1.printStackTrace(); 68 | } 69 | } 70 | return null; 71 | } 72 | } 73 | 74 | public static String md5ForString(String string) { 75 | byte[] hash; 76 | try { 77 | hash = MessageDigest.getInstance("MD5").digest( 78 | string.getBytes("UTF-8")); 79 | } catch (NoSuchAlgorithmException e) { 80 | throw new RuntimeException("Huh, MD5 should be supported?", e); 81 | } catch (UnsupportedEncodingException e) { 82 | throw new RuntimeException("Huh, UTF-8 should be supported?", e); 83 | } 84 | 85 | StringBuilder hex = new StringBuilder(hash.length * 2); 86 | for (byte b : hash) { 87 | if ((b & 0xFF) < 0x10) { 88 | hex.append("0"); 89 | } 90 | hex.append(Integer.toHexString(b & 0xFF)); 91 | } 92 | return hex.toString(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/SPUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util; 18 | 19 | 20 | import android.content.Context; 21 | import android.content.SharedPreferences; 22 | import android.content.SharedPreferences.Editor; 23 | 24 | /** 25 | * Created by ayst.shen@foxmail.com on 17/8/15. 26 | */ 27 | public class SPUtils { 28 | private static final String SP = "auto_upgrade"; 29 | 30 | private static SPUtils instance; 31 | private static SharedPreferences mSp = null; 32 | 33 | private SPUtils(Context context) { 34 | mSp = context.getSharedPreferences(SP, Context.MODE_PRIVATE); 35 | } 36 | 37 | public static SPUtils getInstance(Context context) { 38 | if (instance == null) 39 | instance = new SPUtils(context); 40 | return instance; 41 | } 42 | 43 | /** 44 | * Save data 45 | * 46 | * @param key preference key 47 | * @param value preference value 48 | */ 49 | public void saveData(String key, String value) { 50 | Editor editor = mSp.edit(); 51 | editor.putString(key, value); 52 | editor.apply(); 53 | } 54 | 55 | /** 56 | * Save data 57 | * 58 | * @param key preference key 59 | * @param value preference value 60 | */ 61 | public void saveData(String key, boolean value) { 62 | Editor editor = mSp.edit(); 63 | editor.putBoolean(key, value); 64 | editor.apply(); 65 | } 66 | 67 | /** 68 | * Save data 69 | * 70 | * @param key preference key 71 | * @param value preference value 72 | */ 73 | public void saveData(String key, int value) { 74 | Editor editor = mSp.edit(); 75 | editor.putInt(key, value); 76 | editor.apply(); 77 | } 78 | 79 | /** 80 | * Save data 81 | * 82 | * @param key preference key 83 | * @param value preference value 84 | */ 85 | public void saveData(String key, float value) { 86 | Editor editor = mSp.edit(); 87 | editor.putFloat(key, value); 88 | editor.apply(); 89 | } 90 | 91 | /** 92 | * Get data 93 | * 94 | * @param key preference key 95 | * @param defValue default value 96 | * @return value 97 | */ 98 | public String getData(String key, String defValue) { 99 | return mSp.getString(key, defValue); 100 | } 101 | 102 | /** 103 | * Get data 104 | * 105 | * @param key preference key 106 | * @param defValue default value 107 | * @return value 108 | */ 109 | public boolean getData(String key, boolean defValue) { 110 | return mSp.getBoolean(key, defValue); 111 | } 112 | 113 | /** 114 | * Get data 115 | * 116 | * @param key preference key 117 | * @param defValue default value 118 | * @return value 119 | */ 120 | public int getData(String key, int defValue) { 121 | return mSp.getInt(key, defValue); 122 | } 123 | 124 | /** 125 | * Get data 126 | * 127 | * @param key preference key 128 | * @param defValue default value 129 | * @return value 130 | */ 131 | public float getData(String key, float defValue) { 132 | return mSp.getFloat(key, defValue); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/filecopy/FileCopyTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util.filecopy; 18 | 19 | import android.content.Context; 20 | import android.os.AsyncTask; 21 | import android.util.Log; 22 | 23 | import androidx.documentfile.provider.DocumentFile; 24 | 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.OutputStream; 28 | import java.lang.ref.WeakReference; 29 | 30 | /** 31 | * Asynchronous task to copy a file. 32 | * 33 | * @author ayst.shen@foxmail.com 34 | */ 35 | public class FileCopyTask extends AsyncTask { 36 | private static final String TAG = "FileCopyTask"; 37 | 38 | private WeakReference mWeakContext; 39 | private FileCopyTaskParam mParam; 40 | 41 | public FileCopyTask(Context context) { 42 | mWeakContext = new WeakReference<>(context); 43 | } 44 | 45 | @Override 46 | protected void onPreExecute() { 47 | } 48 | 49 | private void copyFile(DocumentFile from, DocumentFile to) throws Exception { 50 | long size = from.length(); 51 | 52 | if (mWeakContext.get() != null) { 53 | InputStream inputStream = mWeakContext.get().getContentResolver().openInputStream(from.getUri()); 54 | OutputStream outputStream = mWeakContext.get().getContentResolver().openOutputStream(to.getUri()); 55 | 56 | if (null != inputStream && null != outputStream) { 57 | byte[] bytes = new byte[1024]; 58 | int count; 59 | long total = 0; 60 | 61 | while ((count = inputStream.read(bytes)) != -1) { 62 | outputStream.write(bytes, 0, count); 63 | if (size > 0) { 64 | total += count; 65 | publishProgress((int) (total * 100 / size)); 66 | } 67 | } 68 | 69 | outputStream.close(); 70 | inputStream.close(); 71 | } else { 72 | throw new IOException("InputStream or OutputStream is null"); 73 | } 74 | } else { 75 | throw new Exception("Context is null"); 76 | } 77 | } 78 | 79 | @Override 80 | protected Void doInBackground(FileCopyTaskParam... params) { 81 | long time = System.currentTimeMillis(); 82 | mParam = params[0]; 83 | try { 84 | copyFile(DocumentFile.fromFile(mParam.from), DocumentFile.fromFile(mParam.to)); 85 | } catch (Exception e) { 86 | Log.e(TAG, "could not copy file, e: ", e); 87 | if (null != mParam.listener) { 88 | mParam.listener.error(e); 89 | } 90 | } 91 | 92 | Log.i(TAG, "copy time: " + (System.currentTimeMillis() - time)); 93 | return null; 94 | } 95 | 96 | @Override 97 | protected void onPostExecute(Void result) { 98 | if (null != mParam.listener) { 99 | mParam.listener.completed(mParam.to); 100 | } 101 | } 102 | 103 | @Override 104 | protected void onProgressUpdate(Integer... values) { 105 | if (null != mParam.listener) { 106 | mParam.listener.progress(values[0]); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/filecopy/FileCopyTaskParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util.filecopy; 18 | 19 | import java.io.File; 20 | 21 | /** 22 | * Class to hold the files for a copy task. Holds the source and the 23 | * destination file. 24 | * 25 | * @author ayst.shen@foxmail.com 26 | * 27 | */ 28 | public class FileCopyTaskParam { 29 | public File from; 30 | public File to; 31 | public IFileCopyListener listener; 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/ayst/romupgrade/util/filecopy/IFileCopyListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright(c) 2020 Bob Shen 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 com.ayst.romupgrade.util.filecopy; 18 | 19 | public interface IFileCopyListener { 20 | public void progress(int progress); 21 | public void completed(T file); 22 | public void error(Exception e); 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 23 | 28 | 29 | 35 | 38 | 41 | 42 | 43 | 44 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_default_disable.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 23 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_default_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 23 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_default_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 23 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_default_selected.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 23 | 24 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_default_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_default_text_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 176 | 181 | 186 | 187 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_download.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_download_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 22 | 23 | 29 | 30 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | "升级" 19 | "隐藏" 20 | "取消" 21 | "重试" 22 | "确认" 23 | "删除" 24 | "确定" 25 | 26 | "升级" 27 | "下载" 28 | "立即升级" 29 | "暂不升级" 30 | "发现新版本,是否升级?\n" 31 | "升级成功!" 32 | "升级失败!" 33 | "准备安装:" 34 | "安装成功!" 35 | "安装失败!" 36 | "验证失败!" 37 | "安装应用需要打开未知来源权限,请去设置中开启权限!" 38 | "升级包版本小于等于当前系统版本,将不会被升级!" 39 | 40 | 41 | "参数错误!" 42 | "网络错误!" 43 | "无更新!" 44 | 45 | 46 | "参数错误!" 47 | "网络错误!" 48 | "空间不足!" 49 | "安装app,app已安装错误!" 50 | "更新app,app未安装错误!" 51 | "更新app,版本过低错误!" 52 | "sha1校验失败!" 53 | 54 | 55 | "参数错误!" 56 | "sha1校验失败!" 57 | "签名校验失败!" 58 | "文件不存在!" 59 | "卸载app,app未安装!" 60 | "app安装错误!" 61 | "app卸载错误!" 62 | "安装自定义错误!" 63 | "用户取消升级!" 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | #302f57 20 | #191645 21 | #30c7ff 22 | 23 | #80191645 24 | 25 | #00000000 26 | #ffc6e578 27 | 28 | #49d452 29 | #803d9909 30 | #fffa9d3e 31 | #ffc6c6c6 32 | #80c6c6c6 33 | #ffff0000 34 | #80ff0000 35 | #ffe6615c 36 | #80e6615c 37 | 38 | #ff000000 39 | #10000000 40 | #23000000 41 | #33000000 42 | #4d000000 43 | #66000000 44 | #80000000 45 | #99000000 46 | #b3000000 47 | 48 | #ffffffff 49 | #10ffffff 50 | #23ffffff 51 | #33ffffff 52 | #4dffffff 53 | #66ffffff 54 | #80ffffff 55 | #99ffffff 56 | #b3ffffff 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 18sp 21 | 14sp 22 | 12sp 23 | 8sp 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | Upgrade 19 | Hide 20 | Cancel 21 | Retry 22 | OK 23 | Delete 24 | OK 25 | 26 | Upgrade 27 | Download 28 | Upgrade 29 | Cancel 30 | "Found a new version, is it upgraded?\n" 31 | Upgrade success! 32 | Upgrade failed! 33 | Ready to install: 34 | install success! 35 | install failed! 36 | invalid package! 37 | To install the application, you need to open the permissions of unknown source. Please go to Settings to open the permissions! 38 | Upgrade package versions less than or equal to the current system version will not be upgraded! 39 | 40 | 41 | "param error!" 42 | "network error!" 43 | "no update!" 44 | 45 | 46 | "param error!" 47 | "network error!" 48 | "no space!" 49 | "app installed!" 50 | "app not install!" 51 | "low version!" 52 | "sha1 verify error!" 53 | 54 | 55 | "param error!" 56 | "sha1 verify error!" 57 | "sign verify error!" 58 | "file not exist!" 59 | "not installed!" 60 | "install error!" 61 | "uninstall error!" 62 | "custom error!" 63 | "user not confirm!" 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/src/main/res/xml/file_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/system.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/app/system.keystore -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | mavenCentral() 8 | jcenter() 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.4.2' 12 | 13 | 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | 17 | // shenhaibo 18 | } 19 | } 20 | 21 | allprojects { 22 | repositories { 23 | google() 24 | mavenCentral() 25 | jcenter() 26 | maven { 27 | url "https://jitpack.io" 28 | } 29 | } 30 | apply from: rootDir.absolutePath + '/utils.gradle' 31 | } 32 | 33 | task clean(type: Delete) { 34 | delete rootProject.buildDir 35 | } 36 | 37 | Properties properties = new Properties() 38 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 39 | 40 | ext { 41 | compileSdkVersion = 28 42 | buildToolsVersion = "28.0.3" 43 | minSdkVersion = 21 44 | targetSdkVersion = 26 45 | 46 | keyStoreAlias = properties.getProperty("keystore.alias") 47 | keyStorePath = properties.getProperty("keystore.path") 48 | keyStorePassword = properties.getProperty("keystore.store_password") 49 | keyStoreKeyPassword = properties.getProperty("keystore.key_password") 50 | } 51 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | 19 | android.useAndroidX=true 20 | android.enableJetifier=true 21 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Sep 18 16:15:17 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /screenshots/1585270225.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/1585270225.jpg -------------------------------------------------------------------------------- /screenshots/1585270231.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/1585270231.jpg -------------------------------------------------------------------------------- /screenshots/1585270300.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/1585270300.jpg -------------------------------------------------------------------------------- /screenshots/1585270318.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/1585270318.jpg -------------------------------------------------------------------------------- /screenshots/device-2020-01-18-150533.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/device-2020-01-18-150533.png -------------------------------------------------------------------------------- /screenshots/device-2020-01-18-150622.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/device-2020-01-18-150622.png -------------------------------------------------------------------------------- /screenshots/device-2020-01-18-150651.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aystshen/Android-RomUpgrade/01ccdc2301286af214ae9c075f54107c9a90a938/screenshots/device-2020-01-18-150651.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /utils.gradle: -------------------------------------------------------------------------------- 1 | //此文件申请一些公共参数 2 | 3 | ext { 4 | compileSdkVersion = 25 5 | buildToolsVersion = "25.0.2" 6 | minSdkVersion = 18 7 | targetSdkVersion = 21 8 | } --------------------------------------------------------------------------------