Skip to content

Commit df9df86

Browse files
committed
Port module shortcuts to IcePatch
1 parent d15a04d commit df9df86

10 files changed

Lines changed: 658 additions & 9 deletions

File tree

apd/src/module.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,27 @@ bool ValidateModuleId(const std::string& id) {
6161
return true;
6262
}
6363

64+
std::string ResolveModuleIconPath(const std::string& icon_value, const std::string& module_id,
65+
const std::string& module_path, const char* key_name) {
66+
if (icon_value.empty()) {
67+
return "";
68+
}
69+
if (icon_value[0] == '/') {
70+
LOGW("module %s: %s has absolute path, rejected", module_id.c_str(), key_name);
71+
return "";
72+
}
73+
if (icon_value.find("..") != std::string::npos) {
74+
LOGW("module %s: %s has parent traversal, rejected", module_id.c_str(), key_name);
75+
return "";
76+
}
77+
std::string full_path = module_path + "/" + icon_value;
78+
if (!FileExists(full_path)) {
79+
LOGW("module %s: %s not found: %s", module_id.c_str(), key_name, full_path.c_str());
80+
return "";
81+
}
82+
return icon_value;
83+
}
84+
6485
std::map<std::string, std::string> ParseProps(const std::string& content) {
6586
std::map<std::string, std::string> props;
6687
std::stringstream ss(content);
@@ -463,12 +484,32 @@ bool ListModules() {
463484
bool web = DirExists(module_path + "/" + kModuleWebDir);
464485
bool action = FileExists(module_path + "/" + kModuleActionScript) ||
465486
FileExists(module_path + "/" + id + ".lua");
487+
std::string action_icon;
488+
std::string webui_icon;
489+
auto action_icon_it = props.find("actionIcon");
490+
if (action_icon_it != props.end()) {
491+
action_icon = ResolveModuleIconPath(action_icon_it->second, id, module_path, "actionIcon");
492+
}
493+
auto webui_icon_it = props.find("webuiIcon");
494+
if (webui_icon_it != props.end()) {
495+
webui_icon = ResolveModuleIconPath(webui_icon_it->second, id, module_path, "webuiIcon");
496+
}
466497

467498
props["enabled"] = enabled ? "true" : "false";
468499
props["update"] = update ? "true" : "false";
469500
props["remove"] = remove ? "true" : "false";
470501
props["web"] = web ? "true" : "false";
471502
props["action"] = action ? "true" : "false";
503+
if (!action_icon.empty()) {
504+
props["actionIcon"] = action_icon;
505+
} else {
506+
props.erase("actionIcon");
507+
}
508+
if (!webui_icon.empty()) {
509+
props["webuiIcon"] = webui_icon;
510+
} else {
511+
props.erase("webuiIcon");
512+
}
472513
modules.push_back(props);
473514
return true;
474515
});

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
android:autoRemoveFromRecents="true"
8484
android:documentLaunchMode="intoExisting"
8585
android:exported="false"
86+
android:taskAffinity="${applicationId}.webui"
8687
android:theme="@style/Theme.IcePatch.WebUI" />
8788

8889
<activity

app/src/main/java/com/anatdx/icepatch/ui/MainActivity.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import androidx.compose.ui.graphics.Color
5555
import androidx.compose.ui.Alignment
5656
import androidx.compose.ui.Modifier
5757
import androidx.compose.ui.platform.LocalConfiguration
58+
import androidx.compose.ui.platform.LocalContext
5859
import androidx.compose.ui.res.stringResource
5960
import androidx.compose.ui.text.style.TextOverflow
6061
import androidx.compose.ui.unit.dp
@@ -82,6 +83,7 @@ import com.anatdx.icepatch.ui.theme.rememberBackgroundConfig
8283
import com.anatdx.icepatch.ui.viewmodel.SuperUserViewModel
8384
import com.ramcosta.composedestinations.generated.destinations.InstallModeSelectScreenDestination
8485
import com.ramcosta.composedestinations.generated.destinations.InstallScreenDestination
86+
import com.ramcosta.composedestinations.generated.destinations.ExecuteAPMActionScreenDestination
8587
import com.anatdx.icepatch.ui.screen.MODULE_TYPE
8688
import com.anatdx.icepatch.util.KpmInfo
8789
import com.anatdx.icepatch.util.KpmInfoReader
@@ -92,6 +94,7 @@ import androidx.lifecycle.lifecycleScope
9294
import kotlinx.coroutines.launch
9395
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
9496
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
97+
import androidx.core.net.toUri
9598

9699
class MainActivity : AppCompatActivity() {
97100

@@ -239,6 +242,8 @@ class MainActivity : AppCompatActivity() {
239242
}
240243
}
241244

245+
ShortcutIntentHandler(currentIntent = currentIntent, navigator = navigator)
246+
242247
LaunchedEffect(Unit) {
243248
if (SuperUserViewModel.apps.isEmpty()) {
244249
SuperUserViewModel().fetchAppList()
@@ -397,6 +402,42 @@ class MainActivity : AppCompatActivity() {
397402
}
398403
}
399404

405+
@Composable
406+
private fun ShortcutIntentHandler(
407+
currentIntent: Intent?,
408+
navigator: com.ramcosta.composedestinations.navigation.DestinationsNavigator
409+
) {
410+
val context = LocalContext.current
411+
var handled by remember(currentIntent) { mutableStateOf(false) }
412+
413+
LaunchedEffect(currentIntent, handled) {
414+
if (handled) return@LaunchedEffect
415+
val intent = currentIntent ?: return@LaunchedEffect
416+
when (intent.getStringExtra("shortcut_type")) {
417+
"module_action" -> {
418+
val moduleId = intent.getStringExtra("module_id") ?: return@LaunchedEffect
419+
navigator.navigate(ExecuteAPMActionScreenDestination(moduleId)) {
420+
launchSingleTop = true
421+
}
422+
handled = true
423+
}
424+
425+
"module_webui" -> {
426+
val moduleId = intent.getStringExtra("module_id") ?: return@LaunchedEffect
427+
val moduleName = intent.getStringExtra("module_name") ?: moduleId
428+
val webIntent = Intent(context, WebUIActivity::class.java)
429+
.setData("apatch://webui/$moduleId".toUri())
430+
.putExtra("id", moduleId)
431+
.putExtra("name", moduleName)
432+
.putExtra("from_webui_shortcut", true)
433+
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
434+
context.startActivity(webIntent)
435+
handled = true
436+
}
437+
}
438+
}
439+
}
440+
400441

401442
@Composable
402443
private fun BottomBar(navController: NavHostController, visibleDestinations: Set<BottomBarDestination>) {

app/src/main/java/com/anatdx/icepatch/ui/WebUIActivity.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ class WebUIActivity : ComponentActivity() {
109109
val name = intent.getStringExtra("name")!!
110110
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
111111
@Suppress("DEPRECATION")
112-
setTaskDescription(ActivityManager.TaskDescription("APatch - $name"))
112+
setTaskDescription(ActivityManager.TaskDescription("IcePatch - $name"))
113113
} else {
114-
val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("APatch - $name").build()
114+
val taskDescription = ActivityManager.TaskDescription.Builder().setLabel("IcePatch - $name").build()
115115
setTaskDescription(taskDescription)
116116
}
117117

@@ -236,4 +236,4 @@ class WebUIActivity : ComponentActivity() {
236236
}
237237
}
238238
}
239-
}
239+
}

0 commit comments

Comments
 (0)