mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-24 00:59:21 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
642fdc36ee | ||
|
|
c6155443a2 | ||
|
|
b96561bf33 | ||
|
|
61c018ee0d | ||
|
|
41e3e0a133 | ||
|
|
3853a469cd | ||
|
|
62b31b8924 | ||
|
|
ab1d0c96f4 | ||
|
|
96354096e2 | ||
|
|
25b686f183 | ||
|
|
7694236434 | ||
|
|
7c0f1da312 | ||
|
|
019d9747b5 | ||
|
|
e806ec223a | ||
|
|
9f58e76f97 | ||
|
|
371750a184 | ||
|
|
2494bda4e8 | ||
|
|
b67be09cd3 | ||
|
|
09b1c9bc3a | ||
|
|
483fb56e25 |
No files matched your search
@@ -1,7 +1,3 @@
|
||||
# Geode Android Launcher
|
||||
|
||||
Launches a vanilla copy of Geometry Dash with the Geode loader added.
|
||||
|
||||
## TODO:
|
||||
|
||||
* Figure out better way of packaging libgeode
|
||||
@@ -1,98 +0,0 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'org.jetbrains.kotlin.android'
|
||||
id 'org.jetbrains.kotlin.plugin.serialization'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.geode.launcher"
|
||||
minSdk 23
|
||||
targetSdk 34
|
||||
versionCode 10
|
||||
versionName "1.2.0"
|
||||
|
||||
vectorDrawables {
|
||||
useSupportLibrary true
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DUSE_TULIPHOOK:BOOL=OFF"
|
||||
}
|
||||
}
|
||||
|
||||
ndkConfig.abiFilters "arm64-v8a", "armeabi-v7a"
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
|
||||
//noinspection ChromeOsAbiSupport (not my fault)
|
||||
include "arm64-v8a", "armeabi-v7a"
|
||||
|
||||
universalApk true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
// enables a polyfill for java Instant on api levels < 26 (used for updater)
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
buildFeatures {
|
||||
compose true
|
||||
buildConfig true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion compose_compiler
|
||||
}
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += '/META-INF/{AL2.0,LGPL2.1}'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path file('src/main/cpp/CMakeLists.txt')
|
||||
}
|
||||
}
|
||||
namespace 'com.geode.launcher'
|
||||
ndkVersion '26.1.10909125'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform('androidx.compose:compose-bom:2023.10.01')
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation "androidx.compose.ui:ui"
|
||||
implementation 'androidx.compose.material3:material3'
|
||||
implementation "androidx.compose.ui:ui-tooling-preview"
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0"
|
||||
implementation 'androidx.activity:activity-compose:1.8.2'
|
||||
implementation 'androidx.activity:activity-ktx:1.8.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation "androidx.documentfile:documentfile:1.0.1"
|
||||
implementation "com.squareup.okio:okio:3.7.0"
|
||||
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-datetime:0.5.0'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.6.0"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling"
|
||||
debugImplementation "androidx.compose.ui:ui-test-manifest"
|
||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
val composeCompiler: String by rootProject.extra
|
||||
val composeBOM: String by rootProject.extra
|
||||
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.serialization")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.geode.launcher"
|
||||
minSdk = 23
|
||||
targetSdk = 34
|
||||
versionCode = 11
|
||||
versionName = "1.2.1"
|
||||
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments("-DUSE_TULIPHOOK:BOOL=OFF")
|
||||
}
|
||||
}
|
||||
|
||||
//noinspection ChromeOsAbiSupport (not my fault)
|
||||
ndk.abiFilters += listOf("arm64-v8a", "armeabi-v7a")
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = true
|
||||
reset()
|
||||
|
||||
//noinspection ChromeOsAbiSupport. i'm sorry!
|
||||
include("arm64-v8a", "armeabi-v7a")
|
||||
|
||||
isUniversalApk = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
// enables a polyfill for java Instant on api levels < 26 (used for updater)
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = composeCompiler
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
}
|
||||
}
|
||||
namespace = "com.geode.launcher"
|
||||
ndkVersion = "26.1.10909125"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation (platform("androidx.compose:compose-bom:$composeBOM"))
|
||||
implementation ("androidx.core:core-ktx:1.12.0")
|
||||
implementation ("androidx.compose.ui:ui")
|
||||
implementation ("androidx.compose.material3:material3")
|
||||
implementation ("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
|
||||
implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
|
||||
implementation ("androidx.activity:activity-compose:1.8.2")
|
||||
implementation ("androidx.activity:activity-ktx:1.8.2")
|
||||
implementation ("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation ("androidx.documentfile:documentfile:1.0.1")
|
||||
implementation ("com.squareup.okio:okio:3.7.0")
|
||||
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
|
||||
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
|
||||
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.6.2")
|
||||
implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0")
|
||||
debugImplementation ("androidx.compose.ui:ui-tooling")
|
||||
coreLibraryDesugaring ("com.android.tools:desugar_jdk_libs:2.0.4")
|
||||
}
|
||||
@@ -23,14 +23,14 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
@@ -51,9 +51,11 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
@@ -165,12 +167,16 @@ fun SelectLogLevelDialog(
|
||||
fun LogCard(logLine: LogLine, modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val haptics = LocalHapticFeedback.current
|
||||
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(8.dp)
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
onLongPress = {
|
||||
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
copyLogText(context, logLine.asSimpleString)
|
||||
}
|
||||
)
|
||||
@@ -248,7 +254,7 @@ fun ApplicationLogsScreen(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(R.string.back_icon_alt)
|
||||
)
|
||||
}
|
||||
@@ -396,7 +402,7 @@ fun ApplicationLogsScreen(
|
||||
logLines
|
||||
) { line ->
|
||||
LogCard(line)
|
||||
Divider()
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.geode.launcher
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Intent
|
||||
@@ -8,7 +7,6 @@ import android.content.IntentFilter
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
@@ -37,7 +35,7 @@ import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
|
||||
|
||||
class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperListener {
|
||||
class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperListener, GeodeUtils.CapabilityListener {
|
||||
private var mGLSurfaceView: Cocos2dxGLSurfaceView? = null
|
||||
private val sTag = GeometryDashActivity::class.simpleName
|
||||
private var mIsRunning = false
|
||||
@@ -109,7 +107,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
setupRedirection(gdPackageInfo)
|
||||
|
||||
Cocos2dxHelper.init(this, this)
|
||||
|
||||
GeodeUtils.setContext(this)
|
||||
GeodeUtils.setCapabilityListener(this)
|
||||
|
||||
tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME)
|
||||
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
|
||||
@@ -450,4 +450,13 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCapabilityAdded(capability: String): Boolean {
|
||||
if (capability == GeodeUtils.CAPABILITY_EXTENDED_INPUT) {
|
||||
mGLSurfaceView?.useKeyboardEvents = true
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.PressInteraction
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -27,8 +29,11 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.pluralStringResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -46,7 +51,10 @@ import com.geode.launcher.utils.Constants
|
||||
import com.geode.launcher.utils.LaunchUtils
|
||||
import com.geode.launcher.utils.PreferenceUtils
|
||||
import com.geode.launcher.updater.ReleaseManager
|
||||
import com.geode.launcher.utils.GeodeUtils
|
||||
import com.geode.launcher.utils.useCountdownTimer
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import java.net.ConnectException
|
||||
import java.net.UnknownHostException
|
||||
|
||||
@@ -122,7 +130,7 @@ fun LauncherUpdateIndicator(modifier: Modifier = Modifier, openTo: String, onDis
|
||||
|
||||
Spacer(Modifier.size(4.dp))
|
||||
|
||||
TextButton(onClick = { uriHandler.openUri(openTo) },) {
|
||||
TextButton(onClick = { uriHandler.openUri(openTo) }) {
|
||||
Text(stringResource(R.string.launcher_download))
|
||||
}
|
||||
}
|
||||
@@ -137,7 +145,7 @@ fun UpdateProgressIndicator(
|
||||
message: String,
|
||||
releaseViewModel: ReleaseViewModel,
|
||||
modifier: Modifier = Modifier,
|
||||
progress: Float? = null
|
||||
progress: (() -> Float)? = null
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
@@ -151,7 +159,7 @@ fun UpdateProgressIndicator(
|
||||
if (progress == null) {
|
||||
LinearProgressIndicator()
|
||||
} else {
|
||||
LinearProgressIndicator(progress)
|
||||
LinearProgressIndicator(progress = progress)
|
||||
}
|
||||
|
||||
TextButton(
|
||||
@@ -226,21 +234,21 @@ fun LoadFailedDialog(returnTitle: String, returnMessage: String) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpdateWarning() {
|
||||
fun UpdateWarning(inSafeMode: Boolean = false, onDismiss: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val packageManager = context.packageManager
|
||||
|
||||
val gdVersionCode = remember { LaunchUtils.getGeometryDashVersionCode(packageManager) }
|
||||
val gdVersionString = remember { LaunchUtils.getGeometryDashVersionString(packageManager) }
|
||||
|
||||
var showUpdateWarning by remember { mutableStateOf(true) }
|
||||
|
||||
var lastDismissedVersion by PreferenceUtils.useLongPreference(
|
||||
preferenceKey = PreferenceUtils.Key.DISMISSED_GJ_UPDATE
|
||||
)
|
||||
|
||||
if (gdVersionCode != Constants.SUPPORTED_VERSION_CODE && gdVersionCode != lastDismissedVersion) {
|
||||
if (showUpdateWarning) {
|
||||
val canDismissRelease = gdVersionCode >= Constants.SUPPORTED_VERSION_CODE
|
||||
val shouldDismiss = canDismissRelease && gdVersionCode == lastDismissedVersion
|
||||
|
||||
if (gdVersionCode != Constants.SUPPORTED_VERSION_CODE && !shouldDismiss) {
|
||||
AlertDialog(
|
||||
icon = {
|
||||
Icon(
|
||||
@@ -264,29 +272,53 @@ fun UpdateWarning() {
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showUpdateWarning = false
|
||||
onDismiss()
|
||||
lastDismissedVersion = gdVersionCode
|
||||
|
||||
onLaunch(context)
|
||||
onLaunch(context, inSafeMode)
|
||||
}) {
|
||||
Text(stringResource(R.string.message_box_accept))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showUpdateWarning = false }) {
|
||||
TextButton(onClick = { onDismiss() }) {
|
||||
Text(stringResource(R.string.message_box_cancel))
|
||||
}
|
||||
},
|
||||
onDismissRequest = { showUpdateWarning = false }
|
||||
onDismissRequest = { onDismiss() }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
LaunchedEffect(gdVersionCode) {
|
||||
onLaunch(context)
|
||||
onLaunch(context, inSafeMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SafeModeDialog(onDismiss: () -> Unit, onLaunch: () -> Unit) {
|
||||
AlertDialog(
|
||||
icon = {
|
||||
Icon(
|
||||
Icons.Filled.Warning,
|
||||
contentDescription = stringResource(R.string.launcher_warning_icon_alt)
|
||||
)
|
||||
},
|
||||
title = { Text(stringResource(R.string.safe_mode_enable_title)) },
|
||||
text = { Text(stringResource(R.string.safe_mode_enable_description)) },
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.message_box_cancel))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onLaunch) {
|
||||
Text(stringResource(R.string.message_box_continue))
|
||||
}
|
||||
},
|
||||
onDismissRequest = onDismiss
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
@@ -317,8 +349,6 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
|
||||
)
|
||||
}
|
||||
is ReleaseViewModel.ReleaseUIState.InDownload -> {
|
||||
val progress = state.downloaded / state.outOf.toDouble()
|
||||
|
||||
val downloaded = remember(state.downloaded) {
|
||||
formatShortFileSize(context, state.downloaded)
|
||||
}
|
||||
@@ -335,7 +365,10 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
|
||||
),
|
||||
modifier = modifier,
|
||||
releaseViewModel = releaseViewModel,
|
||||
progress = progress.toFloat()
|
||||
progress = {
|
||||
val progress = state.downloaded / state.outOf.toDouble()
|
||||
progress.toFloat()
|
||||
}
|
||||
)
|
||||
}
|
||||
is ReleaseViewModel.ReleaseUIState.InUpdateCheck -> {
|
||||
@@ -389,7 +422,13 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
|
||||
}
|
||||
}
|
||||
|
||||
fun onLaunch(context: Context) {
|
||||
fun onLaunch(context: Context, safeMode: Boolean = false) {
|
||||
if (safeMode) {
|
||||
GeodeUtils.setAdditionalLaunchArguments(GeodeUtils.ARGUMENT_SAFE_MODE)
|
||||
} else {
|
||||
GeodeUtils.clearLaunchArguments()
|
||||
}
|
||||
|
||||
val launchIntent = Intent(context, GeometryDashActivity::class.java)
|
||||
launchIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
||||
@@ -410,7 +449,7 @@ fun MainScreen(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
val shouldAutomaticallyLaunch = PreferenceUtils.useBooleanPreference(
|
||||
val shouldAutomaticallyLaunch by PreferenceUtils.useBooleanPreference(
|
||||
preferenceKey = PreferenceUtils.Key.LOAD_AUTOMATICALLY
|
||||
)
|
||||
|
||||
@@ -423,6 +462,8 @@ fun MainScreen(
|
||||
val geodeInstalled = geodePreinstalled || geodeJustInstalled
|
||||
|
||||
var beginLaunch by remember { mutableStateOf(false) }
|
||||
var showSafeModeDialog by remember { mutableStateOf(false) }
|
||||
var launchInSafeMode by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(shouldUpdate) {
|
||||
if (shouldUpdate && !releaseViewModel.hasPerformedCheck) {
|
||||
@@ -451,15 +492,11 @@ fun MainScreen(
|
||||
)
|
||||
|
||||
if (gdInstalled && geodeInstalled) {
|
||||
if (shouldAutomaticallyLaunch.value && !releaseViewModel.isInUpdate && !disableAutomaticLaunch) {
|
||||
val stopLaunch = releaseViewModel.isInUpdate || disableAutomaticLaunch || showSafeModeDialog
|
||||
if (shouldAutomaticallyLaunch && !stopLaunch) {
|
||||
val countdownTimer = useCountdownTimer(
|
||||
time = 3000,
|
||||
onCountdownFinish = {
|
||||
// just in case this changed async
|
||||
if (shouldAutomaticallyLaunch.value) {
|
||||
beginLaunch = true
|
||||
}
|
||||
}
|
||||
onCountdownFinish = { beginLaunch = true }
|
||||
)
|
||||
|
||||
if (countdownTimer != 0L) {
|
||||
@@ -476,10 +513,42 @@ fun MainScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// compose apis don't provide a good way of adding long press to a button
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val viewConfiguration = LocalViewConfiguration.current
|
||||
val haptics = LocalHapticFeedback.current
|
||||
|
||||
LaunchedEffect(interactionSource) {
|
||||
interactionSource.interactions.collectLatest { interaction ->
|
||||
when (interaction) {
|
||||
is PressInteraction.Press -> {
|
||||
launchInSafeMode = false
|
||||
|
||||
delay(viewConfiguration.longPressTimeoutMillis)
|
||||
|
||||
// perform a second delay to make the action more obvious
|
||||
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
delay(viewConfiguration.longPressTimeoutMillis)
|
||||
|
||||
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
|
||||
showSafeModeDialog = true
|
||||
}
|
||||
|
||||
is PressInteraction.Release -> {
|
||||
if (!showSafeModeDialog) {
|
||||
beginLaunch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
Button(
|
||||
onClick = { beginLaunch = true },
|
||||
enabled = !releaseViewModel.isInUpdate
|
||||
onClick = { },
|
||||
enabled = !releaseViewModel.isInUpdate,
|
||||
interactionSource = interactionSource
|
||||
) {
|
||||
Icon(
|
||||
Icons.Filled.PlayArrow,
|
||||
@@ -545,7 +614,24 @@ fun MainScreen(
|
||||
}
|
||||
|
||||
if (beginLaunch) {
|
||||
UpdateWarning()
|
||||
UpdateWarning(launchInSafeMode) {
|
||||
beginLaunch = false
|
||||
launchInSafeMode = false
|
||||
}
|
||||
}
|
||||
|
||||
if (showSafeModeDialog) {
|
||||
SafeModeDialog(
|
||||
onDismiss = {
|
||||
showSafeModeDialog = false
|
||||
},
|
||||
onLaunch = {
|
||||
launchInSafeMode = true
|
||||
beginLaunch = true
|
||||
|
||||
showSafeModeDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
@@ -145,7 +145,9 @@ fun UpdateIndicator(
|
||||
enablePopup = true
|
||||
val progress = updateStatus.downloaded / updateStatus.outOf.toDouble()
|
||||
|
||||
CircularProgressIndicator(progress.toFloat())
|
||||
CircularProgressIndicator(
|
||||
progress = { progress.toFloat() },
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
@@ -259,7 +261,7 @@ fun SettingsScreen(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = context.getString(R.string.back_icon_alt)
|
||||
)
|
||||
}
|
||||
@@ -351,10 +353,6 @@ fun SettingsScreen(
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}<>[]?:;'\"~`-_+=\\| ".contains(c)
|
||||
}}
|
||||
)
|
||||
SettingsCard(
|
||||
title = stringResource(R.string.preference_ignore_load_failure_name),
|
||||
preferenceKey = PreferenceUtils.Key.IGNORE_LOAD_FAILURE,
|
||||
)
|
||||
OptionsButton(
|
||||
title = context.getString(R.string.preferences_copy_external_button),
|
||||
description = LaunchUtils.getBaseDirectory(context).path,
|
||||
|
||||
@@ -11,8 +11,12 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.toggleable
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Clear
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.RadioButton
|
||||
@@ -173,6 +177,16 @@ fun StringDialog(
|
||||
keyboardType = KeyboardType.Ascii,
|
||||
autoCorrect = false
|
||||
),
|
||||
trailingIcon = {
|
||||
if (enteredValue.isNotEmpty()) {
|
||||
IconButton(onClick = { enteredValue = "" }) {
|
||||
Icon(
|
||||
Icons.Filled.Clear,
|
||||
contentDescription = stringResource(R.string.preference_text_clear)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
|
||||
@@ -3,11 +3,13 @@ package com.geode.launcher
|
||||
import android.database.Cursor
|
||||
import android.database.MatrixCursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.CancellationSignal
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.DocumentsProvider
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.geode.launcher.utils.LaunchUtils
|
||||
import java.io.File
|
||||
|
||||
@@ -150,7 +152,7 @@ class UserDirectoryProvider : DocumentsProvider() {
|
||||
notifyFileChange(file)
|
||||
}
|
||||
|
||||
override fun renameDocument(documentId: String, displayName: String): String? {
|
||||
override fun renameDocument(documentId: String, displayName: String): String {
|
||||
val file = getFileForDocumentId(documentId)
|
||||
val dest = findFileNameForNewFile(File(file.parentFile, displayName))
|
||||
file.renameTo(dest)
|
||||
@@ -203,4 +205,19 @@ class UserDirectoryProvider : DocumentsProvider() {
|
||||
val uri = getDocumentUri(parentDocumentId)
|
||||
context!!.contentResolver.notifyChange(uri, null)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun findDocumentPath(
|
||||
parentDocumentId: String?,
|
||||
childDocumentId: String
|
||||
): DocumentsContract.Path {
|
||||
if (!parentDocumentId.isNullOrEmpty()) {
|
||||
// not implementing this for now...
|
||||
return super.findDocumentPath(parentDocumentId, childDocumentId)
|
||||
}
|
||||
|
||||
val nonRootPath = listOf(ROOT) + childDocumentId.removePrefix(ROOT).split("/")
|
||||
|
||||
return DocumentsContract.Path(ROOT, nonRootPath)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,9 @@ import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
@@ -17,7 +19,9 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.geode.launcher.BuildConfig
|
||||
import com.geode.launcher.R
|
||||
import com.geode.launcher.UserDirectoryProvider
|
||||
import com.geode.launcher.activityresult.GeodeOpenFileActivityResult
|
||||
import com.geode.launcher.activityresult.GeodeOpenFilesActivityResult
|
||||
import com.geode.launcher.activityresult.GeodeSaveFileActivityResult
|
||||
@@ -34,6 +38,7 @@ object GeodeUtils {
|
||||
private lateinit var saveFileResultLauncher: ActivityResultLauncher<GeodeSaveFileActivityResult.SaveFileParams>
|
||||
private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
|
||||
private lateinit var internalRequestPermissionsLauncher: ActivityResultLauncher<Array<String>>
|
||||
private lateinit var internalRequestAllFilesLauncher: ActivityResultLauncher<Intent>
|
||||
|
||||
private var afterRequestPermissions: (() -> Unit)? = null
|
||||
private var afterRequestPermissionsFailure: (() -> Unit)? = null
|
||||
@@ -93,12 +98,26 @@ object GeodeUtils {
|
||||
if (isGranted) {
|
||||
afterRequestPermissions?.invoke()
|
||||
} else {
|
||||
Toast.makeText(activity, activity.getString(R.string.missing_permissions), Toast.LENGTH_SHORT)
|
||||
Toast.makeText(activity, R.string.missing_permissions, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
|
||||
afterRequestPermissionsFailure?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
// only necessary on newer android versions
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
|
||||
internalRequestAllFilesLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { _ ->
|
||||
if (Environment.isExternalStorageManager()) {
|
||||
afterRequestPermissions?.invoke()
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.missing_permissions, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
|
||||
afterRequestPermissionsFailure?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@@ -149,21 +168,48 @@ object GeodeUtils {
|
||||
// TODO As of now this is unused
|
||||
@JvmStatic
|
||||
fun openFolder(path: String): Boolean {
|
||||
activity.get()?.run {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
DocumentFile.fromFile(File(path)).also {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, it.uri)
|
||||
}
|
||||
intent.setType("*/*")
|
||||
val context = activity.get()!!
|
||||
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
startActivity(intent)
|
||||
return true
|
||||
val pathFile = File(path)
|
||||
val baseDirectory = LaunchUtils.getBaseDirectory(context)
|
||||
val isInternalPath = pathFile.startsWith(baseDirectory)
|
||||
|
||||
val intent = if (isInternalPath) {
|
||||
// TODO: figure out how to get this to point to the path it should be pointing at
|
||||
// (the best i got was pointing at a file)
|
||||
// val relativePath = pathFile.relativeTo(baseDirectory)
|
||||
|
||||
Intent(Intent.ACTION_VIEW).apply {
|
||||
data = DocumentsContract.buildRootUri(
|
||||
"${context.packageName}.user", UserDirectoryProvider.ROOT
|
||||
)
|
||||
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val documentFile = DocumentFile.fromFile(File(path))
|
||||
putExtra(DocumentsContract.EXTRA_INITIAL_URI, documentFile.uri)
|
||||
}
|
||||
|
||||
type = "*/*"
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
return try {
|
||||
context.startActivity(intent)
|
||||
true
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private external fun selectFileCallback(path: String)
|
||||
@@ -173,18 +219,24 @@ object GeodeUtils {
|
||||
private external fun failedCallback()
|
||||
|
||||
private fun checkForFilePermissions(onSuccess: () -> Unit, onFailure: () -> Unit) {
|
||||
val permissions = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
|
||||
listOf(
|
||||
Manifest.permission.READ_MEDIA_AUDIO,
|
||||
Manifest.permission.READ_MEDIA_IMAGES,
|
||||
Manifest.permission.READ_MEDIA_VIDEO
|
||||
)
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
|
||||
if (Environment.isExternalStorageManager()) {
|
||||
onSuccess()
|
||||
} else {
|
||||
listOf(
|
||||
val intent = Intent(
|
||||
ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
|
||||
Uri.fromParts("package", BuildConfig.APPLICATION_ID, null)
|
||||
)
|
||||
|
||||
internalRequestAllFilesLauncher.launch(intent)
|
||||
afterRequestPermissions = onSuccess
|
||||
afterRequestPermissionsFailure = onFailure
|
||||
}
|
||||
} else {
|
||||
val permissions = listOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
}
|
||||
|
||||
val context = activity.get()!!
|
||||
|
||||
@@ -195,10 +247,12 @@ object GeodeUtils {
|
||||
if (needsPermissions.isNotEmpty()) {
|
||||
internalRequestPermissionsLauncher.launch(needsPermissions.toTypedArray())
|
||||
afterRequestPermissions = onSuccess
|
||||
afterRequestPermissionsFailure = onFailure
|
||||
} else {
|
||||
onSuccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun selectFile(path: String): Boolean {
|
||||
@@ -317,32 +371,53 @@ object GeodeUtils {
|
||||
return "com.geode.launcher.user" == uri.authority
|
||||
}
|
||||
|
||||
private const val INTERNAL_PERMISSION_PREFIX = "geode.permission_internal"
|
||||
private const val MANAGE_ALL_FILES = "${INTERNAL_PERMISSION_PREFIX}.MANAGE_ALL_FILES"
|
||||
|
||||
@JvmStatic
|
||||
fun getPermissionStatus(permission: String): Boolean {
|
||||
return ContextCompat.checkSelfPermission(
|
||||
activity.get()?.applicationContext ?: return false,
|
||||
permission
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
val context = activity.get() ?: return false
|
||||
|
||||
return when (permission) {
|
||||
MANAGE_ALL_FILES -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2) {
|
||||
Environment.isExternalStorageManager()
|
||||
} else {
|
||||
val permissions = listOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
|
||||
return permissions.all {
|
||||
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
else -> ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun requestPermission(permission: String) {
|
||||
if (permission == MANAGE_ALL_FILES) {
|
||||
// this function handles already having perms for us
|
||||
checkForFilePermissions(onSuccess = {
|
||||
permissionCallback(true)
|
||||
}, onFailure = {
|
||||
permissionCallback(false)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (getPermissionStatus(permission)) {
|
||||
permissionCallback(true)
|
||||
return
|
||||
}
|
||||
|
||||
activity.get()?.run {
|
||||
try {
|
||||
requestPermissionLauncher.launch(permission)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
permissionCallback(false)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
permissionCallback(false)
|
||||
}
|
||||
|
||||
private external fun permissionCallback(granted: Boolean)
|
||||
@@ -371,4 +446,30 @@ object GeodeUtils {
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
interface CapabilityListener {
|
||||
fun onCapabilityAdded(capability: String): Boolean
|
||||
}
|
||||
|
||||
const val CAPABILITY_EXTENDED_INPUT = "extended_input"
|
||||
|
||||
private var capabilityListener: WeakReference<CapabilityListener?> = WeakReference(null)
|
||||
|
||||
fun setCapabilityListener(listener: CapabilityListener) {
|
||||
capabilityListener = WeakReference(listener)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun reportPlatformCapability(capability: String?): Boolean {
|
||||
if (capability.isNullOrEmpty()) {
|
||||
return false
|
||||
}
|
||||
|
||||
return capabilityListener.get()?.onCapabilityAdded(capability) ?: false
|
||||
}
|
||||
|
||||
external fun nativeKeyUp(keyCode: Int, modifiers: Int)
|
||||
external fun nativeKeyDown(keyCode: Int, modifiers: Int, isRepeating: Boolean)
|
||||
external fun nativeActionScroll(scrollX: Float, scrollY: Float)
|
||||
external fun resizeSurface(width: Int, height: Int)
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import com.customRobTop.BaseRobTopActivity
|
||||
import com.geode.launcher.utils.GeodeUtils
|
||||
|
||||
private const val HANDLER_OPEN_IME_KEYBOARD = 2
|
||||
private const val HANDLER_CLOSE_IME_KEYBOARD = 3
|
||||
@@ -37,6 +38,11 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
||||
}
|
||||
|
||||
private lateinit var cocos2dxRenderer: Cocos2dxRenderer
|
||||
var useKeyboardEvents = false
|
||||
set(value) {
|
||||
field = value
|
||||
cocos2dxRenderer.sendResizeEvents = value
|
||||
}
|
||||
|
||||
var cocos2dxEditText: Cocos2dxEditText? = null
|
||||
set(value) {
|
||||
@@ -207,7 +213,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
|
||||
private fun legacyKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
|
||||
return when (keyCode) {
|
||||
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> {
|
||||
if (keyEvent.repeatCount != 0 || BaseRobTopActivity.blockBackButton) {
|
||||
@@ -220,6 +226,71 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (!useKeyboardEvents) {
|
||||
return legacyKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
return when (keyCode) {
|
||||
// ignore system keys
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_MUTE -> {
|
||||
super.onKeyDown(keyCode, event)
|
||||
}
|
||||
else -> {
|
||||
if (BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
queueEvent {
|
||||
GeodeUtils.nativeKeyDown(keyCode, event.modifiers, event.repeatCount != 0)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (!useKeyboardEvents) {
|
||||
return super.onKeyUp(keyCode, event)
|
||||
}
|
||||
|
||||
return when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_MUTE -> {
|
||||
super.onKeyUp(keyCode, event)
|
||||
}
|
||||
else -> {
|
||||
if (event.repeatCount != 0 || BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
queueEvent {
|
||||
GeodeUtils.nativeKeyUp(keyCode, event.modifiers)
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGenericMotionEvent(event: MotionEvent?): Boolean {
|
||||
if (!useKeyboardEvents) {
|
||||
return super.onGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
if (event?.action == MotionEvent.ACTION_SCROLL) {
|
||||
val scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL)
|
||||
val scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
|
||||
queueEvent {
|
||||
GeodeUtils.nativeActionScroll(scrollX, scrollY)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return super.onGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
fun setCocos2dxRenderer(renderer: Cocos2dxRenderer) {
|
||||
this.cocos2dxRenderer = renderer
|
||||
setRenderer(this.cocos2dxRenderer)
|
||||
|
||||
@@ -59,6 +59,7 @@ class Cocos2dxRenderer : GLSurfaceView.Renderer {
|
||||
private var lastTickInNanoSeconds: Long = 0
|
||||
private var screenWidth = 0
|
||||
private var screenHeight = 0
|
||||
var sendResizeEvents = false
|
||||
|
||||
fun setScreenWidthAndHeight(surfaceWidth: Int, surfaceHeight: Int) {
|
||||
screenWidth = surfaceWidth
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
</string>
|
||||
<string name="launcher_update_available">A new launcher update is available!</string>
|
||||
<string name="launcher_update_dismiss">Dismiss</string>
|
||||
<string name="safe_mode_enable_title">Enable safe mode?</string>
|
||||
<string name="safe_mode_enable_description">Safe mode disables loading of all mods, allowing one to remove problematic mods or diagnose crashes. If you\'re not experiencing issues with Geode, you should probably not be using safe mode.</string>
|
||||
|
||||
<!-- Updater strings -->
|
||||
<string name="release_fetch_failed">Update failed.\n%1$s</string>
|
||||
@@ -53,6 +55,7 @@
|
||||
<string name="load_failed_generic_error_description">Uncaught error during game load: `%1$s`.\nCheck application logs for more details.</string>
|
||||
<string name="message_box_cancel">Cancel</string>
|
||||
<string name="message_box_accept">OK</string>
|
||||
<string name="message_box_continue">Continue</string>
|
||||
<string name="missing_permissions">Missing required permissions for this action!</string>
|
||||
|
||||
<!-- SettingsActivity strings -->
|
||||
@@ -95,6 +98,7 @@
|
||||
<string name="preference_launch_arguments_name">Launch arguments</string>
|
||||
<string name="preference_launch_arguments_set_title">Set launch arguments</string>
|
||||
<string name="preference_ignore_load_failure_name">Ignore loader errors</string>
|
||||
<string name="preference_text_clear">Clear</string>
|
||||
|
||||
<!-- ApplicationLogsActivity strings -->
|
||||
<string name="title_activity_application_logs">Application Logs</string>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
buildscript {
|
||||
ext {
|
||||
compose_compiler = '1.5.8'
|
||||
}
|
||||
}
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '8.2.2' apply false
|
||||
id 'com.android.library' version '8.2.2' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.9.22' apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22' apply false
|
||||
}
|
||||
|
||||
tasks.register('clean', Delete) {
|
||||
delete rootProject.layout.buildDirectory
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
buildscript {
|
||||
val composeBOM by extra("2024.02.00")
|
||||
val composeCompiler by extra("1.5.9")
|
||||
}
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.2" apply false
|
||||
id("com.android.library") version "8.2.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22" apply false
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
delete(rootProject.layout.buildDirectory)
|
||||
}
|
||||
@@ -21,3 +21,5 @@ kotlin.code.style=official
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
# nice optimization
|
||||
org.gradle.configuration-cache=true
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
@@ -5,6 +5,8 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
@@ -12,5 +14,6 @@ dependencyResolutionManagement {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "launcher"
|
||||
include ':app'
|
||||
include(":app")
|
||||
Reference in new issue
Block a user