Compare commits

...
20 Commits
Author SHA1 Message Date
qimiko 642fdc36ee cleanup motionevent 2024-02-11 00:55:33 -07:00
qimiko c6155443a2 platformcapability changed to bool 2024-02-10 22:07:22 -07:00
qimiko b96561bf33 add keyboard input support 2024-02-10 18:07:06 -07:00
qimiko 61c018ee0d delay launch with safe mode dialog 2024-02-08 22:11:33 -07:00
qimiko 41e3e0a133 package maintenance 2024-02-07 22:19:10 -07:00
qimiko 3853a469cd add internal request all files perm 2024-02-04 12:46:25 -07:00
qimiko 62b31b8924 hide load failure option 2024-02-03 18:32:14 -07:00
qimiko ab1d0c96f4 have internal links open storage provider 2024-02-03 18:30:54 -07:00
qimiko 96354096e2 minor update 2024-02-03 18:08:31 -07:00
qimiko 25b686f183 fix update warning dismiss 2024-02-03 18:06:07 -07:00
qimiko 7694236434 disallow ignoring older gd version warning 2024-02-03 18:03:40 -07:00
qimiko 7c0f1da312 clear button for text inputs 2024-02-03 05:28:19 -07:00
qimiko 019d9747b5 add ui to launch in safe mode
closes #17
2024-02-02 02:06:43 -07:00
qimiko e806ec223a use all files permission on newer devices 2024-02-02 00:04:07 -07:00
qimiko 9f58e76f97 maintenance 2 2024-02-01 13:15:30 -07:00
qimiko 371750a184 enable gradle cache 2024-01-31 16:51:09 -07:00
qimiko 2494bda4e8 send vibration on log long press 2024-01-30 03:17:23 -07:00
qimiko b67be09cd3 update readme 2024-01-30 02:22:49 -07:00
qimiko 09b1c9bc3a forgot to set failure callback :( 2024-01-30 02:20:48 -07:00
qimiko 483fb56e25 feeling gradle 2024-01-30 02:20:48 -07:00
20 changed files with 561 additions and 249 deletions

No files matched your search

-4
View File
@@ -1,7 +1,3 @@
# 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.
## TODO:
* Figure out better way of packaging libgeode
-98
View File
@@ -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"
}
+103
View File
@@ -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.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons 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.Check
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.Divider
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
@@ -51,9 +51,11 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
@@ -165,12 +167,16 @@ fun SelectLogLevelDialog(
fun LogCard(logLine: LogLine, modifier: Modifier = Modifier) { fun LogCard(logLine: LogLine, modifier: Modifier = Modifier) {
val context = LocalContext.current val context = LocalContext.current
val haptics = LocalHapticFeedback.current
Column( Column(
modifier = modifier modifier = modifier
.padding(8.dp) .padding(8.dp)
.pointerInput(Unit) { .pointerInput(Unit) {
detectTapGestures( detectTapGestures(
onLongPress = { onLongPress = {
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
copyLogText(context, logLine.asSimpleString) copyLogText(context, logLine.asSimpleString)
} }
) )
@@ -248,7 +254,7 @@ fun ApplicationLogsScreen(
navigationIcon = { navigationIcon = {
IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) { IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
Icon( Icon(
imageVector = Icons.Filled.ArrowBack, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.back_icon_alt) contentDescription = stringResource(R.string.back_icon_alt)
) )
} }
@@ -396,7 +402,7 @@ fun ApplicationLogsScreen(
logLines logLines
) { line -> ) { line ->
LogCard(line) LogCard(line)
Divider() HorizontalDivider()
} }
} }
} }
@@ -1,6 +1,5 @@
package com.geode.launcher package com.geode.launcher
import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Intent import android.content.Intent
@@ -8,7 +7,6 @@ import android.content.IntentFilter
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
@@ -37,7 +35,7 @@ import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperListener { class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperListener, GeodeUtils.CapabilityListener {
private var mGLSurfaceView: Cocos2dxGLSurfaceView? = null private var mGLSurfaceView: Cocos2dxGLSurfaceView? = null
private val sTag = GeometryDashActivity::class.simpleName private val sTag = GeometryDashActivity::class.simpleName
private var mIsRunning = false private var mIsRunning = false
@@ -109,7 +107,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
setupRedirection(gdPackageInfo) setupRedirection(gdPackageInfo)
Cocos2dxHelper.init(this, this) Cocos2dxHelper.init(this, this)
GeodeUtils.setContext(this) GeodeUtils.setContext(this)
GeodeUtils.setCapabilityListener(this)
tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME) tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME)
tryLoadLibrary(gdPackageInfo, Constants.COCOS_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.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.Image 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.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@@ -27,8 +29,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource 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.LaunchUtils
import com.geode.launcher.utils.PreferenceUtils import com.geode.launcher.utils.PreferenceUtils
import com.geode.launcher.updater.ReleaseManager import com.geode.launcher.updater.ReleaseManager
import com.geode.launcher.utils.GeodeUtils
import com.geode.launcher.utils.useCountdownTimer import com.geode.launcher.utils.useCountdownTimer
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import java.net.ConnectException import java.net.ConnectException
import java.net.UnknownHostException import java.net.UnknownHostException
@@ -122,7 +130,7 @@ fun LauncherUpdateIndicator(modifier: Modifier = Modifier, openTo: String, onDis
Spacer(Modifier.size(4.dp)) Spacer(Modifier.size(4.dp))
TextButton(onClick = { uriHandler.openUri(openTo) },) { TextButton(onClick = { uriHandler.openUri(openTo) }) {
Text(stringResource(R.string.launcher_download)) Text(stringResource(R.string.launcher_download))
} }
} }
@@ -137,7 +145,7 @@ fun UpdateProgressIndicator(
message: String, message: String,
releaseViewModel: ReleaseViewModel, releaseViewModel: ReleaseViewModel,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
progress: Float? = null progress: (() -> Float)? = null
) { ) {
Column( Column(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@@ -151,7 +159,7 @@ fun UpdateProgressIndicator(
if (progress == null) { if (progress == null) {
LinearProgressIndicator() LinearProgressIndicator()
} else { } else {
LinearProgressIndicator(progress) LinearProgressIndicator(progress = progress)
} }
TextButton( TextButton(
@@ -226,67 +234,91 @@ fun LoadFailedDialog(returnTitle: String, returnMessage: String) {
} }
@Composable @Composable
fun UpdateWarning() { fun UpdateWarning(inSafeMode: Boolean = false, onDismiss: () -> Unit) {
val context = LocalContext.current val context = LocalContext.current
val packageManager = context.packageManager val packageManager = context.packageManager
val gdVersionCode = remember { LaunchUtils.getGeometryDashVersionCode(packageManager) } val gdVersionCode = remember { LaunchUtils.getGeometryDashVersionCode(packageManager) }
val gdVersionString = remember { LaunchUtils.getGeometryDashVersionString(packageManager) } val gdVersionString = remember { LaunchUtils.getGeometryDashVersionString(packageManager) }
var showUpdateWarning by remember { mutableStateOf(true) }
var lastDismissedVersion by PreferenceUtils.useLongPreference( var lastDismissedVersion by PreferenceUtils.useLongPreference(
preferenceKey = PreferenceUtils.Key.DISMISSED_GJ_UPDATE preferenceKey = PreferenceUtils.Key.DISMISSED_GJ_UPDATE
) )
if (gdVersionCode != Constants.SUPPORTED_VERSION_CODE && gdVersionCode != lastDismissedVersion) { val canDismissRelease = gdVersionCode >= Constants.SUPPORTED_VERSION_CODE
if (showUpdateWarning) { val shouldDismiss = canDismissRelease && gdVersionCode == lastDismissedVersion
AlertDialog(
icon = {
Icon(
Icons.Filled.Warning,
contentDescription = stringResource(R.string.launcher_warning_icon_alt)
)
},
title = {
Text(stringResource(R.string.launcher_unsupported_version_title))
},
text = {
val message = if (gdVersionCode >= Constants.SUPPORTED_VERSION_CODE) {
R.string.launcher_unsupported_version_description
} else {
R.string.launcher_outdated_version_description
}
Text(stringResource( if (gdVersionCode != Constants.SUPPORTED_VERSION_CODE && !shouldDismiss) {
message, gdVersionString, Constants.SUPPORTED_VERSION_STRING AlertDialog(
)) icon = {
}, Icon(
confirmButton = { Icons.Filled.Warning,
TextButton(onClick = { contentDescription = stringResource(R.string.launcher_warning_icon_alt)
showUpdateWarning = false )
lastDismissedVersion = gdVersionCode },
title = {
Text(stringResource(R.string.launcher_unsupported_version_title))
},
text = {
val message = if (gdVersionCode >= Constants.SUPPORTED_VERSION_CODE) {
R.string.launcher_unsupported_version_description
} else {
R.string.launcher_outdated_version_description
}
onLaunch(context) Text(stringResource(
}) { message, gdVersionString, Constants.SUPPORTED_VERSION_STRING
Text(stringResource(R.string.message_box_accept)) ))
} },
}, confirmButton = {
dismissButton = { TextButton(onClick = {
TextButton(onClick = { showUpdateWarning = false }) { onDismiss()
Text(stringResource(R.string.message_box_cancel)) lastDismissedVersion = gdVersionCode
}
}, onLaunch(context, inSafeMode)
onDismissRequest = { showUpdateWarning = false } }) {
) Text(stringResource(R.string.message_box_accept))
} }
},
dismissButton = {
TextButton(onClick = { onDismiss() }) {
Text(stringResource(R.string.message_box_cancel))
}
},
onDismissRequest = { onDismiss() }
)
} else { } else {
LaunchedEffect(gdVersionCode) { 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 @Composable
fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier) { fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier) {
val context = LocalContext.current val context = LocalContext.current
@@ -317,8 +349,6 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
) )
} }
is ReleaseViewModel.ReleaseUIState.InDownload -> { is ReleaseViewModel.ReleaseUIState.InDownload -> {
val progress = state.downloaded / state.outOf.toDouble()
val downloaded = remember(state.downloaded) { val downloaded = remember(state.downloaded) {
formatShortFileSize(context, state.downloaded) formatShortFileSize(context, state.downloaded)
} }
@@ -335,7 +365,10 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
), ),
modifier = modifier, modifier = modifier,
releaseViewModel = releaseViewModel, releaseViewModel = releaseViewModel,
progress = progress.toFloat() progress = {
val progress = state.downloaded / state.outOf.toDouble()
progress.toFloat()
}
) )
} }
is ReleaseViewModel.ReleaseUIState.InUpdateCheck -> { 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) val launchIntent = Intent(context, GeometryDashActivity::class.java)
launchIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK launchIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
@@ -410,7 +449,7 @@ fun MainScreen(
) { ) {
val context = LocalContext.current val context = LocalContext.current
val shouldAutomaticallyLaunch = PreferenceUtils.useBooleanPreference( val shouldAutomaticallyLaunch by PreferenceUtils.useBooleanPreference(
preferenceKey = PreferenceUtils.Key.LOAD_AUTOMATICALLY preferenceKey = PreferenceUtils.Key.LOAD_AUTOMATICALLY
) )
@@ -423,6 +462,8 @@ fun MainScreen(
val geodeInstalled = geodePreinstalled || geodeJustInstalled val geodeInstalled = geodePreinstalled || geodeJustInstalled
var beginLaunch by remember { mutableStateOf(false) } var beginLaunch by remember { mutableStateOf(false) }
var showSafeModeDialog by remember { mutableStateOf(false) }
var launchInSafeMode by remember { mutableStateOf(false) }
LaunchedEffect(shouldUpdate) { LaunchedEffect(shouldUpdate) {
if (shouldUpdate && !releaseViewModel.hasPerformedCheck) { if (shouldUpdate && !releaseViewModel.hasPerformedCheck) {
@@ -451,15 +492,11 @@ fun MainScreen(
) )
if (gdInstalled && geodeInstalled) { if (gdInstalled && geodeInstalled) {
if (shouldAutomaticallyLaunch.value && !releaseViewModel.isInUpdate && !disableAutomaticLaunch) { val stopLaunch = releaseViewModel.isInUpdate || disableAutomaticLaunch || showSafeModeDialog
if (shouldAutomaticallyLaunch && !stopLaunch) {
val countdownTimer = useCountdownTimer( val countdownTimer = useCountdownTimer(
time = 3000, time = 3000,
onCountdownFinish = { onCountdownFinish = { beginLaunch = true }
// just in case this changed async
if (shouldAutomaticallyLaunch.value) {
beginLaunch = true
}
}
) )
if (countdownTimer != 0L) { 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 { Row {
Button( Button(
onClick = { beginLaunch = true }, onClick = { },
enabled = !releaseViewModel.isInUpdate enabled = !releaseViewModel.isInUpdate,
interactionSource = interactionSource
) { ) {
Icon( Icon(
Icons.Filled.PlayArrow, Icons.Filled.PlayArrow,
@@ -545,7 +614,24 @@ fun MainScreen(
} }
if (beginLaunch) { 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.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons 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.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
@@ -145,7 +145,9 @@ fun UpdateIndicator(
enablePopup = true enablePopup = true
val progress = updateStatus.downloaded / updateStatus.outOf.toDouble() val progress = updateStatus.downloaded / updateStatus.outOf.toDouble()
CircularProgressIndicator(progress.toFloat()) CircularProgressIndicator(
progress = { progress.toFloat() },
)
} }
else -> {} else -> {}
} }
@@ -259,7 +261,7 @@ fun SettingsScreen(
navigationIcon = { navigationIcon = {
IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) { IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
Icon( Icon(
imageVector = Icons.Filled.ArrowBack, imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = context.getString(R.string.back_icon_alt) contentDescription = context.getString(R.string.back_icon_alt)
) )
} }
@@ -351,10 +353,6 @@ fun SettingsScreen(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}<>[]?:;'\"~`-_+=\\| ".contains(c) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}<>[]?:;'\"~`-_+=\\| ".contains(c)
}} }}
) )
SettingsCard(
title = stringResource(R.string.preference_ignore_load_failure_name),
preferenceKey = PreferenceUtils.Key.IGNORE_LOAD_FAILURE,
)
OptionsButton( OptionsButton(
title = context.getString(R.string.preferences_copy_external_button), title = context.getString(R.string.preferences_copy_external_button),
description = LaunchUtils.getBaseDirectory(context).path, 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.selection.toggleable
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions 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.AlertDialog
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton import androidx.compose.material3.RadioButton
@@ -173,6 +177,16 @@ fun StringDialog(
keyboardType = KeyboardType.Ascii, keyboardType = KeyboardType.Ascii,
autoCorrect = false autoCorrect = false
), ),
trailingIcon = {
if (enteredValue.isNotEmpty()) {
IconButton(onClick = { enteredValue = "" }) {
Icon(
Icons.Filled.Clear,
contentDescription = stringResource(R.string.preference_text_clear)
)
}
}
}
) )
}, },
confirmButton = { confirmButton = {
@@ -3,11 +3,13 @@ package com.geode.launcher
import android.database.Cursor import android.database.Cursor
import android.database.MatrixCursor import android.database.MatrixCursor
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.CancellationSignal import android.os.CancellationSignal
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.provider.DocumentsProvider import android.provider.DocumentsProvider
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.annotation.RequiresApi
import com.geode.launcher.utils.LaunchUtils import com.geode.launcher.utils.LaunchUtils
import java.io.File import java.io.File
@@ -150,7 +152,7 @@ class UserDirectoryProvider : DocumentsProvider() {
notifyFileChange(file) notifyFileChange(file)
} }
override fun renameDocument(documentId: String, displayName: String): String? { override fun renameDocument(documentId: String, displayName: String): String {
val file = getFileForDocumentId(documentId) val file = getFileForDocumentId(documentId)
val dest = findFileNameForNewFile(File(file.parentFile, displayName)) val dest = findFileNameForNewFile(File(file.parentFile, displayName))
file.renameTo(dest) file.renameTo(dest)
@@ -203,4 +205,19 @@ class UserDirectoryProvider : DocumentsProvider() {
val uri = getDocumentUri(parentDocumentId) val uri = getDocumentUri(parentDocumentId)
context!!.contentResolver.notifyChange(uri, null) 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.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION
import android.util.Log import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@@ -17,7 +19,9 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.geode.launcher.BuildConfig
import com.geode.launcher.R import com.geode.launcher.R
import com.geode.launcher.UserDirectoryProvider
import com.geode.launcher.activityresult.GeodeOpenFileActivityResult import com.geode.launcher.activityresult.GeodeOpenFileActivityResult
import com.geode.launcher.activityresult.GeodeOpenFilesActivityResult import com.geode.launcher.activityresult.GeodeOpenFilesActivityResult
import com.geode.launcher.activityresult.GeodeSaveFileActivityResult import com.geode.launcher.activityresult.GeodeSaveFileActivityResult
@@ -34,6 +38,7 @@ object GeodeUtils {
private lateinit var saveFileResultLauncher: ActivityResultLauncher<GeodeSaveFileActivityResult.SaveFileParams> private lateinit var saveFileResultLauncher: ActivityResultLauncher<GeodeSaveFileActivityResult.SaveFileParams>
private lateinit var requestPermissionLauncher: ActivityResultLauncher<String> private lateinit var requestPermissionLauncher: ActivityResultLauncher<String>
private lateinit var internalRequestPermissionsLauncher: ActivityResultLauncher<Array<String>> private lateinit var internalRequestPermissionsLauncher: ActivityResultLauncher<Array<String>>
private lateinit var internalRequestAllFilesLauncher: ActivityResultLauncher<Intent>
private var afterRequestPermissions: (() -> Unit)? = null private var afterRequestPermissions: (() -> Unit)? = null
private var afterRequestPermissionsFailure: (() -> Unit)? = null private var afterRequestPermissionsFailure: (() -> Unit)? = null
@@ -93,12 +98,26 @@ object GeodeUtils {
if (isGranted) { if (isGranted) {
afterRequestPermissions?.invoke() afterRequestPermissions?.invoke()
} else { } else {
Toast.makeText(activity, activity.getString(R.string.missing_permissions), Toast.LENGTH_SHORT) Toast.makeText(activity, R.string.missing_permissions, Toast.LENGTH_SHORT)
.show() .show()
afterRequestPermissionsFailure?.invoke() 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 @JvmStatic
@@ -149,21 +168,48 @@ object GeodeUtils {
// TODO As of now this is unused // TODO As of now this is unused
@JvmStatic @JvmStatic
fun openFolder(path: String): Boolean { fun openFolder(path: String): Boolean {
activity.get()?.run { val context = activity.get()!!
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("*/*")
if (intent.resolveActivity(packageManager) != null) { val pathFile = File(path)
startActivity(intent) val baseDirectory = LaunchUtils.getBaseDirectory(context)
return true 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) private external fun selectFileCallback(path: String)
@@ -173,30 +219,38 @@ object GeodeUtils {
private external fun failedCallback() private external fun failedCallback()
private fun checkForFilePermissions(onSuccess: () -> Unit, onFailure: () -> Unit) { private fun checkForFilePermissions(onSuccess: () -> Unit, onFailure: () -> Unit) {
val permissions = if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) { if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
listOf( if (Environment.isExternalStorageManager()) {
Manifest.permission.READ_MEDIA_AUDIO, onSuccess()
Manifest.permission.READ_MEDIA_IMAGES, } else {
Manifest.permission.READ_MEDIA_VIDEO 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 { } else {
listOf( val permissions = listOf(
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE Manifest.permission.WRITE_EXTERNAL_STORAGE
) )
}
val context = activity.get()!! val context = activity.get()!!
val needsPermissions = permissions.filter { val needsPermissions = permissions.filter {
ContextCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED ContextCompat.checkSelfPermission(context, it) != PackageManager.PERMISSION_GRANTED
} }
if (needsPermissions.isNotEmpty()) { if (needsPermissions.isNotEmpty()) {
internalRequestPermissionsLauncher.launch(needsPermissions.toTypedArray()) internalRequestPermissionsLauncher.launch(needsPermissions.toTypedArray())
afterRequestPermissions = onSuccess afterRequestPermissions = onSuccess
} else { afterRequestPermissionsFailure = onFailure
onSuccess() } else {
onSuccess()
}
} }
} }
@@ -317,32 +371,53 @@ object GeodeUtils {
return "com.geode.launcher.user" == uri.authority 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 @JvmStatic
fun getPermissionStatus(permission: String): Boolean { fun getPermissionStatus(permission: String): Boolean {
return ContextCompat.checkSelfPermission( val context = activity.get() ?: return false
activity.get()?.applicationContext ?: return false,
permission return when (permission) {
) == PackageManager.PERMISSION_GRANTED 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 @JvmStatic
fun requestPermission(permission: String) { 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)) { if (getPermissionStatus(permission)) {
permissionCallback(true) permissionCallback(true)
return return
} }
activity.get()?.run { try {
try { requestPermissionLauncher.launch(permission)
requestPermissionLauncher.launch(permission) } catch (e: ActivityNotFoundException) {
} catch (e: ActivityNotFoundException) { permissionCallback(false)
permissionCallback(false)
}
return
} }
permissionCallback(false)
} }
private external fun permissionCallback(granted: Boolean) private external fun permissionCallback(granted: Boolean)
@@ -371,4 +446,30 @@ object GeodeUtils {
return null 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.MotionEvent
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import com.customRobTop.BaseRobTopActivity import com.customRobTop.BaseRobTopActivity
import com.geode.launcher.utils.GeodeUtils
private const val HANDLER_OPEN_IME_KEYBOARD = 2 private const val HANDLER_OPEN_IME_KEYBOARD = 2
private const val HANDLER_CLOSE_IME_KEYBOARD = 3 private const val HANDLER_CLOSE_IME_KEYBOARD = 3
@@ -37,6 +38,11 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
} }
private lateinit var cocos2dxRenderer: Cocos2dxRenderer private lateinit var cocos2dxRenderer: Cocos2dxRenderer
var useKeyboardEvents = false
set(value) {
field = value
cocos2dxRenderer.sendResizeEvents = value
}
var cocos2dxEditText: Cocos2dxEditText? = null var cocos2dxEditText: Cocos2dxEditText? = null
set(value) { 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) { return when (keyCode) {
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> {
if (keyEvent.repeatCount != 0 || BaseRobTopActivity.blockBackButton) { 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) { fun setCocos2dxRenderer(renderer: Cocos2dxRenderer) {
this.cocos2dxRenderer = renderer this.cocos2dxRenderer = renderer
setRenderer(this.cocos2dxRenderer) setRenderer(this.cocos2dxRenderer)
@@ -59,6 +59,7 @@ class Cocos2dxRenderer : GLSurfaceView.Renderer {
private var lastTickInNanoSeconds: Long = 0 private var lastTickInNanoSeconds: Long = 0
private var screenWidth = 0 private var screenWidth = 0
private var screenHeight = 0 private var screenHeight = 0
var sendResizeEvents = false
fun setScreenWidthAndHeight(surfaceWidth: Int, surfaceHeight: Int) { fun setScreenWidthAndHeight(surfaceWidth: Int, surfaceHeight: Int) {
screenWidth = surfaceWidth screenWidth = surfaceWidth
+4
View File
@@ -30,6 +30,8 @@
</string> </string>
<string name="launcher_update_available">A new launcher update is available!</string> <string name="launcher_update_available">A new launcher update is available!</string>
<string name="launcher_update_dismiss">Dismiss</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 --> <!-- Updater strings -->
<string name="release_fetch_failed">Update failed.\n%1$s</string> <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="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_cancel">Cancel</string>
<string name="message_box_accept">OK</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> <string name="missing_permissions">Missing required permissions for this action!</string>
<!-- SettingsActivity strings --> <!-- SettingsActivity strings -->
@@ -95,6 +98,7 @@
<string name="preference_launch_arguments_name">Launch arguments</string> <string name="preference_launch_arguments_name">Launch arguments</string>
<string name="preference_launch_arguments_set_title">Set 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_ignore_load_failure_name">Ignore loader errors</string>
<string name="preference_text_clear">Clear</string>
<!-- ApplicationLogsActivity strings --> <!-- ApplicationLogsActivity strings -->
<string name="title_activity_application_logs">Application Logs</string> <string name="title_activity_application_logs">Application Logs</string>
-17
View File
@@ -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
}
+16
View File
@@ -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)
}
+2
View File
@@ -21,3 +21,5 @@ kotlin.code.style=official
# resources declared in the library itself and none from the library's dependencies, # resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library # thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
# nice optimization
org.gradle.configuration-cache=true
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
Vendored
+7 -7
View File
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. # 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 ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. # 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" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac 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. # 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"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command; # Collect all arguments for the java command:
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# shell script including quotes and variable substitutions, so put them in # and any embedded shellness will be escaped.
# double quotes to make sure that they get re-expanded; and # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# * put everything else in single quotes, so that it's not re-expanded. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+4 -1
View File
@@ -5,6 +5,8 @@ pluginManagement {
mavenCentral() mavenCentral()
} }
} }
@Suppress("UnstableApiUsage")
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories { repositories {
@@ -12,5 +14,6 @@ dependencyResolutionManagement {
mavenCentral() mavenCentral()
} }
} }
rootProject.name = "launcher" rootProject.name = "launcher"
include ':app' include(":app")