add permission support for termux
this will be used in the future
This commit is contained in:
parent
046e3147d9
commit
3ae52c6638
5 changed files with 98 additions and 9 deletions
|
@ -21,7 +21,7 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't forget to add another "s this is counter intuitive I know but not my fault
|
// don't forget to add another "s this is counter intuitive I know but not my fault
|
||||||
buildConfigField("String", "VASTAI_KEY", "null")
|
buildConfigField("String", "VASTAI_KEY", "\"\"")
|
||||||
buildConfigField("String", "VASIAI_API_ENDPOINT", "\"https://cloud.vast.ai/api/v0\"")
|
buildConfigField("String", "VASIAI_API_ENDPOINT", "\"https://cloud.vast.ai/api/v0\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="com.termux.permission.RUN_COMMAND" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package eu.m724.vastapp.activity
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
|
||||||
|
class PermissionChecker(private val context: Context) {
|
||||||
|
/**
|
||||||
|
* check if the app has a permission
|
||||||
|
* @param permission the permission
|
||||||
|
* @return whether the app has the permission? obviously
|
||||||
|
*/
|
||||||
|
fun hasPermission(permission: String): Boolean {
|
||||||
|
return context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if a permission exists or if the app is installed
|
||||||
|
* @param permission the permission
|
||||||
|
* @return if the permission exists
|
||||||
|
*/
|
||||||
|
fun permissionExists(permission: String): Boolean {
|
||||||
|
try {
|
||||||
|
context.packageManager.getPermissionInfo(permission, 0)
|
||||||
|
return true
|
||||||
|
} catch (e: NameNotFoundException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param permission the permission
|
||||||
|
* @param activity the activity you're calling from
|
||||||
|
* @return if the permission can be asked for, that is if the user didn't check "don't ask again"
|
||||||
|
*/
|
||||||
|
fun canAskForPermission(permission: String, activity: ComponentActivity): Boolean {
|
||||||
|
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* request a permission if that permission is not granted
|
||||||
|
* @param permission the permission
|
||||||
|
* @param activity the activity you're calling from
|
||||||
|
* @param callback an Unit, the first boolean is whether the permission is granted and the second one is whether we asked for it
|
||||||
|
*/
|
||||||
|
fun requestIfNoPermission(permission: String, activity: ComponentActivity, callback: (Boolean, Boolean) -> Unit) {
|
||||||
|
val available = canAskForPermission(permission, activity)
|
||||||
|
|
||||||
|
if (hasPermission(permission)) {
|
||||||
|
callback(true, false)
|
||||||
|
} else if (available) { // no permission but can request
|
||||||
|
requestPermission(permission, activity) { callback(it, true) }
|
||||||
|
} else { // no permission and can't request
|
||||||
|
callback(false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO should this be private? I mean it doesn't check for other stuff so it's a waste to register an activity if we don't have to
|
||||||
|
private fun requestPermission(permission: String, activity: ComponentActivity, callback: (Boolean) -> Unit) {
|
||||||
|
activity.registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission(),
|
||||||
|
callback
|
||||||
|
).launch(permission)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,6 @@ import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.animateColor
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.Spring
|
import androidx.compose.animation.core.Spring
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
@ -48,7 +47,6 @@ import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
@ -65,12 +63,12 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import eu.m724.vastapp.BuildConfig
|
import eu.m724.vastapp.BuildConfig
|
||||||
import eu.m724.vastapp.R
|
import eu.m724.vastapp.R
|
||||||
|
import eu.m724.vastapp.activity.PermissionChecker
|
||||||
import eu.m724.vastapp.activity.dashboard.DashboardActivity
|
import eu.m724.vastapp.activity.dashboard.DashboardActivity
|
||||||
import eu.m724.vastapp.ui.theme.VastappTheme
|
import eu.m724.vastapp.ui.theme.VastappTheme
|
||||||
import eu.m724.vastapp.vastai.data.User
|
import eu.m724.vastapp.vastai.data.User
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.chromium.net.CronetEngine
|
import org.chromium.net.CronetEngine
|
||||||
import org.w3c.dom.Text
|
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
@ -80,9 +78,30 @@ class LoginActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
// TODO move this where and run this when we need it
|
||||||
|
val permissionChecker = PermissionChecker(applicationContext)
|
||||||
|
if (!permissionChecker.permissionExists("com.termux.permission.RUN_COMMAND")) {
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
R.string.no_termux,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
} else {
|
||||||
|
permissionChecker.requestIfNoPermission("com.termux.permission.RUN_COMMAND", this) { granted, asked ->
|
||||||
|
if (granted || !asked) return@requestIfNoPermission
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
applicationContext,
|
||||||
|
getString(R.string.command_permission_denied),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
dashboardLauncher = registerForActivityResult(
|
dashboardLauncher = registerForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) { result -> finish() } // TODO re-login here
|
) { _ -> finish() } // TODO re-login here
|
||||||
|
|
||||||
val executor = Executors.newSingleThreadExecutor()
|
val executor = Executors.newSingleThreadExecutor()
|
||||||
val cronetEngine = CronetEngine.Builder(baseContext).build()
|
val cronetEngine = CronetEngine.Builder(baseContext).build()
|
||||||
|
@ -94,7 +113,7 @@ class LoginActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch { // TODO I was suggested not to launch an activity from a lifecyclescope
|
lifecycleScope.launch { // TODO I was suggested not to launch an activity from a lifecycle scope
|
||||||
loginViewModel.uiState.collect { state ->
|
loginViewModel.uiState.collect { state ->
|
||||||
if (state is LoginUiState.Success) {
|
if (state is LoginUiState.Success) {
|
||||||
loadApp(state.user)
|
loadApp(state.user)
|
||||||
|
@ -134,12 +153,11 @@ class LoginActivity : ComponentActivity() {
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoginApp(loginViewModel: LoginViewModel) {
|
fun LoginApp(loginViewModel: LoginViewModel) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val uiState by loginViewModel.uiState.collectAsState()
|
val uiState by loginViewModel.uiState.collectAsState()
|
||||||
val loginErrorMessage by loginViewModel.error.observeAsState() // TODO put this in uistate
|
val loginErrorMessage by loginViewModel.error.observeAsState() // TODO put this in uiState
|
||||||
val isIdle by remember(uiState) { derivedStateOf { uiState !is LoginUiState.Loading } }
|
val isIdle by remember(uiState) { derivedStateOf { uiState !is LoginUiState.Loading } }
|
||||||
|
|
||||||
var apiKey by rememberSaveable { mutableStateOf(BuildConfig.VASTAI_KEY ?: "") }
|
var apiKey by rememberSaveable { mutableStateOf(BuildConfig.VASTAI_KEY) }
|
||||||
var advancedOpen by rememberSaveable { mutableStateOf(false) }
|
var advancedOpen by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
val transition = updateTransition(targetState = advancedOpen, label = "Advanced Menu Transition")
|
val transition = updateTransition(targetState = advancedOpen, label = "Advanced Menu Transition")
|
||||||
|
|
|
@ -20,4 +20,6 @@
|
||||||
<string name="login_checkbox_20">having fun?</string>
|
<string name="login_checkbox_20">having fun?</string>
|
||||||
<string name="login_checkbox_angry">checkbox is angry</string>
|
<string name="login_checkbox_angry">checkbox is angry</string>
|
||||||
<string name="no_options">none yet sorry</string>
|
<string name="no_options">none yet sorry</string>
|
||||||
|
<string name="command_permission_denied">If you change your mind, do so from settings</string>
|
||||||
|
<string name="no_termux">Termuxn\'t</string>
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in a new issue