mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-26 17:19:24 +00:00
implement deep links support
This commit is contained in:
5 files changed
+131
-24
No files matched your search
@@ -1,3 +1,18 @@
|
|||||||
# Geode Android Launcher
|
# Geode Android Launcher
|
||||||
|
|
||||||
Launches a vanilla copy of Geometry Dash with the Geode loader added.
|
Launches a vanilla copy of Geometry Dash with the Geode loader added.
|
||||||
|
|
||||||
|
## Shortcut Links
|
||||||
|
|
||||||
|
The launcher supports a handful of links as an escape hatch when the main functionality is impaired in some way (such as needing to prevent an automatic launch due to a crash).
|
||||||
|
You can open these links by copying them into the URL bar in some browsers (like Chrome), or through ADB.
|
||||||
|
|
||||||
|
| Link | Action |
|
||||||
|
|---------------------------------------------|--------------------------------------------------------------------------|
|
||||||
|
| geode-launcher://main | Opens to the main activity, forces the user to explicitly confirm launch |
|
||||||
|
| geode-launcher://main/launch | Opens to the game after the launcher is finished checking for updates |
|
||||||
|
| geode-launcher://main/launch?safe-mode=true | Opens to the game like before, but with safe mode enabled |
|
||||||
|
| geode-launcher://settings | Opens the main settings |
|
||||||
|
| geode-launcher://developer-settings | Opens the developer settings |
|
||||||
|
| geode-launcher://logs | Opens the application logs |
|
||||||
|
| geode-launcher://crashes | Opens the crash dumps viewer |
|
||||||
@@ -57,19 +57,43 @@
|
|||||||
android:theme="@style/Theme.GeodeLauncher" />
|
android:theme="@style/Theme.GeodeLauncher" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.DeveloperSettingsActivity"
|
android:name=".preferences.DeveloperSettingsActivity"
|
||||||
android:exported="false"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_developer_settings"
|
android:label="@string/title_activity_developer_settings"
|
||||||
android:theme="@style/Theme.GeodeLauncher" />
|
android:theme="@style/Theme.GeodeLauncher">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="developer-settings" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.ApplicationLogsActivity"
|
android:name=".preferences.ApplicationLogsActivity"
|
||||||
android:exported="false"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_application_logs"
|
android:label="@string/title_activity_application_logs"
|
||||||
android:theme="@style/Theme.GeodeLauncher" />
|
android:theme="@style/Theme.GeodeLauncher">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="logs" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.CrashLogsActivity"
|
android:name=".preferences.CrashLogsActivity"
|
||||||
android:exported="false"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_crash_logs"
|
android:label="@string/title_activity_crash_logs"
|
||||||
android:theme="@style/Theme.GeodeLauncher" />
|
android:theme="@style/Theme.GeodeLauncher">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="crashes" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.ApplicationIconActivity"
|
android:name=".preferences.ApplicationIconActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
@@ -77,9 +101,17 @@
|
|||||||
android:theme="@style/Theme.GeodeLauncher" />
|
android:theme="@style/Theme.GeodeLauncher" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.SettingsActivity"
|
android:name=".preferences.SettingsActivity"
|
||||||
android:exported="false"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_settings"
|
android:label="@string/title_activity_settings"
|
||||||
android:theme="@style/Theme.GeodeLauncher" />
|
android:theme="@style/Theme.GeodeLauncher">
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="settings" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".GeometryDashActivity"
|
android:name=".GeometryDashActivity"
|
||||||
android:configChanges="orientation|screenSize|smallestScreenSize"
|
android:configChanges="orientation|screenSize|smallestScreenSize"
|
||||||
@@ -101,6 +133,13 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="main" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity-alias
|
<activity-alias
|
||||||
@@ -115,6 +154,13 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="main" />
|
||||||
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".MainActivityPride"
|
android:name=".MainActivityPride"
|
||||||
@@ -128,6 +174,13 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="main" />
|
||||||
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
<activity-alias
|
<activity-alias
|
||||||
android:name=".MainActivityGeode"
|
android:name=".MainActivityGeode"
|
||||||
@@ -141,6 +194,13 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="geode-launcher" android:host="main" />
|
||||||
|
</intent-filter>
|
||||||
</activity-alias>
|
</activity-alias>
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
|
|||||||
@@ -96,12 +96,16 @@ class MainActivity : ComponentActivity() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
enableEdgeToEdge()
|
|
||||||
clearDownloadedApks(this)
|
|
||||||
|
|
||||||
val gdInstalled = GamePackageUtils.isGameInstalled(packageManager)
|
val gdInstalled = GamePackageUtils.isGameInstalled(packageManager)
|
||||||
val geodeInstalled = LaunchUtils.isGeodeInstalled(this)
|
val geodeInstalled = LaunchUtils.isGeodeInstalled(this)
|
||||||
|
|
||||||
|
if (gdInstalled && geodeInstalled && intent.getBooleanExtra("restarted", false)) {
|
||||||
|
onLaunch(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
enableEdgeToEdge()
|
||||||
|
clearDownloadedApks(this)
|
||||||
|
|
||||||
val returnMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE)
|
val returnMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE)
|
||||||
val returnExtendedMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE)
|
val returnExtendedMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE)
|
||||||
|
|
||||||
@@ -115,8 +119,23 @@ class MainActivity : ComponentActivity() {
|
|||||||
LoadFailureInfo(LaunchUtils.LauncherError.CRASHED)
|
LoadFailureInfo(LaunchUtils.LauncherError.CRASHED)
|
||||||
} else { null }
|
} else { null }
|
||||||
|
|
||||||
|
var launchArguments: LaunchArguments? = null
|
||||||
|
val intentData = intent.data
|
||||||
|
if (intent.action == Intent.ACTION_VIEW && intentData != null) {
|
||||||
|
val isLaunch = intentData.path == "/launch"
|
||||||
|
val safeMode = if (isLaunch) {
|
||||||
|
intentData.getBooleanQueryParameter("safe-mode", false)
|
||||||
|
} else false
|
||||||
|
|
||||||
|
launchArguments = LaunchArguments(
|
||||||
|
autoSafeMode = safeMode,
|
||||||
|
forcePause = !isLaunch,
|
||||||
|
forceLaunch = isLaunch,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val redirectToAlt = PreferenceUtils.get(this).getBoolean(PreferenceUtils.Key.DISABLE_REDESIGN)
|
val redirectToAlt = PreferenceUtils.get(this).getBoolean(PreferenceUtils.Key.DISABLE_REDESIGN)
|
||||||
if (redirectToAlt) {
|
if (redirectToAlt && launchArguments == null) {
|
||||||
val launchIntent = Intent(this, LegacyMainActivity::class.java)
|
val launchIntent = Intent(this, LegacyMainActivity::class.java)
|
||||||
launchIntent.putExtras(intent)
|
launchIntent.putExtras(intent)
|
||||||
startActivity(launchIntent)
|
startActivity(launchIntent)
|
||||||
@@ -137,6 +156,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
launchViewModel.loadFailure = loadFailureInfo
|
launchViewModel.loadFailure = loadFailureInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (launchArguments != null) {
|
||||||
|
launchViewModel.launchArguments = launchArguments
|
||||||
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(LocalTheme provides theme) {
|
CompositionLocalProvider(LocalTheme provides theme) {
|
||||||
GeodeLauncherTheme(theme = theme, blackBackground = backgroundOption, dynamicColor = !dynamicColorOption) {
|
GeodeLauncherTheme(theme = theme, blackBackground = backgroundOption, dynamicColor = !dynamicColorOption) {
|
||||||
Surface(
|
Surface(
|
||||||
@@ -147,14 +170,6 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gdInstalled && geodeInstalled) {
|
|
||||||
intent.getBooleanExtra("restarted", false).let {
|
|
||||||
if (it) {
|
|
||||||
onLaunch(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +620,7 @@ fun GeodeUpdateIndicator(snackbarHostState: SnackbarHostState, onRetry: () -> Un
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val resources = LocalResources.current
|
val resources = LocalResources.current
|
||||||
|
|
||||||
val releaseState = ReleaseManager.get(context).uiState.value
|
val releaseState = ReleaseManager.get(context).uiState.collectAsState().value
|
||||||
|
|
||||||
if (releaseState !is ReleaseManager.ReleaseManagerState.Failure) {
|
if (releaseState !is ReleaseManager.ReleaseManagerState.Failure) {
|
||||||
return
|
return
|
||||||
@@ -659,7 +674,9 @@ fun AltMainScreen(
|
|||||||
|
|
||||||
val launchUIState by launchViewModel.uiState.collectAsState()
|
val launchUIState by launchViewModel.uiState.collectAsState()
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var launchInSafeMode by remember { mutableStateOf(false) }
|
var launchInSafeMode by remember {
|
||||||
|
mutableStateOf(launchViewModel.launchArguments?.autoSafeMode == true)
|
||||||
|
}
|
||||||
|
|
||||||
var showErrorInfo by remember { mutableStateOf(false) }
|
var showErrorInfo by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
data class LaunchArguments(
|
||||||
|
val autoSafeMode: Boolean,
|
||||||
|
val forcePause: Boolean,
|
||||||
|
val forceLaunch: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
class LaunchViewModel(private val application: Application): ViewModel() {
|
class LaunchViewModel(private val application: Application): ViewModel() {
|
||||||
companion object {
|
companion object {
|
||||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||||
@@ -113,6 +119,8 @@ class LaunchViewModel(private val application: Application): ViewModel() {
|
|||||||
val nextLauncherUpdate = ReleaseManager.get(application).availableLauncherUpdate
|
val nextLauncherUpdate = ReleaseManager.get(application).availableLauncherUpdate
|
||||||
|
|
||||||
var loadFailure: LoadFailureInfo? = null
|
var loadFailure: LoadFailureInfo? = null
|
||||||
|
var launchArguments: LaunchArguments? = null
|
||||||
|
|
||||||
private var hasCheckedForUpdates = false
|
private var hasCheckedForUpdates = false
|
||||||
private var isCancelling = false
|
private var isCancelling = false
|
||||||
private var hasManuallyStarted = false
|
private var hasManuallyStarted = false
|
||||||
@@ -191,8 +199,15 @@ class LaunchViewModel(private val application: Application): ViewModel() {
|
|||||||
return _uiState.emit(LaunchUIState.Cancelled(LaunchCancelReason.GEODE_NOT_FOUND))
|
return _uiState.emit(LaunchUIState.Cancelled(LaunchCancelReason.GEODE_NOT_FOUND))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if forcing immediate launch, then act as if it's manually started (skips timers)
|
||||||
|
val forceImmediate = launchArguments?.forceLaunch == true
|
||||||
|
if (forceImmediate) {
|
||||||
|
hasManuallyStarted = true
|
||||||
|
}
|
||||||
|
|
||||||
val loadAutomatically = PreferenceUtils.get(application).getBoolean(PreferenceUtils.Key.LOAD_AUTOMATICALLY)
|
val loadAutomatically = PreferenceUtils.get(application).getBoolean(PreferenceUtils.Key.LOAD_AUTOMATICALLY)
|
||||||
if (!loadAutomatically && !hasManuallyStarted) {
|
val forcePause = launchArguments?.forcePause == true
|
||||||
|
if (!hasManuallyStarted && (forcePause || !loadAutomatically)) {
|
||||||
return cancelLaunch(true)
|
return cancelLaunch(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ fun onOpenFileManager(context: Context) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
} catch (e: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
context,
|
context,
|
||||||
context.getText(R.string.no_activity_found), Toast.LENGTH_SHORT
|
context.getText(R.string.no_activity_found), Toast.LENGTH_SHORT
|
||||||
|
|||||||
Reference in new issue
Block a user