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
 | 
			
		||||
        buildConfigField("String", "VASTAI_KEY", "null")
 | 
			
		||||
        buildConfigField("String", "VASTAI_KEY", "\"\"")
 | 
			
		||||
        buildConfigField("String", "VASIAI_API_ENDPOINT", "\"https://cloud.vast.ai/api/v0\"")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
    xmlns:tools="http://schemas.android.com/tools">
 | 
			
		||||
 | 
			
		||||
    <uses-permission android:name="android.permission.INTERNET" />
 | 
			
		||||
    <uses-permission android:name="com.termux.permission.RUN_COMMAND" />
 | 
			
		||||
 | 
			
		||||
    <application
 | 
			
		||||
        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.contract.ActivityResultContracts
 | 
			
		||||
import androidx.compose.animation.AnimatedVisibility
 | 
			
		||||
import androidx.compose.animation.animateColor
 | 
			
		||||
import androidx.compose.animation.animateContentSize
 | 
			
		||||
import androidx.compose.animation.core.Spring
 | 
			
		||||
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.mutableStateOf
 | 
			
		||||
import androidx.compose.runtime.remember
 | 
			
		||||
import androidx.compose.runtime.rememberCoroutineScope
 | 
			
		||||
import androidx.compose.runtime.saveable.rememberSaveable
 | 
			
		||||
import androidx.compose.runtime.setValue
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
| 
						 | 
				
			
			@ -65,12 +63,12 @@ import androidx.compose.ui.unit.dp
 | 
			
		|||
import androidx.lifecycle.lifecycleScope
 | 
			
		||||
import eu.m724.vastapp.BuildConfig
 | 
			
		||||
import eu.m724.vastapp.R
 | 
			
		||||
import eu.m724.vastapp.activity.PermissionChecker
 | 
			
		||||
import eu.m724.vastapp.activity.dashboard.DashboardActivity
 | 
			
		||||
import eu.m724.vastapp.ui.theme.VastappTheme
 | 
			
		||||
import eu.m724.vastapp.vastai.data.User
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import org.chromium.net.CronetEngine
 | 
			
		||||
import org.w3c.dom.Text
 | 
			
		||||
import java.util.concurrent.Executors
 | 
			
		||||
import kotlin.random.Random
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -80,9 +78,30 @@ class LoginActivity : ComponentActivity() {
 | 
			
		|||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        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(
 | 
			
		||||
            ActivityResultContracts.StartActivityForResult()
 | 
			
		||||
        ) { result -> finish() } // TODO re-login here
 | 
			
		||||
        ) { _ -> finish() } // TODO re-login here
 | 
			
		||||
 | 
			
		||||
        val executor = Executors.newSingleThreadExecutor()
 | 
			
		||||
        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 ->
 | 
			
		||||
                if (state is LoginUiState.Success) {
 | 
			
		||||
                    loadApp(state.user)
 | 
			
		||||
| 
						 | 
				
			
			@ -134,12 +153,11 @@ class LoginActivity : ComponentActivity() {
 | 
			
		|||
 | 
			
		||||
@Composable
 | 
			
		||||
fun LoginApp(loginViewModel: LoginViewModel) {
 | 
			
		||||
    val coroutineScope = rememberCoroutineScope()
 | 
			
		||||
    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 } }
 | 
			
		||||
 | 
			
		||||
    var apiKey by rememberSaveable { mutableStateOf(BuildConfig.VASTAI_KEY ?: "") }
 | 
			
		||||
    var apiKey by rememberSaveable { mutableStateOf(BuildConfig.VASTAI_KEY) }
 | 
			
		||||
    var advancedOpen by rememberSaveable { mutableStateOf(false) }
 | 
			
		||||
 | 
			
		||||
    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_angry">checkbox is angry</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>
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue