change package name

This commit is contained in:
qimiko committed 2022-11-16 10:26:24 -07:00
1 parent bbbd1eae38
commit fda94aba0f
13 files changed
+25 -25

No files matched your search

@@ -0,0 +1,311 @@
package com.geode.launcher
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.FileUtils
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.customRobTop.BaseRobTopActivity
import com.customRobTop.JniToCpp
import com.geode.launcher.utils.Constants
import com.geode.launcher.utils.LaunchUtils
import org.cocos2dx.lib.Cocos2dxEditText
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
import org.cocos2dx.lib.Cocos2dxHelper
import org.cocos2dx.lib.Cocos2dxRenderer
import org.fmod.FMOD
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.InputStream
import java.io.OutputStream
class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperListener {
private var mGLSurfaceView: Cocos2dxGLSurfaceView? = null
private val sTag = GeometryDashActivity::class.simpleName
private var mIsRunning = false
private var mIsOnPause = false
private var mHasWindowFocus = false
private var mReceiver: BroadcastReceiver? = null
@SuppressLint("UnsafeDynamicallyLoadedCode")
override fun onCreate(savedInstanceState: Bundle?) {
setupUIState()
// return back to main if Geometry Dash isn't found
if (!LaunchUtils.isGeometryDashInstalled(packageManager)) {
val launchIntent = Intent(this, MainActivity::class.java)
launchIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(launchIntent)
}
val gdPackageInfo = packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0)
val gdNativeLibraryPath = "${gdPackageInfo.applicationInfo.nativeLibraryDir}/"
try {
LaunchUtils.addAssetsFromPackage(assets, gdPackageInfo)
} catch (e: NoSuchMethodException) {
e.printStackTrace()
}
try {
// fixes bugs specific to the new app directory, such as package name
LauncherFix.loadLibrary()
LauncherFix.setOriginalDataPath(Constants.GJ_DATA_DIR)
LauncherFix.setDataPath(filesDir.path)
} catch (e: UnsatisfiedLinkError) {
e.printStackTrace()
}
System.load("$gdNativeLibraryPath/lib${Constants.FMOD_LIB_NAME}.so")
System.load("$gdNativeLibraryPath/lib${Constants.COCOS_LIB_NAME}.so")
if (getLoadTesting()) {
loadTestingLibraries()
}
FMOD.init(this)
super.onCreate(savedInstanceState)
setContentView(createView())
Cocos2dxHelper.init(this, this)
JniToCpp.setupHSSAssets(
gdPackageInfo.applicationInfo.sourceDir,
Environment.getExternalStorageDirectory().absolutePath
)
Cocos2dxHelper.nativeSetApkPath(gdPackageInfo.applicationInfo.sourceDir)
BaseRobTopActivity.setCurrentActivity(this)
registerReceiver()
}
private fun createView(): FrameLayout {
val frameLayoutParams = ViewGroup.LayoutParams(-1, -1)
val frameLayout = FrameLayout(this)
frameLayout.layoutParams = frameLayoutParams
val editTextLayoutParams = ViewGroup.LayoutParams(-1, -2)
val editText = Cocos2dxEditText(this)
editText.layoutParams = editTextLayoutParams
frameLayout.addView(editText)
val glSurfaceView = Cocos2dxGLSurfaceView(this)
this.mGLSurfaceView = glSurfaceView
frameLayout.addView(this.mGLSurfaceView)
glSurfaceView.setEGLConfigChooser(5, 6, 5, 0, 16, 8)
if (isAndroidEmulator()) {
glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0)
}
glSurfaceView.initView()
glSurfaceView.setCocos2dxRenderer(Cocos2dxRenderer())
editText.inputType = 145
glSurfaceView.cocos2dxEditText = editText
return frameLayout
}
private fun setupUIState() {
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
hideSystemUi()
}
private fun hideSystemUi() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
// window compat is dumb!!
setLegacyVisibility()
window.decorView.setOnSystemUiVisibilityChangeListener { visibility ->
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
setLegacyVisibility()
}
}
}
WindowCompat.getInsetsController(window, window.decorView).apply {
systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
hide(WindowInsetsCompat.Type.systemBars())
}
}
private fun setLegacyVisibility() {
// setDecorFitsSystemWindows doesn't hide anything
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
override fun onDestroy() {
super.onDestroy()
unregisterReceivers()
FMOD.close()
}
private fun resumeGame() {
mIsRunning = true
// Cocos2dxHelper.onResume()
mGLSurfaceView?.onResume()
}
private fun pauseGame() {
mIsRunning = false
// Cocos2dxHelper.onPause()
mGLSurfaceView?.onPause()
}
override fun onResume() {
super.onResume()
mIsOnPause = false
BaseRobTopActivity.isPaused = false
if (mHasWindowFocus && !this.mIsRunning) {
resumeGame()
}
}
override fun onPause() {
super.onPause()
mIsOnPause = true
BaseRobTopActivity.isPaused = true
if (mIsRunning) {
pauseGame()
}
}
private fun registerReceiver() {
unregisterReceivers()
try {
val filter = IntentFilter(Intent.ACTION_SCREEN_ON)
filter.addAction(Intent.ACTION_SCREEN_OFF)
filter.addAction(Intent.ACTION_USER_PRESENT)
mReceiver = BaseRobTopActivity.ReceiverScreen()
registerReceiver(mReceiver, filter)
} catch (_: Exception) {
}
}
private fun unregisterReceivers() {
if (mReceiver != null) {
unregisterReceiver(mReceiver)
mReceiver = null
}
}
override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
super.onWindowFocusChanged(hasWindowFocus)
mHasWindowFocus = hasWindowFocus
if (hasWindowFocus && !mIsOnPause) {
resumeGame()
}
}
override fun runOnGLThread(runnable: Runnable) {
mGLSurfaceView?.queueEvent(runnable)
}
override fun showDialog(title: String, message: String) {
TODO("Not yet implemented")
}
override fun showEditTextDialog(
title: String,
message: String,
inputMode: Int,
inputFlag: Int,
returnType: Int,
maxLength: Int
) {
TODO("Not yet implemented")
}
private fun isAndroidEmulator(): Boolean {
Log.d(sTag, "model=" + Build.MODEL)
val product = Build.PRODUCT
Log.d(sTag, "product=$product")
var isEmulator = false
if (product != null) {
isEmulator = product == "sdk" || product.contains("_sdk") || product.contains("sdk_")
}
Log.d(sTag, "isEmulator=$isEmulator")
return isEmulator
}
private fun getLoadTesting(): Boolean {
val preferences = getSharedPreferences(getString(R.string.preference_file_key), Context.MODE_PRIVATE)
return preferences.getBoolean(getString(R.string.preference_load_testing), false)
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
private fun loadTestingLibraries() {
// clear data dir
val testDirPath = File(filesDir.path + File.separator + "testlib" + File.separator)
if (testDirPath.exists()) {
testDirPath.deleteRecursively()
}
testDirPath.mkdir()
getExternalFilesDir(null)?.let { dir ->
val testingPath = dir.path + File.separator + "test" + File.separator
File(testingPath).walk().forEach {
if (it.isFile) {
// welcome to the world of Android classloader permissions
val outputFile = File(testDirPath.path + File.separator + it.name)
copyFile(FileInputStream(it), FileOutputStream(outputFile))
try {
println("Loading test library ${outputFile.name}")
System.load(outputFile.path)
} catch (e: UnsatisfiedLinkError) {
e.printStackTrace()
}
}
}
}
}
private fun copyFile(inputStream: InputStream, outputStream: OutputStream) {
// gotta love copying
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FileUtils.copy(inputStream, outputStream)
} else {
inputStream.use { input ->
outputStream.use { output ->
val buffer = ByteArray(4 * 1024)
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}
}
}
@@ -0,0 +1,13 @@
package com.geode.launcher
import com.geode.launcher.utils.Constants
object LauncherFix {
fun loadLibrary() {
System.loadLibrary(Constants.LAUNCHER_FIX_LIB_NAME)
}
external fun setDataPath(dataPath: String)
external fun setOriginalDataPath(dataPath: String)
}
@@ -0,0 +1,169 @@
package com.geode.launcher
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.geode.launcher.ui.theme.GeodeLauncherTheme
import com.geode.launcher.ui.theme.Typography
import com.geode.launcher.utils.LaunchUtils
import com.geode.launcher.utils.countdownTimerWatcher
import com.geode.launcher.utils.preferenceWatcher
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val gdInstalled = LaunchUtils.isGeometryDashInstalled(packageManager)
setContent {
GeodeLauncherTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen(gdInstalled)
}
}
}
}
}
fun onLaunch(context: Context) {
val launchIntent = Intent(context, GeometryDashActivity::class.java)
launchIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(launchIntent)
}
fun onSettings(context: Context) {
val launchIntent = Intent(context, SettingsActivity::class.java)
context.startActivity(launchIntent)
}
@Composable
fun MainScreen(gdInstalled: Boolean = true) {
val context = LocalContext.current
val shouldAutomaticallyLaunch = preferenceWatcher(
preferenceFileKey = R.string.preference_file_key,
preferenceId = R.string.preference_load_automatically
)
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.geode_logo),
contentDescription = context.getString(R.string.launcher_logo_alt),
modifier = Modifier.size(136.dp, 136.dp)
)
Text(
context.getString(R.string.launcher_title),
fontSize = 32.sp,
modifier = Modifier.padding(12.dp)
)
if (gdInstalled) {
if (shouldAutomaticallyLaunch.value) {
val countdownTimer = countdownTimerWatcher(
time = 3000,
onCountdownFinish = {
// just in case this changed async
if (shouldAutomaticallyLaunch.value) {
onLaunch(context)
}
}
)
if (countdownTimer.value != 0) {
Text(
context.resources.getQuantityString(
R.plurals.automatically_load_countdown, countdownTimer.value, countdownTimer.value
),
style = Typography.bodyMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer,
)
Spacer(Modifier.size(12.dp))
}
}
Row {
Button(onClick = { onLaunch(context) }) {
Icon(
Icons.Filled.PlayArrow,
contentDescription = context.getString(R.string.launcher_launch_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_launch))
}
Spacer(Modifier.size(2.dp))
IconButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
}
}
} else {
Text(
context.getString(R.string.game_not_found),
modifier = Modifier.padding(12.dp)
)
OutlinedButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_settings))
}
}
}
}
@Preview(showSystemUi = true)
@Composable
fun MainScreenLightPreview() {
GeodeLauncherTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
MainScreen()
}
}
}
@Preview(showSystemUi = true)
@Composable
fun MainScreenDarkPreview() {
GeodeLauncherTheme(darkTheme = true) {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
MainScreen()
}
}
}
@Preview
@Composable
fun MainScreenNoGeometryDashPreview() {
GeodeLauncherTheme {
MainScreen(gdInstalled = false)
}
}
@@ -0,0 +1,259 @@
package com.geode.launcher
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedDispatcher
import androidx.activity.compose.setContent
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.edit
import com.geode.launcher.ui.theme.GeodeLauncherTheme
import com.geode.launcher.ui.theme.Typography
class SettingsActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
GeodeLauncherTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
SettingsScreen(
onBackPressedDispatcher = onBackPressedDispatcher
)
}
}
}
}
}
/*
fun onExportSaveData(context: Context) {
Toast.makeText(
context,
"This function is not implemented yet!",
Toast.LENGTH_SHORT
).show()
}
*/
fun onOpenFolder(context: Context) {
context.getExternalFilesDir(null)?.let { file ->
val clipboardManager =
context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboardManager.setPrimaryClip(
ClipData.newPlainText(
context.getString(R.string.export_folder_tag),
file.path
)
)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
Toast.makeText(
context,
context.getString(R.string.export_folder_copied),
Toast.LENGTH_SHORT
).show()
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
val context = LocalContext.current
Scaffold(
topBar = {
TopAppBar(
navigationIcon = {
IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = context.getString(R.string.settings_back_icon_alt)
)
}
},
title = {
Text(
context.getString(R.string.title_activity_settings),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
)
},
content = { innerPadding ->
Column(
Modifier
.padding(innerPadding)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
OptionsGroup(context.getString(R.string.preference_category_testing)) {
SettingsCard(
title = context.getString(R.string.preference_load_testing_name),
preferenceId = R.string.preference_load_testing
)
SettingsCard(
title = context.getString(R.string.preference_load_automatically_name),
description = context.getString(R.string.preference_load_automatically_description),
preferenceId = R.string.preference_load_automatically
)
OptionsButton(
title = context.getString(R.string.preferences_copy_external_button),
onClick = { onOpenFolder(context) }
)
}
/*
OptionsGroup("Data") {
OptionsButton(
title = "Export save data",
onClick = { onExportSaveData(context) }
)
}
*/
}
}
)
}
fun toggleSetting(context: Context, @StringRes preferenceId: Int): Boolean {
val preferences = context.getSharedPreferences(
context.getString(R.string.preference_file_key), Context.MODE_PRIVATE
)
val current = preferences.getBoolean(
context.getString(preferenceId), false
)
preferences.edit {
putBoolean(context.getString(preferenceId), !current)
commit()
}
return preferences.getBoolean(context.getString(preferenceId), false)
}
fun getSetting(context: Context, @StringRes preferenceId: Int): Boolean {
val preferences = context.getSharedPreferences(
context.getString(R.string.preference_file_key), Context.MODE_PRIVATE
)
return preferences.getBoolean(context.getString(preferenceId), false)
}
@Composable
fun OptionsGroup(title: String, content: @Composable () -> Unit) {
Column {
Text(
title,
style = Typography.titleMedium,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
)
content()
}
}
@Composable
fun OptionsButton(title: String, onClick: () -> Unit) {
OptionsCard(
title = {
Text(title)
},
modifier = Modifier
.clickable(onClick = onClick, role = Role.Button)
) { }
}
@Composable
fun SettingsCard(title: String, description: String? = null, @StringRes preferenceId: Int) {
val context = LocalContext.current
val settingEnabled = remember {
mutableStateOf(getSetting(context, preferenceId)) }
OptionsCard(
title = {
Column(
Modifier.fillMaxWidth(0.75f),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(title)
if (!description.isNullOrEmpty()) {
Text(
description,
style = Typography.labelMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer
)
}
}
},
modifier = Modifier.toggleable(
value = settingEnabled.value,
onValueChange = { settingEnabled.value = toggleSetting(context, preferenceId) },
role = Role.Switch,
)
) {
Switch(checked = settingEnabled.value, onCheckedChange = null)
}
}
@Composable
fun OptionsCard(modifier: Modifier = Modifier, title: @Composable () -> Unit, content: @Composable () -> Unit) {
Row(
modifier
.fillMaxWidth()
.height(64.dp)
.padding(horizontal = 16.dp),
Arrangement.SpaceBetween,
Alignment.CenterVertically,
) {
title()
content()
}
}
@Preview(showBackground = true)
@Composable
fun OptionsCardPreview() {
GeodeLauncherTheme {
OptionsGroup(title = "Preview Group") {
SettingsCard(
title = "Load files from /test",
description = "Very long testing description goes here. It is incredibly long, it should wrap onto a new line.",
preferenceId = R.string.preference_load_testing
)
SettingsCard(
title = "Testing option 2",
preferenceId = R.string.preference_load_automatically
)
}
}
}
@Preview(showSystemUi = true)
@Composable
fun DefaultPreview() {
GeodeLauncherTheme {
SettingsScreen(null)
}
}
@@ -0,0 +1,11 @@
package com.geode.launcher.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
@@ -0,0 +1,75 @@
package com.geode.launcher.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.ViewCompat
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun GeodeLauncherTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
with ((view.context as Activity).window) {
statusBarColor = colorScheme.background.toArgb()
navigationBarColor = colorScheme.background.toArgb()
}
with (ViewCompat.getWindowInsetsController(view)) {
this?.isAppearanceLightStatusBars = !darkTheme
this?.isAppearanceLightNavigationBars = !darkTheme
}
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
@@ -0,0 +1,40 @@
package com.geode.launcher.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
),
titleMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)
@@ -0,0 +1,15 @@
package com.geode.launcher.utils
import android.annotation.SuppressLint
object Constants {
const val PACKAGE_NAME = "com.robtopx.geometryjump"
const val COCOS_LIB_NAME = "cocos2dcpp"
const val FMOD_LIB_NAME = "fmod"
const val LAUNCHER_FIX_LIB_NAME = "launcherfix"
// this value is hardcoded into GD
@SuppressLint("SdCardPath")
const val GJ_DATA_DIR = "/data/data/${PACKAGE_NAME}"
}
@@ -0,0 +1,46 @@
package com.geode.launcher.utils
import android.os.CountDownTimer
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
const val MS_TO_SEC = 1000
@Composable
fun countdownTimerWatcher(lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current, time: Int, onCountdownFinish: () -> Unit): MutableState<Int> {
val timeData = remember {
mutableStateOf(time / MS_TO_SEC % MS_TO_SEC)
}
val countdownTimer =
object : CountDownTimer(time.toLong(), 1000) {
override fun onTick(millisUntilFinished: Long) {
timeData.value = (millisUntilFinished / MS_TO_SEC % MS_TO_SEC).toInt() + 1
}
override fun onFinish() {
onCountdownFinish()
}
}
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
countdownTimer.start()
} else if (event == Lifecycle.Event.ON_STOP) {
countdownTimer.cancel()
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
return timeData
}
@@ -0,0 +1,24 @@
package com.geode.launcher.utils
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.AssetManager
object LaunchUtils {
fun isGeometryDashInstalled(packageManager: PackageManager): Boolean {
return try {
packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
fun addAssetsFromPackage(assetManager: AssetManager, packageInfo: PackageInfo) {
// this method is officially marked as deprecated but it is the only method we are allowed to reflect
// (the source recommends replacing with AssetManager.setApkAssets(ApkAssets[], boolean) lol)
val clazz = assetManager.javaClass
val aspMethod = clazz.getDeclaredMethod("addAssetPath", String::class.java)
aspMethod.invoke(assetManager, packageInfo.applicationInfo.sourceDir)
}
}
@@ -0,0 +1,51 @@
package com.geode.launcher.utils
import android.content.Context
import android.content.SharedPreferences
import androidx.annotation.StringRes
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
@Composable
fun preferenceWatcher(@StringRes preferenceFileKey: Int, @StringRes preferenceId: Int, lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current): MutableState<Boolean> {
val context = LocalContext.current
val preferences = context.getSharedPreferences(
context.getString(preferenceFileKey), Context.MODE_PRIVATE
)
val preferenceValue = remember {
mutableStateOf(
preferences.getBoolean(
context.getString(preferenceId),
false
)
)
}
val listener = SharedPreferences.OnSharedPreferenceChangeListener {
sharedPreferences, key -> preferenceValue.value = sharedPreferences.getBoolean(key, false)
}
DisposableEffect(lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_START) {
preferences.registerOnSharedPreferenceChangeListener(listener)
} else if (event == Lifecycle.Event.ON_DESTROY) {
preferences.unregisterOnSharedPreferenceChangeListener(listener)
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
return preferenceValue
}