├── .gitattributes ├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── kaly.jks ├── libs │ ├── Xpopup-release.aar │ ├── easyAdapter.jar │ ├── glide.jar │ └── overscroll-release-v1.1-20160904.jar ├── proguard-rules.pro ├── release_mini │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pointer │ │ └── wave │ │ └── easyship │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── 7za │ │ ├── magisk_library.sh │ │ └── run.sh │ ├── java │ │ └── com │ │ │ └── pointer │ │ │ └── wave │ │ │ └── easyship │ │ │ ├── EasyShip.java │ │ │ ├── FlashActivity.java │ │ │ ├── MainActivity.kt │ │ │ ├── android │ │ │ ├── AutoCompletePanel.java │ │ │ ├── ClipboardPanel.java │ │ │ ├── FreeScrollingTextField.java │ │ │ ├── KeysInterpreter.java │ │ │ ├── OnSelectionChangedListener.java │ │ │ ├── TextChangeListener.java │ │ │ ├── TouchNavigationMethod.java │ │ │ └── YoyoNavigationMethod.java │ │ │ ├── base │ │ │ └── BaseFragment.java │ │ │ ├── common │ │ │ ├── ColorScheme.java │ │ │ ├── ColorSchemeDark.java │ │ │ ├── ColorSchemeLight.java │ │ │ ├── Document.java │ │ │ ├── DocumentProvider.java │ │ │ ├── Flag.java │ │ │ ├── JavaLexer.java │ │ │ ├── JavaTokenTypes.java │ │ │ ├── Language.java │ │ │ ├── LanguageCFamily.java │ │ │ ├── LanguageJava.java │ │ │ ├── LanguageNonProg.java │ │ │ ├── Lexer.java │ │ │ ├── LinearSearchStrategy.java │ │ │ ├── Pair.java │ │ │ ├── ProgressObserver.java │ │ │ ├── ProgressSource.java │ │ │ ├── ReadThread.java │ │ │ ├── RowListener.java │ │ │ ├── SearchStrategy.java │ │ │ ├── TextBuffer.java │ │ │ ├── TextBufferCache.java │ │ │ ├── TextWarriorException.java │ │ │ ├── UndoStack.java │ │ │ ├── WriteThread.java │ │ │ ├── activity │ │ │ │ └── BaseActivity.java │ │ │ └── crash │ │ │ │ └── CrashHandler.java │ │ │ ├── core │ │ │ ├── CacheDao.java │ │ │ ├── LoadShellUtil.java │ │ │ ├── ROTAUpdateManager.java │ │ │ ├── RealtimeProcess.java │ │ │ ├── RunTimeUtils.java │ │ │ ├── UpdateEngineCallback.java │ │ │ ├── UpdateParser.java │ │ │ └── ZoomInTransformer.kt │ │ │ ├── editor │ │ │ └── TextEditor.java │ │ │ ├── fragments │ │ │ ├── AboutFragment.java │ │ │ └── HomeFragment.java │ │ │ ├── interfaces │ │ │ ├── OnNavigationStateListener.java │ │ │ ├── OnProcessDestroyListener.java │ │ │ ├── OnProcessErrorListener.java │ │ │ ├── OnRunTimeListener.java │ │ │ └── RealtimeProcessInterface.java │ │ │ ├── ktx │ │ │ ├── AppGlobalScope.kt │ │ │ └── KoinKtx.kt │ │ │ ├── net │ │ │ ├── Api.kt │ │ │ ├── LogInterceptor.kt │ │ │ ├── NetModule.kt │ │ │ └── repo │ │ │ │ └── UpdateRepo.kt │ │ │ ├── pojo │ │ │ ├── TipsBen.java │ │ │ ├── UpdateLogBen.java │ │ │ └── VersionBen.java │ │ │ ├── services │ │ │ ├── ProcessService.java │ │ │ └── RunTimeService.java │ │ │ ├── utils │ │ │ ├── AndroidInfo.java │ │ │ ├── ColorChangeUtils.java │ │ │ ├── DensityUtil.java │ │ │ ├── FileInterface.java │ │ │ ├── FileUtil.java │ │ │ ├── HttpUtils.java │ │ │ ├── JsonUtils.java │ │ │ ├── KeepShell.kt │ │ │ ├── KeepShellPublic.kt │ │ │ ├── SAF.java │ │ │ ├── ShellExecutor.java │ │ │ ├── ShellUtil.java │ │ │ └── ZipUtils.java │ │ │ ├── view │ │ │ └── SymbolView.java │ │ │ └── widget │ │ │ ├── HelpsDialog.java │ │ │ ├── IOSOverScrollView.java │ │ │ ├── MarqueeTextView.java │ │ │ ├── NavigationBar.java │ │ │ ├── OverRecyclerView.java │ │ │ ├── OverScrollView.java │ │ │ ├── PermissionsDialog.java │ │ │ ├── TipsPagerAdapter.java │ │ │ ├── UpdateLogDialog.java │ │ │ ├── ViewPagerAdapterForView.java │ │ │ ├── adapter │ │ │ └── HelpsListAdapter.java │ │ │ └── feedback │ │ │ └── TouchFeedback.java │ └── res │ │ ├── animator │ │ ├── fragment_slide_hide.xml │ │ └── fragment_slide_show.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_content_copy_white_24dp.png │ │ ├── ic_content_cut_white_24dp.png │ │ ├── ic_content_paste_white_24dp.png │ │ ├── ic_launcher_background.xml │ │ ├── ic_select_all_white_24dp.png │ │ ├── icon_method.png │ │ ├── shape_editor.xml │ │ ├── shape_flash_button.xml │ │ ├── shape_indicator.xml │ │ ├── shape_indicator_select.xml │ │ └── shape_shadow_layout.xml │ │ ├── layout │ │ ├── activity_flash.xml │ │ ├── activity_main.xml │ │ ├── dialog_full_base.xml │ │ ├── dialog_helps.xml │ │ ├── dialog_permissin_write.xml │ │ ├── dialog_update_log.xml │ │ ├── fragment_about.xml │ │ ├── fragment_home.xml │ │ ├── fragment_navigation.xml │ │ ├── item_auto_panel.xml │ │ ├── item_help_layout.xml │ │ ├── item_tips_viewpager.xml │ │ ├── layout_navigation_item.xml │ │ └── symbol_view.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_alipay.png │ │ ├── ic_developer.png │ │ ├── ic_download.png │ │ ├── ic_es_logo.png │ │ ├── ic_es_logo_round.png │ │ ├── ic_file.png │ │ ├── ic_folder.png │ │ ├── ic_github.png │ │ ├── ic_help.png │ │ ├── ic_home.png │ │ ├── ic_idea.png │ │ ├── ic_launcher.webp │ │ ├── ic_launcher_round.webp │ │ ├── ic_log.png │ │ ├── ic_logo_white_24dp.png │ │ ├── ic_mail.png │ │ ├── ic_notification.png │ │ ├── ic_root.png │ │ ├── ic_settings.png │ │ ├── ic_start.png │ │ ├── ic_sub_notification.png │ │ ├── ic_tips.png │ │ ├── ic_update.png │ │ └── ic_waring.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ ├── colors.xml │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── configs.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── network_security_config.xml │ │ └── update_path.xml │ └── test │ └── java │ └── com │ └── pointer │ └── wave │ └── easyship │ ├── ExampleUnitTest.kt │ └── Test.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # IntelliJ 37 | *.iml 38 | .idea/workspace.xml 39 | .idea/tasks.xml 40 | .idea/gradle.xml 41 | .idea/assetWizardSettings.xml 42 | .idea/dictionaries 43 | .idea/libraries 44 | .idea/caches 45 | 46 | # Keystore files 47 | # Uncomment the following lines if you do not want to check your keystore files in. 48 | #*.jks 49 | #*.keystore 50 | 51 | # External native build folder generated in Android Studio 2.2 and later 52 | .externalNativeBuild 53 | 54 | # Google Services (e.g. APIs or Firebase) 55 | google-services.json 56 | 57 | # Freeline 58 | freeline.py 59 | freeline/ 60 | freeline_project_description.json 61 | 62 | # fastlane 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | fastlane/readme.md 68 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 LumnyTool 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EasyShip 2 | 一叶孤舟 · VAB设备系统更新 3 | 4 | 描述:一个用于绕过系统更新程序从而使用户进行自定义更新系统版本的APP 5 | 6 | 特性: 7 | 1. VAB检测 8 | 2. 更新进度可视化 9 | 3. 快速启动 10 | 4. 断点续更 11 | ...... 12 | 13 | 目前还有诸多没有完善的地方,欢迎大家push~ 14 | 15 | 企鹅交流群:1009818916 16 | 17 | EasyShip永久保持免费且开源,欢迎使用! 18 | 19 | # 一叶孤舟2发布! 20 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | compileSdk 31 8 | 9 | dataBinding { 10 | enabled = true 11 | } 12 | 13 | signingConfigs { 14 | ly_Signing { 15 | keyAlias 'kaly' 16 | keyPassword '0409kaly' 17 | storeFile file('./kaly.jks') 18 | storePassword '0409kaly' 19 | } 20 | } 21 | 22 | defaultConfig { 23 | applicationId "com.pointer.wave.easyship" 24 | minSdk 26 25 | targetSdk 32 26 | versionCode 22041122 27 | versionName "1.0" 28 | 29 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 30 | 31 | ndk { 32 | //abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64" 33 | abiFilters "armeabi-v7a", "arm64-v8a" 34 | } 35 | 36 | renderscriptTargetApi 31 37 | renderscriptSupportModeEnabled true 38 | } 39 | 40 | buildTypes { 41 | release { 42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 43 | zipAlignEnabled true 44 | debuggable false 45 | minifyEnabled = false 46 | signingConfig signingConfigs.ly_Signing 47 | } 48 | release_mini { 49 | shrinkResources true 50 | zipAlignEnabled true 51 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 52 | minifyEnabled true 53 | debuggable false 54 | } 55 | debug { 56 | signingConfig signingConfigs.ly_Signing 57 | minifyEnabled false 58 | } 59 | } 60 | compileOptions { 61 | sourceCompatibility JavaVersion.VERSION_1_8 62 | targetCompatibility JavaVersion.VERSION_1_8 63 | } 64 | kotlinOptions { 65 | jvmTarget = '1.8' 66 | } 67 | } 68 | 69 | dependencies { 70 | 71 | implementation fileTree(dir: "libs", include: ["*.jar","*.aar"]) 72 | implementation 'androidx.core:core-ktx:1.7.0' 73 | implementation 'androidx.appcompat:appcompat:1.3.0' 74 | implementation 'com.google.android.material:material:1.4.0' 75 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 76 | testImplementation 'junit:junit:4.13.2' 77 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 78 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 79 | 80 | //全面屏适配 81 | implementation 'com.gyf.immersionbar:immersionbar:2.3.3' 82 | // fragment快速实现(可选) 83 | implementation 'com.gyf.immersionbar:immersionbar-components:3.0.0' 84 | // kotlin扩展(可选) 85 | implementation 'com.gyf.immersionbar:immersionbar-ktx:3.0.0' 86 | 87 | //高斯模糊控件 88 | implementation 'com.github.mmin18:realtimeblurview:1.2.1' 89 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 90 | 91 | // Retrofit 网络请求 https://github.com/square/retrofit 92 | implementation 'com.squareup.retrofit2:retrofit:2.9.0' 93 | implementation "com.squareup.okhttp3:logging-interceptor:4.9.2" 94 | implementation 'com.squareup.retrofit2:converter-scalars:2.0.0' 95 | implementation 'com.squareup.retrofit2:converter-moshi:2.9.0' 96 | implementation 'com.squareup.retrofit2:converter-gson:2.9.0' 97 | 98 | //GSON 99 | implementation 'com.google.code.gson:gson:+' 100 | 101 | // Coroutine 协程 https://developer.android.google.cn/kotlin/coroutines 102 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" 103 | 104 | // Koin 依赖注入 https://insert-koin.io 105 | implementation "io.insert-koin:koin-android:3.1.3" 106 | 107 | //WebSocket 108 | implementation 'org.java-websocket:Java-WebSocket:1.5.2' 109 | 110 | implementation 'me.itangqi.waveloadingview:library:0.3.5' 111 | 112 | implementation 'com.github.devlight:infinitecycleviewpager:1.0.2' 113 | 114 | implementation "androidx.viewpager2:viewpager2:1.0.0" 115 | } -------------------------------------------------------------------------------- /app/kaly.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/kaly.jks -------------------------------------------------------------------------------- /app/libs/Xpopup-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/libs/Xpopup-release.aar -------------------------------------------------------------------------------- /app/libs/easyAdapter.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/libs/easyAdapter.jar -------------------------------------------------------------------------------- /app/libs/glide.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/libs/glide.jar -------------------------------------------------------------------------------- /app/libs/overscroll-release-v1.1-20160904.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/libs/overscroll-release-v1.1-20160904.jar -------------------------------------------------------------------------------- /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 | -keep class android.support.v8.renderscript.** { *; } 24 | -keep class androidx.renderscript.** { *; } 25 | 26 | -keep class androidx.recyclerview.widget.**{*;} 27 | -keep class androidx.viewpager2.widget.**{*;} 28 | 29 | -keep class com.pointer.wave.easyship.pojo.**{*;} 30 | 31 | -dontwarn com.lxj.xpopup.widget.** 32 | -keep class com.lxj.xpopup.widget.**{*;} -------------------------------------------------------------------------------- /app/release_mini/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.pointer.wave.easyship", 8 | "variantName": "release_mini", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 22041122, 15 | "versionName": "1.0", 16 | "outputFile": "app-release_mini.apk" 17 | } 18 | ], 19 | "elementType": "File" 20 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/pointer/wave/easyship/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.pointer.wave.easyship", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 11 | 13 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 70 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 97 | 98 | 103 | 104 | 109 | 112 | 113 | 114 | 117 | 118 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /app/src/main/assets/7za: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/assets/7za -------------------------------------------------------------------------------- /app/src/main/assets/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | workdir=$( 3 | cd $(dirname $0) 4 | pwd 5 | ) 6 | zipDir=$1 7 | rn=$2 8 | 9 | setprop persist.logd.size 8388608 10 | 11 | if [ ! -f "/data/7za" ]; then 12 | cp -r "$workdir"/7za /data 13 | chmod 777 /data/7za 14 | else 15 | chmod 777 /data/7za 16 | fi 17 | 18 | if [ ! -f "$zipDir"/update ]; then 19 | rm -rf "$zipDir"/update* 20 | fi 21 | 22 | echo "开始解析ROM" 23 | /data/7za x "$zipDir"/"$rn" -r -o"$zipDir"/update >/dev/null 24 | echo "解析完毕" 25 | 26 | if [ -f "$zipDir/update/payload.bin" ]; then 27 | echo "ROM核心文件校验成功" 28 | source "$zipDir"/update/payload_properties.txt 29 | update_engine_client --payload=file://"$zipDir"/update/payload.bin --update --headers=" 30 | FILE_HASH=$FILE_HASH 31 | FILE_SIZE=$FILE_SIZE 32 | METADATA_HASH=$METADATA_HASH 33 | METADATA_SIZE=$METADATA_SIZE" 34 | logcat -s update_engine:v 35 | else 36 | echo "ROM核心文件校验失败,请检查ROM完整性!" 37 | fi -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/EasyShip.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Application; 5 | import android.content.Context; 6 | 7 | import com.pointer.wave.easyship.common.crash.CrashHandler; 8 | import com.pointer.wave.easyship.core.CacheDao; 9 | import com.pointer.wave.easyship.utils.KeepShellPublic; 10 | import com.pointer.wave.easyship.core.RealtimeProcess; 11 | 12 | 13 | public class EasyShip extends Application { 14 | 15 | @SuppressLint("StaticFieldLeak") 16 | public static Context context; 17 | public static CacheDao cacheDao; 18 | public static boolean isVab; 19 | private static RealtimeProcess process; 20 | private static RealtimeProcess.MSG msg; 21 | 22 | private CacheDao dao; 23 | 24 | @Override 25 | public void onCreate() { 26 | super.onCreate(); 27 | setContext(this); 28 | 29 | dao = new CacheDao(); 30 | new Thread(()->{ 31 | //初始化Magisk的shell函数库 32 | String cmdSync = KeepShellPublic.INSTANCE.doCmdSync(". " + dao.getShellFilePath() + "/" + dao.lisName + "\n" + 33 | "mount_partitions\n"); 34 | String[] split = cmdSync.split("\n"); 35 | System.arraycopy(split, 0, info, 0, split.length); 36 | }).start(); 37 | //异常捕获 38 | CrashHandler.init(this); 39 | isVab = isVAB(); 40 | } 41 | 42 | public static RealtimeProcess getProcess() { 43 | return process; 44 | } 45 | 46 | public static void setProcess(RealtimeProcess process) { 47 | EasyShip.process = process; 48 | } 49 | 50 | public synchronized static RealtimeProcess.MSG getMsg() { 51 | return msg; 52 | } 53 | 54 | public synchronized static void setMsg(RealtimeProcess.MSG msg) { 55 | EasyShip.msg = msg; 56 | } 57 | 58 | public static Context getContext() { 59 | return context; 60 | } 61 | 62 | private void setContext(Context context) { 63 | EasyShip.context = context; 64 | } 65 | 66 | public static String[] info = new String[2]; 67 | private boolean isVAB(){ 68 | String cmdSync = KeepShellPublic.INSTANCE.doCmdSync("find /dev/block/ -type l -name boot_a"); 69 | System.out.println("cmdSync = " + cmdSync); 70 | return cmdSync.split("\n").length > 1; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship 2 | 3 | import android.content.Intent 4 | import android.net.Uri 5 | import android.os.Build 6 | import android.os.Bundle 7 | import android.widget.Toast 8 | import androidx.fragment.app.Fragment 9 | import androidx.fragment.app.FragmentActivity 10 | import androidx.lifecycle.lifecycleScope 11 | import androidx.viewpager2.adapter.FragmentStateAdapter 12 | import androidx.viewpager2.widget.ViewPager2 13 | import com.google.gson.Gson 14 | import com.lxj.xpopup.XPopup 15 | import com.pointer.wave.easyship.common.activity.BaseActivity 16 | import com.pointer.wave.easyship.core.ZoomInTransformer 17 | import com.pointer.wave.easyship.fragments.AboutFragment 18 | import com.pointer.wave.easyship.fragments.HomeFragment 19 | import com.pointer.wave.easyship.net.repo.UpdateRepo 20 | import com.pointer.wave.easyship.pojo.VersionBen 21 | import com.pointer.wave.easyship.utils.AndroidInfo 22 | import com.pointer.wave.easyship.widget.NavigationBar 23 | import kotlinx.coroutines.flow.catch 24 | import kotlinx.coroutines.flow.launchIn 25 | import kotlinx.coroutines.flow.onEach 26 | 27 | class MainActivity : BaseActivity() { 28 | 29 | private var cancel = false 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | setContentView(R.layout.activity_main) 34 | 35 | val fragments = listOf(HomeFragment(), AboutFragment()) 36 | 37 | val adapter = ViewPagerAdapter(fragments, this) 38 | val pager = findViewById(R.id.pager).apply { 39 | setAdapter(adapter) 40 | //isUserInputEnabled = false 41 | } 42 | 43 | findViewById(R.id.main_navigation).apply { 44 | bindData(pager, arrayOf("开始", "关于"), intArrayOf(R.mipmap.ic_home, R.mipmap.ic_settings)) 45 | setPositionListener { _, position -> 46 | pager.currentItem = position 47 | } 48 | } 49 | // FIXME: 2022/4/4 本来我是想 ViewPager 滑动的时候切换下面的 Tab,但是 Tab 上面那个有动画的 View 似乎没效果,我也不清楚应该怎么修复,就暂时禁用了 50 | } 51 | 52 | override fun onWindowFocusChanged(hasFocus: Boolean) { 53 | super.onWindowFocusChanged(hasFocus) 54 | if (hasFocus && !cancel) { 55 | cancel = true 56 | lifecycleScope.launchWhenResumed { 57 | UpdateRepo().update() 58 | .catch { 59 | it.printStackTrace() 60 | } 61 | .onEach { 62 | val gson = Gson() 63 | val versionBen = gson.fromJson(it.content, VersionBen::class.java) 64 | val androidInfo = AndroidInfo(this@MainActivity) 65 | if (versionBen.versionCode.toInt() > androidInfo.versionCode) { 66 | XPopup.Builder(this@MainActivity) 67 | .isDestroyOnDismiss(true) 68 | .asConfirm("有更新啦~", versionBen.updateContent, "知道了", "去更新", { 69 | val intent = Intent().apply { 70 | action = "android.intent.action.VIEW" 71 | data = Uri.parse(versionBen.downloadUrl) 72 | } 73 | startActivity(intent) 74 | }, null, false).show() 75 | } 76 | } 77 | .catch { 78 | it.printStackTrace() 79 | }.launchIn(lifecycleScope) 80 | } 81 | } 82 | } 83 | 84 | inner class ViewPagerAdapter( 85 | private val fragments: List, 86 | activity: FragmentActivity 87 | ) : 88 | FragmentStateAdapter(activity) { 89 | override fun getItemCount(): Int = fragments.size 90 | 91 | override fun createFragment(position: Int): Fragment = fragments[position] 92 | } 93 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/android/KeysInterpreter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.android; 10 | 11 | import android.view.KeyEvent; 12 | 13 | import com.pointer.wave.easyship.common.Language; 14 | 15 | /** 16 | * Interprets shortcut key combinations and contains utility methods 17 | * to map Android keycodes to Unicode equivalents. 18 | */ 19 | public class KeysInterpreter { 20 | public static boolean isSwitchPanel(KeyEvent event){ 21 | return (event.isShiftPressed() && 22 | (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)); 23 | } 24 | 25 | /** 26 | * Maps shortcut keys and Android keycodes to printable characters. 27 | * Note that whitespace is considered printable. 28 | * 29 | * @param event The KeyEvent to interpret 30 | * @return The printable character the event represents, 31 | * or Language.NULL_CHAR if the event does not represent a printable char 32 | */ 33 | public static char keyEventToPrintableChar(KeyEvent event){ 34 | char c = Language.NULL_CHAR; 35 | 36 | // convert tab, backspace, newline and space keycodes to standard ASCII values 37 | if (isNewline(event)){ 38 | c = Language.NEWLINE; 39 | } 40 | else if (isBackspace(event)){ 41 | c = Language.BACKSPACE; 42 | } 43 | // This should be before the check for isSpace() because the 44 | // shortcut for TAB uses the SPACE key. 45 | else if (isTab(event)){ 46 | c = Language.TAB; 47 | } 48 | else if (isSpace(event)){ 49 | c = ' '; 50 | } 51 | else if (event.isPrintingKey()){ 52 | c = (char) event.getUnicodeChar(event.getMetaState()); 53 | } 54 | 55 | return c; 56 | } 57 | 58 | private static boolean isTab(KeyEvent event){ 59 | return (event.isShiftPressed() && 60 | (event.getKeyCode() == KeyEvent.KEYCODE_SPACE)) || 61 | (event.getKeyCode() == KeyEvent.KEYCODE_TAB); 62 | } 63 | 64 | private static boolean isBackspace(KeyEvent event) { 65 | return (event.getKeyCode() == KeyEvent.KEYCODE_DEL); 66 | } 67 | 68 | private static boolean isNewline(KeyEvent event) { 69 | return (event.getKeyCode() == KeyEvent.KEYCODE_ENTER); 70 | } 71 | 72 | private static boolean isSpace(KeyEvent event) { 73 | return (event.getKeyCode() == KeyEvent.KEYCODE_SPACE); 74 | } 75 | 76 | public static boolean isNavigationKey(KeyEvent event) { 77 | int keyCode = event.getKeyCode(); 78 | return keyCode == KeyEvent.KEYCODE_DPAD_DOWN || 79 | keyCode == KeyEvent.KEYCODE_DPAD_UP || 80 | keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || 81 | keyCode == KeyEvent.KEYCODE_DPAD_LEFT; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/android/OnSelectionChangedListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.android; 10 | 11 | public interface OnSelectionChangedListener { 12 | void onSelectionChanged(boolean active, int selStart, int selEnd); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/android/TextChangeListener.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.android; 2 | 3 | public interface TextChangeListener 4 | { 5 | 6 | void onNewLine(String c, int _caretPosition, int p2); 7 | 8 | void onDel(CharSequence text, int _caretPosition, int newCursorPosition); 9 | 10 | void onAdd(CharSequence text, int _caretPosition, int newCursorPosition); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/base/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.base; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.annotation.LayoutRes; 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | import androidx.fragment.app.Fragment; 12 | 13 | abstract public class BaseFragment extends Fragment { 14 | 15 | public BaseFragment(){ 16 | super(); 17 | } 18 | 19 | public BaseFragment(@LayoutRes int layoutId){ 20 | super(layoutId); 21 | } 22 | 23 | private boolean firstResumed = false; 24 | 25 | @Nullable 26 | @Override 27 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 28 | View rootView = super.onCreateView(inflater,container,savedInstanceState); 29 | if(rootView == null){ 30 | rootView = onCreateViewByReturn(inflater, container); 31 | } 32 | return rootView; 33 | } 34 | 35 | protected View onCreateViewByReturn(@NonNull LayoutInflater inflater, @Nullable ViewGroup container){ 36 | return null; 37 | } 38 | 39 | @Override 40 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 41 | super.onViewCreated(view, savedInstanceState); 42 | initView(view); 43 | } 44 | 45 | @Override 46 | public void onDestroyView() { 47 | super.onDestroyView(); 48 | firstResumed = false; 49 | } 50 | 51 | @Override 52 | public void onResume() { 53 | super.onResume(); 54 | if(!firstResumed){ 55 | firstResumed = true; 56 | loadSingleData(); 57 | } 58 | loadData(); 59 | } 60 | 61 | protected void initView(View root){} 62 | 63 | protected void loadSingleData(){} 64 | 65 | protected void loadData(){} 66 | } 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/ColorScheme.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | 10 | package com.pointer.wave.easyship.common; 11 | 12 | import java.util.HashMap; 13 | 14 | public abstract class ColorScheme { 15 | public enum Colorable { 16 | FOREGROUND, BACKGROUND, SELECTION_FOREGROUND, SELECTION_BACKGROUND, 17 | CARET_FOREGROUND, CARET_BACKGROUND, CARET_DISABLED, LINE_HIGHLIGHT, 18 | NON_PRINTING_GLYPH, COMMENT, KEYWORD, NAME, NUMBER,STRING, 19 | SECONDARY,DATATYPE,YSF 20 | } 21 | 22 | protected HashMap _colors = generateDefaultColors(); 23 | 24 | public void setColor(Colorable colorable, int color) { 25 | _colors.put(colorable, color); 26 | } 27 | 28 | public int getColor(Colorable colorable) { 29 | Integer color = _colors.get(colorable); 30 | if (color == null) { 31 | TextWarriorException.fail("Color not specified for " + colorable); 32 | return 0; 33 | } 34 | return color.intValue(); 35 | } 36 | 37 | // Currently, color scheme is tightly coupled with semantics of the token types 38 | public int getTokenColor(int tokenType) { 39 | Colorable element; 40 | switch (tokenType) { 41 | case Lexer.NORMAL: 42 | element = Colorable.FOREGROUND; 43 | break; 44 | case Lexer.KEYWORD: 45 | element = Colorable.KEYWORD; 46 | break; 47 | case Lexer.NAME: 48 | element = Colorable.NAME; 49 | break; 50 | case Lexer.DOUBLE_SYMBOL_LINE: //fall-through 51 | case Lexer.DOUBLE_SYMBOL_DELIMITED_MULTILINE: 52 | //case Lexer.SINGLE_SYMBOL_LINE_B: 53 | element = Colorable.COMMENT; 54 | break; 55 | case Lexer.SINGLE_SYMBOL_DELIMITED_A: //fall-through 56 | case Lexer.SINGLE_SYMBOL_DELIMITED_B: 57 | element = Colorable.STRING; 58 | break; 59 | case Lexer.NUMBER: 60 | element = Colorable.NUMBER; 61 | break; 62 | case Lexer.SINGLE_SYMBOL_LINE_A: //fall-through 63 | case Lexer.SINGLE_SYMBOL_WORD: 64 | case Lexer.OPERATOR: 65 | element = Colorable.SECONDARY; 66 | break; 67 | case Lexer.SINGLE_SYMBOL_LINE_B: //类型 68 | element = Colorable.NAME; 69 | break; 70 | case Lexer.DATATYPE: 71 | element = Colorable.DATATYPE; 72 | break; 73 | case Lexer.YSF: 74 | element = Colorable.YSF; 75 | break; 76 | default: 77 | TextWarriorException.fail("Invalid token type"); 78 | element = Colorable.FOREGROUND; 79 | break; 80 | } 81 | return getColor(element); 82 | } 83 | 84 | /** 85 | * Whether this color scheme uses a dark background, like black or dark grey. 86 | */ 87 | public abstract boolean isDark(); 88 | 89 | private HashMap generateDefaultColors() { 90 | // High-contrast, black-on-white color scheme 91 | HashMap colors = new HashMap(Colorable.values().length); 92 | colors.put(Colorable.FOREGROUND, 0xFF000000);//文本颜色 93 | colors.put(Colorable.BACKGROUND, 0xFFFFFFFF);//编辑器背景色 94 | colors.put(Colorable.SELECTION_FOREGROUND, 0xFFFFFFFF);//选择文本的前景色 95 | colors.put(Colorable.SELECTION_BACKGROUND, 0xFF999999);//选择文本的背景色 96 | colors.put(Colorable.CARET_FOREGROUND, 0xff0099cc); 97 | colors.put(Colorable.CARET_BACKGROUND, 0xFF40B0FF);//选择文本的水滴背景色 98 | colors.put(Colorable.CARET_DISABLED, 0xFF000000); 99 | colors.put(Colorable.LINE_HIGHLIGHT, 0x20888888);//当前行颜色 100 | 101 | colors.put(Colorable.NON_PRINTING_GLYPH, 0xffd0d0d0);//行号 102 | colors.put(Colorable.COMMENT, 0xff009b00); //注释 103 | colors.put(Colorable.KEYWORD, 0xff2c82c8); //关键字 104 | colors.put(Colorable.NAME, 0xff000000); // Eclipse default color 105 | colors.put(Colorable.NUMBER, 0xffbc0000); // 数字 106 | colors.put(Colorable.STRING, 0xffbc0000); //字符串 107 | colors.put(Colorable.SECONDARY, 0xff6f008a);//宏定义 108 | colors.put(Colorable.DATATYPE, 0xff0096ff);//数据类型 109 | colors.put(Colorable.YSF,0xff007c1f);//运算符 110 | return colors; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/ColorSchemeDark.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | 10 | package com.pointer.wave.easyship.common; 11 | 12 | 13 | public class ColorSchemeDark extends ColorScheme { 14 | @Override 15 | public boolean isDark() { 16 | return true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/ColorSchemeLight.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | 10 | package com.pointer.wave.easyship.common; 11 | 12 | 13 | /** 14 | * Off-black on off-white background color scheme 15 | */ 16 | public class ColorSchemeLight extends ColorScheme { 17 | @Override 18 | public boolean isDark() { 19 | return false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/Flag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | public class Flag { 12 | private boolean state = false; 13 | 14 | synchronized public final void set(){ 15 | state = true; 16 | } 17 | 18 | synchronized public final void clear(){ 19 | state = false; 20 | } 21 | 22 | synchronized public final boolean isSet(){ 23 | return state; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/JavaTokenTypes.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.common; 2 | 3 | public interface JavaTokenTypes { 4 | int TokenNameWHITESPACE=1; 5 | int TokenNameCOMMENT_LINE=2; 6 | int TokenNameCOMMENT_BLOCK=3; 7 | int TokenNameIdentifier=4; 8 | int TokenNameabstract=5; 9 | int TokenNameassert=6; 10 | int TokenNameboolean=7; 11 | int TokenNamebreak=8; 12 | int TokenNamebyte=9; 13 | int TokenNamecase=10; 14 | int TokenNamecatch=11; 15 | int TokenNamechar=12; 16 | int TokenNameclass=13; 17 | int TokenNamecontinue=14; 18 | int TokenNameconst=15; 19 | int TokenNamedefault=16; 20 | int TokenNamedo=17; 21 | int TokenNamedouble=18; 22 | int TokenNameelse=19; 23 | int TokenNameenum=20; 24 | int TokenNameextends=21; 25 | int TokenNamefalse=22; 26 | int TokenNamefinal=23; 27 | int TokenNamefinally=24; 28 | int TokenNamefloat=25; 29 | int TokenNamefor=26; 30 | int TokenNamegoto=27; 31 | int TokenNameif=28; 32 | int TokenNameimplements=29; 33 | int TokenNameimport=30; 34 | int TokenNameinstanceof=31; 35 | int TokenNameint=32; 36 | int TokenNameinterface=33; 37 | int TokenNamelong=34; 38 | int TokenNamenative=35; 39 | int TokenNamenew=36; 40 | int TokenNamenull=37; 41 | int TokenNamepackage=38; 42 | int TokenNameprivate=39; 43 | int TokenNameprotected=40; 44 | int TokenNamepublic=41; 45 | int TokenNamereturn=42; 46 | int TokenNameshort=43; 47 | int TokenNamestatic=44; 48 | int TokenNamestrictfp=45; 49 | int TokenNamesuper=46; 50 | int TokenNameswitch=47; 51 | int TokenNamesynchronized=48; 52 | int TokenNamethis=49; 53 | int TokenNamethrow=50; 54 | int TokenNamethrows=51; 55 | int TokenNametransient=52; 56 | int TokenNametrue=53; 57 | int TokenNametry=54; 58 | int TokenNamevoid=55; 59 | int TokenNamevolatile=56; 60 | int TokenNamewhile=57; 61 | int TokenNameIntegerLiteral=58; 62 | int TokenNameLongLiteral=59; 63 | int TokenNameFloatingPointLiteral=60; 64 | int TokenNameDoubleLiteral=61; 65 | int TokenNameCharacterLiteral=62; 66 | int TokenNameStringLiteral=63; 67 | int TokenNamePLUS_PLUS=64; 68 | int TokenNameMINUS_MINUS=65; 69 | int TokenNameEQUAL_EQUAL=66; 70 | int TokenNameLESS_EQUAL=67; 71 | int TokenNameGREATER_EQUAL=68; 72 | int TokenNameNOT_EQUAL=69; 73 | int TokenNameLEFT_SHIFT=70; 74 | int TokenNameRIGHT_SHIFT=71; 75 | int TokenNameUNSIGNED_RIGHT_SHIFT=72; 76 | int TokenNamePLUS_EQUAL=73; 77 | int TokenNameMINUS_EQUAL=74; 78 | int TokenNameMULTIPLY_EQUAL=75; 79 | int TokenNameDIVIDE_EQUAL=76; 80 | int TokenNameAND_EQUAL=77; 81 | int TokenNameOR_EQUAL=78; 82 | int TokenNameXOR_EQUAL=79; 83 | int TokenNameREMAINDER_EQUAL=80; 84 | int TokenNameLEFT_SHIFT_EQUAL=81; 85 | int TokenNameRIGHT_SHIFT_EQUAL=82; 86 | int TokenNameUNSIGNED_RIGHT_SHIFT_EQUAL=83; 87 | int TokenNameOR_OR=84; 88 | int TokenNameAND_AND=85; 89 | int TokenNamePLUS=86; 90 | int TokenNameMINUS=87; 91 | int TokenNameNOT=88; 92 | int TokenNameREMAINDER=89; 93 | int TokenNameXOR=90; 94 | int TokenNameAND=91; 95 | int TokenNameMULTIPLY=92; 96 | int TokenNameOR=93; 97 | int TokenNameTWIDDLE=94; 98 | int TokenNameDIVIDE=95; 99 | int TokenNameGREATER=96; 100 | int TokenNameLESS=97; 101 | int TokenNameLPAREN=98; 102 | int TokenNameRPAREN=99; 103 | int TokenNameLBRACE=100; 104 | int TokenNameRBRACE=101; 105 | int TokenNameLBRACKET=102; 106 | int TokenNameRBRACKET=103; 107 | int TokenNameSEMICOLON=404; 108 | int TokenNameQUESTION=105; 109 | int TokenNameCOLON=106; 110 | int TokenNameCOMMA=107; 111 | int TokenNameDOT=108; 112 | int TokenNameEQUAL=109; 113 | int TokenNameAT=110; 114 | int TokenNameELLIPSIS=113; 115 | int TokenNameARROW=114; 116 | int TokenNameCOLON_COLON=115; 117 | int TokenNameEOF=116; 118 | int TokenNameERROR=117; 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/LanguageCFamily.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | import java.util.HashMap; 12 | 13 | /** 14 | * Singleton class containing C-like symbols and operators but no keywords 15 | */ 16 | public abstract class LanguageCFamily 17 | { 18 | public final static char EOF = '\uFFFF'; 19 | public final static char NULL_CHAR = '\u0000'; 20 | public final static char NEWLINE = '\n'; 21 | public final static char BACKSPACE = '\b'; 22 | public final static char TAB = '\t'; 23 | 24 | protected HashMap _keywords; 25 | protected HashMap _operators; 26 | 27 | private final static char[] basic_c_operators = { 28 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-', 29 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>', 30 | '?', '~', '%', '^' 31 | }; 32 | 33 | { replaceOperators(basic_c_operators); } 34 | 35 | 36 | protected void registerKeywords(String[] keywords) 37 | { 38 | _keywords = new HashMap(keywords.length); 39 | for (int i = 0; i < keywords.length; ++i) 40 | { 41 | _keywords.put(keywords[i], new Integer(Lexer.KEYWORD)); 42 | } 43 | } 44 | 45 | protected void replaceOperators(char[] operators) 46 | { 47 | _operators = new HashMap(operators.length); 48 | for (int i = 0; i < operators.length; ++i) 49 | { 50 | _operators.put(new Character(operators[i]), new Integer(Lexer.OPERATOR)); 51 | } 52 | } 53 | 54 | public final boolean isOperator(char c) 55 | { 56 | return _operators.containsKey(new Character(c)); 57 | } 58 | 59 | public final boolean isKeyword(String s) 60 | { 61 | return _keywords.containsKey(s); 62 | } 63 | 64 | public boolean isWhitespace(char c) 65 | { 66 | return (c == ' ' || c == '\n' || c == '\t' || 67 | c == '\r' || c == '\f' || c == EOF); 68 | } 69 | 70 | public boolean isSentenceTerminator(char c) 71 | { 72 | return (c == '.'); 73 | } 74 | 75 | public boolean isEscapeChar(char c) 76 | { 77 | return (c == '\\'); 78 | } 79 | 80 | /** 81 | * Derived classes that do not do represent C-like programming languages 82 | * should return false; otherwise return true 83 | */ 84 | public boolean isProgLang() 85 | { 86 | return true; 87 | } 88 | 89 | /** 90 | * Whether the word after c is a token 91 | */ 92 | public boolean isWordStart(char c) 93 | { 94 | return false; 95 | } 96 | 97 | /** 98 | * Whether cSc is a token, where S is a sequence of characters that are on the same line 99 | */ 100 | public boolean isDelimiterA(char c) 101 | { 102 | return (c == '"'); 103 | } 104 | 105 | /** 106 | * Same concept as isDelimiterA(char), but Language and its subclasses can 107 | * specify a second type of symbol to use here 108 | */ 109 | public boolean isDelimiterB(char c) 110 | { 111 | return (c == '\''); 112 | } 113 | 114 | /** 115 | * Whether cL is a token, where L is a sequence of characters until the end of the line 116 | */ 117 | public boolean isLineAStart(char c) 118 | { 119 | return (c == '#'); 120 | } 121 | 122 | /** 123 | * Same concept as isLineAStart(char), but Language and its subclasses can 124 | * specify a second type of symbol to use here 125 | */ 126 | public boolean isLineBStart(char c) 127 | { 128 | return false; 129 | } 130 | 131 | /** 132 | * Whether c0c1L is a token, where L is a sequence of characters until the end of the line 133 | */ 134 | public boolean isLineStart(char c0, char c1) 135 | { 136 | return (c0 == '/' && c1 == '/'); 137 | } 138 | 139 | /** 140 | * Whether c0c1 signifies the start of a multi-line token 141 | */ 142 | public boolean isMultilineStartDelimiter(char c0, char c1) 143 | { 144 | return (c0 == '/' && c1 == '*'); 145 | } 146 | 147 | /** 148 | * Whether c0c1 signifies the end of a multi-line token 149 | */ 150 | public boolean isMultilineEndDelimiter(char c0, char c1) 151 | { 152 | return (c0 == '*' && c1 == '/'); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/LanguageJava.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided"as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | /** 12 | * Singleton class containing the symbols and operators of the Java language 13 | */ 14 | public class LanguageJava extends Language { 15 | private static Language _theOne = null; 16 | 17 | private final static String[] keywords = { 18 | 19 | }; 20 | 21 | private final static String[] function = { 22 | "r.TCQualityGrade", "foliage.MinLOD","foliage.LODDistanceScale","+CVars=","r.PUBGDeviceFPSLow","r.PUBGDeviceFPSMid","r.PUBGDeviceFPSHigh","r.PUBGDeviceFPSHDR","r.UserVulkanSetting","r.PUBGQualityLevel","r.PUBGMaxSupportQualityLevel","r.PUBGMSAASupport","r.PUBGLDR","r.PUBGCRLRuntmieMinMem","r.PUBGRenderSwitch","r.PUBGLimit","r.PUBGVersion","r.MobileContentScaleFactor","r.Mobile.TonemapperFilm","r.Mobile.AlwaysResolveDepth","r.MobileMSAA","r.Mobile.ForceDepthResolve","r.Mobile.TextureMipmapBias","r.MobileSimpleShader","r.MobileNumDynamicPointLights","r.Mobile.EarlyZPass","r.Mobile.SceneColorFormat","r.UserQualitySetting","r.UserHDRSetting","r.UseProgramBinaryCache","r.UseShaderPrecompileCount","r.UseShaderPrecompileMemLimit","r.UserMSAASetting","r.ShadowQuality","r.Shadow.MaxCSMResolution","r.Shadow.CSM.MaxMobileCascades","r.Shadow.DistanceScale","r.MetalVertexParameterSize","r.MetalPixelParameterSize","r.MetalComputeParameterSize","r.MaterialQualityLevel","r.BinningControlHintQCOMDriver","r.BinningControlHintQCOM","r.Android.DisableVulkanSupport","r.Android.DisableOpenGLES31Support","r.ACESStyle","r.BloomQuality","r.DefaultFeature.AntiAliasing","r.Decal.StencilSizeThreshold","r.DepthOfFieldQuality","r.DetailMode","r.EmitterSpawnRateScale","r.LightShaftQuality","r.MSAACount","r.MaxAnisotropy","r.NumBufferedOcclusionQueries","r.ParticleLODBias","r.RefractionQuality","r.StaticMeshLODDistanceScale","r.StaticMeshLODLevelLimited","r.Streaming.PoolSize","r.TextureStreaming","r.TagCullingSupport" 23 | }; 24 | 25 | private final static char[] BASIC_C_OPERATORS = { 26 | '(', ')', '{', '}', '.', ',', ';', '=', '+', '-', 27 | '/', '*', '&', '!', '|', ':', '[', ']', '<', '>', 28 | '?', '~', '%', '^' 29 | }; 30 | 31 | public static Language getInstance() { 32 | if (_theOne == null) { 33 | _theOne = new LanguageJava(); 34 | } 35 | return _theOne; 36 | } 37 | 38 | private LanguageJava() { 39 | updateUserWord(); 40 | setOperators(BASIC_C_OPERATORS); 41 | setKeywords(keywords); 42 | setNames(function); 43 | } 44 | 45 | /** 46 | * Java has no preprocessors. Override base class implementation 47 | */ 48 | public boolean isLineAStart(char c) { 49 | return false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/LanguageNonProg.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | /** 12 | * Singleton class that represents a non-programming language without keywords, 13 | * operators etc. 14 | */ 15 | public class LanguageNonProg extends Language{ 16 | private static Language _theOne = null; 17 | 18 | private final static String[] keywords = {}; 19 | 20 | private final static char[] operators = {}; 21 | 22 | 23 | public static Language getInstance(){ 24 | if(_theOne == null){ 25 | _theOne = new LanguageNonProg(); 26 | } 27 | return _theOne; 28 | } 29 | 30 | private LanguageNonProg(){ 31 | super.setKeywords(keywords); 32 | super.setOperators(operators); 33 | } 34 | 35 | @Override 36 | public boolean isProgLang(){ 37 | return false; 38 | } 39 | 40 | @Override 41 | public boolean isEscapeChar(char c){ 42 | return false; 43 | } 44 | 45 | @Override 46 | public boolean isDelimiterA(char c){ 47 | return false; 48 | } 49 | 50 | @Override 51 | public boolean isDelimiterB(char c){ 52 | return false; 53 | } 54 | 55 | @Override 56 | public boolean isLineAStart(char c){ 57 | return false; 58 | } 59 | 60 | @Override 61 | public boolean isLineStart(char c0, char c1){ 62 | return false; 63 | } 64 | 65 | @Override 66 | public boolean isMultilineStartDelimiter(char c0, char c1){ 67 | return false; 68 | } 69 | 70 | @Override 71 | public boolean isMultilineEndDelimiter(char c0, char c1){ 72 | return false; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/LinearSearchStrategy.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.common; 2 | 3 | public class LinearSearchStrategy implements SearchStrategy { 4 | private int _unitsDone = 0; 5 | 6 | public LinearSearchStrategy() { 7 | } 8 | 9 | protected boolean equals(DocumentProvider var1, String var2, int var3, boolean var4) { 10 | if (var1.docLength() - var3 < var2.length()) { 11 | var4 = false; 12 | } else { 13 | int var5 = 0; 14 | 15 | while(true) { 16 | if (var5 >= var2.length()) { 17 | var4 = true; 18 | break; 19 | } 20 | 21 | if (var4 && var2.charAt(var5) != var1.charAt(var5 + var3)) { 22 | var4 = false; 23 | break; 24 | } 25 | 26 | if (!var4 && Character.toLowerCase(var2.charAt(var5)) != Character.toLowerCase(var1.charAt(var5 + var3))) { 27 | var4 = false; 28 | break; 29 | } 30 | 31 | ++var5; 32 | } 33 | } 34 | 35 | return var4; 36 | } 37 | 38 | @Override 39 | public int find(DocumentProvider var1, String var2, int var3, int var4, boolean var5, boolean var6) { 40 | int var7 = var3; 41 | if (var2.length() == 0) { 42 | var3 = -1; 43 | } else { 44 | var3 = var3; 45 | if (var7 < 0) { 46 | TextWarriorException.fail("TextBuffer.find: Invalid start position"); 47 | var3 = 0; 48 | } 49 | 50 | var7 = var4; 51 | if (var4 > var1.docLength()) { 52 | TextWarriorException.fail("TextBuffer.find: Invalid end position"); 53 | var7 = var1.docLength(); 54 | } 55 | 56 | for(var4 = Math.min(var7, var1.docLength() - var2.length() + 1); var3 < var4 && (!this.equals(var1, var2, var3, var5) || var6 && !this.isSandwichedByWhitespace(var1, var3, var2.length())); ++this._unitsDone) { 57 | ++var3; 58 | } 59 | 60 | if (var3 >= var4) { 61 | var3 = -1; 62 | } 63 | } 64 | 65 | return var3; 66 | } 67 | 68 | @Override 69 | public int findBackwards(DocumentProvider var1, String var2, int var3, int var4, boolean var5, boolean var6) { 70 | int var7 = var3; 71 | var3 = var4; 72 | if (var2.length() == 0) { 73 | var3 = -1; 74 | } else { 75 | int var8 = var7; 76 | if (var7 >= var1.docLength()) { 77 | TextWarriorException.fail("Invalid start position given to TextBuffer.find"); 78 | var8 = var1.docLength() - 1; 79 | } 80 | 81 | var4 = var4; 82 | if (var3 < -1) { 83 | TextWarriorException.fail("Invalid end position given to TextBuffer.find"); 84 | var4 = -1; 85 | } 86 | 87 | for(var3 = Math.min(var8, var1.docLength() - var2.length()); var3 > var4 && (!this.equals(var1, var2, var3, var5) || var6 && !this.isSandwichedByWhitespace(var1, var3, var2.length())); --var3) { 88 | } 89 | 90 | if (var3 <= var4) { 91 | var3 = -1; 92 | } 93 | } 94 | 95 | return var3; 96 | } 97 | 98 | @Override 99 | public int getProgress() { 100 | return this._unitsDone; 101 | } 102 | 103 | protected boolean isSandwichedByWhitespace(DocumentProvider var1, int var2, int var3) { 104 | Language var4 = Lexer.getLanguage(); 105 | boolean var5; 106 | if (var2 == 0) { 107 | var5 = true; 108 | } else { 109 | var5 = var4.isWhitespace(var1.charAt(var2 - 1)); 110 | } 111 | 112 | var2 += var3; 113 | boolean var6; 114 | if (var2 == var1.docLength()) { 115 | var6 = true; 116 | } else { 117 | var6 = var4.isWhitespace(var1.charAt(var2)); 118 | } 119 | 120 | if (var5 && var6) { 121 | var5 = true; 122 | } else { 123 | var5 = false; 124 | } 125 | 126 | return var5; 127 | } 128 | 129 | @Override 130 | public Pair replaceAll(DocumentProvider var1, String var2, String var3, int var4, boolean var5, boolean var6) { 131 | int var7 = 0; 132 | int var8 = var4; 133 | this._unitsDone = 0; 134 | char[] var9 = var3.toCharArray(); 135 | var4 = this.find(var1, var2, 0, var1.docLength(), var5, var6); 136 | long var10 = System.nanoTime(); 137 | var1.beginBatchEdit(); 138 | 139 | while(var4 != -1) { 140 | var1.deleteAt(var4, var2.length(), var10); 141 | var1.insertBefore(var9, var4, var10); 142 | int var12 = var8; 143 | if (var4 < var8) { 144 | var12 = var8 + (var3.length() - var2.length()); 145 | } 146 | 147 | ++var7; 148 | this._unitsDone += var2.length(); 149 | var4 = this.find(var1, var2, var4 + var3.length(), var1.docLength(), var5, var6); 150 | var8 = var12; 151 | } 152 | 153 | var1.endBatchEdit(); 154 | return new Pair(var7, Math.max(var8, 0)); 155 | } 156 | 157 | @Override 158 | public int wrappedFind(DocumentProvider var1, String var2, int var3, boolean var4, boolean var5) { 159 | int var6 = this.find(var1, var2, var3, var1.docLength(), var4, var5); 160 | int var7 = var6; 161 | if (var6 < 0) { 162 | var7 = this.find(var1, var2, 0, var3, var4, var5); 163 | } 164 | 165 | return var7; 166 | } 167 | 168 | @Override 169 | public int wrappedFindBackwards(DocumentProvider var1, String var2, int var3, boolean var4, boolean var5) { 170 | int var6 = this.findBackwards(var1, var2, var3, -1, var4, var5); 171 | int var7 = var6; 172 | if (var6 < 0) { 173 | var7 = this.findBackwards(var1, var2, var1.docLength() - 1, var3, var4, var5); 174 | } 175 | 176 | return var7; 177 | } 178 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/Pair.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | public final class Pair { 12 | private int _first; 13 | private int _second; 14 | 15 | public Pair(int x, int y){ 16 | _first = x; 17 | _second = y; 18 | } 19 | 20 | public final int getFirst(){ 21 | return _first; 22 | } 23 | 24 | public final int getSecond(){ 25 | return _second; 26 | } 27 | 28 | public final void setFirst(int value){ 29 | _first = value; 30 | } 31 | 32 | public final void setSecond(int value){ 33 | _second = value; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return "("+_first+","+_second+")"; 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/ProgressObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | /** 12 | * Can be registered with a {@link ProgressSource} of interest, which will cause 13 | * progress updates to be sent to the ProgressObserver. 14 | */ 15 | public interface ProgressObserver { 16 | void onComplete(int requestCode, Object result); 17 | 18 | void onError(int requestCode, int errorCode, String message); 19 | 20 | void onCancel(int requestCode); 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/ProgressSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | /** 12 | * Represents tasks that carry out long computations 13 | */ 14 | public interface ProgressSource { 15 | /* Nature of computation tasks */ 16 | int NONE = 0; 17 | int READ = 1; 18 | int WRITE = 2; 19 | int FIND = 4; 20 | int FIND_BACKWARDS = 8; 21 | int REPLACE_ALL = 16; 22 | int ANALYZE_TEXT = 32; 23 | /* Error codes */ 24 | int ERROR_UNKNOWN = 0; 25 | int ERROR_OUT_OF_MEMORY = 1; 26 | int ERROR_INDEX_OUT_OF_RANGE = 2; 27 | 28 | /** Minimum progress value */ 29 | int getMin(); 30 | 31 | /** Maximum progress value */ 32 | int getMax(); 33 | 34 | /** Current progress value */ 35 | int getCurrent(); 36 | 37 | /** Whether computation is done */ 38 | boolean isDone(); 39 | 40 | /** Aborts computation */ 41 | void forceStop(); 42 | 43 | /** Registers observers that will be informed of changes to the progress state */ 44 | void registerObserver(ProgressObserver obsv); 45 | 46 | /** Removes all attached observers */ 47 | void removeObservers(); 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/ReadThread.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.common; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | 6 | import java.io.FileInputStream; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | import java.nio.charset.StandardCharsets; 10 | 11 | /** 12 | * Created by zyw on 2017/10/2. 13 | */ 14 | public class ReadThread extends Thread 15 | { 16 | public static final int MSG_READ_OK =0x101; 17 | public static final int MSG_READ_FAIL =0x102; 18 | private Handler handler; 19 | private String path; 20 | public ReadThread(String path, Handler handler) 21 | { 22 | this.path=path; 23 | this.handler=handler; 24 | } 25 | @Override 26 | public void run() { 27 | synchronized (handler) { 28 | readFile(path); 29 | } 30 | } 31 | 32 | private void readFile(String file) 33 | { 34 | boolean isOk=false; 35 | FileInputStream fileInputStream = null; 36 | InputStreamReader inputStreamReader=null; 37 | StringBuilder stringBuilder=new StringBuilder(); 38 | try { 39 | fileInputStream=new FileInputStream(file); 40 | inputStreamReader = new InputStreamReader(fileInputStream, StandardCharsets.UTF_8); 41 | char[] buf=new char[1024]; 42 | int len=0; 43 | while ((len=inputStreamReader.read(buf))!=-1){ 44 | stringBuilder.append(new String(buf,0,len)); 45 | } 46 | isOk=true; 47 | 48 | } catch (IOException e) { 49 | e.printStackTrace(); 50 | isOk=false; 51 | }finally { 52 | if(inputStreamReader!=null) 53 | { 54 | try { 55 | inputStreamReader.close(); 56 | } catch (IOException e) { 57 | e.printStackTrace(); 58 | } 59 | } 60 | } 61 | if(isOk) 62 | { 63 | if(handler!=null) 64 | handler.sendMessage(Message.obtain(handler, MSG_READ_OK,stringBuilder.toString())); 65 | }else 66 | { 67 | if(handler!=null) 68 | handler.sendMessage(Message.obtain(handler, MSG_READ_FAIL)); 69 | } 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/RowListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | public interface RowListener { 12 | void onRowChange(int newRowIndex); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/SearchStrategy.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.common; 2 | 3 | public interface SearchStrategy { 4 | int find(DocumentProvider var1, String var2, int var3, int var4, boolean var5, boolean var6); 5 | 6 | int findBackwards(DocumentProvider var1, String var2, int var3, int var4, boolean var5, boolean var6); 7 | 8 | int getProgress(); 9 | 10 | Pair replaceAll(DocumentProvider var1, String var2, String var3, int var4, boolean var5, boolean var6); 11 | 12 | int wrappedFind(DocumentProvider var1, String var2, int var3, boolean var4, boolean var5); 13 | 14 | int wrappedFindBackwards(DocumentProvider var1, String var2, int var3, boolean var4, boolean var5); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/TextBufferCache.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | /** 12 | * A LRU cache that stores the last seek line and its corresponding index so 13 | * that future lookups can start from the cached position instead of 14 | * the beginning of the file 15 | * 16 | * _cache.Pair.First = line index 17 | * _cache.Pair.Second = character offset of first character in that line 18 | * 19 | * TextBufferCache always has one valid entry (0,0) signifying that in line 0, 20 | * the first character is at offset 0. This is true even for an "empty" file, 21 | * which is not really empty because TextBuffer inserts a EOF character in it. 22 | * 23 | * Therefore, _cache[0] is always occupied by the entry (0,0). It is not affected 24 | * by invalidateCache, cache miss, etc. operations 25 | */ 26 | public class TextBufferCache { 27 | private static final int CACHE_SIZE = 4; // minimum = 1 28 | private Pair[] _cache = new Pair[CACHE_SIZE]; 29 | 30 | public TextBufferCache(){ 31 | _cache[0] = new Pair(0, 0); // invariant lineIndex and charOffset relation 32 | for (int i = 1; i < CACHE_SIZE; ++i){ 33 | _cache[i] = new Pair(-1, -1); 34 | // -1 line index is used implicitly in calculations in getNearestMatch 35 | } 36 | } 37 | 38 | //TODO consider extracting common parts with getNearestCharOffset(int) 39 | public Pair getNearestLine(int lineIndex){ 40 | int nearestMatch = 0; 41 | int nearestDistance = Integer.MAX_VALUE; 42 | for(int i = 0; i < CACHE_SIZE; ++i){ 43 | int distance = Math.abs(lineIndex - _cache[i].getFirst()); 44 | if (distance < nearestDistance){ 45 | nearestDistance = distance; 46 | nearestMatch = i; 47 | } 48 | } 49 | 50 | Pair nearestEntry = _cache[nearestMatch]; 51 | makeHead(nearestMatch); 52 | return nearestEntry; 53 | } 54 | 55 | public Pair getNearestCharOffset(int charOffset){ 56 | int nearestMatch = 0; 57 | int nearestDistance = Integer.MAX_VALUE; 58 | for(int i = 0; i < CACHE_SIZE; ++i){ 59 | int distance = Math.abs(charOffset - _cache[i].getSecond()); 60 | if (distance < nearestDistance){ 61 | nearestDistance = distance; 62 | nearestMatch = i; 63 | } 64 | } 65 | 66 | Pair nearestEntry = _cache[nearestMatch]; 67 | makeHead(nearestMatch); 68 | return nearestEntry; 69 | } 70 | 71 | /** 72 | * Place _cache[newHead] at the top of the list 73 | */ 74 | private void makeHead(int newHead){ 75 | if(newHead == 0){ 76 | return; 77 | } 78 | 79 | Pair temp = _cache[newHead]; 80 | for(int i = newHead; i > 1; --i){ 81 | _cache[i] = _cache[i-1]; 82 | } 83 | _cache[1] = temp; // _cache[0] is always occupied by (0,0) 84 | } 85 | 86 | public void updateEntry(int lineIndex, int charOffset){ 87 | if(lineIndex <= 0){ 88 | // lineIndex 0 always has 0 charOffset; ignore. Also ignore negative lineIndex 89 | return; 90 | } 91 | 92 | if(!replaceEntry(lineIndex, charOffset)){ 93 | insertEntry(lineIndex, charOffset); 94 | } 95 | } 96 | 97 | private boolean replaceEntry(int lineIndex, int charOffset){ 98 | for (int i = 1; i < CACHE_SIZE; ++i){ 99 | if(_cache[i].getFirst() == lineIndex){ 100 | _cache[i].setSecond(charOffset); 101 | return true; 102 | } 103 | } 104 | return false; 105 | } 106 | 107 | private void insertEntry(int lineIndex, int charOffset){ 108 | makeHead(CACHE_SIZE-1); // rotate right list of entries 109 | // replace head (most recently used entry) with new entry 110 | _cache[1] = new Pair(lineIndex, charOffset); 111 | } 112 | 113 | /** 114 | * Invalidate all cache entries that have char offset >= fromCharOffset 115 | */ 116 | final protected void invalidateCache(int fromCharOffset){ 117 | for (int i = 1; i < CACHE_SIZE; ++i){ 118 | if(_cache[i].getSecond() >= fromCharOffset){ 119 | _cache[i] = new Pair(-1, -1); 120 | } 121 | } 122 | } 123 | } 124 | 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/TextWarriorException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013 Tah Wei Hoon. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the Apache License Version 2.0, 5 | * with full text available at http://www.apache.org/licenses/LICENSE-2.0.html 6 | * 7 | * This software is provided "as is". Use at your own risk. 8 | */ 9 | package com.pointer.wave.easyship.common; 10 | 11 | import android.util.Log; 12 | 13 | public class TextWarriorException extends Exception { 14 | private static final boolean NDEBUG = false; // set to true to suppress assertions 15 | private static final long serialVersionUID = -8393914265675038931L; 16 | 17 | public TextWarriorException(String msg){ 18 | super(msg); 19 | } 20 | 21 | static public void fail(final String details){ 22 | assertVerbose(false, details); 23 | } 24 | 25 | @SuppressWarnings("all") //suppress dead code warning when NDEBUG == true 26 | static public void assertVerbose(boolean condition, final String details){ 27 | if(NDEBUG){ 28 | return; 29 | } 30 | 31 | if (!condition){ 32 | /* BlackBerry dialog way of displaying errors 33 | UiApplication.getUiApplication().invokeLater(new Runnable() 34 | { 35 | public void run() 36 | { 37 | Dialog.alert(details); 38 | } 39 | }); 40 | */ 41 | 42 | /* For Android, a Context has to be passed into this method 43 | * to display the error message on the device screen */ 44 | 45 | System.err.print("TextWarrior assertion failed: "); 46 | System.err.println(details); 47 | Log.d("lua",details); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/common/WriteThread.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.common; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | 6 | import java.io.File; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.OutputStreamWriter; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | /** 13 | * Created by zyw on 2017/10/2. 14 | */ 15 | public class WriteThread extends Thread { 16 | 17 | public static final int MSG_WRITE_OK =0x201; 18 | public static final int MSG_WRITE_FAIL =0x202; 19 | private Handler handler; 20 | private String outputPath; 21 | private String text; 22 | public WriteThread(String text,String outputPath, Handler handler) 23 | { 24 | this.outputPath=outputPath; 25 | this.text=text; 26 | this.handler=handler; 27 | } 28 | @Override 29 | public void run() { 30 | 31 | synchronized (handler) { 32 | writeFile(text, new File(outputPath)); 33 | } 34 | 35 | } 36 | 37 | /** 38 | * 写文件 39 | * @param text 40 | * @param outputFile 41 | * @throws IOException 42 | */ 43 | private void writeFile( final String text,File outputFile) { 44 | boolean isOk=false; 45 | if(!outputFile.getParentFile().exists()) 46 | outputFile.getParentFile().mkdirs(); 47 | FileOutputStream fileOutputStream=null; 48 | OutputStreamWriter outputStreamWriter=null; 49 | try { 50 | fileOutputStream=new FileOutputStream(outputFile); 51 | outputStreamWriter = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8); 52 | outputStreamWriter.write(text); 53 | outputStreamWriter.flush(); 54 | isOk=true; 55 | }catch (IOException e){ 56 | e.printStackTrace(); 57 | isOk=false; 58 | 59 | }finally { 60 | if(outputStreamWriter!=null) 61 | try { 62 | outputStreamWriter.close(); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | if(isOk) { 68 | if (handler != null) 69 | handler.sendMessage(Message.obtain(handler, MSG_WRITE_OK)); 70 | } 71 | else 72 | { 73 | if(handler!=null) 74 | handler.sendMessage(Message.obtain(handler, MSG_WRITE_FAIL)); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/core/LoadShellUtil.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.core; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | 10 | public class LoadShellUtil { 11 | 12 | public final Context context; 13 | public String _sh = ""; 14 | public final String workFolder; 15 | public final String shellName = "run.sh"; 16 | public final String shell2Name = "run.sh"; 17 | public final String _7zaName = "7za"; 18 | 19 | public LoadShellUtil(Context context){ 20 | this.context = context; 21 | workFolder = this.context.getFilesDir().getPath() + "/work"; 22 | } 23 | 24 | public void moveResources(){ 25 | copy(shellName); 26 | //copy(shell2Name); 27 | copy(_7zaName); 28 | } 29 | 30 | public void remove(){ 31 | File file = new File(workFolder); 32 | file.delete(); 33 | } 34 | 35 | private boolean copy(String filename){ 36 | File flFile = new File(workFolder); 37 | if (!flFile.exists()) { 38 | flFile.mkdirs(); 39 | } 40 | boolean isCopysucss = false; 41 | try { 42 | InputStream is = this.context.getAssets().open(filename);//从assets文件夹中复制文件 43 | File file = new File(workFolder + "/" + filename); 44 | FileOutputStream fos = new FileOutputStream(file); 45 | byte[] buffer = new byte[1024]; 46 | int byteCount; 47 | while ((byteCount = is.read(buffer)) != -1) { 48 | fos.write(buffer, 0, byteCount); 49 | } 50 | fos.flush(); 51 | fos.close(); 52 | is.close(); 53 | isCopysucss = true; 54 | } catch (IOException e) { 55 | e.printStackTrace(); 56 | } 57 | return isCopysucss; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/core/RealtimeProcess.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.core; 2 | 3 | import com.pointer.wave.easyship.core.CacheDao; 4 | import com.pointer.wave.easyship.interfaces.RealtimeProcessInterface; 5 | 6 | import java.io.BufferedReader; 7 | import java.io.DataOutputStream; 8 | import java.io.File; 9 | import java.io.IOException; 10 | import java.io.InputStreamReader; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Random; 14 | 15 | 16 | public class RealtimeProcess{ 17 | // 是否在执行 18 | private boolean isRunning = false; 19 | // 存放命令行 20 | private ArrayList commandList = new ArrayList<>(); 21 | // 保存所有的输出信息 22 | private StringBuffer mStringBuffer = new StringBuffer(); 23 | private BufferedReader readStdout = null; 24 | private BufferedReader readStderr = null; 25 | // 回调用到的接口 26 | private RealtimeProcessInterface mInterface; 27 | private int resultCode = 0; 28 | private String ROOT_DIR = null; 29 | private String tmp1 = null; 30 | private String tmp2 = null; 31 | private final MSG msg; 32 | private final ErrorMSG errorMSG; 33 | 34 | public MSG getMsg() { 35 | return msg; 36 | } 37 | 38 | public ErrorMSG getErrorMSG() { 39 | return errorMSG; 40 | } 41 | 42 | public RealtimeProcess(RealtimeProcessInterface mInterface){ 43 | // 实例化接口对象 44 | this.mInterface = mInterface; 45 | this.errorMSG = new ErrorMSG(); 46 | this.msg = new MSG(); 47 | } 48 | public void setCommand(String ...commands){ 49 | // 遍历命令 50 | for(String cmd : commands){ 51 | RealtimeProcessCommand mRealtimeProcessCommand = new RealtimeProcessCommand(); 52 | mRealtimeProcessCommand.setCommand(cmd); 53 | commandList.add(mRealtimeProcessCommand); 54 | } 55 | } 56 | public void setDirectory(String directory){ 57 | this.ROOT_DIR = directory; 58 | } 59 | 60 | public void start() throws IOException, InterruptedException{ 61 | isRunning = true; 62 | Process p = Runtime.getRuntime().exec("su"); 63 | DataOutputStream write = new DataOutputStream(p.getOutputStream()); 64 | for(RealtimeProcessCommand mRealtimeProcessCommand : commandList){ 65 | write.writeBytes(mRealtimeProcessCommand.getCommand()); 66 | write.writeBytes("\n"); 67 | write.flush(); 68 | } 69 | write.writeBytes("exit"); 70 | write.writeBytes("\n"); 71 | write.flush(); 72 | exec(p); 73 | } 74 | public String getAllResult(){ 75 | return mStringBuffer.toString(); 76 | } 77 | 78 | private final Random random = new Random(); 79 | private void exec(final Process process){ 80 | // 获取标准输出 81 | readStdout = new BufferedReader(new InputStreamReader(process.getInputStream())); 82 | // 获取错误输出 83 | readStderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); 84 | // 创建线程执行 85 | try { 86 | // 逐行读取 87 | while((tmp1 = readStdout.readLine()) != null || (tmp2 = readStderr.readLine()) != null){ 88 | if(tmp1 != null){ 89 | mStringBuffer.append(tmp1 + "\n"); 90 | // 回调接口方法 91 | mInterface.onNewStdoutListener(msg.append(tmp1)); 92 | System.out.println("tmp1 = " + tmp1); 93 | } 94 | if(tmp2 != null){ 95 | mStringBuffer.append(tmp2 + "\n"); 96 | mInterface.onNewStderrListener(errorMSG.append(tmp2)); 97 | System.out.println("tmp2 = " + tmp2); 98 | } 99 | // Thread.currentThread().sleep(random.nextInt(101)); 100 | } 101 | resultCode = process.waitFor(); 102 | } catch (IOException | InterruptedException e) { 103 | // TODO Auto-generated catch block 104 | e.printStackTrace(); 105 | } 106 | isRunning = false; 107 | mInterface.onProcessFinish(resultCode); 108 | } 109 | public boolean isRunning(){ 110 | return this.isRunning; 111 | } 112 | public int getCommandSize(){ 113 | return commandList.size(); 114 | } 115 | public RealtimeProcessCommand getRealtimeProcessCommand(int p){ 116 | return commandList.get(p); 117 | } 118 | 119 | static class RealtimeProcessCommand{ 120 | private String directory = null; 121 | private String command = null; 122 | public RealtimeProcessCommand(){} 123 | 124 | public void setDirectory(String directory){ 125 | this.directory = directory; 126 | } 127 | public void setCommand(String command){ 128 | this.command = command; 129 | } 130 | public String getDirectory(){ 131 | return this.directory; 132 | } 133 | public String getCommand(){ 134 | return this.command; 135 | } 136 | 137 | } 138 | 139 | static public class MSG{ 140 | private final List list = new ArrayList<>(); 141 | 142 | public synchronized MSG append(String s){ 143 | list.add(s + "\n"); 144 | return this; 145 | } 146 | 147 | public synchronized String getLastLine(){ 148 | return list.size() > 0 ? list.get(list.size() - 1) : ""; 149 | } 150 | 151 | @Override 152 | public synchronized String toString() { 153 | StringBuilder stringBuilder = new StringBuilder(); 154 | for (String s : list) { 155 | stringBuilder.append(s); 156 | } 157 | return stringBuilder.toString(); 158 | } 159 | 160 | public List getList() { 161 | return list; 162 | } 163 | } 164 | 165 | static public class ErrorMSG{ 166 | private final List list = new ArrayList<>(); 167 | 168 | public List getList() { 169 | return list; 170 | } 171 | 172 | public synchronized ErrorMSG append(String s){ 173 | list.add(s + "\n"); 174 | return this; 175 | } 176 | 177 | public synchronized String getLastLine(){ 178 | return list.size() > 0 ? list.get(list.size() - 1) : ""; 179 | } 180 | 181 | @Override 182 | public synchronized String toString() { 183 | StringBuilder stringBuilder = new StringBuilder(); 184 | for (String s : list) { 185 | stringBuilder.append(s); 186 | } 187 | return stringBuilder.toString(); 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/core/RunTimeUtils.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.core; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | public class RunTimeUtils implements OnResultListener { 8 | 9 | private static RunTimeUtils INSTANCE = new RunTimeUtils(); 10 | 11 | private OnResultListener resultListener; 12 | private RunTimeUtils(){ 13 | resultListener = this; 14 | } 15 | 16 | public static RunTimeUtils getInstance(){ 17 | return INSTANCE; 18 | } 19 | 20 | public void cmd(String cmd) throws IOException { 21 | Process su = Runtime.getRuntime().exec("su"); 22 | InputStream inputStream = su.getInputStream(); 23 | OutputStream outputStream = su.getOutputStream(); 24 | InputStream errorStream = su.getErrorStream(); 25 | } 26 | 27 | @Override 28 | public void onResult(String result) { 29 | 30 | } 31 | 32 | public OnResultListener getRunning() { 33 | return resultListener; 34 | } 35 | 36 | public void setRunning(OnResultListener resultListener) { 37 | this.resultListener = resultListener; 38 | } 39 | } 40 | interface OnResultListener{ 41 | void onResult(String result); 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/core/UpdateEngineCallback.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.core; 2 | 3 | public abstract class UpdateEngineCallback { 4 | 5 | public abstract void onStatusUpdate(int status, float percent); 6 | 7 | public abstract void onPayloadApplicationComplete(int errorCode); 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/core/UpdateParser.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.core; 2 | 3 | import android.util.Log; 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.io.InputStreamReader; 8 | import java.util.Arrays; 9 | import java.util.Enumeration; 10 | import java.util.Locale; 11 | import java.util.zip.ZipEntry; 12 | import java.util.zip.ZipFile; 13 | 14 | 15 | /** Parse an A/B update zip file. */ 16 | public class UpdateParser { 17 | 18 | private static final String TAG = "ROTAUpdateManager"; 19 | private static final String PAYLOAD_BIN_FILE = "payload.bin"; 20 | private static final String PAYLOAD_PROPERTIES = "payload_properties.txt"; 21 | private static final String FILE_URL_PREFIX = "file://"; 22 | private static final int ZIP_FILE_HEADER = 30; 23 | 24 | private UpdateParser() { 25 | } 26 | 27 | /** 28 | * Parse a zip file containing a system update and return a non null ParsedUpdate. 29 | */ 30 | static ParsedUpdate parse(File file) throws IOException { 31 | long payloadOffset = 0; 32 | long payloadSize = 0; 33 | boolean payloadFound = false; 34 | String[] props = null; 35 | 36 | try (ZipFile zipFile = new ZipFile(file)) { 37 | Enumeration entries = zipFile.entries(); 38 | while (entries.hasMoreElements()) { 39 | ZipEntry entry = entries.nextElement(); 40 | long fileSize = entry.getCompressedSize(); 41 | if (!payloadFound) { 42 | payloadOffset += ZIP_FILE_HEADER + entry.getName().length(); 43 | if (entry.getExtra() != null) { 44 | payloadOffset += entry.getExtra().length; 45 | } 46 | } 47 | 48 | if (entry.isDirectory()) { 49 | continue; 50 | } else if (entry.getName().equals(PAYLOAD_BIN_FILE)) { 51 | payloadSize = fileSize; 52 | payloadFound = true; 53 | } else if (entry.getName().equals(PAYLOAD_PROPERTIES)) { 54 | try (BufferedReader buffer = new BufferedReader( 55 | new InputStreamReader(zipFile.getInputStream(entry)))) { 56 | props = buffer.lines().toArray(String[]::new); 57 | } 58 | } 59 | if (!payloadFound) { 60 | payloadOffset += fileSize; 61 | } 62 | 63 | //if (Log.isLoggable(TAG, Log.DEBUG)) { 64 | Log.d(TAG, String.format("Entry %s", entry.getName())); 65 | //} 66 | } 67 | } 68 | return new ParsedUpdate(file, payloadOffset, payloadSize, props); 69 | } 70 | 71 | /** Information parsed from an update file. */ 72 | static class ParsedUpdate { 73 | final String mUrl; 74 | final long mOffset; 75 | final long mSize; 76 | final String[] mProps; 77 | 78 | ParsedUpdate(File file, long offset, long size, String[] props) { 79 | mUrl = FILE_URL_PREFIX + file.getAbsolutePath(); 80 | mOffset = offset; 81 | mSize = size; 82 | mProps = props; 83 | } 84 | 85 | /** Verify the update information is correct. */ 86 | boolean isValid() { 87 | return mOffset >= 0 && mSize > 0 && mProps != null; 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return String.format(Locale.getDefault(), 93 | "ParsedUpdate: URL=%s, offset=%d, size=%s, props=%s", 94 | mUrl, mOffset, mSize, Arrays.toString(mProps)); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/core/ZoomInTransformer.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.core 2 | 3 | import android.view.View 4 | import androidx.viewpager2.widget.ViewPager2 5 | import kotlin.math.abs 6 | 7 | 8 | class ZoomInTransformer : ViewPager2.PageTransformer { 9 | override fun transformPage(page: View, pos: Float) { 10 | val scale = if (pos < 0) pos + 1f else abs(1f - pos) 11 | page.scaleX = scale 12 | page.scaleY = scale 13 | page.pivotX = page.width * 0.2f 14 | page.pivotY = page.height * 0.2f 15 | page.alpha = if (pos < -1f || pos > 1f) 0f else 1f - (scale - 1f) 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/interfaces/OnNavigationStateListener.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.interfaces; 2 | 3 | public interface OnNavigationStateListener { 4 | void onNavigationState(boolean b, int i); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/interfaces/OnProcessDestroyListener.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.interfaces; 2 | 3 | public interface OnProcessDestroyListener { 4 | void onDestroy(); 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/interfaces/OnProcessErrorListener.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.interfaces; 2 | 3 | import com.pointer.wave.easyship.core.CacheDao; 4 | 5 | public interface OnProcessErrorListener { 6 | void onErrorRead(CacheDao.MSG msg); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/interfaces/OnRunTimeListener.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.interfaces; 2 | 3 | import com.pointer.wave.easyship.core.CacheDao; 4 | 5 | public interface OnRunTimeListener { 6 | void onRead(final CacheDao.MSG read); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/interfaces/RealtimeProcessInterface.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.interfaces; 2 | 3 | import com.pointer.wave.easyship.core.RealtimeProcess; 4 | 5 | public interface RealtimeProcessInterface { 6 | void onNewStdoutListener(RealtimeProcess.MSG msg); 7 | 8 | void onNewStderrListener(RealtimeProcess.ErrorMSG errorMSG); 9 | 10 | void onProcessFinish(int resultCode); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/ktx/AppGlobalScope.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.ktx 2 | 3 | import android.os.Looper 4 | import kotlinx.coroutines.* 5 | import java.io.Closeable 6 | import java.util.concurrent.atomic.AtomicReference 7 | import kotlin.coroutines.AbstractCoroutineContextElement 8 | import kotlin.coroutines.CoroutineContext 9 | 10 | // 保存 CoroutineScope 11 | private var scopeRef: AtomicReference = AtomicReference() 12 | 13 | // 自定义的 CoroutineScope 14 | val appGlobalScope: CoroutineScope 15 | get() { 16 | while (true) { 17 | val existing = scopeRef.get() as CoroutineScope? 18 | if (existing != null) { 19 | return existing 20 | } 21 | val newScope = SafeCoroutineScope(Dispatchers.Main.immediate) 22 | if (scopeRef.compareAndSet(null, newScope)) { 23 | return newScope 24 | } 25 | } 26 | } 27 | 28 | // 不会崩溃的 CoroutineScope 29 | private class SafeCoroutineScope(context: CoroutineContext) : CoroutineScope, Closeable { 30 | override val coroutineContext: CoroutineContext = 31 | SupervisorJob() + context + UncaughtCoroutineExceptionHandler() 32 | 33 | override fun close() { 34 | coroutineContext.cancelChildren() 35 | } 36 | } 37 | 38 | // 自定义 CoroutineExceptionHandler 39 | private class UncaughtCoroutineExceptionHandler : CoroutineExceptionHandler, 40 | AbstractCoroutineContextElement(CoroutineExceptionHandler) { 41 | override fun handleException(context: CoroutineContext, exception: Throwable) { 42 | // 处理异常 43 | } 44 | } 45 | 46 | 47 | val isOnMainThread: Boolean 48 | get() = Looper.myLooper() == Looper.getMainLooper() 49 | 50 | fun runOnMainThread(block: () -> Unit) { 51 | if (isOnMainThread) block.invoke() 52 | else appGlobalScope.launch(Dispatchers.Main) { block.invoke() } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/ktx/KoinKtx.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.ktx 2 | 3 | import org.koin.core.context.GlobalContext 4 | import org.koin.core.parameter.ParametersDefinition 5 | import org.koin.core.qualifier.Qualifier 6 | import org.koin.mp.KoinPlatformTools 7 | 8 | inline fun get( 9 | qualifier: Qualifier? = null, 10 | noinline parameters: ParametersDefinition? = null 11 | ): T { 12 | return GlobalContext.get().get(qualifier, parameters) 13 | } 14 | 15 | inline fun inject( 16 | qualifier: Qualifier? = null, 17 | mode: LazyThreadSafetyMode = KoinPlatformTools.defaultLazyMode(), 18 | noinline parameters: ParametersDefinition? = null 19 | ): Lazy { 20 | return GlobalContext.get().inject(qualifier, mode, parameters) 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/net/Api.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.net 2 | 3 | import com.pointer.wave.easyship.pojo.TipsBen 4 | import okhttp3.FormBody 5 | import retrofit2.http.Body 6 | import retrofit2.http.POST 7 | 8 | interface Api { 9 | @POST("http://ly.lumnytool.club/api/read.php") 10 | suspend fun tips( 11 | @Body body: FormBody 12 | ): TipsBen 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/net/NetModule.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.net 2 | 3 | import okhttp3.OkHttpClient 4 | import retrofit2.Retrofit 5 | import retrofit2.converter.gson.GsonConverterFactory 6 | import retrofit2.converter.scalars.ScalarsConverterFactory 7 | 8 | private val okhttp = OkHttpClient.Builder() 9 | .addInterceptor(LogInterceptor()) 10 | .build() 11 | 12 | val retrofit: Retrofit = Retrofit.Builder() 13 | .client(okhttp) 14 | .baseUrl("https://bot.k2t3k.tk/api/") 15 | .addConverterFactory(ScalarsConverterFactory.create()) 16 | .addConverterFactory(GsonConverterFactory.create()) 17 | .build() -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/net/repo/UpdateRepo.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.net.repo 2 | 3 | import com.pointer.wave.easyship.net.Api 4 | import com.pointer.wave.easyship.net.retrofit 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.flow.flow 7 | import kotlinx.coroutines.flow.flowOn 8 | import okhttp3.FormBody 9 | 10 | class UpdateRepo { 11 | private val api by lazy { retrofit.create(Api::class.java) } 12 | 13 | suspend fun update() = 14 | flow { 15 | val body = FormBody.Builder().apply { 16 | add("id","103169318") 17 | add("api","easy_ship") 18 | add("dir","update") 19 | add("name","update.txt") 20 | } 21 | emit( 22 | api.tips( 23 | body.build() 24 | ) 25 | ) 26 | }.flowOn(Dispatchers.IO) 27 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/pojo/TipsBen.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.pojo; 2 | 3 | public class TipsBen { 4 | private String name; 5 | private String is_dir; 6 | private String time; 7 | private String size; 8 | private String content; 9 | 10 | public TipsBen() { 11 | } 12 | 13 | public TipsBen(String name, String is_dir, String time, String size, String content) { 14 | this.name = name; 15 | this.is_dir = is_dir; 16 | this.time = time; 17 | this.size = size; 18 | this.content = content; 19 | } 20 | 21 | public String getName() { 22 | return name; 23 | } 24 | 25 | public void setName(String name) { 26 | this.name = name; 27 | } 28 | 29 | public String getIs_dir() { 30 | return is_dir; 31 | } 32 | 33 | public void setIs_dir(String is_dir) { 34 | this.is_dir = is_dir; 35 | } 36 | 37 | public String getTime() { 38 | return time; 39 | } 40 | 41 | public void setTime(String time) { 42 | this.time = time; 43 | } 44 | 45 | public String getSize() { 46 | return size; 47 | } 48 | 49 | public void setSize(String size) { 50 | this.size = size; 51 | } 52 | 53 | public String getContent() { 54 | return content; 55 | } 56 | 57 | public void setContent(String content) { 58 | this.content = content; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "TipsBen{" + 64 | "name='" + name + '\'' + 65 | ", is_dir='" + is_dir + '\'' + 66 | ", time='" + time + '\'' + 67 | ", size='" + size + '\'' + 68 | ", content='" + content + '\'' + 69 | '}'; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/pojo/UpdateLogBen.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.pojo; 2 | 3 | public class UpdateLogBen { 4 | 5 | private String versionName; 6 | private String versionCode; 7 | private String updateContent; 8 | private String downloadUrl; 9 | 10 | public UpdateLogBen() { 11 | } 12 | 13 | public UpdateLogBen(String versionName, String versionCode, String updateContent) { 14 | this.versionName = versionName; 15 | this.versionCode = versionCode; 16 | this.updateContent = updateContent; 17 | } 18 | 19 | public String getVersionName() { 20 | return versionName; 21 | } 22 | 23 | public void setVersionName(String versionName) { 24 | this.versionName = versionName; 25 | } 26 | 27 | public String getVersionCode() { 28 | return versionCode; 29 | } 30 | 31 | public void setVersionCode(String versionCode) { 32 | this.versionCode = versionCode; 33 | } 34 | 35 | public String getDownloadUrl() { 36 | return downloadUrl; 37 | } 38 | 39 | public void setDownloadUrl(String downloadUrl) { 40 | this.downloadUrl = downloadUrl; 41 | } 42 | 43 | public String getUpdateContent() { 44 | return updateContent; 45 | } 46 | 47 | public void setUpdateContent(String updateContent) { 48 | this.updateContent = updateContent; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "

" + versionName + "

" + 54 | "

版本号:" + versionCode + "

" + 55 | "

更新内容:

" + updateContent.replace("\n", "
"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/pojo/VersionBen.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.pojo; 2 | 3 | public class VersionBen { 4 | 5 | private String versionName; 6 | private String versionCode; 7 | private String updateContent; 8 | private String downloadUrl; 9 | 10 | public VersionBen() { 11 | } 12 | 13 | public VersionBen(String versionName, String versionCode, String updateContent) { 14 | this.versionName = versionName; 15 | this.versionCode = versionCode; 16 | this.updateContent = updateContent; 17 | } 18 | 19 | public String getVersionName() { 20 | return versionName; 21 | } 22 | 23 | public void setVersionName(String versionName) { 24 | this.versionName = versionName; 25 | } 26 | 27 | public String getVersionCode() { 28 | return versionCode; 29 | } 30 | 31 | public void setVersionCode(String versionCode) { 32 | this.versionCode = versionCode; 33 | } 34 | 35 | public String getDownloadUrl() { 36 | return downloadUrl; 37 | } 38 | 39 | public void setDownloadUrl(String downloadUrl) { 40 | this.downloadUrl = downloadUrl; 41 | } 42 | 43 | public String getUpdateContent() { 44 | return updateContent; 45 | } 46 | 47 | public void setUpdateContent(String updateContent) { 48 | this.updateContent = updateContent; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/services/ProcessService.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.services; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | public class ProcessService extends Service { 10 | 11 | @Nullable 12 | @Override 13 | public IBinder onBind(Intent intent) { 14 | return null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/ColorChangeUtils.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ArgbEvaluator; 6 | import android.animation.ObjectAnimator; 7 | import android.annotation.SuppressLint; 8 | import android.os.Handler; 9 | import android.view.View; 10 | 11 | 12 | public class ColorChangeUtils { 13 | 14 | private final int[] colors; 15 | private long duration = 2000; 16 | private long delay = 5000; 17 | private int index = -1; 18 | 19 | private final Handler mHandler; 20 | private final Runnable mRunnable; 21 | 22 | public ColorChangeUtils(int[] colors, View mView) { 23 | this.colors = colors; 24 | mHandler = new Handler(mView.getContext().getMainLooper()); 25 | mRunnable = new Runnable() { 26 | @Override 27 | public void run() { 28 | @SuppressLint("ObjectAnimatorBinding") ObjectAnimator backgroundColor = ObjectAnimator.ofInt(mView, "backgroundColor", getColorTemp(), nextColor()); 29 | backgroundColor.setDuration(duration); 30 | backgroundColor.setEvaluator(new ArgbEvaluator()); 31 | backgroundColor.addListener(new AnimatorListenerAdapter() { 32 | @Override 33 | public void onAnimationEnd(Animator animation) { 34 | super.onAnimationEnd(animation); 35 | mHandler.postDelayed(mRunnable, delay); 36 | } 37 | }); 38 | backgroundColor.start(); 39 | } 40 | }; 41 | 42 | @SuppressLint("ObjectAnimatorBinding") ObjectAnimator backgroundColor = ObjectAnimator.ofInt(mView, "backgroundColor", getColorTemp(), nextColor()); 43 | backgroundColor.setDuration(0); 44 | backgroundColor.addListener(new Animator.AnimatorListener() { 45 | @Override 46 | public void onAnimationStart(Animator animation) { 47 | 48 | } 49 | 50 | @Override 51 | public void onAnimationEnd(Animator animation) { 52 | 53 | } 54 | 55 | @Override 56 | public void onAnimationCancel(Animator animation) { 57 | 58 | } 59 | 60 | @Override 61 | public void onAnimationRepeat(Animator animation) { 62 | 63 | } 64 | }); 65 | backgroundColor.start(); 66 | } 67 | 68 | 69 | private int nextColor(){ 70 | int i = index + 1; 71 | if (i >= colors.length) i = 0; 72 | return colors[i]; 73 | } 74 | 75 | private int getColorTemp(){ 76 | index++; 77 | if (index >= colors.length) index = 0; 78 | return colors[index]; 79 | } 80 | 81 | public long getDuration() { 82 | return duration; 83 | } 84 | 85 | public void setDuration(long duration) { 86 | this.duration = duration; 87 | } 88 | 89 | public long getDelay() { 90 | return delay; 91 | } 92 | 93 | public void setDelay(long delay) { 94 | this.delay = delay; 95 | } 96 | 97 | public void startAnimation(){ 98 | mHandler.postDelayed(mRunnable, delay); 99 | } 100 | 101 | public void stopAnimation(){ 102 | mHandler.removeCallbacks(mRunnable); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils; 2 | 3 | import android.content.Context; 4 | 5 | public class DensityUtil { 6 | 7 | public static int dip2px(Context context, float dpValue) { 8 | final float scale = context.getResources().getDisplayMetrics().density; 9 | return (int) (dpValue * scale + 0.5f); 10 | } 11 | 12 | public static int px2dip(Context context, float pxValue) { 13 | final float scale = context.getResources().getDisplayMetrics().density; 14 | return (int) (pxValue / scale + 0.5f); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/FileInterface.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils; 2 | 3 | public interface FileInterface { 4 | public boolean exists(); 5 | 6 | public String getName(); 7 | 8 | public String getParent(); 9 | 10 | public String getPath(); 11 | 12 | public boolean canRead(); 13 | 14 | public boolean canWrite(); 15 | 16 | public boolean isDirectory(); 17 | 18 | public boolean isFile(); 19 | 20 | public long lastModified(); 21 | 22 | public long length(); 23 | 24 | public boolean createNewFile(); 25 | 26 | public boolean delete(); 27 | 28 | public String[] list(); 29 | 30 | public boolean mkDirs(); 31 | 32 | public boolean renameTo(String name); 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Build; 5 | import android.os.Environment; 6 | 7 | import java.io.BufferedInputStream; 8 | import java.io.BufferedOutputStream; 9 | import java.io.ByteArrayOutputStream; 10 | import java.io.File; 11 | import java.io.FileInputStream; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.io.OutputStream; 16 | import java.nio.charset.StandardCharsets; 17 | 18 | public class FileUtil implements FileInterface { 19 | 20 | public static String getExternalStoragePath(){ 21 | return Environment.getExternalStorageDirectory().getPath(); 22 | } 23 | 24 | public static String getDirStoragePath(){ 25 | return Environment.getExternalStorageDirectory().getPath() + "/Android/data"; 26 | } 27 | 28 | @SuppressLint("SdCardPath") 29 | public static String getSDCardStoragePath(){ 30 | return "/sdcard/Android/data"; 31 | } 32 | 33 | private final String path; 34 | private final File file; 35 | private final SAF saf; 36 | public FileUtil(String path){ 37 | this.path = path; 38 | file = new File(path); 39 | saf = new SAF(path); 40 | } 41 | 42 | private boolean isFileDir(){ 43 | return (path.startsWith(getDirStoragePath()) || path.startsWith(getSDCardStoragePath())) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R; 44 | } 45 | 46 | public String readText(){ 47 | try { 48 | return new String(readBytes(openInputStream())); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | return null; 52 | } 53 | } 54 | 55 | public boolean writeText(String text){ 56 | try{ 57 | return writeBytes(openOutputStream(), text.getBytes(StandardCharsets.UTF_8)); 58 | }catch (Exception e){ 59 | e.printStackTrace(); 60 | return false; 61 | } 62 | } 63 | 64 | public static byte[] readBytes(InputStream inputStream){ 65 | try(ByteArrayOutputStream byteArrayOutputStream =new ByteArrayOutputStream(1024); 66 | BufferedInputStream bis = new BufferedInputStream(inputStream)) { 67 | byte[] temp=new byte[1024]; 68 | int size = 0; 69 | while((size = bis.read(temp)) != -1) { 70 | byteArrayOutputStream.write(temp,0,size); 71 | } 72 | return byteArrayOutputStream.toByteArray(); 73 | }catch (IOException e){ 74 | e.printStackTrace(); 75 | return null; 76 | }finally { 77 | if (inputStream != null){ 78 | try { 79 | inputStream.close(); 80 | } catch (IOException e) { 81 | e.printStackTrace(); 82 | } 83 | } 84 | } 85 | } 86 | 87 | public static boolean writeBytes(OutputStream os, byte[] bs){ 88 | try(BufferedOutputStream bos = new BufferedOutputStream(os)){ 89 | bos.write(bs); 90 | return true; 91 | }catch (IOException e){ 92 | e.printStackTrace(); 93 | return false; 94 | }finally { 95 | if (os != null){ 96 | try { 97 | os.close(); 98 | } catch (IOException e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | } 103 | } 104 | 105 | public InputStream openInputStream() throws IOException { 106 | if (isFileDir()) return saf.openInputStream(); 107 | else return new FileInputStream(path); 108 | } 109 | 110 | public OutputStream openOutputStream() throws IOException { 111 | if (isFileDir()) return saf.openOutputStream(); 112 | else return new FileOutputStream(path); 113 | } 114 | 115 | 116 | @Override 117 | public boolean exists() { 118 | if (isFileDir()) return saf.exists(); 119 | else return file.exists(); 120 | } 121 | 122 | @Override 123 | public String getName() { 124 | if (isFileDir()) return saf.getName(); 125 | else return file.getName(); 126 | } 127 | 128 | @Override 129 | public String getParent() { 130 | if (isFileDir()) return saf.getParent(); 131 | else return file.getParent(); 132 | } 133 | 134 | @Override 135 | public String getPath() { 136 | if (isFileDir()) return saf.getPath(); 137 | else return file.getPath(); 138 | } 139 | 140 | @Override 141 | public boolean canRead() { 142 | if (isFileDir()) return saf.canRead(); 143 | else return file.canRead(); 144 | } 145 | 146 | @Override 147 | public boolean canWrite() { 148 | if (isFileDir()) return saf.canWrite(); 149 | else return file.canWrite(); 150 | } 151 | 152 | @Override 153 | public boolean isDirectory() { 154 | if (isFileDir()) return saf.isDirectory(); 155 | else return file.isDirectory(); 156 | } 157 | 158 | @Override 159 | public boolean isFile() { 160 | if (isFileDir()) return saf.isFile(); 161 | else return file.isFile(); 162 | } 163 | 164 | @Override 165 | public long lastModified() { 166 | if (isFileDir()) return saf.lastModified(); 167 | else return file.lastModified(); 168 | } 169 | 170 | @Override 171 | public long length() { 172 | if (isFileDir()) return saf.length(); 173 | else return file.length(); 174 | } 175 | 176 | @Override 177 | public boolean createNewFile() { 178 | if (isFileDir()) return saf.createNewFile(); 179 | else { 180 | try { 181 | return file.createNewFile(); 182 | } catch (IOException e) { 183 | e.printStackTrace(); 184 | return false; 185 | } 186 | } 187 | } 188 | 189 | @Override 190 | public boolean delete() { 191 | if (isFileDir()) return saf.delete(); 192 | else return file.delete(); 193 | } 194 | 195 | @Override 196 | public String[] list() { 197 | if (isFileDir()) return saf.list(); 198 | else return file.list(); 199 | } 200 | 201 | @Override 202 | public boolean mkDirs() { 203 | if (isFileDir()) return saf.mkDirs(); 204 | else return file.mkdirs(); 205 | } 206 | 207 | @Override 208 | public boolean renameTo(String name) { 209 | if (isFileDir()) return saf.renameTo(name); 210 | else return file.renameTo(new File(getParent() + "/" + name)); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/JsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils; 2 | 3 | import android.util.Log; 4 | 5 | import org.json.JSONArray; 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | /** 10 | * JSON实用工具,方便快速访问JSON各节点 11 | * 12 | * 支持点连接访问 13 | */ 14 | public class JsonUtils { 15 | 16 | String TAG = "JSONUTILS"; 17 | 18 | private JSONObject jsonObject; 19 | 20 | //临时对象 21 | private JSONObject tmpJsonObject; 22 | private JSONArray tmpJsonArray; 23 | private Object tmpObject; 24 | 25 | /** 26 | * 初始化 27 | * 28 | * @param json 29 | */ 30 | public JsonUtils(String json) { 31 | try { 32 | this.tmpJsonObject = this.jsonObject = new JSONObject(json); 33 | } catch (JSONException e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | 38 | /** 39 | * 获取JSON对象 40 | * 41 | * @param key 42 | * @return 43 | */ 44 | public JsonUtils getJSONObject(String key) { 45 | try { 46 | this.tmpJsonObject = this.tmpJsonObject.getJSONObject(key); 47 | } catch (JSONException e) { 48 | Log.e(TAG, "键值:" + key + "不存在或是一个数组,正在尝试解析为数组..."); 49 | this.getJSONArray(key); 50 | } 51 | 52 | return this; 53 | } 54 | 55 | /** 56 | * 获取对象,指非JSON对象,调用这个方法可以进行格式化返回结果,如toInt,toString等 57 | * 58 | * @param key 59 | * @return 60 | */ 61 | public JsonUtils get(String key) { 62 | try { 63 | this.tmpObject = this.tmpJsonObject.get(key); 64 | } catch (JSONException e) { 65 | Log.e(TAG, "键值:" + key + "不存在或是一个数组,正在尝试解析为数组..."); 66 | this.getJSONArray(key); 67 | } 68 | 69 | return this; 70 | } 71 | 72 | //重置临时JSON对象,调用toXXX函数时自动重置 73 | public JsonUtils reset() { 74 | this.tmpJsonObject = this.jsonObject; 75 | return this; 76 | } 77 | 78 | 79 | /** 80 | * 获取JSON数组对象 81 | * 82 | * @param key 83 | * @return 84 | */ 85 | public JsonUtils getJSONArray(String key) { 86 | try { 87 | this.tmpJsonArray = this.tmpJsonObject.getJSONArray(key); 88 | } catch (JSONException e) { 89 | Log.e(TAG, "键值:" + key + "不是一个数组"); 90 | } 91 | 92 | return this; 93 | } 94 | 95 | /** 96 | * 获取指定索引的数组项,JSON数组 97 | * 98 | * @param idx 99 | * @return 100 | */ 101 | public JsonUtils getJSONArrayItem(int idx) { 102 | try { 103 | this.tmpJsonObject = this.tmpJsonArray.getJSONObject(idx); 104 | } catch (JSONException e) { 105 | e.printStackTrace(); 106 | } 107 | 108 | return this; 109 | } 110 | 111 | /** 112 | * 获取指定索引的数组项,非JSON数组 113 | * 114 | * @param idx 115 | * @return 116 | */ 117 | public JsonUtils getArraytem(int idx) { 118 | try { 119 | this.tmpObject = this.tmpJsonArray.get(idx); 120 | } catch (JSONException e) { 121 | e.printStackTrace(); 122 | } 123 | 124 | return this; 125 | } 126 | 127 | /** 128 | * 点连接的方式访问键值,示例:a.b.c 129 | * @param key 130 | * @return 131 | */ 132 | public JsonUtils getByDotKey(String key) 133 | { 134 | String[] keys = key.split("\\."); 135 | 136 | String tmpKey = ""; 137 | String[] tmpKeys; 138 | 139 | JSONObject tmpJsonObject = this.tmpJsonObject; 140 | 141 | try 142 | { 143 | for(int i=0;i=keys.length) 156 | { 157 | this.tmpObject = tmpJsonObject.get(tmpKey); 158 | }else{ 159 | tmpJsonObject = tmpJsonObject.getJSONObject(tmpKey); 160 | } 161 | } 162 | } 163 | }catch (Exception e) 164 | { 165 | e.printStackTrace(); 166 | } 167 | 168 | return this; 169 | } 170 | 171 | public int toInt() 172 | { 173 | reset(); 174 | return Integer.valueOf(tmpObject.toString()); 175 | } 176 | 177 | public float toFloat() 178 | { 179 | reset(); 180 | return Float.valueOf(tmpObject.toString()); 181 | } 182 | 183 | public String toString() 184 | { 185 | reset(); 186 | return tmpObject.toString(); 187 | } 188 | 189 | public Boolean toBoolean() 190 | { 191 | reset(); 192 | return Boolean.valueOf(tmpObject.toString()); 193 | } 194 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/KeepShellPublic.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils 2 | 3 | /** 4 | * Created by Hello on 2018/01/23. 5 | */ 6 | object KeepShellPublic { 7 | private val keepShells = HashMap() 8 | 9 | fun getInstance(key: String, rootMode: Boolean): KeepShell { 10 | synchronized(keepShells) { 11 | if (!keepShells.containsKey(key)) { 12 | keepShells.put(key, KeepShell(rootMode)) 13 | } 14 | return keepShells.get(key)!! 15 | } 16 | } 17 | 18 | fun destroyInstance(key: String) { 19 | synchronized(keepShells) { 20 | if (!keepShells.containsKey(key)) { 21 | return 22 | } else { 23 | val keepShell = keepShells.get(key)!! 24 | keepShells.remove(key) 25 | keepShell.tryExit() 26 | } 27 | } 28 | } 29 | 30 | fun destroyAll() { 31 | synchronized(keepShells) { 32 | while (keepShells.isNotEmpty()) { 33 | val key = keepShells.keys.first() 34 | val keepShell = keepShells.get(key)!! 35 | keepShells.remove(key) 36 | keepShell.tryExit() 37 | } 38 | } 39 | } 40 | 41 | public val defaultKeepShell = KeepShell() 42 | public val secondaryKeepShell = KeepShell() 43 | 44 | fun getDefaultInstance(): KeepShell { 45 | return if (defaultKeepShell.isIdle || !secondaryKeepShell.isIdle) { 46 | defaultKeepShell 47 | } else { 48 | secondaryKeepShell 49 | } 50 | } 51 | 52 | fun doCmdSync(commands: List): Boolean { 53 | val stringBuilder = StringBuilder() 54 | 55 | for (cmd in commands) { 56 | stringBuilder.append(cmd) 57 | stringBuilder.append("\n\n") 58 | } 59 | 60 | return doCmdSync(stringBuilder.toString()) != "error" 61 | } 62 | 63 | //执行脚本 64 | fun doCmdSync(cmd: String): String { 65 | return getDefaultInstance().doCmdSync(cmd) 66 | } 67 | 68 | //执行脚本 69 | fun checkRoot(): Boolean { 70 | return defaultKeepShell.checkRoot() 71 | } 72 | 73 | fun tryExit() { 74 | defaultKeepShell.tryExit() 75 | secondaryKeepShell.tryExit() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/utils/ShellExecutor.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.io.OutputStream; 6 | 7 | public class ShellExecutor { 8 | private static String extraEnvPath = ""; 9 | private static String defaultEnvPath = ""; // /sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin 10 | 11 | public static void setExtraEnvPath(String extraEnvPath) { 12 | ShellExecutor.extraEnvPath = extraEnvPath; 13 | } 14 | 15 | private static String getEnvPath() { 16 | // FIXME:非root模式下,默认的 TMPDIR=/data/local/tmp 变量可能会导致某些需要写缓存的场景(例如使用source指令)脚本执行失败! 17 | if (extraEnvPath != null && !extraEnvPath.isEmpty()) { 18 | if (defaultEnvPath.isEmpty()) { 19 | try { 20 | Process process = Runtime.getRuntime().exec("sh"); 21 | OutputStream outputStream = process.getOutputStream(); 22 | outputStream.write("echo $PATH".getBytes()); 23 | outputStream.flush(); 24 | outputStream.close(); 25 | 26 | InputStream inputStream = process.getInputStream(); 27 | byte[] cache = new byte[16384]; 28 | int length = inputStream.read(cache); 29 | inputStream.close(); 30 | process.destroy(); 31 | 32 | String path = new String(cache, 0, length).trim(); 33 | if (path.length() > 0) { 34 | defaultEnvPath = path; 35 | } else { 36 | throw new RuntimeException("未能获取到$PATH参数"); 37 | } 38 | } catch (Exception ex) { 39 | defaultEnvPath = "/sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"; 40 | } 41 | } 42 | 43 | String path = defaultEnvPath; 44 | 45 | return ( "PATH=" + path + ":" + extraEnvPath); 46 | } 47 | 48 | return null; 49 | } 50 | 51 | private static Process getProcess(String run) throws IOException { 52 | String env = getEnvPath(); 53 | Runtime runtime = Runtime.getRuntime(); 54 | /* 55 | // 部分机型会有Aborted错误 56 | if (env != null) { 57 | return runtime.exec(run, new String[]{ 58 | env 59 | }); 60 | } 61 | */ 62 | Process process = runtime.exec(run); 63 | if (env != null) { 64 | OutputStream outputStream = process.getOutputStream(); 65 | outputStream.write("export ".getBytes()); 66 | outputStream.write(env.getBytes()); 67 | outputStream.write("\n".getBytes()); 68 | outputStream.flush(); 69 | } 70 | return process; 71 | } 72 | 73 | public static Process getSuperUserRuntime() throws IOException { 74 | return getProcess("su"); 75 | } 76 | 77 | public static Process getRuntime() throws IOException { 78 | return getProcess("sh"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/view/SymbolView.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.view; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Color; 6 | import android.graphics.Rect; 7 | import android.view.Gravity; 8 | import android.view.LayoutInflater; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.view.ViewTreeObserver; 13 | import android.view.WindowManager; 14 | import android.view.inputmethod.InputMethodManager; 15 | import android.widget.LinearLayout; 16 | import android.widget.PopupWindow; 17 | import android.widget.TextView; 18 | 19 | import com.pointer.wave.easyship.R; 20 | import com.pointer.wave.easyship.utils.DensityUtil; 21 | 22 | /* 23 | 符号栏类 24 | */ 25 | public class SymbolView { 26 | private final int TILE_WIDTH = 60; 27 | private PopupWindow popupWindow; 28 | private View rootView; 29 | private OnSymbolViewClick onSymbolViewClick; 30 | private boolean visible = false; 31 | private InputMethodManager inputMethodManager; 32 | private boolean isFirst = true; 33 | private int maxLayoutHeight = 0;//布局总长 34 | private int currentLayoutHeight = 0;//当前布局高 35 | private Context context; 36 | private boolean isUC = true; 37 | 38 | @SuppressLint({"ClickableViewAccessibility", "ResourceType"}) 39 | public SymbolView(final Context context, final View rootView) { 40 | this.context = context; 41 | this.rootView = rootView; 42 | popupWindow = new PopupWindow(context); 43 | inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); 44 | View view = LayoutInflater.from(context).inflate(R.layout.symbol_view, null); 45 | LinearLayout linearLayout = view.findViewById(R.id.linear_container); 46 | final float[] tempPoint = new float[2]; 47 | String symbol = "←

换行
删行
+CVars=
=
r.
[
]
.
+
[UserCustom DeviceProfile]"; 48 | if (!isFirst){ 49 | symbol = "←

换行
+CVars=
=
r.
[
]
.
+
[UserCustom DeviceProfile]
[FansSwitcher]
[FansCustom]"; 50 | } 51 | String[] symbolArrary = symbol.split("
"); 52 | for (int i = 0; i < symbolArrary.length; i++) { 53 | TextView textView = new TextView(context); 54 | textView.setGravity(Gravity.CENTER); 55 | textView.setText(symbolArrary[i]); 56 | textView.setClickable(true); 57 | textView.setTextSize(20); 58 | //textView.setWidth(); 59 | textView.setPadding(DensityUtil.dip2px(context, 10), DensityUtil.dip2px(context, 5), DensityUtil.dip2px(context, 10), DensityUtil.dip2px(context, 5)); 60 | textView.setOnTouchListener(new View.OnTouchListener() { 61 | @Override 62 | public boolean onTouch(View v, MotionEvent event) { 63 | int color = v.getDrawingCacheBackgroundColor(); 64 | int motionEvent = event.getAction(); 65 | TextView tv = (TextView) v; 66 | 67 | if (motionEvent == MotionEvent.ACTION_DOWN) { 68 | tempPoint[0] = event.getX(); 69 | tempPoint[1] = event.getY(); 70 | tv.setBackgroundColor(0xffcecfd1); 71 | 72 | } else if (motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_CANCEL) { 73 | tv.setBackgroundColor(Color.parseColor(context.getString(R.color.white))); 74 | if (Math.abs(event.getX() - tempPoint[0]) < TILE_WIDTH) { 75 | if (onSymbolViewClick != null) 76 | onSymbolViewClick.onClick(tv, tv.getText().toString()); 77 | } 78 | } 79 | return true; 80 | } 81 | }); 82 | LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); 83 | linearLayout.setGravity(Gravity.CENTER); 84 | linearLayout.addView(textView, layoutParams); 85 | 86 | } 87 | popupWindow.setWidth(LinearLayout.LayoutParams.MATCH_PARENT); 88 | //popupWindow.setHeight(EditCodeActivity.height); 89 | popupWindow.getBackground().setAlpha(0);//窗口完全透明 90 | view.setBackgroundColor(Color.parseColor(context.getString(R.color.white)));//视图不完全透明 91 | popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 92 | popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 93 | popupWindow.setContentView(view); 94 | rootView.getViewTreeObserver().addOnGlobalLayoutListener( 95 | new ViewTreeObserver.OnGlobalLayoutListener() { 96 | @Override 97 | public void onGlobalLayout() { 98 | Rect r = new Rect(); 99 | rootView.getWindowVisibleDisplayFrame(r); 100 | if (isFirst) { 101 | maxLayoutHeight = r.bottom;//初始化时为布局的最高高度 102 | currentLayoutHeight = r.bottom;//当前弹出的布局高 103 | isFirst = false; 104 | } else { 105 | currentLayoutHeight = r.bottom;//当前弹出的布局高 106 | } 107 | if (currentLayoutHeight == maxLayoutHeight || !visible) { 108 | hide(); 109 | } else if (currentLayoutHeight < maxLayoutHeight) { 110 | show(rootView.getHeight() - r.bottom); 111 | } 112 | } 113 | }); 114 | } 115 | 116 | public void setUC(boolean UC) { 117 | isUC = UC; 118 | 119 | } 120 | 121 | public void setVisible(boolean visible) { 122 | this.visible = visible; 123 | } 124 | 125 | private void show(int bottom) { 126 | popupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, bottom); 127 | } 128 | 129 | private void hide() { 130 | popupWindow.dismiss(); 131 | } 132 | 133 | public void setOnSymbolViewClick(OnSymbolViewClick onSymbolViewClick) { 134 | this.onSymbolViewClick = onSymbolViewClick; 135 | } 136 | 137 | 138 | public interface OnSymbolViewClick { 139 | void onClick(View view, String text); 140 | } 141 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/HelpsDialog.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.annotation.SuppressLint; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.AsyncTask; 9 | import android.os.Handler; 10 | import android.text.Editable; 11 | import android.text.Html; 12 | import android.text.SpannableStringBuilder; 13 | import android.text.Spanned; 14 | import android.text.TextWatcher; 15 | import android.text.style.ClickableSpan; 16 | import android.text.style.URLSpan; 17 | import android.view.View; 18 | import android.widget.EditText; 19 | import android.widget.LinearLayout; 20 | import android.widget.TextView; 21 | 22 | import androidx.annotation.NonNull; 23 | import androidx.appcompat.app.AppCompatActivity; 24 | import androidx.recyclerview.widget.RecyclerView; 25 | import androidx.recyclerview.widget.StaggeredGridLayoutManager; 26 | 27 | import com.github.mmin18.widget.RealtimeBlurView; 28 | import com.google.gson.Gson; 29 | import com.lxj.xpopup.XPopup; 30 | import com.lxj.xpopup.core.BottomPopupView; 31 | import com.lxj.xpopup.enums.PopupAnimation; 32 | import com.pointer.wave.easyship.R; 33 | import com.pointer.wave.easyship.pojo.TipsBen; 34 | import com.pointer.wave.easyship.utils.HttpUtils; 35 | import com.pointer.wave.easyship.widget.adapter.HelpsListAdapter; 36 | 37 | import java.io.IOException; 38 | import java.util.ArrayList; 39 | import java.util.Arrays; 40 | import java.util.List; 41 | 42 | import okhttp3.Call; 43 | import okhttp3.Callback; 44 | import okhttp3.Response; 45 | 46 | public class HelpsDialog extends BottomPopupView { 47 | 48 | public static AppCompatActivity activity; 49 | 50 | @SuppressLint("StaticFieldLeak") 51 | public static HelpsDialog dialog; 52 | public HelpsDialog(@NonNull Context context, List list) { 53 | super(context); 54 | dialog = this; 55 | this.list = list; 56 | } 57 | 58 | @Override 59 | protected int getImplLayoutId() { 60 | return R.layout.dialog_full_base; 61 | } 62 | 63 | public static void showDialog(AppCompatActivity appCompatActivity, List list){ 64 | activity = appCompatActivity; 65 | new XPopup.Builder(appCompatActivity) 66 | .animationDuration(750) 67 | .dismissOnTouchOutside(false) 68 | .hasShadowBg(false) 69 | .hasBlurBg(false) 70 | .isViewMode(true) 71 | .popupAnimation(PopupAnimation.TranslateAlphaFromBottom) 72 | .moveUpToKeyboard(false) 73 | .isDestroyOnDismiss(true) 74 | .asCustom(new HelpsDialog(appCompatActivity, list)) 75 | .show(); 76 | } 77 | 78 | private LinearLayout relativeLayout; 79 | private View inflate; 80 | private List list = new ArrayList<>(); 81 | private HelpsListAdapter adapter; 82 | @Override 83 | protected void onCreate() { 84 | super.onCreate(); 85 | relativeLayout = findViewById(R.id.root_view); 86 | inflate = View.inflate(getContext(), R.layout.dialog_helps, null); 87 | addChildView(inflate); 88 | RealtimeBlurView blurView = findViewById(R.id.blur_view); 89 | translation(blurView); 90 | 91 | RecyclerView recyclerView = inflate.findViewById(R.id.list_view); 92 | adapter = new HelpsListAdapter(list, activity); 93 | recyclerView.setLayoutManager(new StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.VERTICAL)); 94 | recyclerView.setAdapter(adapter); 95 | 96 | EditText editText = inflate.findViewById(R.id.editTextTextPersonName); 97 | editText.addTextChangedListener(new TextWatcher() { 98 | @Override 99 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 100 | 101 | } 102 | 103 | @Override 104 | public void onTextChanged(CharSequence s, int start, int before, int count) { 105 | 106 | } 107 | 108 | @Override 109 | public void afterTextChanged(Editable s) { 110 | search(s.toString()); 111 | } 112 | }); 113 | } 114 | 115 | @SuppressLint("StaticFieldLeak") 116 | private void search(String text) { 117 | new AsyncTask>(){ 118 | 119 | @Override 120 | protected List doInBackground(String... strings) { 121 | List arrayList = new ArrayList<>(); 122 | list.forEach((item)->{ 123 | for (String string : strings) { 124 | if (item.contains(string)){ 125 | arrayList.add(item); 126 | } 127 | } 128 | }); 129 | return arrayList; 130 | } 131 | 132 | @Override 133 | protected void onPostExecute(List s) { 134 | super.onPostExecute(s); 135 | adapter.updateList(s); 136 | } 137 | }.execute(text); 138 | } 139 | 140 | 141 | 142 | private void addChildView(View view) { 143 | relativeLayout.removeAllViews(); 144 | relativeLayout.addView(view); 145 | } 146 | 147 | private void translation(View view){ 148 | ObjectAnimator translationY = ObjectAnimator.ofFloat(view, "translationY", 0, 0.1f); 149 | translationY.setDuration(100); 150 | translationY.start(); 151 | new Handler(view.getContext().getMainLooper()).postDelayed(()->{ 152 | translation(view); 153 | }, 200); 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/MarqueeTextView.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | import android.view.ViewDebug; 7 | import android.widget.TextView; 8 | 9 | @SuppressLint("AppCompatCustomView") 10 | public class MarqueeTextView extends TextView { 11 | 12 | public MarqueeTextView(Context context) { 13 | super(context); 14 | } 15 | 16 | public MarqueeTextView(Context context, AttributeSet attrs, int defStyle) { 17 | super(context, attrs, defStyle); 18 | } 19 | 20 | public MarqueeTextView(Context context, AttributeSet attrs) { 21 | super(context, attrs); 22 | } 23 | 24 | @Override 25 | @ViewDebug.ExportedProperty(category = "focus") 26 | public boolean isFocused() { 27 | // TODO Auto-generated method stub 28 | return true; 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/OverRecyclerView.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.View; 9 | 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import com.mixiaoxiao.overscroll.OverScrollDelegate; 13 | 14 | public class OverRecyclerView extends RecyclerView implements OverScrollDelegate.OverScrollable { 15 | private OverScrollDelegate mOverScrollDelegate; 16 | 17 | public OverRecyclerView(Context context) { 18 | super(context); 19 | this.createOverScrollDelegate(context); 20 | } 21 | 22 | public OverRecyclerView(Context context, AttributeSet attrs) { 23 | super(context, attrs); 24 | this.createOverScrollDelegate(context); 25 | } 26 | 27 | public OverRecyclerView(Context context, AttributeSet attrs, int defStyle) { 28 | super(context, attrs, defStyle); 29 | this.createOverScrollDelegate(context); 30 | } 31 | 32 | private void createOverScrollDelegate(Context context) { 33 | this.mOverScrollDelegate = new OverScrollDelegate(this); 34 | } 35 | 36 | public boolean onInterceptTouchEvent(MotionEvent ev) { 37 | return this.mOverScrollDelegate.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); 38 | } 39 | 40 | @SuppressLint("ClickableViewAccessibility") 41 | public boolean onTouchEvent(MotionEvent event) { 42 | return this.mOverScrollDelegate.onTouchEvent(event) || super.onTouchEvent(event); 43 | } 44 | 45 | @SuppressLint("MissingSuperCall") 46 | public void draw(Canvas canvas) { 47 | //super.draw(canvas); 48 | this.mOverScrollDelegate.draw(canvas); 49 | } 50 | 51 | protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 52 | return this.mOverScrollDelegate.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 53 | } 54 | 55 | public int superComputeVerticalScrollExtent() { 56 | return super.computeVerticalScrollExtent(); 57 | } 58 | 59 | public int superComputeVerticalScrollOffset() { 60 | return super.computeVerticalScrollOffset(); 61 | } 62 | 63 | public int superComputeVerticalScrollRange() { 64 | return super.computeVerticalScrollRange(); 65 | } 66 | 67 | public void superOnTouchEvent(MotionEvent event) { 68 | super.onTouchEvent(event); 69 | } 70 | 71 | public void superDraw(Canvas canvas) { 72 | super.draw(canvas); 73 | } 74 | 75 | public boolean superAwakenScrollBars() { 76 | return super.awakenScrollBars(); 77 | } 78 | 79 | public boolean superOverScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { 80 | return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); 81 | } 82 | 83 | public View getOverScrollableView() { 84 | return this; 85 | } 86 | 87 | public OverScrollDelegate getOverScrollDelegate() { 88 | return this.mOverScrollDelegate; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/OverScrollView.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.view.MotionEvent; 8 | import android.view.ViewConfiguration; 9 | 10 | import com.mixiaoxiao.overscroll.OverScrollScrollView; 11 | 12 | public class OverScrollView extends OverScrollScrollView { 13 | public OverScrollView(Context context) { 14 | super(context); 15 | } 16 | 17 | public OverScrollView(Context context, AttributeSet attrs) { 18 | super(context, attrs); 19 | } 20 | 21 | public OverScrollView(Context context, AttributeSet attrs, int defStyle) { 22 | super(context, attrs, defStyle); 23 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 24 | } 25 | 26 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 27 | public OverScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 28 | super(context, attrs, defStyleAttr, defStyleRes); 29 | mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 30 | } 31 | 32 | private int downX, downY; 33 | private int mTouchSlop; 34 | @Override 35 | public boolean onInterceptTouchEvent(MotionEvent ev) { 36 | int action = ev.getAction(); 37 | switch (action) { 38 | case MotionEvent.ACTION_DOWN: 39 | downX = (int) ev.getRawX(); 40 | downY = (int) ev.getRawY(); 41 | break; 42 | case MotionEvent.ACTION_MOVE: 43 | int moveY = (int) ev.getRawY(); 44 | // 判断是否滑动,若滑动就拦截事件 45 | if (Math.abs(moveY - downY) > mTouchSlop) { 46 | return true; 47 | } 48 | break; 49 | default: 50 | break; 51 | } 52 | 53 | return super.onInterceptTouchEvent(ev); 54 | } 55 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/TipsPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.TextView; 7 | 8 | import androidx.annotation.NonNull; 9 | import androidx.viewpager.widget.PagerAdapter; 10 | 11 | import com.pointer.wave.easyship.R; 12 | import com.pointer.wave.easyship.pojo.TipsBen; 13 | 14 | import java.util.List; 15 | 16 | public class TipsPagerAdapter extends PagerAdapter{ 17 | private final Context context; 18 | private final List mListView; 19 | public TipsPagerAdapter(List list, Context context) { 20 | this.context = context; 21 | this.mListView = list; 22 | } 23 | 24 | @Override 25 | public int getCount() { 26 | return mListView != null ? mListView.size() : 0; 27 | } 28 | 29 | @Override 30 | public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { 31 | return view == object; 32 | } 33 | 34 | @Override 35 | public void destroyItem(ViewGroup view, int position, @NonNull Object object) { 36 | view.removeView((View) object); 37 | } 38 | 39 | @NonNull 40 | @Override 41 | public Object instantiateItem(@NonNull ViewGroup view, int position) { 42 | View a = View.inflate(context, R.layout.item_tips_viewpager, null); 43 | a.findViewById(R.id.tips_title).setText(mListView.get(position).getContent()); 44 | view.addView(a); 45 | return a; 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/UpdateLogDialog.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.annotation.SuppressLint; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.Uri; 8 | import android.os.Handler; 9 | import android.text.Html; 10 | import android.text.SpannableStringBuilder; 11 | import android.text.Spanned; 12 | import android.text.style.ClickableSpan; 13 | import android.text.style.URLSpan; 14 | import android.view.View; 15 | import android.widget.ImageView; 16 | import android.widget.LinearLayout; 17 | import android.widget.TextView; 18 | 19 | import androidx.annotation.NonNull; 20 | import androidx.appcompat.app.AppCompatActivity; 21 | 22 | import com.github.mmin18.widget.RealtimeBlurView; 23 | import com.lxj.xpopup.XPopup; 24 | import com.lxj.xpopup.core.BottomPopupView; 25 | import com.lxj.xpopup.enums.PopupAnimation; 26 | import com.pointer.wave.easyship.R; 27 | import com.pointer.wave.easyship.common.activity.BaseActivity; 28 | 29 | public class UpdateLogDialog extends BottomPopupView { 30 | 31 | public static AppCompatActivity activity; 32 | 33 | @SuppressLint("StaticFieldLeak") 34 | public static UpdateLogDialog dialog; 35 | private String html; 36 | 37 | public UpdateLogDialog(@NonNull Context context, String html) { 38 | super(context); 39 | dialog = this; 40 | this.html = html; 41 | } 42 | 43 | @Override 44 | protected int getImplLayoutId() { 45 | return R.layout.dialog_full_base; 46 | } 47 | 48 | public static void showDialog(AppCompatActivity appCompatActivity, String html){ 49 | activity = appCompatActivity; 50 | new XPopup.Builder(appCompatActivity) 51 | .animationDuration(750) 52 | .dismissOnTouchOutside(false) 53 | .hasShadowBg(false) 54 | .hasBlurBg(false) 55 | .isViewMode(true) 56 | .popupAnimation(PopupAnimation.TranslateAlphaFromBottom) 57 | .moveUpToKeyboard(false) 58 | .isDestroyOnDismiss(true) 59 | .isRequestFocus(true) 60 | .asCustom(new UpdateLogDialog(appCompatActivity, html)) 61 | .show(); 62 | } 63 | 64 | private LinearLayout relativeLayout; 65 | private View inflate; 66 | @Override 67 | protected void onCreate() { 68 | super.onCreate(); 69 | relativeLayout = findViewById(R.id.root_view); 70 | inflate = View.inflate(getContext(), R.layout.dialog_update_log, null); 71 | addChildView(inflate); 72 | RealtimeBlurView blurView = findViewById(R.id.blur_view); 73 | translation(blurView); 74 | 75 | TextView logView = inflate.findViewById(R.id.log); 76 | logView.setText(getClickableHtml(html)); 77 | } 78 | 79 | private void addChildView(View view) { 80 | relativeLayout.removeAllViews(); 81 | relativeLayout.addView(view); 82 | } 83 | 84 | private void translation(View view){ 85 | ObjectAnimator translationY = ObjectAnimator.ofFloat(view, "translationY", 0, 0.1f); 86 | translationY.setDuration(100); 87 | translationY.start(); 88 | new Handler(view.getContext().getMainLooper()).postDelayed(()->{ 89 | translation(view); 90 | }, 200); 91 | } 92 | 93 | public void setLinkClickable(SpannableStringBuilder clickableHtml, URLSpan urlSpan) { 94 | int start = clickableHtml.getSpanStart(urlSpan); 95 | int end = clickableHtml.getSpanEnd(urlSpan); 96 | int flags = clickableHtml.getSpanFlags(urlSpan); 97 | ClickableSpan clickableSpan = new ClickableSpan() { 98 | @Override 99 | public void onClick(View widget) { 100 | if(urlSpan.getURL()!=null){ 101 | Intent intent = new Intent(); 102 | intent.setAction("android.intent.action.VIEW"); 103 | intent.setData(Uri.parse(urlSpan.getURL())); 104 | getContext().startActivity(intent); 105 | } 106 | } 107 | }; 108 | clickableHtml.setSpan(clickableSpan, start, end, flags); 109 | } 110 | 111 | public CharSequence getClickableHtml(String text) { 112 | Spanned spannedHtml = Html.fromHtml(text); 113 | SpannableStringBuilder clickableHtmlBuilder = new SpannableStringBuilder(spannedHtml); 114 | URLSpan[] urls = clickableHtmlBuilder.getSpans(0, spannedHtml.length(), URLSpan.class); 115 | for (final URLSpan span : urls){ 116 | setLinkClickable(clickableHtmlBuilder, span); 117 | } 118 | return clickableHtmlBuilder; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/ViewPagerAdapterForView.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget; 2 | 3 | import android.os.Parcelable; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.viewpager.widget.PagerAdapter; 8 | 9 | import java.util.List; 10 | 11 | public class ViewPagerAdapterForView extends PagerAdapter { 12 | 13 | private final List mListView; 14 | 15 | public ViewPagerAdapterForView(List list) 16 | { 17 | // TODO Auto-generated method stub 18 | this.mListView = list; 19 | } 20 | 21 | @Override 22 | /**这个方法,是从ViewGroup中移出当前View**/ 23 | public void destroyItem(View container, int position, Object object) 24 | { 25 | // TODO Auto-generated method stub 26 | ((ViewGroup) container ).removeView(mListView.get(position)); 27 | } 28 | 29 | @Override 30 | public void finishUpdate(View view) 31 | { 32 | // TODO Auto-generated method stub 33 | 34 | } 35 | 36 | @Override 37 | /**这个方法,是获取当前窗体界面数**/ 38 | public int getCount() 39 | { 40 | // TODO Auto-generated method stub 41 | return mListView.size(); 42 | } 43 | 44 | @Override 45 | /**这个方法,return一个对象,这个对象表明了PagerAdapter适配器选择哪个对象*放在当前的ViewPager中**/ 46 | public Object instantiateItem(View container, int position) 47 | { 48 | // TODO Auto-generated method stub 49 | ((ViewGroup) container).addView(mListView.get(position), 0); 50 | return mListView.get(position); 51 | } 52 | 53 | @Override 54 | /**这个方法,在帮助文档中原文是could be implemented as return view == object,*也就是用于判断是否由对象生成界面**/ 55 | public boolean isViewFromObject(View view, Object object) 56 | { 57 | // TODO Auto-generated method stub 58 | return view == (object); 59 | } 60 | 61 | @Override 62 | public void restoreState(Parcelable state, ClassLoader loader) 63 | { 64 | // TODO Auto-generated method stub 65 | 66 | } 67 | 68 | @Override 69 | public Parcelable saveState() 70 | { 71 | // TODO Auto-generated method stub 72 | return null; 73 | } 74 | 75 | @Override 76 | public void startUpdate(View v) 77 | { 78 | // TODO Auto-generated method stub 79 | 80 | } 81 | 82 | } -------------------------------------------------------------------------------- /app/src/main/java/com/pointer/wave/easyship/widget/adapter/HelpsListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship.widget.adapter; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.net.Uri; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.TextView; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.google.gson.Gson; 15 | import com.pointer.wave.easyship.R; 16 | import com.pointer.wave.easyship.pojo.TipsBen; 17 | import com.pointer.wave.easyship.widget.feedback.TouchFeedback; 18 | 19 | import java.util.List; 20 | 21 | public class HelpsListAdapter extends RecyclerView.Adapter implements TouchFeedback.OnFeedBackListener { 22 | 23 | private List list; 24 | private TouchFeedback touchFeedback; 25 | private final Gson gson = new Gson(); 26 | public HelpsListAdapter(List list, Context context) { 27 | this.list = list; 28 | touchFeedback = TouchFeedback.newInstance(context); 29 | } 30 | 31 | @NonNull 32 | @Override 33 | public HelpHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 34 | HelpHolder helpHolder = new HelpHolder(View.inflate(parent.getContext(), R.layout.item_help_layout, null)); 35 | //touchFeedback.setOnFeedBackListener(this, helpHolder.itemView, false); 36 | return helpHolder; 37 | } 38 | 39 | @Override 40 | public void onBindViewHolder(@NonNull HelpHolder holder, int position) { 41 | TipsBen tipsBen = gson.fromJson(list.get(position), TipsBen.class); 42 | holder.textView.setText(tipsBen.getName().replace(".txt", "")); 43 | holder.button.setOnClickListener((v)->{ 44 | Intent intent = new Intent(); 45 | intent.setAction("android.intent.action.VIEW"); 46 | intent.setData(Uri.parse(tipsBen.getContent())); 47 | v.getContext().startActivity(intent); 48 | }); 49 | } 50 | 51 | @Override 52 | public int getItemCount() { 53 | return list.size(); 54 | } 55 | 56 | @SuppressLint("NotifyDataSetChanged") 57 | public void updateList(List list){ 58 | this.list = list; 59 | notifyDataSetChanged(); 60 | } 61 | 62 | @Override 63 | public void onClick(View view) { 64 | 65 | } 66 | 67 | @Override 68 | public void onLongClick(View view) { 69 | 70 | } 71 | 72 | static class HelpHolder extends RecyclerView.ViewHolder { 73 | 74 | public TextView textView; 75 | public TextView button; 76 | public HelpHolder(@NonNull View itemView) { 77 | super(itemView); 78 | textView = itemView.findViewById(R.id.text); 79 | button = itemView.findViewById(R.id.show_help); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /app/src/main/res/animator/fragment_slide_hide.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | 17 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/animator/fragment_slide_show.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_copy_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/drawable/ic_content_copy_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_cut_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/drawable/ic_content_cut_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_paste_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/drawable/ic_content_paste_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_select_all_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/drawable/ic_select_all_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_method.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/drawable/icon_method.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_editor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_flash_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_indicator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_indicator_select.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_shadow_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_full_base.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_helps.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 30 | 31 | 35 | 36 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_permissin_write.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 26 | 27 | 37 | 38 | 45 | 46 | 52 | 53 | 58 | 59 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 84 | 85 | 93 | 94 | 95 | 96 | 97 | 98 | 104 | 105 | 111 | 112 | 121 | 122 | 129 | 130 | 131 | 132 | 141 | 142 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_update_log.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 17 | 18 | 22 | 23 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 30 | 31 | 36 | 37 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_auto_panel.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_help_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 22 | 23 | 28 | 29 | 36 | 37 | 38 | 39 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_tips_viewpager.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 21 | 22 | 26 | 27 | 34 | 35 | 36 | 37 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /app/src/main/res/layout/layout_navigation_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 15 | 16 | 22 | 23 | 31 | 32 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/symbol_view.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_alipay.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_developer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_developer.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_download.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_es_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_es_logo.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_es_logo_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_es_logo_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_file.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_folder.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_github.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_help.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_home.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_idea.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_log.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_logo_white_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_logo_white_24dp.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_mail.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_notification.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_root.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_settings.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_start.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_sub_notification.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_sub_notification.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_tips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_tips.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_update.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_waring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-mdpi/ic_waring.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FFFFFFFF 9 | #FF000000 10 | #FFEF5361 11 | #FFFD6D4B 12 | #FFFFCF47 13 | #FF9FD661 14 | #FF3FD1AD 15 | #FF2CBDF4 16 | #FFAD8FEF 17 | #FFEE85C0 18 | #8CFFFFFF 19 | #88CE9C 20 | #8CDCF7E8 21 | #AADCF7E8 22 | #D9D9D9 23 | #FFF5F5F5 24 | #FF474658 25 | #60000000 26 | #ff5f7c89 27 | #8c5f7c89 28 | #eceff2 29 | #fff2df 30 | #e8f6e9 31 | #e4f2fd 32 | #dff7f9 33 | #e8eaf6 34 | #EFEBE8 35 | #795547 36 | #ffebed 37 | #f3e5f6 38 | #9d27b1 39 | #3DDC84 40 | #8Ceceff2 41 | #FF119EFF 42 | #CC000000 43 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #FFEF5361 11 | #FFFD6D4B 12 | #FFFFCF47 13 | #FF9FD661 14 | #FF3FD1AD 15 | #FF2CBDF4 16 | #FFAD8FEF 17 | #FFEE85C0 18 | #8CFFFFFF 19 | #88CE9C 20 | #8CDCF7E8 21 | #AADCF7E8 22 | #D9D9D9 23 | #FFF5F5F5 24 | #FF474658 25 | #60000000 26 | #ff5f7c89 27 | #8c5f7c89 28 | #eceff2 29 | #fff2df 30 | #e8f6e9 31 | #e4f2fd 32 | #dff7f9 33 | #e8eaf6 34 | #EFEBE8 35 | #795547 36 | #ffebed 37 | #f3e5f6 38 | #9d27b1 39 | #3DDC84 40 | #8Ceceff2 41 | #FF119EFF 42 | #CC000000 43 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 一叶孤舟 3 | 4 | Hello blank fragment 5 | 1. 设备获取Root权限且以授予本软件权限; 6 |
2. 设备支持V-AB系统分区; 7 |

8 | 可能出现的问题: 9 |
10 | 1. 选择ROM路径时一定要从“本机存储”中选择,否则可能会无法选择ROM; 11 |
12 | 2. 由于Android对后台服务的限制,请在更新时尽量保证本软件为前台应用(当通知栏出现更新进度后就可以挂后台了); 13 |
14 | 3. 经测试,本软件支持官方ROM升级/降级操作,官改包与类原生请用户自行测试; 15 |
16 | 4. 如果提示更新失败,请重启手机并等候一段时间再进行刷写操作 17 |
18 | 5. 如有其他问题欢迎补充~]]>
19 |
-------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/xml/update_path.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/test/java/com/pointer/wave/easyship/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | 18 | @Test 19 | fun test(){ 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/test/java/com/pointer/wave/easyship/Test.java: -------------------------------------------------------------------------------- 1 | package com.pointer.wave.easyship; 2 | 3 | public class Test { 4 | 5 | @org.junit.Test 6 | public void test(){ 7 | 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | maven { url 'https://repo1.maven.org/maven2/' } 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:7.1.2' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | task clean(type: Delete) { 19 | delete rootProject.buildDir 20 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## For more details on how to configure your build environment visit 2 | # http://www.gradle.org/docs/current/userguide/build_environment.html 3 | # 4 | # Specifies the JVM arguments used for the daemon process. 5 | # The setting is particularly useful for tweaking memory settings. 6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 8 | # 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | #Sun Mar 20 19:33:50 CST 2022 14 | android.nonTransitiveRClass=true 15 | kotlin.code.style=official 16 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding\=UTF-8 17 | android.useAndroidX=true 18 | android.enableJetifier=true 19 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lumyuan/EasyShip/82a53ba2f2ad91cc5dd914e55ed98fe6f9742c06/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 20 15:46:56 CST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | jcenter() 14 | } 15 | } 16 | rootProject.name = "EasyShip" 17 | include ':app' 18 | --------------------------------------------------------------------------------