diff --git a/app/build.gradle b/app/build.gradle index 962f5a2..730e6f6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "top.niunaijun.blackdex" minSdkVersion 21 targetSdkVersion 30 - versionCode 1 - versionName "1.0" + versionCode 2 + versionName "1.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -72,6 +72,7 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.preference:preference-ktx:1.1.1' implementation project(':Bcore') @@ -86,6 +87,7 @@ dependencies { //third implementation 'com.afollestad.material-dialogs:core:3.3.0' + implementation 'com.afollestad.material-dialogs:files:3.2.1' //dialog implementation 'com.github.nukc.stateview:kotlin:2.2.0' //状态控制控件 @@ -94,6 +96,9 @@ dependencies { implementation 'com.github.Ferfalk:SimpleSearchView:0.2.0' //searchView + implementation 'com.umeng.umsdk:common:9.3.8' + implementation 'com.umeng.umsdk:asms:1.2.2' + //友盟 testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fc179a4..35731a0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,29 +3,38 @@ xmlns:tools="http://schemas.android.com/tools" package="top.niunaijun.blackdex"> - - + + + + + - - - - - + + \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/App.kt b/app/src/main/java/top/niunaijun/blackdex/App.kt deleted file mode 100644 index 1ba0721..0000000 --- a/app/src/main/java/top/niunaijun/blackdex/App.kt +++ /dev/null @@ -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() - } -} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/app/App.kt b/app/src/main/java/top/niunaijun/blackdex/app/App.kt new file mode 100644 index 0000000..53abbf8 --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/app/App.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/app/AppManager.kt b/app/src/main/java/top/niunaijun/blackdex/app/AppManager.kt new file mode 100644 index 0000000..2789c5b --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/app/AppManager.kt @@ -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) {} +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/app/BlackDexLoader.kt b/app/src/main/java/top/niunaijun/blackdex/app/BlackDexLoader.kt new file mode 100644 index 0000000..708718e --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/app/BlackDexLoader.kt @@ -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 + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/biz/cache/AppSharedPreferenceDelegate.kt b/app/src/main/java/top/niunaijun/blackdex/biz/cache/AppSharedPreferenceDelegate.kt new file mode 100644 index 0000000..aa859da --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/biz/cache/AppSharedPreferenceDelegate.kt @@ -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(context: Context, private val default: Data, spName: String? = null) : ReadWriteProperty { + + 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") + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/data/BlackDexConfiguration.kt b/app/src/main/java/top/niunaijun/blackdex/data/BlackDexConfiguration.kt deleted file mode 100644 index a9bd5c4..0000000 --- a/app/src/main/java/top/niunaijun/blackdex/data/BlackDexConfiguration.kt +++ /dev/null @@ -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 - } - -} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/data/DexDumpRepository.kt b/app/src/main/java/top/niunaijun/blackdex/data/DexDumpRepository.kt index c02956e..af6ced9 100644 --- a/app/src/main/java/top/niunaijun/blackdex/data/DexDumpRepository.kt +++ b/app/src/main/java/top/niunaijun/blackdex/data/DexDumpRepository.kt @@ -28,7 +28,8 @@ class DexDumpRepository { fun getAppList(mAppListLiveData: MutableLiveData>) { - val installedApplications: List = getPackageManager().getInstalledApplications(0) + val installedApplications: List = + getPackageManager().getInstalledApplications(0) val installedList = mutableListOf() for (installedApplication in installedApplications) { @@ -40,9 +41,9 @@ class DexDumpRepository { val info = AppInfo( - installedApplication.loadLabel(getPackageManager()).toString(), - installedApplication.packageName, - installedApplication.loadIcon(getPackageManager()) + installedApplication.loadLabel(getPackageManager()).toString(), + installedApplication.packageName, + installedApplication.loadIcon(getPackageManager()) ) installedList.add(info) } @@ -56,30 +57,32 @@ class DexDumpRepository { val result = if (URLUtil.isValidUrl(source)) { BlackDexCore.get().dumpDex(Uri.parse(source)) + } else if (source.contains("/")) { + BlackDexCore.get().dumpDex(File(source)) } else { BlackDexCore.get().dumpDex(source) } - if(result){ + if (result) { dumpTaskId++ startCountdown(dexDumpLiveData) - }else{ + } else { dexDumpLiveData.postValue(DumpInfo(DumpInfo.TIMEOUT)) } } - fun dumpSuccess(){ + fun dumpSuccess() { dumpTaskId++ } - private fun startCountdown(dexDumpLiveData: MutableLiveData){ + private fun startCountdown(dexDumpLiveData: MutableLiveData) { GlobalScope.launch { val tempId = dumpTaskId delay(10000) - if(tempId == dumpTaskId){ + if (tempId == dumpTaskId) { dexDumpLiveData.postValue(DumpInfo(DumpInfo.TIMEOUT)) } } diff --git a/app/src/main/java/top/niunaijun/blackdex/util/FileUtil.kt b/app/src/main/java/top/niunaijun/blackdex/util/FileUtil.kt new file mode 100644 index 0000000..c61a94a --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/util/FileUtil.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/view/base/BaseActivity.kt b/app/src/main/java/top/niunaijun/blackdex/view/base/BaseActivity.kt index be3359d..cf25e4f 100644 --- a/app/src/main/java/top/niunaijun/blackdex/view/base/BaseActivity.kt +++ b/app/src/main/java/top/niunaijun/blackdex/view/base/BaseActivity.kt @@ -11,7 +11,12 @@ import androidx.appcompat.widget.Toolbar */ 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) toolbar.setTitle(title) if (showBack) { @@ -20,10 +25,13 @@ open class BaseActivity : AppCompatActivity() { toolbar.setNavigationOnClickListener { if (onBack != null) { onBack() + } else { + finish() } - finish() } } } } + + } \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/view/base/PermissionActivity.kt b/app/src/main/java/top/niunaijun/blackdex/view/base/PermissionActivity.kt new file mode 100644 index 0000000..82d4e23 --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/view/base/PermissionActivity.kt @@ -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() + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/view/main/MainActivity.kt b/app/src/main/java/top/niunaijun/blackdex/view/main/MainActivity.kt index 76e286d..75f3108 100644 --- a/app/src/main/java/top/niunaijun/blackdex/view/main/MainActivity.kt +++ b/app/src/main/java/top/niunaijun/blackdex/view/main/MainActivity.kt @@ -1,36 +1,35 @@ package top.niunaijun.blackdex.view.main -import android.Manifest import android.content.Intent -import android.content.pm.PackageManager import android.net.Uri -import android.os.Build import android.os.Bundle -import android.provider.Settings +import android.os.Environment import android.view.Menu import android.view.MenuItem import android.view.inputmethod.InputMethodManager -import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.RequiresApi import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.files.fileChooser import com.ferfalk.simplesearchview.SimpleSearchView import com.roger.catloadinglibrary.CatLoadingView +import com.umeng.analytics.MobclickAgent import top.niunaijun.blackbox.BlackDexCore import top.niunaijun.blackbox.core.system.dump.IBDumpMonitor import top.niunaijun.blackbox.entity.dump.DumpResult -import top.niunaijun.blackbox.utils.compat.BuildCompat import top.niunaijun.blackdex.R import top.niunaijun.blackdex.data.entity.AppInfo import top.niunaijun.blackdex.data.entity.DumpInfo import top.niunaijun.blackdex.databinding.ActivityMainBinding +import top.niunaijun.blackdex.util.FileUtil import top.niunaijun.blackdex.util.InjectionUtil import top.niunaijun.blackdex.util.LoadingUtil 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() @@ -47,11 +46,8 @@ class MainActivity : BaseActivity() { setContentView(viewBinding.root) initToolbar(viewBinding.toolbarLayout.toolbar, R.string.app_name) - initView() - initViewModel() - initSearchView() BlackDexCore.get().registerDumpMonitor(mMonitor) @@ -73,7 +69,25 @@ class MainActivity : BaseActivity() { } 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 -> { loadingView.dismiss() MaterialDialog(this).show { - title(text = "脱壳失败") - message(text = "未知错误,可能是不兼容或者带有环境检测导致失败,可前往GitHub(https://github.com/CodingGay/BlackDex)提Issue") - negativeButton(text = "Github") { - val intent = Intent( - Intent.ACTION_VIEW, - Uri.parse("https://github.com/CodingGay/BlackDex/issues") - ) - startActivity(intent) - } - positiveButton(text = "确定") + title(res = R.string.unpack_fail) + message(res = R.string.jump_issue) + negativeButton(res = R.string.github) { + negativeButton(text = "Github") { + val intent = Intent( + Intent.ACTION_VIEW, + Uri.parse("https://github.com/CodingGay/BlackDex/issues") + ) + startActivity(intent) + } + positiveButton(res = R.string.confirm) + } } } else -> { viewModel.dexDumpSuccess() val title = if (it.state == DumpInfo.SUCCESS) { - "脱壳成功" + getString(R.string.unpack_success) } else { - "脱壳失败" + getString(R.string.unpack_fail) } loadingView.dismiss() MaterialDialog(this).show { title(text = title) message(text = it.msg) - positiveButton(text = "确定") + positiveButton(res = R.string.confirm) } } } @@ -145,14 +161,14 @@ class MainActivity : BaseActivity() { viewModel.mDexDumpLiveData.postValue( DumpInfo( DumpInfo.SUCCESS, - "DEX文件储存在:${result.dir}" + getString(R.string.dex_save, result.dir) ) ) } else { viewModel.mDexDumpLiveData.postValue( DumpInfo( DumpInfo.FAIL, - "错误原因: ${result.msg}" + getString(R.string.error_msg, result.msg) ) ) } @@ -181,7 +197,6 @@ class MainActivity : BaseActivity() { }) } - private fun filterApp(newText: String) { val newList = this.appList.filter { 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() { if (viewBinding.searchView.isSearchOpen) { viewBinding.searchView.closeSearch() @@ -283,9 +245,24 @@ class MainActivity : BaseActivity() { Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/CodingGay/BlackDex")) startActivity(intent) } + + R.id.main_setting -> { + val intent = + Intent(this, SettingActivity::class.java) + startActivity(intent) + } } return super.onOptionsItemSelected(item) } + override fun onResume() { + super.onResume() + MobclickAgent.onResume(this) + } + + override fun onPause() { + super.onPause() + MobclickAgent.onPause(this) + } } \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/view/setting/SettingActivity.kt b/app/src/main/java/top/niunaijun/blackdex/view/setting/SettingActivity.kt new file mode 100644 index 0000000..499f6cd --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/view/setting/SettingActivity.kt @@ -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() + } +} \ No newline at end of file diff --git a/app/src/main/java/top/niunaijun/blackdex/view/setting/SettingFragment.kt b/app/src/main/java/top/niunaijun/blackdex/view/setting/SettingFragment.kt new file mode 100644 index 0000000..11a03be --- /dev/null +++ b/app/src/main/java/top/niunaijun/blackdex/view/setting/SettingFragment.kt @@ -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 + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml new file mode 100644 index 0000000..204dd8c --- /dev/null +++ b/app/src/main/res/layout/activity_setting.xml @@ -0,0 +1,22 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml index d13cc84..e39f96e 100644 --- a/app/src/main/res/menu/menu_main.xml +++ b/app/src/main/res/menu/menu_main.xml @@ -9,6 +9,11 @@ android:title="@string/filter" app:showAsAction="ifRoom" /> + + + 过滤 + 选择 + 软件日志 + 开源地址 + 软件设置 + 脱壳成功 + 脱壳失败 + 确定 + Github + 未知错误,可能是不兼容或者带有环境检测导致失败,可前往GitHub(https://github.com/CodingGay/BlackDex)提Issue + 取消 + DEX文件储存在: %1$s + 错误原因: %1$s + 授予权限 + 再次申请 + 下次再说 + 前往授予 + 申请失败 + 拒绝授予读写本地文件权限将无法使用该功能 + 该功能需要您授予本软件读写全部文件权限才行正常运行 + + 文件储存 + 使用默认储存路径 + 自定义储存路径 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c64d382..d423754 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,26 @@ - 过滤 - 选择 - 软件日志 - 开源地址 + Filter + Select + Logcat + Open Source + Setting + Unpack Success + Unpack Fail + Confirm + Github + 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 + Cancel + .dex file save in: %1$s + Error: %1$s + Grant Permission + Again + Later + Grant + Grant Fail + Denying permission to read and write to local files will disable this feature + This function requires you to grant the software the permission to read and write all files to run normally + + File + Use Default Path + Customize the storage path \ No newline at end of file diff --git a/app/src/main/res/xml/setting.xml b/app/src/main/res/xml/setting.xml new file mode 100644 index 0000000..155e6b3 --- /dev/null +++ b/app/src/main/res/xml/setting.xml @@ -0,0 +1,15 @@ + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index fa9fc45..6f8c0cb 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { jcenter { url 'https://maven.aliyun.com/repository/jcenter' } jcenter { url 'https://maven.aliyun.com/nexus/content/groups/public/' } 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' } } dependencies { @@ -30,7 +30,7 @@ allprojects { jcenter { url 'https://maven.aliyun.com/repository/jcenter' } jcenter { url 'https://maven.aliyun.com/nexus/content/groups/public/' } 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' } } }