Merge pull request #10 from wukaicheng/main

一些新功能
pull/15/head
Milk 4 years ago committed by GitHub
commit 9e49e89e78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      app/build.gradle
  2. 25
      app/src/main/AndroidManifest.xml
  3. 26
      app/src/main/java/top/niunaijun/blackdex/App.kt
  4. 40
      app/src/main/java/top/niunaijun/blackdex/app/App.kt
  5. 26
      app/src/main/java/top/niunaijun/blackdex/app/AppManager.kt
  6. 87
      app/src/main/java/top/niunaijun/blackdex/app/BlackDexLoader.kt
  7. 67
      app/src/main/java/top/niunaijun/blackdex/biz/cache/AppSharedPreferenceDelegate.kt
  8. 41
      app/src/main/java/top/niunaijun/blackdex/data/BlackDexConfiguration.kt
  9. 21
      app/src/main/java/top/niunaijun/blackdex/data/DexDumpRepository.kt
  10. 16
      app/src/main/java/top/niunaijun/blackdex/util/FileUtil.kt
  11. 12
      app/src/main/java/top/niunaijun/blackdex/view/base/BaseActivity.kt
  12. 99
      app/src/main/java/top/niunaijun/blackdex/view/base/PermissionActivity.kt
  13. 141
      app/src/main/java/top/niunaijun/blackdex/view/main/MainActivity.kt
  14. 25
      app/src/main/java/top/niunaijun/blackdex/view/setting/SettingActivity.kt
  15. 87
      app/src/main/java/top/niunaijun/blackdex/view/setting/SettingFragment.kt
  16. 22
      app/src/main/res/layout/activity_setting.xml
  17. 5
      app/src/main/res/menu/menu_main.xml
  18. 26
      app/src/main/res/values-zh/string.xml
  19. 28
      app/src/main/res/values/strings.xml
  20. 15
      app/src/main/res/xml/setting.xml
  21. 4
      build.gradle

@ -11,8 +11,8 @@ android {
applicationId "top.niunaijun.blackdex" applicationId "top.niunaijun.blackdex"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 30 targetSdkVersion 30
versionCode 1 versionCode 2
versionName "1.0" versionName "1.1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@ -72,6 +72,7 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.3.0' implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation project(':Bcore') implementation project(':Bcore')
@ -86,6 +87,7 @@ dependencies {
//third //third
implementation 'com.afollestad.material-dialogs:core:3.3.0' implementation 'com.afollestad.material-dialogs:core:3.3.0'
implementation 'com.afollestad.material-dialogs:files:3.2.1'
//dialog //dialog
implementation 'com.github.nukc.stateview:kotlin:2.2.0' implementation 'com.github.nukc.stateview:kotlin:2.2.0'
// //
@ -94,6 +96,9 @@ dependencies {
implementation 'com.github.Ferfalk:SimpleSearchView:0.2.0' implementation 'com.github.Ferfalk:SimpleSearchView:0.2.0'
//searchView //searchView
implementation 'com.umeng.umsdk:common:9.3.8'
implementation 'com.umeng.umsdk:asms:1.2.2'
//
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'

@ -3,29 +3,38 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
package="top.niunaijun.blackdex"> package="top.niunaijun.blackdex">
<!-- Android 11 需要 --> <!-- Android 11 需要 -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" <uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" /> tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<application <application
android:name="top.niunaijun.blackboxa.app.App"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:name=".App" android:requestLegacyExternalStorage="true"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.BlackDex"> android:theme="@style/Theme.BlackDex">
<activity android:name=".view.base.WelcomeActivity" <activity
android:name=".view.base.WelcomeActivity"
android:theme="@style/WelcomeTheme"> android:theme="@style/WelcomeTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity>
<activity android:name=".view.main.MainActivity">
</activity> </activity>
<activity android:name=".view.main.MainActivity" />
<activity android:name=".view.setting.SettingActivity" />
</application> </application>
</manifest> </manifest>

@ -1,26 +0,0 @@
package top.niunaijun.blackdex
import android.app.Application
import android.content.Context
import top.niunaijun.blackbox.BlackDexCore
import top.niunaijun.blackbox.app.configuration.ClientConfiguration
import top.niunaijun.blackdex.data.BlackDexConfiguration
/**
*
* @Description:
* @Author: wukaicheng
* @CreateDate: 2021/5/23 14:00
*/
class App : Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
BlackDexCore.get().doAttachBaseContext(base,BlackDexConfiguration(base!!))
}
override fun onCreate() {
super.onCreate()
BlackDexCore.get().doCreate()
}
}

@ -0,0 +1,40 @@
package top.niunaijun.blackboxa.app
import android.annotation.SuppressLint
import android.app.Application
import android.content.Context
import com.umeng.commonsdk.UMConfigure
import top.niunaijun.blackdex.app.AppManager
/**
*
* @Description:
* @Author: wukaicheng
* @CreateDate: 2021/4/29 21:21
*/
class App : Application() {
companion object {
@SuppressLint("StaticFieldLeak")
@Volatile
private lateinit var mContext: Context
@JvmStatic
fun getContext(): Context {
return mContext
}
}
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
mContext = base!!
UMConfigure.init(base, "60b373136c421a3d97d23c29", "Github", 0, "")
AppManager.doAttachBaseContext(base)
}
override fun onCreate() {
super.onCreate()
AppManager.doOnCreate(mContext)
}
}

@ -0,0 +1,26 @@
package top.niunaijun.blackdex.app
import android.content.Context
object AppManager {
@JvmStatic
val mBlackBoxLoader by lazy {
BlackDexLoader()
}
fun doAttachBaseContext(context: Context) {
try {
mBlackBoxLoader.attachBaseContext(context)
mBlackBoxLoader.addLifecycleCallback()
} catch (e: Exception) {
e.printStackTrace()
}
}
fun doOnCreate(context: Context) {
mBlackBoxLoader.doOnCreate(context)
initThirdService(context)
}
private fun initThirdService(context: Context) {}
}

@ -0,0 +1,87 @@
package top.niunaijun.blackdex.app
import android.content.Context
import top.niunaijun.blackbox.BlackDexCore
import top.niunaijun.blackbox.app.configuration.ClientConfiguration
import top.niunaijun.blackbox.utils.FileUtils
import top.niunaijun.blackbox.utils.compat.BuildCompat
import top.niunaijun.blackboxa.app.App
import top.niunaijun.blackdex.biz.cache.AppSharedPreferenceDelegate
import java.io.File
/**
*
* @Description:
* @Author: wukaicheng
* @CreateDate: 2021/5/6 23:38
*/
class BlackDexLoader {
private var mSavePath by AppSharedPreferenceDelegate(App.getContext(), "")
private var mSaveEnable by AppSharedPreferenceDelegate(App.getContext(), true)
private var mDir = if (mSaveEnable) {
getDexDumpDir(App.getContext())
} else {
mSavePath
}
fun addLifecycleCallback() {
}
fun attachBaseContext(context: Context) {
BlackDexCore.get().doAttachBaseContext(context, object : ClientConfiguration() {
override fun getHostPackageName(): String {
return context.packageName
}
override fun getDexDumpDir(): String {
return mDir
}
})
}
fun doOnCreate(context: Context) {
BlackDexCore.get().doCreate()
}
fun saveEnable(): Boolean {
return mSaveEnable
}
fun saveEnable(state: Boolean) {
this.mSaveEnable = state
}
fun getSavePath(): String {
return mSavePath
}
fun setSavePath(path: String) {
this.mSavePath = path
}
companion object {
val TAG: String = BlackDexLoader::class.java.simpleName
fun getDexDumpDir(context: Context): String {
return if (BuildCompat.isR()) {
val dump = File(
context.externalCacheDir?.parentFile?.parentFile?.parentFile?.parentFile,
"Download/dexDump"
)
FileUtils.mkdirs(dump)
dump.absolutePath
} else {
val dump = File(context.externalCacheDir?.parentFile, "dump")
FileUtils.mkdirs(dump)
dump.absolutePath
}
}
}
}

@ -0,0 +1,67 @@
package top.niunaijun.blackdex.biz.cache
import android.content.Context
import android.text.TextUtils
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
*
* @desc:目前只支持 5种基本数据类型如果要支持obj请继承该类并重写他的相关方法 findData/putData
*
* @author: mini
* @created by 2021/5/10
*/
open class AppSharedPreferenceDelegate<Data>(context: Context, private val default: Data, spName: String? = null) : ReadWriteProperty<Any, Data?> {
private val mSharedPreferences by lazy {
val tmpCacheName = if (TextUtils.isEmpty(spName)) {
AppSharedPreferenceDelegate::class.java.simpleName
} else {
spName
}
return@lazy context.getSharedPreferences(tmpCacheName, Context.MODE_PRIVATE)
}
override fun getValue(thisRef: Any, property: KProperty<*>): Data {
return findData(property.name, default)
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: Data?) {
putData(property.name, value)
}
protected fun findData(key: String, default: Data): Data {
with(mSharedPreferences) {
val result: Any? = when (default) {
is Int -> getInt(key, default)
is Long -> getLong(key, default)
is Float -> getFloat(key, default)
is String -> getString(key, default)
is Boolean -> getBoolean(key, default)
else -> throw IllegalArgumentException("This type $default can not be saved into sharedPreferences")
}
return result as? Data ?: default
}
}
protected fun putData(key: String, value: Data?) {
mSharedPreferences.edit {
if (value == null) {
remove(key)
} else {
when (value) {
is Int -> putInt(key, value)
is Long -> putLong(key, value)
is Float -> putFloat(key, value)
is String -> putString(key, value)
is Boolean -> putBoolean(key, value)
else -> throw IllegalArgumentException("This type $default can not be saved into Preferences")
}
}
}
}
}

@ -1,41 +0,0 @@
package top.niunaijun.blackdex.data
import android.content.Context
import top.niunaijun.blackbox.app.configuration.ClientConfiguration
import top.niunaijun.blackbox.utils.FileUtils
import top.niunaijun.blackbox.utils.compat.BuildCompat
import java.io.File
/**
*
* @Description: 启动配置文件
* @Author: wukaicheng
* @CreateDate: 2021/5/23 14:04
*/
class BlackDexConfiguration(private val context: Context) : ClientConfiguration() {
companion object {
fun getDexDumpDir(context: Context): String {
return if (BuildCompat.isR()) {
val dump = File(context.externalCacheDir?.parentFile?.parentFile?.parentFile?.parentFile, "Download/dexDump")
FileUtils.mkdirs(dump)
dump.absolutePath
} else {
val dump = File(context.externalCacheDir?.parentFile, "dump")
FileUtils.mkdirs(dump)
dump.absolutePath
}
}
}
private val dir = getDexDumpDir(context)
override fun getHostPackageName(): String {
return context.packageName
}
override fun getDexDumpDir(): String {
return dir
}
}

@ -28,7 +28,8 @@ class DexDumpRepository {
fun getAppList(mAppListLiveData: MutableLiveData<List<AppInfo>>) { fun getAppList(mAppListLiveData: MutableLiveData<List<AppInfo>>) {
val installedApplications: List<ApplicationInfo> = getPackageManager().getInstalledApplications(0) val installedApplications: List<ApplicationInfo> =
getPackageManager().getInstalledApplications(0)
val installedList = mutableListOf<AppInfo>() val installedList = mutableListOf<AppInfo>()
for (installedApplication in installedApplications) { for (installedApplication in installedApplications) {
@ -40,9 +41,9 @@ class DexDumpRepository {
val info = AppInfo( val info = AppInfo(
installedApplication.loadLabel(getPackageManager()).toString(), installedApplication.loadLabel(getPackageManager()).toString(),
installedApplication.packageName, installedApplication.packageName,
installedApplication.loadIcon(getPackageManager()) installedApplication.loadIcon(getPackageManager())
) )
installedList.add(info) installedList.add(info)
} }
@ -56,30 +57,32 @@ class DexDumpRepository {
val result = if (URLUtil.isValidUrl(source)) { val result = if (URLUtil.isValidUrl(source)) {
BlackDexCore.get().dumpDex(Uri.parse(source)) BlackDexCore.get().dumpDex(Uri.parse(source))
} else if (source.contains("/")) {
BlackDexCore.get().dumpDex(File(source))
} else { } else {
BlackDexCore.get().dumpDex(source) BlackDexCore.get().dumpDex(source)
} }
if(result){ if (result) {
dumpTaskId++ dumpTaskId++
startCountdown(dexDumpLiveData) startCountdown(dexDumpLiveData)
}else{ } else {
dexDumpLiveData.postValue(DumpInfo(DumpInfo.TIMEOUT)) dexDumpLiveData.postValue(DumpInfo(DumpInfo.TIMEOUT))
} }
} }
fun dumpSuccess(){ fun dumpSuccess() {
dumpTaskId++ dumpTaskId++
} }
private fun startCountdown(dexDumpLiveData: MutableLiveData<DumpInfo>){ private fun startCountdown(dexDumpLiveData: MutableLiveData<DumpInfo>) {
GlobalScope.launch { GlobalScope.launch {
val tempId = dumpTaskId val tempId = dumpTaskId
delay(10000) delay(10000)
if(tempId == dumpTaskId){ if (tempId == dumpTaskId) {
dexDumpLiveData.postValue(DumpInfo(DumpInfo.TIMEOUT)) dexDumpLiveData.postValue(DumpInfo(DumpInfo.TIMEOUT))
} }
} }

@ -0,0 +1,16 @@
package top.niunaijun.blackdex.util
import java.io.File
/**
*
* @Description: file util
* @Author: wukaicheng
* @CreateDate: 2021/5/30 20:25
*/
object FileUtil {
fun filterApk(file: File):Boolean{
return (file.extension == "apk") or file.isDirectory
}
}

@ -11,7 +11,12 @@ import androidx.appcompat.widget.Toolbar
*/ */
open class BaseActivity : AppCompatActivity() { open class BaseActivity : AppCompatActivity() {
protected fun initToolbar(toolbar: Toolbar,title:Int, showBack: Boolean = false, onBack: (() -> Unit)? = null) { protected fun initToolbar(
toolbar: Toolbar,
title: Int,
showBack: Boolean = false,
onBack: (() -> Unit)? = null
) {
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
toolbar.setTitle(title) toolbar.setTitle(title)
if (showBack) { if (showBack) {
@ -20,10 +25,13 @@ open class BaseActivity : AppCompatActivity() {
toolbar.setNavigationOnClickListener { toolbar.setNavigationOnClickListener {
if (onBack != null) { if (onBack != null) {
onBack() onBack()
} else {
finish()
} }
finish()
} }
} }
} }
} }
} }

@ -0,0 +1,99 @@
package top.niunaijun.blackdex.view.base
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import com.afollestad.materialdialogs.MaterialDialog
import top.niunaijun.blackbox.utils.compat.BuildCompat
import top.niunaijun.blackdex.R
/**
*
* @Description:request permission activity
* @Author: wukaicheng
* @CreateDate: 2021/5/30 21:27
*/
open class PermissionActivity:BaseActivity() {
protected var requestPermissionCallback: ((Boolean) -> Unit)? = null
protected fun requestStoragePermission() {
@RequiresApi(Build.VERSION_CODES.R)
if (BuildCompat.isR()) {
if (Environment.isExternalStorageManager()) {
//fuck 请求了读取全部文件权限竟然还要申请普通读写权限
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
MaterialDialog(this).show {
title(R.string.grant_permission)
message(res = R.string.request_storage_msg)
negativeButton(res = R.string.request_later) {
if(requestPermissionCallback!=null){
requestPermissionCallback!!(false)
}
}
positiveButton(res = R.string.jump_grant) {
val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.data = Uri.fromParts("package", packageName, null)
startActivity(intent)
}
}
}
} else if (BuildCompat.isM() && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}else{
if(requestPermissionCallback!=null){
requestPermissionCallback!!(true)
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) {
if(requestPermissionCallback!=null){
requestPermissionCallback!!(true)
}
} else {
MaterialDialog(this).show {
title(res = R.string.request_fail)
message(res = R.string.denied_msg)
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
positiveButton(res = R.string.request_again) {
requestStoragePermission()
}
} else {
positiveButton(res = R.string.jump_grant) {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
}
}
negativeButton(res = R.string.request_later) {
if(requestPermissionCallback!=null){
requestPermissionCallback!!(false)
}
}
}
}
}
override fun onStart() {
super.onStart()
if(requestPermissionCallback!=null){
requestStoragePermission()
}
}
}

@ -1,36 +1,35 @@
package top.niunaijun.blackdex.view.main package top.niunaijun.blackdex.view.main
import android.Manifest
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.os.Environment
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.files.fileChooser
import com.ferfalk.simplesearchview.SimpleSearchView import com.ferfalk.simplesearchview.SimpleSearchView
import com.roger.catloadinglibrary.CatLoadingView import com.roger.catloadinglibrary.CatLoadingView
import com.umeng.analytics.MobclickAgent
import top.niunaijun.blackbox.BlackDexCore import top.niunaijun.blackbox.BlackDexCore
import top.niunaijun.blackbox.core.system.dump.IBDumpMonitor import top.niunaijun.blackbox.core.system.dump.IBDumpMonitor
import top.niunaijun.blackbox.entity.dump.DumpResult import top.niunaijun.blackbox.entity.dump.DumpResult
import top.niunaijun.blackbox.utils.compat.BuildCompat
import top.niunaijun.blackdex.R import top.niunaijun.blackdex.R
import top.niunaijun.blackdex.data.entity.AppInfo import top.niunaijun.blackdex.data.entity.AppInfo
import top.niunaijun.blackdex.data.entity.DumpInfo import top.niunaijun.blackdex.data.entity.DumpInfo
import top.niunaijun.blackdex.databinding.ActivityMainBinding import top.niunaijun.blackdex.databinding.ActivityMainBinding
import top.niunaijun.blackdex.util.FileUtil
import top.niunaijun.blackdex.util.InjectionUtil import top.niunaijun.blackdex.util.InjectionUtil
import top.niunaijun.blackdex.util.LoadingUtil import top.niunaijun.blackdex.util.LoadingUtil
import top.niunaijun.blackdex.util.inflate import top.niunaijun.blackdex.util.inflate
import top.niunaijun.blackdex.view.base.BaseActivity import top.niunaijun.blackdex.view.base.PermissionActivity
import top.niunaijun.blackdex.view.setting.SettingActivity
class MainActivity : BaseActivity() {
class MainActivity : PermissionActivity() {
private val viewBinding: ActivityMainBinding by inflate() private val viewBinding: ActivityMainBinding by inflate()
@ -47,11 +46,8 @@ class MainActivity : BaseActivity() {
setContentView(viewBinding.root) setContentView(viewBinding.root)
initToolbar(viewBinding.toolbarLayout.toolbar, R.string.app_name) initToolbar(viewBinding.toolbarLayout.toolbar, R.string.app_name)
initView() initView()
initViewModel() initViewModel()
initSearchView() initSearchView()
BlackDexCore.get().registerDumpMonitor(mMonitor) BlackDexCore.get().registerDumpMonitor(mMonitor)
@ -73,7 +69,25 @@ class MainActivity : BaseActivity() {
} }
viewBinding.fab.setOnClickListener { viewBinding.fab.setOnClickListener {
openDocumentedResult.launch("application/vnd.android.package-archive") this.requestPermissionCallback = {
if (it) {
this.requestPermissionCallback = null
val initialDir = Environment.getExternalStorageDirectory()
MaterialDialog(this).show {
fileChooser(
this@MainActivity,
initialDirectory = initialDir,
filter = FileUtil::filterApk,
) { _, file ->
viewModel.startDexDump(file.absolutePath)
}
negativeButton(res = R.string.cancel)
}
}
}
requestStoragePermission()
} }
} }
@ -105,31 +119,33 @@ class MainActivity : BaseActivity() {
DumpInfo.TIMEOUT -> { DumpInfo.TIMEOUT -> {
loadingView.dismiss() loadingView.dismiss()
MaterialDialog(this).show { MaterialDialog(this).show {
title(text = "脱壳失败") title(res = R.string.unpack_fail)
message(text = "未知错误,可能是不兼容或者带有环境检测导致失败,可前往GitHub(https://github.com/CodingGay/BlackDex)提Issue") message(res = R.string.jump_issue)
negativeButton(text = "Github") { negativeButton(res = R.string.github) {
val intent = Intent( negativeButton(text = "Github") {
Intent.ACTION_VIEW, val intent = Intent(
Uri.parse("https://github.com/CodingGay/BlackDex/issues") Intent.ACTION_VIEW,
) Uri.parse("https://github.com/CodingGay/BlackDex/issues")
startActivity(intent) )
} startActivity(intent)
positiveButton(text = "确定") }
positiveButton(res = R.string.confirm)
}
} }
} }
else -> { else -> {
viewModel.dexDumpSuccess() viewModel.dexDumpSuccess()
val title = if (it.state == DumpInfo.SUCCESS) { val title = if (it.state == DumpInfo.SUCCESS) {
"脱壳成功" getString(R.string.unpack_success)
} else { } else {
"脱壳失败" getString(R.string.unpack_fail)
} }
loadingView.dismiss() loadingView.dismiss()
MaterialDialog(this).show { MaterialDialog(this).show {
title(text = title) title(text = title)
message(text = it.msg) message(text = it.msg)
positiveButton(text = "确定") positiveButton(res = R.string.confirm)
} }
} }
} }
@ -145,14 +161,14 @@ class MainActivity : BaseActivity() {
viewModel.mDexDumpLiveData.postValue( viewModel.mDexDumpLiveData.postValue(
DumpInfo( DumpInfo(
DumpInfo.SUCCESS, DumpInfo.SUCCESS,
"DEX文件储存在:${result.dir}" getString(R.string.dex_save, result.dir)
) )
) )
} else { } else {
viewModel.mDexDumpLiveData.postValue( viewModel.mDexDumpLiveData.postValue(
DumpInfo( DumpInfo(
DumpInfo.FAIL, DumpInfo.FAIL,
"错误原因: ${result.msg}" getString(R.string.error_msg, result.msg)
) )
) )
} }
@ -181,7 +197,6 @@ class MainActivity : BaseActivity() {
}) })
} }
private fun filterApp(newText: String) { private fun filterApp(newText: String) {
val newList = this.appList.filter { val newList = this.appList.filter {
it.name.contains(newText, true) or it.packageName.contains(newText, true) it.name.contains(newText, true) or it.packageName.contains(newText, true)
@ -204,59 +219,6 @@ class MainActivity : BaseActivity() {
} }
} }
private fun requestStoragePermission() {
if (BuildCompat.isM() && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}
private val openDocumentedResult =
registerForActivityResult(ActivityResultContracts.GetContent()) {
it?.run {
viewModel.startDexDump(it.toString())
}
}
@RequiresApi(Build.VERSION_CODES.M)
private val requestPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (!it) {
MaterialDialog(this).show {
title(text = "申请失败")
message(text = "请授予我们读写本地文件权限,否则软件将无法正常运行。")
if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
positiveButton(text = "再次申请") {
requestStoragePermission()
}
} else {
positiveButton(text = "手动授予") {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
try {
startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
negativeButton(text = "退出软件") {
finish()
}
}
}
}
override fun onStart() {
super.onStart()
requestStoragePermission()
}
override fun onBackPressed() { override fun onBackPressed() {
if (viewBinding.searchView.isSearchOpen) { if (viewBinding.searchView.isSearchOpen) {
viewBinding.searchView.closeSearch() viewBinding.searchView.closeSearch()
@ -283,9 +245,24 @@ class MainActivity : BaseActivity() {
Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/CodingGay/BlackDex")) Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/CodingGay/BlackDex"))
startActivity(intent) startActivity(intent)
} }
R.id.main_setting -> {
val intent =
Intent(this, SettingActivity::class.java)
startActivity(intent)
}
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onResume() {
super.onResume()
MobclickAgent.onResume(this)
}
override fun onPause() {
super.onPause()
MobclickAgent.onPause(this)
}
} }

@ -0,0 +1,25 @@
package top.niunaijun.blackdex.view.setting
import android.os.Bundle
import top.niunaijun.blackdex.R
import top.niunaijun.blackdex.databinding.ActivitySettingBinding
import top.niunaijun.blackdex.util.inflate
import top.niunaijun.blackdex.view.base.BaseActivity
import top.niunaijun.blackdex.view.base.PermissionActivity
class SettingActivity : PermissionActivity() {
private val viewBinding: ActivitySettingBinding by inflate()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(viewBinding.root)
initToolbar(viewBinding.toolbarLayout.toolbar, R.string.app_setting,true)
supportFragmentManager.beginTransaction().replace(R.id.fragment,SettingFragment()).commit()
}
fun setRequestCallback(callback:((Boolean)->Unit)?){
this.requestPermissionCallback = callback
requestStoragePermission()
}
}

@ -0,0 +1,87 @@
package top.niunaijun.blackdex.view.setting
import android.os.Bundle
import android.os.Environment
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.files.folderChooser
import top.niunaijun.blackboxa.app.App
import top.niunaijun.blackdex.R
import top.niunaijun.blackdex.app.AppManager
import top.niunaijun.blackdex.app.BlackDexLoader
import java.io.File
/**
*
* @Description:
* @Author: wukaicheng
* @CreateDate: 2021/5/28 19:55
*/
class SettingFragment : PreferenceFragmentCompat() {
private lateinit var savePathPreference: Preference
private lateinit var saveEnablePreference: SwitchPreferenceCompat
private val initialDirectory = AppManager.mBlackBoxLoader.getSavePath()
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.setting)
savePathPreference = findPreference("save_path")!!
savePathPreference.onPreferenceClickListener = mSavedPathClick
savePathPreference.summary = initialDirectory
saveEnablePreference = findPreference("save_enable")!!
saveEnablePreference.onPreferenceChangeListener = mSaveEnableChange
saveEnablePreference.isChecked = AppManager.mBlackBoxLoader.saveEnable()
}
private val mSavedPathClick = Preference.OnPreferenceClickListener {
val initialFile = with(initialDirectory) {
if (initialDirectory.isEmpty()) {
Environment.getExternalStorageDirectory()
} else {
File(this)
}
}
MaterialDialog(requireContext()).show {
folderChooser(
requireContext(),
initialDirectory = initialFile,
allowFolderCreation = true
) { _, file ->
AppManager.mBlackBoxLoader.setSavePath(file.absolutePath)
savePathPreference.summary = file.absolutePath
}
negativeButton(res = R.string.cancel)
}
return@OnPreferenceClickListener true
}
private val mSaveEnableChange = Preference.OnPreferenceChangeListener { _, newValue ->
if (newValue == false) {
(requireActivity() as SettingActivity).setRequestCallback(requestResult)
} else {
AppManager.mBlackBoxLoader.saveEnable(true)
saveEnablePreference.isChecked = true
}
return@OnPreferenceChangeListener true
}
private val requestResult = { hasPermission: Boolean ->
AppManager.mBlackBoxLoader.saveEnable(!hasPermission)
saveEnablePreference.isChecked = !hasPermission
if (AppManager.mBlackBoxLoader.getSavePath().isEmpty()) {
val path = BlackDexLoader.getDexDumpDir(App.getContext())
AppManager.mBlackBoxLoader.setSavePath(path)
savePathPreference.summary = path
}
}
}

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".view.setting.SettingActivity">
<include
android:id="@+id/toolbar_layout"
layout="@layout/view_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

@ -9,6 +9,11 @@
android:title="@string/filter" android:title="@string/filter"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item
android:id="@+id/main_setting"
android:title="@string/app_setting"
app:showAsAction="never"/>
<item <item
android:id="@+id/main_git" android:id="@+id/main_git"
android:title="@string/open_source" android:title="@string/open_source"

@ -0,0 +1,26 @@
<resources>
<string name="filter">过滤</string>
<string name="choose">选择</string>
<string name="log">软件日志</string>
<string name="open_source">开源地址</string>
<string name="app_setting">软件设置</string>
<string name="unpack_success">脱壳成功</string>
<string name="unpack_fail">脱壳失败</string>
<string name="confirm">确定</string>
<string name="github">Github</string>
<string name="jump_issue">未知错误,可能是不兼容或者带有环境检测导致失败,可前往GitHub(https://github.com/CodingGay/BlackDex)提Issue</string>
<string name="cancel">取消</string>
<string name="dex_save">DEX文件储存在: %1$s</string>
<string name="error_msg">错误原因: %1$s</string>
<string name="grant_permission">授予权限</string>
<string name="request_again">再次申请</string>
<string name="request_later">下次再说</string>
<string name="jump_grant">前往授予</string>
<string name="request_fail">申请失败</string>
<string name="denied_msg">拒绝授予读写本地文件权限将无法使用该功能</string>
<string name="request_storage_msg">该功能需要您授予本软件读写全部文件权限才行正常运行</string>
<string name="file_save">文件储存</string>
<string name="use_default_file">使用默认储存路径</string>
<string name="rewrite_file_path">自定义储存路径</string>
</resources>

@ -1,6 +1,26 @@
<resources> <resources>
<string name="filter">过滤</string> <string name="filter">Filter</string>
<string name="choose">选择</string> <string name="choose">Select</string>
<string name="log">软件日志</string> <string name="log">Logcat</string>
<string name="open_source">开源地址</string> <string name="open_source">Open Source</string>
<string name="app_setting">Setting</string>
<string name="unpack_success">Unpack Success</string>
<string name="unpack_fail">Unpack Fail</string>
<string name="confirm">Confirm</string>
<string name="github">Github</string>
<string name="jump_issue">Unknown Error,This may be an incompatibility or may have an environmental detection that causes the failure,You can jump to GitHub(https://github.com/CodingGay/BlackDex)open Issue</string>
<string name="cancel">Cancel</string>
<string name="dex_save">.dex file save in: %1$s</string>
<string name="error_msg">Error: %1$s</string>
<string name="grant_permission">Grant Permission</string>
<string name="request_again">Again</string>
<string name="request_later">Later</string>
<string name="jump_grant">Grant</string>
<string name="request_fail">Grant Fail</string>
<string name="denied_msg">Denying permission to read and write to local files will disable this feature</string>
<string name="request_storage_msg">This function requires you to grant the software the permission to read and write all files to run normally</string>
<string name="file_save">File</string>
<string name="use_default_file">Use Default Path</string>
<string name="rewrite_file_path">Customize the storage path</string>
</resources> </resources>

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/file_save">
<SwitchPreferenceCompat
android:disableDependentsState="true"
android:key="save_enable"
android:title="@string/use_default_file" />
<Preference
android:dependency="save_enable"
android:key="save_path"
android:title="@string/rewrite_file_path" />
</PreferenceCategory>
</PreferenceScreen>

@ -10,7 +10,7 @@ buildscript {
jcenter { url 'https://maven.aliyun.com/repository/jcenter' } jcenter { url 'https://maven.aliyun.com/repository/jcenter' }
jcenter { url 'https://maven.aliyun.com/nexus/content/groups/public/' } jcenter { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://dl.bintray.com/umsdk/release' } maven { url 'https://repo1.maven.org/maven2/' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
dependencies { dependencies {
@ -30,7 +30,7 @@ allprojects {
jcenter { url 'https://maven.aliyun.com/repository/jcenter' } jcenter { url 'https://maven.aliyun.com/repository/jcenter' }
jcenter { url 'https://maven.aliyun.com/nexus/content/groups/public/' } jcenter { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
maven { url 'https://dl.bintray.com/umsdk/release' } maven { url 'https://repo1.maven.org/maven2/' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
} }

Loading…
Cancel
Save