From 1e3ebdbe5aca4a8b638bb419ebc400dabd81c7e6 Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:26:57 +0530 Subject: [PATCH 01/26] feat: Add stable projectUuid to XmlHeader for persistent identification --- .../catrobat/catroid/content/XmlHeader.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/catroid/src/main/java/org/catrobat/catroid/content/XmlHeader.java b/catroid/src/main/java/org/catrobat/catroid/content/XmlHeader.java index 4116210a771..7b1a63397b9 100644 --- a/catroid/src/main/java/org/catrobat/catroid/content/XmlHeader.java +++ b/catroid/src/main/java/org/catrobat/catroid/content/XmlHeader.java @@ -1,6 +1,6 @@ /* * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2025 The Catrobat Team + * Copyright (C) 2010-2026 The Catrobat Team * () * * This program is free software: you can redistribute it and/or modify @@ -31,6 +31,7 @@ import java.io.Serializable; import java.util.Arrays; import java.util.List; +import java.util.UUID; public class XmlHeader implements Serializable { @@ -54,6 +55,10 @@ public class XmlHeader implements Serializable { public boolean scenesEnabled = true; private String listeningLanguageTag = ""; + // Stable identifier persisted in code.xml for future-proofing (e.g. shortcut IDs). + // Lazily generated on first access; older projects will get a UUID assigned on next save. + private String projectUuid; + //============================================================================================== // mutable fields only used by Catroweb (share.catrob.at website) so far //============================================================================================== @@ -273,4 +278,19 @@ public String getListeningLanguageTag() { public void setListeningLanguageTag(String listeningLanguageTag) { this.listeningLanguageTag = listeningLanguageTag; } + + /** + * Returns the stable project UUID. If not yet set (e.g. older project), generates one. + * The UUID is persisted when the project is next saved to code.xml. + */ + public String getProjectUuid() { + if (projectUuid == null || projectUuid.isEmpty()) { + projectUuid = UUID.randomUUID().toString(); + } + return projectUuid; + } + + public void setProjectUuid(String projectUuid) { + this.projectUuid = projectUuid; + } } From d47dda07de5260d4ff1c47e3f71ca7c196317352 Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:27:09 +0530 Subject: [PATCH 02/26] feat: Add UI resources and manifest registration for shortcut feature --- catroid/build.gradle | 1 + catroid/src/main/AndroidManifest.xml | 9 ++++++++- catroid/src/main/res/menu/menu_project_activity.xml | 7 ++++++- catroid/src/main/res/values/strings.xml | 6 ++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/catroid/build.gradle b/catroid/build.gradle index 8789356a11e..91a880353fd 100644 --- a/catroid/build.gradle +++ b/catroid/build.gradle @@ -400,6 +400,7 @@ dependencies { // Lifecycle implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version" // Room implementation("androidx.room:room-runtime:$room_version") diff --git a/catroid/src/main/AndroidManifest.xml b/catroid/src/main/AndroidManifest.xml index a7a593a8f32..5abcffc0bd8 100644 --- a/catroid/src/main/AndroidManifest.xml +++ b/catroid/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + Pin to Home Screen + Shortcuts are not supported on this device + Project is busy, try again + Project not found, shortcut disabled From 0844892c1ad5f0a358542236573edc23ae7996da Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:27:16 +0530 Subject: [PATCH 03/26] feat: Implement ShortcutHelper for shortcut management and icon loading --- .../catroid/ui/shortcut/ShortcutHelper.kt | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutHelper.kt diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutHelper.kt b/catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutHelper.kt new file mode 100644 index 00000000000..67e75798628 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutHelper.kt @@ -0,0 +1,255 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2026 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.catroid.ui.shortcut + +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.util.Log +import android.widget.Toast +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.catrobat.catroid.R +import org.catrobat.catroid.common.Constants +import org.catrobat.catroid.common.FlavoredConstants +import org.catrobat.catroid.utils.FileMetaDataExtractor +import java.io.File + +/** + * Utility for creating and managing pinned home-screen shortcuts for Catroid projects. + * + * **ID strategy:** Shortcut ID = encoded directory name (unique per project). This is + * the filesystem-safe encoding of the project name produced by + * [FileMetaDataExtractor.encodeSpecialCharsForFileSystem]. The directory name is guaranteed + * unique within Catroid's project root. + * + * A stable UUID is also stored in `code.xml` (see [org.catrobat.catroid.content.XmlHeader.getProjectUuid]) + * for future-proofing — e.g. if we later want to survive renames without re-pinning — but + * the current shortcut ID is still the encoded directory name. + */ +object ShortcutHelper { + + private const val TAG = "ShortcutHelper" + + fun isShortcutSupported(context: Context): Boolean = + ShortcutManagerCompat.isRequestPinShortcutSupported(context) + + /** + * Loads the project screenshot bitmap from the project directory on a background thread. + * Checks project root first, then searches scene subdirectories (which is where Catroid + * typically stores automatic_screenshot.png and manual_screenshot.png). + * + * First attempts full-resolution ARGB_8888. On OutOfMemoryError, retries at half-resolution + * with RGB_565. If both fail, returns null. + */ + suspend fun loadProjectIcon(projectName: String): Bitmap? = withContext(Dispatchers.IO) { + val encodedName = FileMetaDataExtractor.encodeSpecialCharsForFileSystem(projectName) + val projectDir = File(FlavoredConstants.DEFAULT_ROOT_DIRECTORY, encodedName) + val screenshotFile = findScreenshotFile(projectDir) ?: return@withContext null + decodeBitmapWithFallback(screenshotFile.absolutePath) + } + + /** + * Searches for the best available screenshot file in the project directory. + * Priority: manual_screenshot > automatic_screenshot. + * Looks first in the project root, then in scene subdirectories. + */ + private fun findScreenshotFile(projectDir: File): File? { + if (!projectDir.exists() || !projectDir.isDirectory) return null + + // Check project root first + val rootManual = File(projectDir, Constants.SCREENSHOT_MANUAL_FILE_NAME) + if (rootManual.exists() && rootManual.length() > 0) return rootManual + + val rootAutomatic = File(projectDir, Constants.SCREENSHOT_AUTOMATIC_FILE_NAME) + if (rootAutomatic.exists() && rootAutomatic.length() > 0) return rootAutomatic + + // Search scene subdirectories + val sceneDirs = projectDir.listFiles { file -> file.isDirectory } ?: return null + for (sceneDir in sceneDirs) { + val manual = File(sceneDir, Constants.SCREENSHOT_MANUAL_FILE_NAME) + if (manual.exists() && manual.length() > 0) return manual + + val automatic = File(sceneDir, Constants.SCREENSHOT_AUTOMATIC_FILE_NAME) + if (automatic.exists() && automatic.length() > 0) return automatic + } + + return null + } + + private const val ICON_SIZE = 192 // px — standard adaptive icon size + + private fun decodeBitmapWithFallback(path: String): Bitmap? { + // Attempt ARGB_8888 + try { + val options = BitmapFactory.Options().apply { + inPreferredConfig = Bitmap.Config.ARGB_8888 + } + val bitmap = BitmapFactory.decodeFile(path, options) + if (bitmap != null) return scaleBitmap(bitmap) + } catch (_: OutOfMemoryError) { + Log.w(TAG, "OOM loading icon, retrying at half-resolution") + } + + // Retry at half-resolution with RGB_565 + try { + val options = BitmapFactory.Options().apply { + inSampleSize = 2 + inPreferredConfig = Bitmap.Config.RGB_565 + } + val bitmap = BitmapFactory.decodeFile(path, options) + if (bitmap != null) return scaleBitmap(bitmap) + } catch (_: OutOfMemoryError) { + Log.e(TAG, "OOM loading half-resolution icon, falling back to default") + } + + return null + } + + private fun scaleBitmap(source: Bitmap): Bitmap { + if (source.width <= ICON_SIZE && source.height <= ICON_SIZE) return source + val scaled = Bitmap.createScaledBitmap(source, ICON_SIZE, ICON_SIZE, true) + if (scaled !== source) source.recycle() + return scaled + } + + /** + * Requests a pinned shortcut for the given project. Must be called on the main thread. + * + * Shortcut ID = encoded directory name (unique per project). + * + * @param context Application or Activity context + * @param projectName The project name (used as the shortcut label) + * @param icon Optional pre-loaded bitmap for the icon; uses the default app icon if null + */ + fun pinProject(context: Context, projectName: String, icon: Bitmap?) { + if (!isShortcutSupported(context)) { + Toast.makeText( + context, + R.string.shortcut_not_supported, + Toast.LENGTH_SHORT + ).show() + return + } + + val shortcutId = encodeShortcutId(projectName) + + val trampolineIntent = Intent(context, ShortcutTrampolineActivity::class.java).apply { + action = Intent.ACTION_VIEW + putExtra(ShortcutTrampolineActivity.EXTRA_PROJECT_NAME, projectName) + } + + val iconCompat = if (icon != null) { + IconCompat.createWithAdaptiveBitmap(icon) + } else { + IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground) + } + + val shortcutInfo = ShortcutInfoCompat.Builder(context, shortcutId) + .setShortLabel(projectName) + .setLongLabel(projectName) + .setIcon(iconCompat) + .setIntent(trampolineIntent) + .build() + + val callbackIntent = ShortcutManagerCompat.createShortcutResultIntent(context, shortcutInfo) + val pendingIntent = PendingIntent.getBroadcast( + context, + 0, + callbackIntent, + PendingIntent.FLAG_IMMUTABLE + ) + + ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, pendingIntent.intentSender) + } + + /** + * Updates any existing pinned shortcut after a project rename. + * + * Disables the old shortcut (by old encoded directory name) and — if the launcher supports + * updating — pushes a new dynamic shortcut with the new label and intent so existing + * home-screen icons reflect the new name. + * + * Call from a coroutine; the icon load runs on [Dispatchers.IO] internally. + * + * @param context Application or Activity context + * @param oldName The previous project name (before rename) + * @param newName The new project name (after rename) + */ + suspend fun updateShortcutOnRename(context: Context, oldName: String, newName: String) { + val oldShortcutId = encodeShortcutId(oldName) + val newShortcutId = encodeShortcutId(newName) + + // Disable the old shortcut so tapping it shows "project not found" gracefully + try { + ShortcutManagerCompat.disableShortcuts( + context, + listOf(oldShortcutId), + context.getString(R.string.shortcut_project_not_found) + ) + } catch (e: Exception) { + Log.w(TAG, "Could not disable old shortcut '$oldShortcutId': ${e.message}") + } + + // Build a replacement shortcut with the new name + val icon = loadProjectIcon(newName) + val iconCompat = if (icon != null) { + IconCompat.createWithAdaptiveBitmap(icon) + } else { + IconCompat.createWithResource(context, R.drawable.ic_launcher_foreground) + } + + val trampolineIntent = Intent(context, ShortcutTrampolineActivity::class.java).apply { + action = Intent.ACTION_VIEW + putExtra(ShortcutTrampolineActivity.EXTRA_PROJECT_NAME, newName) + } + + val newShortcut = ShortcutInfoCompat.Builder(context, newShortcutId) + .setShortLabel(newName) + .setLongLabel(newName) + .setIcon(iconCompat) + .setIntent(trampolineIntent) + .build() + + // Push a dynamic shortcut so the launcher can pick up the change. + // Pinned shortcuts cannot be directly updated, but pushing a dynamic shortcut + // with the new ID keeps the shortcut registry consistent. + try { + ShortcutManagerCompat.pushDynamicShortcut(context, newShortcut) + } catch (e: Exception) { + Log.w(TAG, "Could not push updated shortcut '$newShortcutId': ${e.message}") + } + } + + /** + * Encodes a project name into the shortcut ID (= encoded directory name). + */ + private fun encodeShortcutId(projectName: String): String = + FileMetaDataExtractor.encodeSpecialCharsForFileSystem(projectName) +} From f20a7007b1c68e60acd515e2f73005cbc91100ac Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:27:22 +0530 Subject: [PATCH 04/26] feat: Add ShortcutTrampolineActivity for secure project launching --- .../ui/shortcut/ShortcutTrampolineActivity.kt | 176 ++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutTrampolineActivity.kt diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutTrampolineActivity.kt b/catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutTrampolineActivity.kt new file mode 100644 index 00000000000..44abcd45dd7 --- /dev/null +++ b/catroid/src/main/java/org/catrobat/catroid/ui/shortcut/ShortcutTrampolineActivity.kt @@ -0,0 +1,176 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2026 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.catroid.ui.shortcut + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.core.content.pm.ShortcutManagerCompat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.catrobat.catroid.ProjectManager +import org.catrobat.catroid.R +import org.catrobat.catroid.common.Constants +import org.catrobat.catroid.common.FlavoredConstants +import org.catrobat.catroid.stage.StageActivity +import org.catrobat.catroid.utils.FileMetaDataExtractor +import java.io.File + +/** + * Transparent trampoline activity that validates the project on disk and launches + * StageActivity. Exported so the home launcher can start it via pinned shortcuts. + * Excluded from the recents list. + * + * Uses plain [Activity] (not AppCompatActivity) so that + * [android:theme="@android:style/Theme.Translucent.NoTitleBar"] works without + * requiring an AppCompat theme. + */ +class ShortcutTrampolineActivity : Activity() { + + private val job = Job() + private val scope = CoroutineScope(Dispatchers.Main + job) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val projectName = intent?.getStringExtra(EXTRA_PROJECT_NAME) + if (projectName.isNullOrBlank()) { + Log.e(TAG, "No project name in shortcut intent") + finish() + return + } + + scope.launch { + val projectDir = withContext(Dispatchers.IO) { + resolveProjectDirectory(projectName) + } + + if (projectDir == null) { + disableShortcutAndFinish(projectName) + return@launch + } + + val codeXml = File(projectDir, Constants.CODE_XML_FILE_NAME) + val isAccessible = withContext(Dispatchers.IO) { + codeXml.exists() && codeXml.canRead() + } + + if (!isAccessible) { + Toast.makeText( + this@ShortcutTrampolineActivity, + R.string.project_busy_try_again, + Toast.LENGTH_SHORT + ).show() + finish() + return@launch + } + + val projectManager = ProjectManager.getInstance() + if (projectManager == null) { + Log.e(TAG, "ProjectManager not initialized") + Toast.makeText( + this@ShortcutTrampolineActivity, + R.string.project_busy_try_again, + Toast.LENGTH_SHORT + ).show() + finish() + return@launch + } + + val appContext = applicationContext + val loaded = withContext(Dispatchers.IO) { + try { + @Suppress("DEPRECATION") + projectManager.loadProject(projectDir, appContext) + true + } catch (e: Exception) { + Log.e(TAG, "Failed to load project: ${e.message}", e) + false + } + } + + if (!loaded) { + Toast.makeText( + this@ShortcutTrampolineActivity, + R.string.project_busy_try_again, + Toast.LENGTH_SHORT + ).show() + finish() + return@launch + } + + val project = projectManager.currentProject + if (project != null) { + projectManager.currentlyEditedScene = project.defaultScene + projectManager.setCurrentlyPlayingScene(project.defaultScene) + projectManager.startScene = project.defaultScene + } + + val stageIntent = Intent( + this@ShortcutTrampolineActivity, + StageActivity::class.java + ).apply { + putExtra(StageActivity.EXTRA_IS_FROM_SHORTCUT, true) + } + startActivity(stageIntent) + finish() + } + } + + override fun onDestroy() { + super.onDestroy() + job.cancel() + } + + private fun resolveProjectDirectory(projectName: String): File? { + val encodedName = FileMetaDataExtractor.encodeSpecialCharsForFileSystem(projectName) + val projectDir = File(FlavoredConstants.DEFAULT_ROOT_DIRECTORY, encodedName) + return if (projectDir.exists() && projectDir.isDirectory) projectDir else null + } + + private fun disableShortcutAndFinish(projectName: String) { + val encodedName = FileMetaDataExtractor.encodeSpecialCharsForFileSystem(projectName) + try { + ShortcutManagerCompat.disableShortcuts( + this, + listOf(encodedName), + getString(R.string.shortcut_project_not_found) + ) + } catch (e: Exception) { + Log.w(TAG, "Could not disable shortcut: ${e.message}") + } + Toast.makeText(this, R.string.shortcut_project_not_found, Toast.LENGTH_SHORT).show() + finish() + } + + companion object { + private const val TAG = "ShortcutTrampoline" + const val EXTRA_PROJECT_NAME = "shortcut_project_name" + } +} From 8a76b41246134bf31fde3a1efbe7dbd9d5e11b91 Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:27:29 +0530 Subject: [PATCH 05/26] feat: Integrate shortcut creation and rename sync in ProjectListFragment --- .../fragment/ProjectListFragment.kt | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.kt b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.kt index bfb5e9fae3a..62234b19a5d 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.kt +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/fragment/ProjectListFragment.kt @@ -1,6 +1,6 @@ /* * Catroid: An on-device visual programming system for Android devices - * Copyright (C) 2010-2025 The Catrobat Team + * Copyright (C) 2010-2026 The Catrobat Team * () * * This program is free software: you can redistribute it and/or modify @@ -69,6 +69,7 @@ import org.catrobat.catroid.ui.recyclerview.adapter.RVAdapter import org.catrobat.catroid.ui.recyclerview.adapter.multiselection.MultiSelectionManager import org.catrobat.catroid.ui.recyclerview.viewholder.CheckableViewHolder import org.catrobat.catroid.ui.runtimepermissions.RequiresPermissionTask +import org.catrobat.catroid.ui.shortcut.ShortcutHelper import org.catrobat.catroid.utils.ToastUtil import org.koin.android.ext.android.inject import java.io.File @@ -421,9 +422,19 @@ class ProjectListFragment( item ?: return name ?: return if (name != item.name) { + val oldName = item.name setShowProgressBar(true) ProjectRenamer(item.directory, name) - .renameProjectAsync({ success: Boolean -> onRenameFinished(success) }) + .renameProjectAsync({ success: Boolean -> + onRenameFinished(success) + if (success) { + coroutineScope.launch { + ShortcutHelper.updateShortcutOnRename( + requireContext(), oldName, name + ) + } + } + }) } } @@ -485,13 +496,18 @@ class ProjectListFragment( override fun onSettingsClick(item: ProjectData?, view: View?) { val itemList: MutableList = ArrayList() itemList.add(item) - val hiddenMenuOptionIds = intArrayOf( + + val hiddenMenuOptionIds = mutableListOf( R.id.new_group, R.id.new_scene, R.id.show_details, R.id.from_local, R.id.edit ) + if (!ShortcutHelper.isShortcutSupported(requireContext())) { + hiddenMenuOptionIds.add(R.id.pin_to_home_screen) + } + val popupMenu = UiUtils.createSettingsPopUpMenu( view, requireContext(), - R.menu.menu_project_activity, hiddenMenuOptionIds + R.menu.menu_project_activity, hiddenMenuOptionIds.toIntArray() ) popupMenu.setOnMenuItemClickListener { menuItem: MenuItem -> when (menuItem.itemId) { @@ -499,6 +515,7 @@ class ProjectListFragment( R.id.rename -> showRenameDialog(item) R.id.delete -> deleteItems(itemList) R.id.project_options -> showProjectOptionsFragment(item) + R.id.pin_to_home_screen -> pinProjectToHomeScreen(item) } true } @@ -600,4 +617,15 @@ class ProjectListFragment( } } } + + private fun pinProjectToHomeScreen(item: ProjectData?) { + item ?: return + val projectName = item.name + coroutineScope.launch { + val icon = ShortcutHelper.loadProjectIcon(projectName) + withContext(mainDispatcher) { + ShortcutHelper.pinProject(requireContext(), projectName, icon) + } + } + } } From f118e60a8680b3543b47f272327bfc866cbbd465 Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:27:37 +0530 Subject: [PATCH 06/26] feat: Handle shortcut launches and back press in StageActivity --- .../java/org/catrobat/catroid/stage/StageActivity.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java b/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java index 9267e8b21c6..e919602c125 100644 --- a/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java +++ b/catroid/src/main/java/org/catrobat/catroid/stage/StageActivity.java @@ -96,6 +96,7 @@ public class StageActivity extends AndroidApplication implements PermissionHandl public static StageListener stageListener; public static final int REQUEST_START_STAGE = 101; + public static final String EXTRA_IS_FROM_SHORTCUT = "is_from_shortcut"; public static final int REGISTER_INTENT = 0; private static final int PERFORM_INTENT = 1; @@ -125,10 +126,12 @@ public class StageActivity extends AndroidApplication implements PermissionHandl public CountingIdlingResource idlingResource = new CountingIdlingResource("StageActivity"); private PermissionRequestActivityExtension permissionRequestActivityExtension = new PermissionRequestActivityExtension(); public static WeakReference activeStageActivity; + private boolean isFromShortcut; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + isFromShortcut = getIntent().getBooleanExtra(EXTRA_IS_FROM_SHORTCUT, false); StageLifeCycleController.stageCreate(this); activeStageActivity = new WeakReference<>(this); } @@ -240,6 +243,11 @@ protected void onNewIntent(Intent intent) { @Override public void onBackPressed() { + if (isFromShortcut) { + manageLoadAndFinish(); + finish(); + return; + } if (BuildConfig.FEATURE_APK_GENERATOR_ENABLED) { BluetoothDeviceService service = ServiceProvider.getService(CatroidService.BLUETOOTH_DEVICE_SERVICE); if (service != null) { From 77acf07c55a3801cae4b0a83748cb8302fb46bd6 Mon Sep 17 00:00:00 2001 From: harshsomankar123-tech Date: Wed, 22 Apr 2026 22:59:09 +0530 Subject: [PATCH 07/26] feat: Add custom UI resources for pin-to-home confirmation dialog --- .../main/res/drawable/bg_shortcut_button.xml | 29 ++++++++ .../main/res/drawable/bg_shortcut_dialog.xml | 29 ++++++++ .../main/res/layout/dialog_shortcut_pin.xml | 68 +++++++++++++++++++ catroid/src/main/res/values/styles.xml | 9 +++ 4 files changed, 135 insertions(+) create mode 100644 catroid/src/main/res/drawable/bg_shortcut_button.xml create mode 100644 catroid/src/main/res/drawable/bg_shortcut_dialog.xml create mode 100644 catroid/src/main/res/layout/dialog_shortcut_pin.xml diff --git a/catroid/src/main/res/drawable/bg_shortcut_button.xml b/catroid/src/main/res/drawable/bg_shortcut_button.xml new file mode 100644 index 00000000000..50e65dbc585 --- /dev/null +++ b/catroid/src/main/res/drawable/bg_shortcut_button.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/catroid/src/main/res/drawable/bg_shortcut_dialog.xml b/catroid/src/main/res/drawable/bg_shortcut_dialog.xml new file mode 100644 index 00000000000..96eace25f58 --- /dev/null +++ b/catroid/src/main/res/drawable/bg_shortcut_dialog.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/catroid/src/main/res/layout/dialog_shortcut_pin.xml b/catroid/src/main/res/layout/dialog_shortcut_pin.xml new file mode 100644 index 00000000000..b223afc563a --- /dev/null +++ b/catroid/src/main/res/layout/dialog_shortcut_pin.xml @@ -0,0 +1,68 @@ + + + + + + + + + +