commit
9e49e89e78
@ -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 |
||||
} |
||||
|
||||
} |
@ -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 |
||||
} |
||||
} |
@ -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() |
||||
} |
||||
} |
||||
|
||||
} |
@ -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> |
@ -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> |
||||
<string name="filter">过滤</string> |
||||
<string name="choose">选择</string> |
||||
<string name="log">软件日志</string> |
||||
<string name="open_source">开源地址</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,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> |
@ -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> |
Loading…
Reference in new issue