1.使用自定义的文件选择器来选择本地文件

2.修复自定义储存目录的相关Bug
3.英文支持
pull/10/head
kai-city 4 years ago
parent 87217696dd
commit 34627566d6
  1. 3
      app/build.gradle
  2. 2
      app/src/main/java/top/niunaijun/blackdex/app/App.kt
  3. 4
      app/src/main/java/top/niunaijun/blackdex/app/BlackDexLoader.kt
  4. 5
      app/src/main/java/top/niunaijun/blackdex/data/DexDumpRepository.kt
  5. 16
      app/src/main/java/top/niunaijun/blackdex/util/FileUtil.kt
  6. 9
      app/src/main/java/top/niunaijun/blackdex/view/base/BaseActivity.kt
  7. 99
      app/src/main/java/top/niunaijun/blackdex/view/base/PermissionActivity.kt
  8. 108
      app/src/main/java/top/niunaijun/blackdex/view/main/MainActivity.kt
  9. 8
      app/src/main/java/top/niunaijun/blackdex/view/setting/SettingActivity.kt
  10. 32
      app/src/main/java/top/niunaijun/blackdex/view/setting/SettingFragment.kt
  11. 26
      app/src/main/res/values-zh/string.xml
  12. 29
      app/src/main/res/values/strings.xml
  13. 6
      app/src/main/res/xml/setting.xml
  14. 4
      build.gradle

@ -96,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'

@ -3,6 +3,7 @@ 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
/**
@ -28,6 +29,7 @@ class App : Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
mContext = base!!
UMConfigure.init(base, "60b373136c421a3d97d23c29", "Github", 0, "")
AppManager.doAttachBaseContext(base)
}

@ -20,7 +20,7 @@ class BlackDexLoader {
private var mSavePath by AppSharedPreferenceDelegate(App.getContext(), "")
private var mSaveEnable by AppSharedPreferenceDelegate(App.getContext(), false)
private var mSaveEnable by AppSharedPreferenceDelegate(App.getContext(), true)
private var mDir = if (mSaveEnable) {
getDexDumpDir(App.getContext())
@ -73,7 +73,7 @@ class BlackDexLoader {
return if (BuildCompat.isR()) {
val dump = File(
context.externalCacheDir?.parentFile?.parentFile?.parentFile?.parentFile,
"hupu/dexDump"
"Download/dexDump"
)
FileUtils.mkdirs(dump)
dump.absolutePath

@ -28,7 +28,8 @@ class DexDumpRepository {
fun getAppList(mAppListLiveData: MutableLiveData<List<AppInfo>>) {
val installedApplications: List<ApplicationInfo> = getPackageManager().getInstalledApplications(0)
val installedApplications: List<ApplicationInfo> =
getPackageManager().getInstalledApplications(0)
val installedList = mutableListOf<AppInfo>()
for (installedApplication in installedApplications) {
@ -56,6 +57,8 @@ 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)
}

@ -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() {
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) {
@ -27,4 +32,6 @@ open class BaseActivity : AppCompatActivity() {
}
}
}
}

@ -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,38 +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()
@ -72,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()
}
}
@ -104,31 +119,31 @@ class MainActivity : BaseActivity() {
DumpInfo.TIMEOUT -> {
loadingView.dismiss()
MaterialDialog(this).show {
title(text = "脱壳失败")
message(text = "未知错误,可前往GitHub(https://github.com/CodingGay/BlackDex)提Issue")
negativeButton(text = "Github") {
title(res = R.string.unpack_fail)
message(res = R.string.jump_issue)
negativeButton(res = R.string.github) {
val intent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://github.com/CodingGay/BlackDex/issues")
)
startActivity(intent)
}
positiveButton(text = "确定")
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)
}
}
}
@ -144,14 +159,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)
)
)
}
@ -202,52 +217,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
startActivity(intent)
}
}
negativeButton(text = "退出软件") {
finish()
}
}
}
}
override fun onStart() {
super.onStart()
requestStoragePermission()
}
override fun onBackPressed() {
if (viewBinding.searchView.isSearchOpen) {
viewBinding.searchView.closeSearch()
@ -285,4 +254,13 @@ class MainActivity : BaseActivity() {
return super.onOptionsItemSelected(item)
}
override fun onResume() {
super.onResume()
MobclickAgent.onResume(this)
}
override fun onPause() {
super.onPause()
MobclickAgent.onPause(this)
}
}

@ -5,8 +5,9 @@ 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 : BaseActivity() {
class SettingActivity : PermissionActivity() {
private val viewBinding: ActivitySettingBinding by inflate()
@ -16,4 +17,9 @@ class SettingActivity : BaseActivity() {
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()
}
}

@ -7,10 +7,13 @@ 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:
@ -33,29 +36,52 @@ class SettingFragment : PreferenceFragmentCompat() {
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.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
Environment.getExternalStorageDirectory()
} else {
File(this)
}
}
MaterialDialog(requireContext()).show {
folderChooser(requireContext(), initialDirectory = initialFile,allowFolderCreation = true) { _, file ->
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 ->
AppManager.mBlackBoxLoader.saveEnable(newValue as Boolean)
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,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,7 +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="filter">Filter</string>
<string name="choose">Select</string>
<string name="log">Logcat</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,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>

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="文件储存">
<PreferenceCategory android:title="@string/file_save">
<SwitchPreferenceCompat
android:disableDependentsState="true"
android:key="save_enable"
android:title="使用默认储存路径" />
android:title="@string/use_default_file" />
<Preference
android:dependency="save_enable"
android:key="save_path"
android:title="自定义储存路径" />
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/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' }
}
}

Loading…
Cancel
Save