Skip to content

Commit 863cdd3

Browse files
committed
import: add power-user advanced mode
1 parent 54bf91d commit 863cdd3

12 files changed

Lines changed: 189 additions & 15 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.kyhsgeekcode.disassembler.importing
2+
3+
import com.kyhsgeekcode.disassembler.R
4+
5+
sealed class ImportEntryPoint(val labelRes: Int) {
6+
object SafImport : ImportEntryPoint(R.string.select_file)
7+
object AdvancedImport : ImportEntryPoint(R.string.advanced_import)
8+
}
9+
10+
interface ImportEntryPointCatalog {
11+
fun visibleEntryPoints(powerUserMode: Boolean): List<ImportEntryPoint>
12+
}
13+
14+
object DefaultImportEntryPointCatalog : ImportEntryPointCatalog {
15+
override fun visibleEntryPoints(powerUserMode: Boolean): List<ImportEntryPoint> {
16+
return if (powerUserMode) {
17+
listOf(ImportEntryPoint.SafImport, ImportEntryPoint.AdvancedImport)
18+
} else {
19+
listOf(ImportEntryPoint.SafImport)
20+
}
21+
}
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.kyhsgeekcode.disassembler.preference
2+
3+
import android.content.Context
4+
import com.kyhsgeekcode.disassembler.MainActivity
5+
6+
object PowerUserModeSettings {
7+
const val POWER_USER_IMPORT_MODE_KEY = "power_user_import_mode"
8+
9+
fun isEnabled(context: Context): Boolean {
10+
return context.getSharedPreferences(MainActivity.SETTINGKEY, Context.MODE_PRIVATE)
11+
.getBoolean(POWER_USER_IMPORT_MODE_KEY, false)
12+
}
13+
}

app/src/main/java/com/kyhsgeekcode/disassembler/preference/SettingsFragment.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.os.Bundle
88
import androidx.preference.ListPreference
99
import androidx.preference.Preference
1010
import androidx.preference.PreferenceFragmentCompat
11+
import androidx.preference.SwitchPreferenceCompat
1112
import com.kyhsgeekcode.disassembler.MainActivity
1213
import com.kyhsgeekcode.disassembler.R
1314
import com.kyhsgeekcode.disassembler.disasmtheme.ColorHelper
@@ -117,6 +118,7 @@ class SettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClic
117118
false
118119
}
119120
lp.onPreferenceChangeListener = this
121+
findPreference<SwitchPreferenceCompat>(PowerUserModeSettings.POWER_USER_IMPORT_MODE_KEY)
120122
val scrn = findPreference<Preference>("openscrn")
121123
scrn?.setOnPreferenceClickListener {
122124
LibsBuilder()

app/src/main/java/com/kyhsgeekcode/disassembler/ui/MainTab.kt

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.kyhsgeekcode.disassembler.ui
22

33
import android.app.Activity
44
import android.content.Intent
5+
import androidx.activity.compose.ManagedActivityResultLauncher
56
import androidx.activity.compose.rememberLauncherForActivityResult
67
import androidx.activity.result.contract.ActivityResultContracts
78
import androidx.compose.foundation.layout.Arrangement
@@ -14,20 +15,34 @@ import androidx.compose.material3.AlertDialog
1415
import androidx.compose.material3.Button
1516
import androidx.compose.material3.Text
1617
import androidx.compose.runtime.Composable
18+
import androidx.compose.runtime.DisposableEffect
1719
import androidx.compose.runtime.collectAsState
20+
import androidx.compose.runtime.getValue
21+
import androidx.compose.runtime.mutableStateOf
22+
import androidx.compose.runtime.remember
23+
import androidx.compose.runtime.setValue
1824
import androidx.compose.ui.Modifier
1925
import androidx.compose.ui.platform.LocalContext
2026
import androidx.compose.ui.res.stringResource
2127
import androidx.compose.ui.unit.dp
28+
import androidx.lifecycle.DefaultLifecycleObserver
29+
import androidx.lifecycle.LifecycleOwner
2230
import com.kyhsgeekcode.disassembler.R
31+
import com.kyhsgeekcode.disassembler.importing.DefaultImportEntryPointCatalog
32+
import com.kyhsgeekcode.disassembler.importing.ImportEntryPoint
33+
import com.kyhsgeekcode.disassembler.preference.PowerUserModeSettings
2334
import com.kyhsgeekcode.disassembler.viewmodel.MainViewModel
2435
import com.kyhsgeekcode.filechooser.NewFileChooserActivity
2536

2637
@Composable
2738
fun ProjectOverview(viewModel: MainViewModel) {
2839
val context = LocalContext.current
40+
val lifecycleOwner = context as? LifecycleOwner
41+
var powerUserModeEnabled by remember {
42+
mutableStateOf(PowerUserModeSettings.isEnabled(context))
43+
}
2944

30-
val launcher = rememberLauncherForActivityResult(
45+
val advancedImportLauncher = rememberLauncherForActivityResult(
3146
ActivityResultContracts.StartActivityForResult()
3247
) {
3348
if (it.resultCode == Activity.RESULT_OK) {
@@ -37,8 +52,46 @@ fun ProjectOverview(viewModel: MainViewModel) {
3752
}
3853
}
3954
}
55+
val safImportLauncher = rememberLauncherForActivityResult(
56+
ActivityResultContracts.OpenDocument()
57+
) { selectedUri ->
58+
if (selectedUri != null) {
59+
try {
60+
context.contentResolver.takePersistableUriPermission(
61+
selectedUri,
62+
Intent.FLAG_GRANT_READ_URI_PERMISSION
63+
)
64+
} catch (_: SecurityException) {
65+
}
66+
viewModel.onSelectIntent(
67+
Intent().apply {
68+
putExtra("uri", selectedUri)
69+
putExtra("openProject", false)
70+
}
71+
)
72+
}
73+
}
4074

4175
val askCopy = viewModel.askCopy.collectAsState()
76+
val visibleEntryPoints = remember(powerUserModeEnabled) {
77+
DefaultImportEntryPointCatalog.visibleEntryPoints(powerUserModeEnabled)
78+
}
79+
80+
DisposableEffect(lifecycleOwner, context) {
81+
if (lifecycleOwner == null) {
82+
onDispose {}
83+
} else {
84+
val observer = object : DefaultLifecycleObserver {
85+
override fun onResume(owner: LifecycleOwner) {
86+
powerUserModeEnabled = PowerUserModeSettings.isEnabled(context)
87+
}
88+
}
89+
lifecycleOwner.lifecycle.addObserver(observer)
90+
onDispose {
91+
lifecycleOwner.lifecycle.removeObserver(observer)
92+
}
93+
}
94+
}
4295

4396
Column(
4497
Modifier
@@ -47,11 +100,20 @@ fun ProjectOverview(viewModel: MainViewModel) {
47100
) {
48101
Text(text = stringResource(id = R.string.main_select_source_guide))
49102
Row(Modifier.fillMaxWidth()) {
50-
Button(onClick = {
51-
val j = Intent(context, NewFileChooserActivity::class.java)
52-
launcher.launch(j)
53-
}) {
54-
Text(text = stringResource(id = R.string.select_file))
103+
for (entryPoint in visibleEntryPoints) {
104+
Button(
105+
modifier = Modifier.padding(end = 8.dp),
106+
onClick = {
107+
launchImportEntryPoint(
108+
entryPoint,
109+
context = context,
110+
safImportLauncher = safImportLauncher,
111+
advancedImportLauncher = advancedImportLauncher
112+
)
113+
}
114+
) {
115+
Text(text = stringResource(id = entryPoint.labelRes))
116+
}
55117
}
56118
}
57119
}
@@ -89,3 +151,20 @@ fun ProjectOverview(viewModel: MainViewModel) {
89151
)
90152
}
91153
}
154+
155+
private fun launchImportEntryPoint(
156+
entryPoint: ImportEntryPoint,
157+
context: android.content.Context,
158+
safImportLauncher: ManagedActivityResultLauncher<Array<String>, android.net.Uri?>,
159+
advancedImportLauncher: ManagedActivityResultLauncher<Intent, androidx.activity.result.ActivityResult>
160+
) {
161+
when (entryPoint) {
162+
ImportEntryPoint.SafImport -> safImportLauncher.launch(arrayOf("*/*"))
163+
ImportEntryPoint.AdvancedImport -> {
164+
val intent = Intent(context, NewFileChooserActivity::class.java).apply {
165+
putExtra(NewFileChooserActivity.EXTRA_POWER_USER_MODE, true)
166+
}
167+
advancedImportLauncher.launch(intent)
168+
}
169+
}
170+
}

app/src/main/java/com/kyhsgeekcode/filechooser/NewFileChooserActivity.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,15 @@ import java.net.URL
2828

2929

3030
class NewFileChooserActivity : AppCompatActivity(), ProgressHandler {
31+
companion object {
32+
const val EXTRA_POWER_USER_MODE = "power_user_mode"
33+
}
34+
3135
private var _binding: ActivityNewFileChooserBinding? = null
3236
private val binding get() = _binding!!
37+
private val powerUserMode by lazy {
38+
intent?.getBooleanExtra(EXTRA_POWER_USER_MODE, false) ?: false
39+
}
3340

3441
private val snackProgressBarManager by lazy {
3542
SnackProgressBarManager(
@@ -66,7 +73,7 @@ class NewFileChooserActivity : AppCompatActivity(), ProgressHandler {
6673
super.onCreate(savedInstanceState)
6774
_binding = ActivityNewFileChooserBinding.inflate(layoutInflater)
6875
setContentView(binding.root)
69-
adapter = NewFileChooserAdapter(this)
76+
adapter = NewFileChooserAdapter(this, powerUserMode)
7077
lifecycleScope.launch {
7178
adapter.tryAddRootItems()
7279
}

app/src/main/java/com/kyhsgeekcode/filechooser/NewFileChooserAdapter.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import kotlin.collections.ArrayList
1818

1919
class NewFileChooserAdapter(
2020
private
21-
val parentActivity: NewFileChooserActivity
21+
val parentActivity: NewFileChooserActivity,
22+
private val powerUserMode: Boolean
2223
) : RecyclerView.Adapter<NewFileChooserAdapter.ViewHolder>() {
2324
val TAG = "Adapter"
2425
private val values: MutableList<FileItem> = ArrayList()
@@ -165,11 +166,7 @@ class NewFileChooserAdapter(
165166
}
166167

167168
suspend fun tryAddRootItems() {
168-
val items: List<FileItem> = try {
169-
FileItem.rootItem.listSubItems()
170-
} catch (e: Exception) {
171-
arrayListOf(FileItem(e.message ?: ""))
172-
}
169+
val items: List<FileItem> = FileItem.rootEntries(powerUserMode)
173170
values.addAll(items)
174171
}
175172

@@ -252,7 +249,11 @@ class NewFileChooserAdapter(
252249
val lastItem = backStack.pop()
253250
currentParentItem = lastItem
254251
CoroutineScope(Dispatchers.Default).launch {
255-
val items = listSubItemsCached(currentParentItem)
252+
val items = if (currentParentItem == FileItem.rootItem) {
253+
FileItem.rootEntries(powerUserMode)
254+
} else {
255+
listSubItemsCached(currentParentItem)
256+
}
256257
addItemsToListSorted(items)
257258
withContext(Dispatchers.Main) {
258259
notifyDataSetChanged()

app/src/main/java/com/kyhsgeekcode/filechooser/model/FileItem.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,22 @@ open class FileItem : Serializable {
207207
companion object {
208208
val rootItem = object : FileItem("Main") {
209209
override suspend fun listSubItems(publisher: (Int, Int) -> Unit): List<FileItem> {
210-
return listOf(fileRoot, fileSdcard, apps, processes, others, zoo, hash)
210+
return rootEntries(powerUserMode = true)
211211
}
212212

213213
override fun canExpand(): Boolean = true
214214
override fun isRawAvailable(): Boolean = false
215215
override fun isProjectAble(): Boolean = false
216216
}
217217

218+
fun rootEntries(powerUserMode: Boolean): List<FileItem> {
219+
return if (powerUserMode) {
220+
listOf(fileRoot, fileSdcard, apps, processes, others, zoo, hash)
221+
} else {
222+
listOf(others)
223+
}
224+
}
225+
218226
val fileRoot = FileItem(file = File("/"))
219227

220228
val fileSdcard = FileItem(file = Environment.getExternalStorageDirectory())

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454
<string name="preview">preview</string>ㄴ
5555
<string name="main_select_source_guide">Select any file to disassemble, then open sub files from the drawer left.</string>
5656
<string name="select_file">Select file</string>
57+
<string name="advanced_import">Advanced import</string>
58+
<string name="power_user_features">Power-user features</string>
59+
<string name="power_user_import_title">Enable power-user import options</string>
60+
<string name="power_user_import_summary">Show advanced non-SAF import entry points such as root and direct filesystem browsing.</string>
5761
<string name="architecture">Architecture:</string>
5862
<string name="finish_setup">Finish Setup</string>
5963
<string name="override_autosetup">Override autosetup</string>

app/src/main/res/xml-v30/pref_settings.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
app:preference_selector="wheel"
2121
app:default_color="colorPrimary"/-->
2222
</PreferenceCategory>
23+
<PreferenceCategory android:title="@string/power_user_features">
24+
<SwitchPreferenceCompat
25+
android:defaultValue="false"
26+
android:key="power_user_import_mode"
27+
android:summary="@string/power_user_import_summary"
28+
android:title="@string/power_user_import_title" />
29+
</PreferenceCategory>
2330
<PreferenceCategory android:title="@string/app_info">
2431
<Preference
2532
android:fragment="com.kyhsgeekcode.disassembler.preference.DeveloperInfoFragment"

app/src/main/res/xml/pref_settings.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
app:preference_selector="wheel"
2121
app:default_color="colorPrimary"/-->
2222
</PreferenceCategory>
23+
<PreferenceCategory android:title="@string/power_user_features">
24+
<SwitchPreferenceCompat
25+
android:defaultValue="false"
26+
android:key="power_user_import_mode"
27+
android:summary="@string/power_user_import_summary"
28+
android:title="@string/power_user_import_title" />
29+
</PreferenceCategory>
2330
<!-- <PreferenceCategory android:title="Security configuration">-->
2431
<!-- <Preference android:title="Manage this app\'s all storage access">-->
2532
<!-- <intent android:action="android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION" />-->

0 commit comments

Comments
 (0)