├── .gitignore
├── .idea
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── LICENSE
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── top
│ │ └── zsh2401
│ │ └── imagehelper
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── top
│ │ │ └── zsh2401
│ │ │ └── imagehelper
│ │ │ ├── App.kt
│ │ │ ├── core
│ │ │ ├── RebootHelper.kt
│ │ │ ├── image
│ │ │ │ ├── CommandHelper.kt
│ │ │ │ ├── FileHelper.kt
│ │ │ │ ├── ImageExtractor.kt
│ │ │ │ ├── ImageFinder.kt
│ │ │ │ ├── ImageFlasher.kt
│ │ │ │ ├── ImageNotFoundException.kt
│ │ │ │ └── Images.kt
│ │ │ └── su
│ │ │ │ ├── ExecResult.kt
│ │ │ │ ├── NoSuException.kt
│ │ │ │ ├── Su.kt
│ │ │ │ └── SuManager.kt
│ │ │ └── ux
│ │ │ ├── DonateHelper.kt
│ │ │ ├── FileUtil.java
│ │ │ ├── Flow.kt
│ │ │ ├── LanguageHelper.kt
│ │ │ ├── MainActivity.kt
│ │ │ └── PageAdapater.kt
│ └── res
│ │ ├── drawable
│ │ └── btm_nav_color.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── item_view_page_extract.xml
│ │ ├── item_view_page_flash.xml
│ │ └── nav_header.xml
│ │ ├── menu
│ │ ├── bottom_menu.xml
│ │ └── nav_menu.xml
│ │ ├── mipmap-mdpi
│ │ ├── ic_about.png
│ │ ├── ic_boot.png
│ │ ├── ic_donate.png
│ │ ├── ic_extract.png
│ │ ├── ic_flash.png
│ │ ├── ic_flash_main.png
│ │ ├── ic_github.png
│ │ ├── ic_language.png
│ │ └── ic_rec.png
│ │ ├── values-zh-rCN
│ │ └── strings.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── top
│ └── zsh2401
│ └── imagehelper
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
166 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'kotlin-android-extensions'
6 |
7 | android {
8 | compileSdkVersion 26
9 | defaultConfig {
10 | applicationId "top.zsh2401.imagehelper"
11 | minSdkVersion 19
12 | targetSdkVersion 26
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | }
22 |
23 | dependencies {
24 | implementation fileTree(dir: 'libs', include: ['*.jar'])
25 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
26 | implementation 'com.android.support:appcompat-v7:26.1.0'
27 | implementation 'com.android.support:design:26.1.0'
28 | implementation 'com.android.support:percent:26.1.0'
29 | implementation 'com.android.support.constraint:constraint-layout:1.0.2'
30 | testImplementation 'junit:junit:4.12'
31 | androidTestImplementation 'com.android.support.test:runner:1.0.1'
32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
33 | }
34 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/top/zsh2401/imagehelper/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper
2 |
3 | import android.support.test.InstrumentationRegistry
4 | import android.support.test.runner.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.getTargetContext()
22 | assertEquals("top.zsh2401.imagehelper", appContext.packageName)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/App.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.content.res.Configuration
6 | import android.net.Uri
7 | import android.os.Environment
8 | import android.util.Log
9 | import top.zsh2401.imagehelper.core.image.ImageExtractor
10 | import top.zsh2401.imagehelper.core.image.ImageFinder
11 | import top.zsh2401.imagehelper.core.image.Images
12 | import top.zsh2401.imagehelper.ux.FileUtil
13 | import android.provider.MediaStore
14 |
15 |
16 |
17 | /**
18 | * Created by zsh24 on 02/01/2018.
19 | */
20 | class App : Application() {
21 | companion object {
22 | val TAG = "App"
23 |
24 | val current:App get() = _current
25 | private lateinit var _current:App
26 |
27 | lateinit var gConfig:Configuration
28 | val context: Context
29 | get() =_context
30 | private lateinit var _context:Context
31 | }
32 | override fun onCreate() {
33 | super.onCreate()
34 | var storage = Environment.getExternalStorageDirectory().absolutePath
35 | // getRealPathFromURI("content://")
36 | Log.d(TAG,storage)
37 | val config = resources.configuration
38 | _current = this
39 | gConfig = config
40 | _context = this.applicationContext
41 | }
42 |
43 | fun getRealPathFromURI(contentUri: Uri): String? {
44 | var res: String? = null
45 | val proj = arrayOf(MediaStore.Images.Media.DATA)
46 | val cursor = contentResolver.query(contentUri, proj, null, null, null)
47 | if (cursor!!.moveToFirst()) {
48 | val column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
49 | res = cursor.getString(column_index)
50 | }
51 | cursor.close()
52 | return res
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/RebootHelper.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core
2 |
3 | import top.zsh2401.imagehelper.core.su.SuManager
4 |
5 | /**
6 | * Created by zsh24 on 02/02/2018.
7 | */
8 | fun rebootToRecovery(){
9 | SuManager.getSu().setCommandAndExecute("reboot recovery")
10 | }
11 | fun reboot(){
12 | SuManager.getSu().setCommandAndExecute("reboot")
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/CommandHelper.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | /**
4 | * Created by zsh24 on 02/01/2018.
5 | */
6 | object CommandHelper {
7 | fun haveFindCommand():Boolean{
8 | try{
9 | Runtime.getRuntime().exec("find").destroy()
10 | return true
11 | }catch (ex:Exception){
12 | ex.printStackTrace()
13 | return false
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/FileHelper.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | import android.net.Uri
4 | import top.zsh2401.imagehelper.App
5 | import java.io.File
6 | import java.io.FileOutputStream
7 |
8 | /**
9 | * Created by zsh24 on 02/02/2018.
10 | */
11 | fun getTempFile(uri: Uri):String{
12 | val tempPath = App.current.externalCacheDir.absolutePath + "/temp_file"
13 | val reader = App.current.contentResolver.openInputStream(uri)
14 | val writer = FileOutputStream(tempPath)
15 | val buffer = ByteArray(1024)
16 | var readSize = 0
17 | while(true){
18 | readSize = reader.read(buffer)
19 | if(readSize==-1){
20 | break
21 | }
22 | writer.write(buffer,0,readSize)
23 | }
24 | writer.flush()
25 | writer.close()
26 | reader.close()
27 | return tempPath
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/ImageExtractor.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | import top.zsh2401.imagehelper.core.su.Su
4 | import top.zsh2401.imagehelper.core.su.SuManager
5 |
6 | /**
7 | * Created by zsh24 on 01/31/2018.
8 | */
9 | class ImageExtractor(private val img:Images) :Runnable{
10 | private lateinit var su: Su
11 | override fun run(){
12 | var path = ImageFinder.pathOf(img)
13 | su = SuManager.getSu()
14 | su.setCommandAndExecute("cp $path /sdcard/${img.toString().toLowerCase()}.img")
15 | waitFor()
16 | }
17 | fun runAsync(callback:((Boolean)->Unit)? = null){
18 | Thread({
19 | try{
20 | run()
21 | callback?.invoke(su.process.exitValue() == 0)
22 | }catch (ex:Exception){
23 | callback?.invoke(false)
24 | }
25 | }).start()
26 | }
27 | fun cancel(){
28 | su.process.destroy()
29 | }
30 | fun waitFor():Int{
31 | return su.waitFor()
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/ImageFinder.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | import android.util.Log
4 | import top.zsh2401.imagehelper.core.su.SuManager
5 |
6 | /**
7 | * Created by zsh24 on 02/01/2018.
8 | */
9 |
10 | object ImageFinder {
11 | val TAG = "ImageFinder"
12 | @Throws(ImageNotFoundException::class)
13 | fun pathOf(img: Images):String{
14 | return method1(img) ?:
15 | method2(img) ?:
16 | throw ImageNotFoundException()
17 | }
18 | private fun method1(img: Images):String?{
19 | if(!CommandHelper.haveFindCommand())return null
20 | var findResult =
21 | SuManager.execSuCommand(
22 | "find /dev/ -name ${img.toString().toLowerCase()}")
23 | Log.d(TAG,"output: "+ findResult.output)
24 | if(findResult.exitValue != 0)return null
25 | var lines = findResult.output.split("\n")
26 | Log.d(TAG,"splited...")
27 | var line:String? = null
28 | for(i in lines.indices){
29 | Log.d(TAG,"finding...crt: " + lines[i])
30 | if(pathIsRight(lines[i])){
31 | line = lines[i]
32 | break
33 | }
34 | }
35 | Log.d(TAG,"method1 result: " + (line?:"null"))
36 | return line
37 | }
38 | private fun method2(img: Images):String?{
39 | var maybePath1 =
40 | "/dev/block/platform/*/by-name/${img.toString().toLowerCase()}"
41 | var maybePath2 =
42 | "/dev/block/platform/soc*/*/by-name/${img.toString().toLowerCase()}"
43 | var result:String?= null
44 | if(pathIsRight(maybePath1)){
45 | result = maybePath1
46 | }else if(pathIsRight(maybePath2)){
47 | result = maybePath2
48 | }
49 | Log.d(TAG,"method2 result: " + (result?:"null"))
50 | return result
51 | }
52 | private fun pathIsRight(path:String):Boolean{
53 | if(path.isNullOrEmpty())return false
54 | return SuManager.execSuCommand("ls -l " + path).exitValue == 0
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/ImageFlasher.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | import android.media.Image
4 | import android.net.Uri
5 | import android.util.Log
6 | import top.zsh2401.imagehelper.App
7 | import top.zsh2401.imagehelper.core.su.Su
8 | import top.zsh2401.imagehelper.core.su.SuManager
9 | import top.zsh2401.imagehelper.ux.FileUtil
10 | import top.zsh2401.imagehelper.ux.Flow
11 | import java.io.File
12 | import java.net.URI
13 |
14 | /**
15 | * Created by zsh24 on 02/01/2018.
16 | */
17 | class ImageFlasher(private val img:Images,private val fileUri:Uri):Runnable {
18 | private val TAG = "Image Flasher"
19 | lateinit var su: Su
20 | override fun run() {
21 | Log.d(TAG,"the uri: " + fileUri)
22 | var tempPath =getTempFile(fileUri)
23 | var targetPath = ImageFinder.pathOf(img)
24 | su = SuManager.getSu()
25 | su.setCommandAndExecute("mv $tempPath $targetPath")
26 | su.waitFor()
27 | }
28 | fun runAsync(callback:((Boolean)->Unit)?=null){
29 | Thread({
30 | try{
31 | run()
32 | callback?.invoke(su.process.exitValue() == 0)
33 | }catch (ex:Exception){
34 | ex.printStackTrace()
35 | callback?.invoke(false)
36 | }
37 | }).start()
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/ImageNotFoundException.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | /**
4 | * Created by zsh24 on 02/01/2018.
5 | */
6 | class ImageNotFoundException:Exception()
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/image/Images.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.image
2 |
3 | /**
4 | * Created by zsh24 on 01/31/2018.
5 | */
6 | enum class Images {
7 | Boot,
8 | Recovery
9 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/su/ExecResult.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.su
2 |
3 | /**
4 | * Created by zsh24 on 02/01/2018.
5 | */
6 | data class ExecResult(val output:String,val exitValue:Int){
7 | override fun toString(): String {
8 | return output
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/su/NoSuException.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.su
2 |
3 | /**
4 | * Created by zsh24 on 02/01/2018.
5 | */
6 | class NoSuException:Exception()
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/su/Su.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.su
2 |
3 | import java.io.BufferedInputStream
4 | import java.io.BufferedReader
5 | import java.io.DataOutputStream
6 | import java.io.InputStreamReader
7 |
8 | /**
9 | * Created by zsh24 on 02/01/2018.
10 | */
11 | class Su(val process:Process) {
12 | var writer:DataOutputStream =
13 | DataOutputStream(process.outputStream)
14 | var reader:BufferedReader =
15 | BufferedReader(InputStreamReader(process.inputStream))
16 |
17 | fun setCommandAndExecute(vararg commands:String){
18 | for(cmd in commands){
19 | writer.writeBytes(cmd + "\n")
20 | }
21 | writer.writeBytes("exit\n")
22 | writer.flush()
23 | }
24 | fun getOutput():String{
25 | var sb = StringBuilder()
26 | var line:String? = null
27 | while(true){
28 | line = reader.readLine()
29 | if(line.isNullOrEmpty())break
30 | sb.append(line + "\n")
31 | }
32 | return sb.toString()
33 | }
34 | fun waitFor():Int{
35 | return process.waitFor()
36 | }
37 | fun stop(){
38 | process.destroy()
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/core/su/SuManager.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.core.su
2 |
3 | import java.io.*
4 |
5 |
6 |
7 | /**
8 | * Created by zsh24 on 02/01/2018.
9 | */
10 | object SuManager {
11 | val TAG = "SuManager"
12 | fun deviceIsRoot():Boolean{
13 | try{
14 | return File("/system/bin/su").exists() ||
15 | File("/system/xbin/su").exists()
16 | }catch (e:Exception){
17 | e.printStackTrace()
18 | }
19 | return false
20 | }
21 | fun haveRootPermission():Boolean{
22 | try{
23 | // if(!deviceIsRoot())return false
24 | Runtime.getRuntime().exec("su").destroy()
25 | return true
26 | }catch (ex:Exception){
27 | ex.printStackTrace()
28 | return false
29 | }
30 | }
31 | @Throws(NoSuException::class)
32 | fun getSu():Su{
33 | suCheck()
34 | return Su(Runtime.getRuntime().exec("su"))
35 | }
36 | @Throws(NoSuException::class)
37 | fun execSuCommand(cmd:String): ExecResult {
38 | suCheck()
39 | val su = Runtime.getRuntime().exec("su")
40 | val writer = DataOutputStream(su.outputStream)
41 | val reader = BufferedReader(InputStreamReader(su.inputStream))
42 | //exe command and wait to exit...
43 | writer.writeBytes(cmd + "\n")
44 | writer.writeBytes("exit\n")
45 | writer.flush()
46 | su.waitFor()
47 | // reader.readText()
48 | return ExecResult(reader.readText(), su.exitValue())
49 | }
50 | fun suCheck(){
51 | if(!haveRootPermission()){
52 | throw NoSuException()
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/ux/DonateHelper.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.ux
2 |
3 | import android.content.ClipboardManager
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import top.zsh2401.imagehelper.App
8 |
9 | /**
10 | * Created by zsh24 on 02/01/2018.
11 | */
12 | private val alipayClientPkgName = "com.eg.android.AlipayGphone"
13 | private val wechatAccount = "Ryme2401"
14 | private val alipayAccount = "zsh2401@163.com"
15 | private val alipayRedpacketCode = ""
16 | private val URL_FORMAT= "intent://platformapi/startapp?saId=10000007&" +
17 | "clientVersion=3.7.0.0718&qrcode=https%3A%2F%2Fqr.alipay.com%2FFKX06104YFIEAHNXXKP7E2%3F_s" +
18 | "%3Dweb-other&_t=1472443966571#Intent;" +
19 | "scheme=alipayqr;package=com.eg.android.AlipayGphone;end"
20 | private val clipboardManager:ClipboardManager =
21 | App.current.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
22 |
23 | fun gotoAlipay():Boolean{
24 | if(isInstallAlipayClient()){
25 | App.context.startActivity(Intent.parseUri(URL_FORMAT,Intent.URI_INTENT_SCHEME))
26 | return true
27 | }
28 | return false
29 | }
30 | fun copyWechatAccount(){
31 | clipboardManager.text = wechatAccount
32 | }
33 | fun copyAlipayAccount(){
34 | clipboardManager.text = alipayAccount
35 | }
36 | fun copyAlipayRedpacketCode(){
37 | clipboardManager.text = alipayRedpacketCode
38 | }
39 |
40 | private fun isInstallAlipayClient() :Boolean{
41 | try{
42 | return App.context.packageManager.getPackageInfo(alipayClientPkgName,0) != null
43 | }catch (ex: PackageManager.NameNotFoundException){
44 | return false
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/ux/FileUtil.java:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.ux;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.os.Build;
8 | import android.os.Environment;
9 | import android.provider.DocumentsContract;
10 | import android.provider.MediaStore;
11 | import android.util.Log;
12 |
13 | import java.io.File;
14 | import java.io.FileInputStream;
15 | import java.io.FileOutputStream;
16 | import java.io.InputStream;
17 | import java.net.URISyntaxException;
18 |
19 | /**
20 | * Created by zsh24 on 02/02/2018.
21 | */
22 |
23 | public class FileUtil{
24 |
25 |
26 | ///////////////////////复制文件//////////////////////////////
27 | /**
28 | * 复制单个文件
29 | * @param oldPath String 原文件路径 如:c:/fqf.txt
30 | * @param newPath String 复制后路径 如:f:/fqf.txt
31 | * @return boolean
32 | */
33 | public static boolean copyFile(String oldPath, String newPath) {
34 | boolean isok = true;
35 | try {
36 | int bytesum = 0;
37 | int byteread = 0;
38 | File oldfile = new File(oldPath);
39 | if (oldfile.exists()) { //文件存在时
40 | InputStream inStream = new FileInputStream(oldPath); //读入原文件
41 | FileOutputStream fs = new FileOutputStream(newPath);
42 | byte[] buffer = new byte[1024];
43 | int length;
44 | while ( (byteread = inStream.read(buffer)) != -1) {
45 | bytesum += byteread; //字节数 文件大小
46 | //System.out.println(bytesum);
47 | fs.write(buffer, 0, byteread);
48 | }
49 | fs.flush();
50 | fs.close();
51 | inStream.close();
52 | }
53 | else
54 | {
55 | Log.d("FileUtil","file not exist");
56 | isok = false;
57 | }
58 | }
59 | catch (Exception e) {
60 | Log.d("FileUtil","Copy failed..");
61 | e.printStackTrace();
62 | isok = false;
63 | }
64 | return isok;
65 | }
66 | public static String getRealFilePath( final Context context, final Uri uri ) {
67 | if ( null == uri ) return null;
68 | final String scheme = uri.getScheme();
69 | String data = null;
70 | if ( scheme == null )
71 | data = uri.getPath();
72 | else if ( ContentResolver.SCHEME_FILE.equals( scheme ) ) {
73 | data = uri.getPath();
74 | } else if ( ContentResolver.SCHEME_CONTENT.equals( scheme ) ) {
75 | Cursor cursor = context.getContentResolver().query( uri, new String[] { MediaStore.Images.ImageColumns.DATA }, null, null, null );
76 | if ( null != cursor ) {
77 | if ( cursor.moveToFirst() ) {
78 | int index = cursor.getColumnIndex( MediaStore.Images.ImageColumns.DATA );
79 | if ( index > -1 ) {
80 | data = cursor.getString( index );
81 | }
82 | }
83 | cursor.close();
84 | }
85 | }
86 | return data;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/ux/Flow.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.ux
2 |
3 | import android.app.Activity
4 | import android.app.AlertDialog
5 | import android.app.ProgressDialog
6 | import android.content.Intent
7 | import android.net.Uri
8 | import android.support.design.widget.Snackbar
9 | import android.util.Log
10 | import android.view.View
11 | import top.zsh2401.imagehelper.App
12 | import top.zsh2401.imagehelper.R
13 | import top.zsh2401.imagehelper.core.image.ImageExtractor
14 | import top.zsh2401.imagehelper.core.image.ImageFlasher
15 | import top.zsh2401.imagehelper.core.image.Images
16 | import top.zsh2401.imagehelper.core.rebootToRecovery
17 | import top.zsh2401.imagehelper.core.su.SuManager
18 | import java.io.File
19 | import java.net.URI
20 |
21 | /**
22 | * Created by zsh24 on 02/01/2018.
23 | */
24 | object Flow {
25 | val FILE_SELECT_REQUEST_CODE = 2401
26 | val TAG = "Flow"
27 | lateinit var view:View
28 | lateinit var mainActivity: MainActivity
29 | fun extractRecovery(){
30 | extract(Images.Recovery)
31 | }
32 | fun extractBoot(){
33 | extract(Images.Boot)
34 | }
35 | private fun extract(img:Images){
36 | if(!rootCheck())return
37 | var bar = ProgressDialog(mainActivity)
38 | bar.setMessage(App.current.getString(R.string.msg_extracting))
39 | var extractor = ImageExtractor(img)
40 | bar.setOnDismissListener({
41 | extractor.cancel()
42 | })
43 | bar.show()
44 | extractor.runAsync({isSuccessful->
45 | mainActivity.runOnUiThread({
46 | if(bar.isShowing)
47 | bar.dismiss()
48 | var builder = AlertDialog.Builder(mainActivity)
49 | builder.setTitle(
50 | if(isSuccessful)R.string.title_successed
51 | else R.string.title_failed)
52 | builder.setMessage(
53 | if(isSuccessful)R.string.msg_extract_ok
54 | else R.string.msg_extract_failed)
55 | builder.setNegativeButton("ok",null)
56 | builder.show()
57 | })
58 | })
59 | }
60 | fun flashRecovery(){
61 | flash(Images.Recovery)
62 | }
63 | fun flashBoot(){
64 | flash(Images.Boot)
65 | }
66 | private fun flash(img:Images){
67 | if(!rootCheck())return
68 | selectAFile()
69 | mainActivity.onFileSelectedCallback = {path->
70 | flash(path,img)
71 | }
72 | }
73 | private fun flash(pathUri: Uri, img: Images){
74 | var bar = ProgressDialog(mainActivity)
75 | bar.setMessage(App.current.getString(R.string.msg_extracting))
76 | var flasher = ImageFlasher(img,pathUri)
77 | bar.setOnDismissListener({
78 | flasher.su.process.destroy()
79 | })
80 | flasher.runAsync({isSuccessful->
81 | var dialog = AlertDialog.Builder(mainActivity)
82 | dialog.setTitle(
83 | if(isSuccessful) R.string.title_successed
84 | else R.string.title_failed)
85 | dialog.setMessage(
86 | if(isSuccessful)R.string.msg_flash_ok
87 | else R.string.msg_flash_failed)
88 | if(isSuccessful && img == Images.Recovery){
89 | dialog.setPositiveButton(R.string.reboot_to_recovery,{_,_->
90 | rebootToRecovery()
91 | })
92 | dialog.setNegativeButton(R.string.donot_reboot,null)
93 | }else{
94 | dialog.setPositiveButton("ok",null)
95 | }
96 |
97 | mainActivity.runOnUiThread({
98 | bar.dismiss()
99 | dialog.show()
100 | })
101 | })
102 | bar.show()
103 | }
104 | private fun selectAFile(){
105 | var intent = Intent(Intent.ACTION_GET_CONTENT)
106 | intent.setType("*/*")
107 | intent.addCategory(Intent.CATEGORY_OPENABLE)
108 | mainActivity.startActivityForResult(Intent.createChooser(intent, "请选择一个要上传的文件"),
109 | FILE_SELECT_REQUEST_CODE)
110 | }
111 | private fun rootCheck():Boolean{
112 | if(SuManager.haveRootPermission()){
113 | return true
114 | }else{
115 | Snackbar.make(view,R.string.warning_no_root,Snackbar.LENGTH_LONG)
116 | .setAction("ok",null)
117 | .show()
118 | return false
119 | }
120 | }
121 | }
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/ux/LanguageHelper.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.ux
2 |
3 | import top.zsh2401.imagehelper.App
4 | import top.zsh2401.imagehelper.R
5 | import java.util.*
6 |
7 | /**
8 | * Created by zsh24 on 02/01/2018.
9 | */
10 | object LanguageHelper{
11 | val languages:Array = arrayOf(
12 | App.context.getString(R.string.l_auto),
13 | App.context.getString(R.string.l_zh_cn),
14 | App.context.getString(R.string.l_en_us))
15 |
16 | fun setLanguageBySerial(languageCode:Int){
17 | when(languageCode){
18 | 1->{
19 | App.gConfig.locale = Locale.CHINESE}
20 | 2->{
21 | App.gConfig.locale = Locale.ENGLISH
22 | }
23 | else->{
24 | App.gConfig.locale = Locale.getDefault()}
25 | }
26 |
27 | }
28 | fun setAuto(){
29 | setLanguageBySerial(0)
30 | }
31 |
32 | fun getCurrentLanguageSerial():Int{
33 | if(isSetAutoLanguage())return 0
34 | var crtLaguageName = App.context.getString(R.string.language_name)
35 | return languages.indexOf(crtLaguageName)
36 | }
37 | fun isSetAutoLanguage():Boolean{
38 | return true
39 | }
40 | private fun getSavedChoice():Int{
41 | return 1
42 | }
43 | private fun saveChoice(choice:Int){
44 |
45 | }
46 | fun initLanguage(){
47 | setLanguageBySerial(getSavedChoice())
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/top/zsh2401/imagehelper/ux/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package top.zsh2401.imagehelper.ux
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.content.ContentUris
6 | import android.content.Context
7 | import android.content.Intent
8 | import android.content.pm.PackageManager
9 | import android.net.Uri
10 | import android.os.Build
11 | import android.os.Bundle
12 | import android.os.Environment
13 | import android.provider.DocumentsContract
14 | import android.provider.MediaStore
15 | import android.support.design.widget.BottomNavigationView
16 | import android.support.design.widget.NavigationView
17 | import android.support.design.widget.Snackbar
18 | import android.support.v4.app.ActivityCompat
19 | import android.support.v4.content.ContextCompat
20 | import android.support.v4.view.ViewPager
21 | import android.support.v4.widget.DrawerLayout
22 | import android.support.v7.app.ActionBarDrawerToggle
23 | import android.support.v7.app.AlertDialog
24 | import android.support.v7.app.AppCompatActivity
25 | import android.support.v7.widget.Toolbar
26 | import android.util.Log
27 | import android.view.MenuItem
28 | import android.view.View
29 | import android.view.Window
30 | import android.view.WindowManager
31 | import android.widget.Button
32 | import top.zsh2401.imagehelper.App
33 | import top.zsh2401.imagehelper.R
34 | import java.net.URI
35 |
36 | class MainActivity : AppCompatActivity(),
37 | View.OnClickListener,
38 | BottomNavigationView.OnNavigationItemSelectedListener,
39 | ViewPager.OnPageChangeListener
40 | {
41 |
42 | private val TAG = "MainActivity"
43 | private lateinit var viewList:ArrayList
44 | private lateinit var mNavView:NavigationView
45 | private lateinit var mToolbar:Toolbar
46 | private lateinit var mDrawer:DrawerLayout
47 | private lateinit var mViewPager: ViewPager
48 | private lateinit var mBottomNav:BottomNavigationView
49 | override fun onCreate(savedInstanceState: Bundle?) {
50 | super.onCreate(savedInstanceState)
51 | setContentView(R.layout.activity_main)
52 | initViewObj()
53 | initViewPager()
54 | initDrawer()
55 | Flow.mainActivity = this
56 | Flow.view = mNavView
57 | initEvent()
58 | checkAndRequestPermission()
59 | }
60 | private val myPermissionRequestCode = 2405
61 | private fun checkAndRequestPermission(){
62 | if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M)return
63 | var permissions = arrayOf(
64 | "android.permission.READ_EXTERNAL_STORAGE",
65 | "android.permission.WRITE_EXTERNAL_STORAGE")
66 | var allGrated =checkPermissionAllGranted(permissions)
67 | if(allGrated)return
68 | ActivityCompat.requestPermissions(this, permissions,myPermissionRequestCode)
69 | }
70 |
71 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
72 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
73 | if(requestCode==myPermissionRequestCode){
74 | var isAllGranted = true
75 | for(grant in grantResults){
76 | if(grant != PackageManager.PERMISSION_GRANTED){
77 | isAllGranted = false
78 | break
79 | }
80 | }
81 |
82 | if(!isAllGranted){
83 | var builder = AlertDialog.Builder(this)
84 | builder.setTitle(R.string.title_sorry)
85 | builder.setMessage(R.string.msg_permission_deined)
86 | builder.setOnDismissListener({
87 | finish()
88 | })
89 | builder.setNegativeButton("ok",null)
90 | // builder.setNeutralButton("ok",null)
91 | builder.show()
92 | }
93 | }
94 | }
95 |
96 | private fun checkPermissionAllGranted(permissions: Array): Boolean {
97 | for (permission in permissions) {
98 | if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
99 | // 只要有一个权限没有被授予, 则直接返回 false
100 | return false
101 | }
102 | }
103 | return true
104 | }
105 |
106 | private fun initEvent(){
107 | mBottomNav.setOnNavigationItemSelectedListener(this)
108 | mViewPager.addOnPageChangeListener(this)
109 | initNavEvent()
110 | viewList[0].findViewById