-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Replace old view system with compose navigation
- Loading branch information
Showing
6 changed files
with
142 additions
and
248 deletions.
There are no files selected for viewing
252 changes: 142 additions & 110 deletions
252
app/src/main/kotlin/org/cuberite/android/MainActivity.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,139 +1,171 @@ | ||
package org.cuberite.android | ||
|
||
import android.Manifest | ||
import android.content.DialogInterface | ||
import android.content.pm.PackageManager | ||
import android.os.Build | ||
import android.content.SharedPreferences | ||
import android.os.Bundle | ||
import android.util.Log | ||
import android.view.MenuItem | ||
import androidx.activity.result.contract.ActivityResultContracts | ||
import androidx.appcompat.app.AlertDialog | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.core.content.ContextCompat | ||
import androidx.fragment.app.Fragment | ||
import com.google.android.material.bottomnavigation.BottomNavigationView | ||
import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
import com.google.android.material.elevation.SurfaceColors | ||
import com.google.android.material.navigation.NavigationBarView | ||
import org.cuberite.android.fragments.ConsoleFragment | ||
import org.cuberite.android.fragments.ControlFragment | ||
import org.cuberite.android.fragments.SettingsFragment | ||
|
||
class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListener { | ||
private var permissionPopup: AlertDialog? = null | ||
import androidx.activity.ComponentActivity | ||
import androidx.activity.compose.rememberLauncherForActivityResult | ||
import androidx.activity.compose.setContent | ||
import androidx.activity.enableEdgeToEdge | ||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.material3.AlertDialog | ||
import androidx.compose.material3.Icon | ||
import androidx.compose.material3.NavigationBar | ||
import androidx.compose.material3.NavigationBarItem | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.material3.Text | ||
import androidx.compose.material3.TextButton | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.res.painterResource | ||
import androidx.compose.ui.res.stringResource | ||
import androidx.core.content.edit | ||
import androidx.lifecycle.compose.LifecycleResumeEffect | ||
import androidx.navigation.compose.currentBackStackEntryAsState | ||
import androidx.navigation.compose.rememberNavController | ||
import org.cuberite.android.extension.isExternalStorageGranted | ||
import org.cuberite.android.ui.console.navigation.navigateToConsole | ||
import org.cuberite.android.ui.control.navigation.navigateToControl | ||
import org.cuberite.android.ui.navigation.CuberiteNavGraph | ||
import org.cuberite.android.ui.navigation.TopLevelNavigation | ||
import org.cuberite.android.ui.navigation.TopLevelNavigation.Console | ||
import org.cuberite.android.ui.navigation.TopLevelNavigation.Control | ||
import org.cuberite.android.ui.navigation.TopLevelNavigation.Settings | ||
import org.cuberite.android.ui.settings.navigation.navigateToSettings | ||
import org.cuberite.android.ui.theme.CuberiteTheme | ||
|
||
class MainActivity : ComponentActivity() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
enableEdgeToEdge() | ||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.container) | ||
if (savedInstanceState == null) { | ||
loadFragment(ControlFragment()) | ||
setContent { | ||
CuberiteTheme { | ||
Cuberite() | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Set colors | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
window.statusBarColor = SurfaceColors.SURFACE_0.getColor(this) | ||
} | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { | ||
window.navigationBarColor = SurfaceColors.SURFACE_2.getColor(this) | ||
} | ||
@Composable | ||
fun Cuberite() { | ||
val navController = rememberNavController() | ||
val navBackStackEntry by navController.currentBackStackEntryAsState() | ||
val currentDestination = navBackStackEntry?.destination | ||
|
||
// Set navigation bar listener | ||
val navigation: BottomNavigationView = findViewById(R.id.bottom_navigation) | ||
navigation.setOnItemSelectedListener(this) | ||
val permissionLauncher = rememberLauncherForActivityResult(RequestPermission()) { isGranted -> | ||
MainApplication.preferences.edit { | ||
if (isGranted) { | ||
putString("cuberiteLocation", "${MainApplication.publicDir}/cuberite-server") | ||
} else { | ||
putString("cuberiteLocation", "${MainApplication.privateDir}/cuberite-server") | ||
} | ||
} | ||
} | ||
|
||
override fun onNavigationItemSelected(item: MenuItem): Boolean { | ||
var fragment: Fragment? = null | ||
when (item.itemId) { | ||
R.id.item_control -> { | ||
fragment = ControlFragment() | ||
} | ||
var showPermissionPopup by remember { mutableStateOf(false) } | ||
|
||
R.id.item_console -> { | ||
fragment = ConsoleFragment() | ||
} | ||
PermissionCheckSideEffect( | ||
preferences = MainApplication.preferences, | ||
onRequest = { showPermissionPopup = true }, | ||
onPause = { showPermissionPopup = false }, | ||
) | ||
|
||
R.id.item_settings -> { | ||
fragment = SettingsFragment() | ||
Scaffold( | ||
modifier = Modifier.fillMaxSize(), | ||
bottomBar = { | ||
NavigationBar { | ||
TopLevelNavigation.entries.forEach { navigation -> | ||
val selected = remember(currentDestination?.route) { | ||
currentDestination?.route == navigation.route | ||
} | ||
NavigationBarItem( | ||
selected = selected, | ||
onClick = { | ||
when (navigation) { | ||
Control -> navController.navigateToControl() | ||
Console -> navController.navigateToConsole() | ||
Settings -> navController.navigateToSettings() | ||
} | ||
}, | ||
icon = { | ||
Icon( | ||
painter = painterResource(navigation.iconRes), | ||
contentDescription = null, | ||
) | ||
}, | ||
label = { | ||
Text(text = stringResource(navigation.labelRes)) | ||
}, | ||
) | ||
} | ||
} | ||
} | ||
return loadFragment(fragment) | ||
}, | ||
) { innerPadding -> | ||
CuberiteNavGraph( | ||
navController = navController, | ||
modifier = Modifier.padding(innerPadding), | ||
) | ||
} | ||
|
||
private fun loadFragment(fragment: Fragment?): Boolean { | ||
if (fragment != null) { | ||
supportFragmentManager | ||
.beginTransaction() | ||
.replace(R.id.fragment_container, fragment) | ||
.commit() | ||
return true | ||
} | ||
return false | ||
if (showPermissionPopup) { | ||
PermissionPopup( | ||
onDismiss = { showPermissionPopup = false }, | ||
onConfirm = { | ||
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) | ||
}, | ||
) | ||
} | ||
} | ||
|
||
private val requestPermissionLauncher = | ||
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> | ||
if (isGranted) { | ||
Log.i(LOG, "Got permissions, using public directory") | ||
MainApplication.preferences.edit() | ||
.putString("cuberiteLocation", "${MainApplication.publicDir}/cuberite-server") | ||
.apply() | ||
} else { | ||
Log.i(LOG, "Permissions denied, boo, using private directory") | ||
MainApplication.preferences.edit() | ||
.putString("cuberiteLocation", "${MainApplication.privateDir}/cuberite-server") | ||
.apply() | ||
@Composable | ||
private fun PermissionPopup( | ||
onDismiss: () -> Unit, | ||
onConfirm: () -> Unit, | ||
) { | ||
AlertDialog( | ||
onDismissRequest = onDismiss, | ||
confirmButton = { | ||
TextButton(onClick = onConfirm) { | ||
Text(text = stringResource(R.string.ok)) | ||
} | ||
}, | ||
title = { | ||
Text(text = stringResource(R.string.status_permissions_needed)) | ||
}, | ||
text = { | ||
Text(text = stringResource(R.string.message_externalstorage_permission)) | ||
} | ||
) | ||
} | ||
|
||
private fun showPermissionPopup() { | ||
permissionPopup = MaterialAlertDialogBuilder(this) | ||
.setTitle(getString(R.string.status_permissions_needed)) | ||
.setMessage(R.string.message_externalstorage_permission) | ||
.setCancelable(false) | ||
.setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> | ||
Log.d(LOG, "Requesting permissions for external storage") | ||
permissionPopup = null | ||
requestPermissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) | ||
} | ||
.create() | ||
permissionPopup!!.show() | ||
@Composable | ||
private fun PermissionCheckSideEffect( | ||
preferences: SharedPreferences, | ||
onRequest: () -> Unit, | ||
onPause: () -> Unit, | ||
) { | ||
val context = LocalContext.current | ||
val location = remember { | ||
preferences.getString("cuberiteLocation", "") | ||
} | ||
|
||
private fun checkPermissions() { | ||
val location = MainApplication.preferences.getString("cuberiteLocation", "") | ||
if (ContextCompat.checkSelfPermission( | ||
this, | ||
Manifest.permission.WRITE_EXTERNAL_STORAGE | ||
) != PackageManager.PERMISSION_GRANTED | ||
) { | ||
// User is running Android 6 or above, show permission popup on first run | ||
// or if user granted permission and later denied it | ||
LifecycleResumeEffect(true) { | ||
if (!context.isExternalStorageGranted) { | ||
if (location!!.isEmpty() || location.startsWith(MainApplication.publicDir)) { | ||
showPermissionPopup() | ||
onRequest() | ||
} | ||
} else if (location!!.isEmpty() || location.startsWith(MainApplication.privateDir)) { | ||
val editor = MainApplication.preferences.edit() | ||
editor.putString("cuberiteLocation", "${MainApplication.publicDir}/cuberite-server") | ||
editor.apply() | ||
preferences.edit { | ||
putString("cuberiteLocation", "${MainApplication.publicDir}/cuberite-server") | ||
} | ||
} | ||
} | ||
|
||
override fun onPause() { | ||
super.onPause() | ||
if (permissionPopup != null) { | ||
permissionPopup!!.dismiss() | ||
permissionPopup = null | ||
onPauseOrDispose { | ||
onPause() | ||
} | ||
} | ||
|
||
override fun onResume() { | ||
super.onResume() | ||
checkPermissions() | ||
} | ||
|
||
companion object { | ||
private const val LOG = "Cuberite/MainActivity" | ||
} | ||
} |
34 changes: 0 additions & 34 deletions
34
app/src/main/kotlin/org/cuberite/android/fragments/ConsoleFragment.kt
This file was deleted.
Oops, something went wrong.
31 changes: 0 additions & 31 deletions
31
app/src/main/kotlin/org/cuberite/android/fragments/ControlFragment.kt
This file was deleted.
Oops, something went wrong.
34 changes: 0 additions & 34 deletions
34
app/src/main/kotlin/org/cuberite/android/fragments/SettingsFragment.kt
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.