Skip to content

Commit ab18499

Browse files
committed
添加 apk备份
添加 重启应用 添加 停止应用 添加 清理应用数据 添加 卸载应用
1 parent 97a06a9 commit ab18499

File tree

20 files changed

+314
-28
lines changed

20 files changed

+314
-28
lines changed

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ dependencies {
5959
// https://mvnrepository.com/artifact/dom4j/dom4j
6060
implementation 'dom4j:dom4j:1.6.1'
6161
implementation 'com.evrencoskun.library:tableview:0.8.8'
62+
implementation 'gdut.bsx:share2:0.9.3'
6263
kapt 'com.google.dagger:dagger-compiler:2.16'
6364
implementation project(':commonutil')
6465
implementation project(':mmkv')

app/src/main/AndroidManifest.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
88
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
99
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
10+
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
1011

1112
<application
1213
android:name=".DeveloperApplication"
1314
android:allowBackup="true"
1415
android:icon="@mipmap/ic_launcher"
1516
android:label="@string/app_name"
1617
android:roundIcon="@mipmap/ic_launcher_round"
18+
android:sharedUserId="android.uid.system"
1719
android:supportsRtl="true"
1820
android:theme="@style/AppTheme"
1921
tools:ignore="GoogleAppIndexingWarning">
@@ -55,6 +57,17 @@
5557
android:theme="@style/AppTheme.NoActionBar" />
5658
<activity android:name=".ui.activity.sharedpreferencesedit.SharedPreferenceEditActivity" />
5759
<activity android:name=".ui.activity.databaseedit.DatabaseEditActivity"></activity>
60+
61+
<provider
62+
android:name="androidx.core.content.FileProvider"
63+
android:authorities="com.wrbug.developerhelper.fileprovider"
64+
android:exported="false"
65+
android:grantUriPermissions="true">
66+
<meta-data
67+
android:name="android.support.FILE_PROVIDER_PATHS"
68+
android:resource="@xml/filepaths" />
69+
70+
</provider>
5871
</application>
5972

6073
</manifest>

app/src/main/java/com/wrbug/developerhelper/ui/activity/hierachy/AppInfoPagerAdapter.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class AppInfoPagerAdapter(
4545
private fun initAppSettingTab() {
4646
tabList.add(context.getString(R.string.app_setting))
4747
val view = AppSettingView(context)
48+
view.apkInfo=apkInfo
4849
viewList.add(view)
4950
}
5051

app/src/main/java/com/wrbug/developerhelper/ui/activity/main/MainActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.wrbug.developerhelper.R
1313
import com.wrbug.developerhelper.basecommon.BaseVMActivity
1414
import com.wrbug.developerhelper.basecommon.obtainViewModel
1515
import com.wrbug.developerhelper.basecommon.setupActionBar
16+
import com.wrbug.developerhelper.commonutil.AppManagerUtils
1617
import com.wrbug.developerhelper.commonutil.ClipboardUtils
1718
import com.wrbug.developerhelper.constant.ReceiverConstant
1819
import com.wrbug.developerhelper.databinding.ActivityMainBinding

app/src/main/java/com/wrbug/developerhelper/ui/activity/main/viewmodel/MainViewModel.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ class MainViewModel @Inject constructor() : BaseViewModel() {
2222
fun checkStatus() {
2323
openAccessibility.set(DeveloperHelperAccessibilityService.serviceRunning)
2424
openFloatWindow.set(DeviceUtils.isFloatWindowOpened())
25-
openRoot.set(configKv.getOpenRoot())
26-
openXposed.set(configKv.getOpenXposed())
25+
openRoot.set(configKv.isOpenRoot())
26+
openXposed.set(configKv.isOpenXposed())
2727
if (DeviceUtils.isFloatWindowOpened()) {
2828
FloatWindowService.start(application)
2929
}

app/src/main/java/com/wrbug/developerhelper/ui/widget/appsettingview/AppSettingView.kt

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,33 @@
11
package com.wrbug.developerhelper.ui.widget.appsettingview
22

3+
import android.Manifest
4+
import android.app.Activity
5+
import android.app.AlertDialog
36
import android.content.Context
7+
import android.content.DialogInterface
48
import android.util.AttributeSet
59
import android.view.LayoutInflater
6-
import android.widget.FrameLayout
710
import android.widget.ScrollView
811
import com.wrbug.developerhelper.R
12+
import com.wrbug.developerhelper.basecommon.showToast
13+
import com.wrbug.developerhelper.commonutil.AppManagerUtils
14+
import com.wrbug.developerhelper.commonutil.entity.ApkInfo
15+
import com.wrbug.developerhelper.mmkv.ConfigKv
16+
import com.wrbug.developerhelper.mmkv.manager.MMKVManager
17+
import kotlinx.android.synthetic.main.view_app_setting.view.*
18+
import android.content.Intent
19+
import android.net.Uri
20+
import com.wrbug.developerhelper.basecommon.BaseActivity
21+
import com.wrbug.developerhelper.util.BackupUtils
22+
import com.wrbug.developerhelper.util.DeviceUtils
23+
import gdut.bsx.share2.Share2
24+
import gdut.bsx.share2.ShareContentType
25+
926

1027
class AppSettingView : ScrollView {
28+
var apkInfo: ApkInfo? = null
29+
private val configKv = MMKVManager.get(ConfigKv::class.java)
30+
1131
constructor(context: Context) : super(context) {
1232
initView()
1333
}
@@ -18,5 +38,151 @@ class AppSettingView : ScrollView {
1838

1939
private fun initView() {
2040
LayoutInflater.from(context).inflate(R.layout.view_app_setting, this)
41+
if (configKv.isOpenRoot().not()) {
42+
backupApkBtn.isEnabled = false
43+
backupApkDataDirBtn.isEnabled = false
44+
restartAppBtn.isEnabled = false
45+
stopAppBtn.isEnabled = false
46+
deleteAppDataBtn.isEnabled = false
47+
}
48+
initListener()
49+
}
50+
51+
private fun initListener() {
52+
backupApkBtn.setOnClickListener {
53+
doBackupApk()
54+
}
55+
backupApkDataDirBtn.setOnClickListener {
56+
doBackupDataDir()
57+
}
58+
restartAppBtn.setOnClickListener {
59+
doRestartApp()
60+
}
61+
stopAppBtn.setOnClickListener {
62+
doStopApp()
63+
}
64+
deleteAppDataBtn.setOnClickListener {
65+
doDeleteAppData()
66+
}
67+
uninstallAppBtn.setOnClickListener {
68+
doUninstallApp()
69+
}
70+
}
71+
72+
private fun doUninstallApp() {
73+
apkInfo?.run {
74+
AppManagerUtils.uninstallApp(context, applicationInfo.packageName)
75+
}
76+
}
77+
78+
private fun doDeleteAppData() {
79+
if (checkRoot().not()) {
80+
return
81+
}
82+
apkInfo?.run {
83+
showNotice(context.getString(R.string.confirm_delete_app_data), DialogInterface.OnClickListener { _, _ ->
84+
if (AppManagerUtils.clearAppData(applicationInfo.packageName)) {
85+
showToast(context.getString(R.string.clear_complete))
86+
}
87+
})
88+
89+
}
90+
}
91+
92+
private fun doStopApp() {
93+
if (checkRoot().not()) {
94+
return
95+
}
96+
apkInfo?.run {
97+
showNotice(context.getString(R.string.confirm_stop_app), DialogInterface.OnClickListener { _, _ ->
98+
AppManagerUtils.forceStopApp(applicationInfo.packageName)
99+
})
100+
}
101+
}
102+
103+
private fun doRestartApp() {
104+
if (checkRoot().not()) {
105+
return
106+
}
107+
apkInfo?.run {
108+
showNotice(context.getString(R.string.confirm_restart_app), DialogInterface.OnClickListener { _, _ ->
109+
if (!AppManagerUtils.forceStopApp(applicationInfo.packageName)) {
110+
showToast(context.getString(R.string.restart_failed))
111+
return@OnClickListener
112+
}
113+
val intent = context.packageManager.getLaunchIntentForPackage(applicationInfo.packageName)
114+
intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
115+
context.startActivity(intent)
116+
})
117+
}
118+
}
119+
120+
private fun doBackupDataDir() {
121+
if (checkRoot().not()) {
122+
return
123+
}
124+
apkInfo?.run {
125+
126+
}
127+
}
128+
129+
private fun doBackupApk() {
130+
if (checkRoot().not()) {
131+
return
132+
}
133+
apkInfo?.run {
134+
val uri = BackupUtils.backupApk(
135+
applicationInfo.packageName,
136+
applicationInfo.publicSourceDir,
137+
"${getAppName()}_${packageInfo.versionName}.apk"
138+
)
139+
if (uri == null) {
140+
showToast(context.getString(R.string.backup_failed))
141+
return
142+
}
143+
if (context !is BaseActivity) {
144+
showToast(context.getString(R.string.backup_success_msg))
145+
return
146+
}
147+
showShareApkDialog(uri)
148+
149+
}
150+
}
151+
152+
private fun showShareApkDialog(uri: Uri) {
153+
showNotice(
154+
context.getString(R.string.backup_success_and_share_msg),
155+
DialogInterface.OnClickListener { _, _ ->
156+
(context as BaseActivity).requestPermission(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
157+
object : BaseActivity.PermissionCallback() {
158+
override fun granted() {
159+
Share2.Builder(context as Activity)
160+
.setContentType(ShareContentType.FILE)
161+
.setShareFileUri(uri)
162+
.setOnActivityResult(10)
163+
.build()
164+
.shareBySystem()
165+
}
166+
167+
})
168+
169+
})
170+
}
171+
172+
private fun checkRoot(): Boolean {
173+
if (configKv.isOpenRoot().not()) {
174+
showToast(context.getString(R.string.please_open_root))
175+
return false
176+
}
177+
return true
178+
}
179+
180+
private fun showNotice(msg: String, listener: DialogInterface.OnClickListener) {
181+
AlertDialog.Builder(context)
182+
.setTitle(R.string.notice)
183+
.setMessage(msg)
184+
.setNegativeButton(R.string.cancel, null)
185+
.setPositiveButton(R.string.ok, listener)
186+
.create().show()
21187
}
22188
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.wrbug.developerhelper.util
2+
3+
import android.net.Uri
4+
import android.os.Environment
5+
import com.wrbug.developerhelper.commonutil.shell.ShellManager
6+
import java.io.File
7+
8+
object BackupUtils {
9+
private val backupDir: File by lazy {
10+
val file = File(Environment.getExternalStorageDirectory(), "com.wrbug.developerHelper/backup")
11+
if (file.exists().not()) {
12+
file.mkdirs()
13+
}
14+
file
15+
}
16+
17+
fun backupApk(packageName: String, apkPath: String, fileName: String): Uri? {
18+
val apkDir = File(backupDir, "apks/$packageName/$fileName")
19+
if (ShellManager.cpFile(apkPath, apkDir.absolutePath)) {
20+
return apkDir.toUri()
21+
}
22+
return null
23+
}
24+
}

app/src/main/java/com/wrbug/developerhelper/util/FileUtils.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package com.wrbug.developerhelper.util
22

33
import android.media.MediaMetadataRetriever
4-
import android.os.Environment
4+
import android.net.Uri
5+
import android.os.Build
6+
import androidx.core.content.FileProvider
7+
import com.wrbug.developerhelper.basecommon.BaseApp
58
import org.dom4j.Document
69
import org.dom4j.io.OutputFormat
710
import org.dom4j.io.XMLWriter
811

912
import java.io.*
10-
import org.dom4j.io.OutputFormat.createPrettyPrint
1113

1214

1315
/**
@@ -111,3 +113,11 @@ object FileUtils {
111113
}
112114

113115
}
116+
117+
fun File.toUri(): Uri? {
118+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
119+
FileProvider.getUriForFile(BaseApp.instance, "com.wrbug.developerhelper.fileprovider", this)
120+
} else {
121+
Uri.fromFile(this)
122+
}
123+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+
<item android:drawable="@color/colorPrimary" android:state_enabled="true" />
4+
<item android:drawable="@color/colorPrimaryDark" android:state_pressed="true" />
5+
<item android:drawable="@color/material_color_grey_500" android:state_enabled="false" />
6+
7+
</selector>

0 commit comments

Comments
 (0)