Move permission request into compose

This commit is contained in:
Damontecres 2025-10-15 17:20:27 -04:00
parent d1441db422
commit bdddbab3f9
No known key found for this signature in database
2 changed files with 57 additions and 46 deletions

View file

@ -1,5 +1,8 @@
package com.github.damontecres.wholphin.ui.setup package com.github.damontecres.wholphin.ui.setup
import android.Manifest
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.focusable import androidx.compose.foundation.focusable
import androidx.compose.foundation.gestures.scrollBy import androidx.compose.foundation.gestures.scrollBy
@ -19,8 +22,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
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.input.key.Key import androidx.compose.ui.input.key.Key
@ -96,6 +101,17 @@ fun InstallUpdatePage(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.init(preferences.appPreferences.updateUrl) viewModel.init(preferences.appPreferences.updateUrl)
} }
var permissions by remember { mutableStateOf(viewModel.updater.hasPermissions()) }
val launcher =
rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission(),
) { isGranted: Boolean ->
if (isGranted) {
permissions = true
} else {
// TODO
}
}
when (val state = loading) { when (val state = loading) {
is LoadingState.Error -> ErrorMessage(state, modifier) is LoadingState.Error -> ErrorMessage(state, modifier)
LoadingState.Loading, LoadingState.Loading,
@ -109,7 +125,11 @@ fun InstallUpdatePage(
currentVersion = viewModel.currentVersion, currentVersion = viewModel.currentVersion,
release = it, release = it,
onInstallRelease = { onInstallRelease = {
viewModel.installRelease(it) if (!permissions) {
launcher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
} else {
viewModel.installRelease(it)
}
}, },
onCancel = { onCancel = {
viewModel.navigationManager.goBack() viewModel.navigationManager.goBack()
@ -197,8 +217,10 @@ fun InstallUpdatePageContent(
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), shape = RoundedCornerShape(16.dp)) .background(
.padding(16.dp), MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
shape = RoundedCornerShape(16.dp),
).padding(16.dp),
) { ) {
Text( Text(
text = "Update available", text = "Update available",

View file

@ -10,14 +10,12 @@ import android.os.Build
import android.os.Environment import android.os.Environment
import android.provider.MediaStore import android.provider.MediaStore
import android.widget.Toast import android.widget.Toast
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.hilt.StandardOkHttpClient import com.github.damontecres.wholphin.hilt.StandardOkHttpClient
import com.github.damontecres.wholphin.ui.findActivity
import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.showToast import com.github.damontecres.wholphin.ui.showToast
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
@ -173,7 +171,6 @@ class UpdateChecker
} }
suspend fun installRelease(release: Release) { suspend fun installRelease(release: Release) {
val activity = context.findActivity()!!
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
cleanup() cleanup()
val request = val request =
@ -209,59 +206,40 @@ class UpdateChecker
} }
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
intent.data = uri intent.data = uri
context.startActivity(intent) context.startActivity(intent)
} else { } else {
Timber.e("Resolver URI is null, trying fallback") Timber.e("Resolver URI is null, trying fallback")
// showToast(context, "Unable to download the apk") // showToast(context, "Unable to download the apk")
val targetFile = fallbackDownload(it) val targetFile = fallbackDownload(it)
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
intent.data =
FileProvider.getUriForFile(
context,
context.packageName + ".provider",
targetFile,
)
context.startActivity(intent)
}
} else {
val targetFile = fallbackDownload(it)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.data = intent.data =
FileProvider.getUriForFile( FileProvider.getUriForFile(
activity, context,
activity.packageName + ".provider", context.packageName + ".provider",
targetFile, targetFile,
) )
activity.startActivity(intent) context.startActivity(intent)
}
} else {
if (ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
) != PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_EXTERNAL_STORAGE,
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
activity,
arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
),
PERMISSION_REQUEST_CODE,
)
} else { } else {
val targetFile = fallbackDownload(it) val intent = Intent(Intent.ACTION_VIEW)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { intent.setDataAndType(Uri.fromFile(targetFile), APK_MIME_TYPE)
val intent = Intent(Intent.ACTION_INSTALL_PACKAGE) intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) context.startActivity(intent)
intent.data =
FileProvider.getUriForFile(
activity,
activity.packageName + ".provider",
targetFile,
)
activity.startActivity(intent)
} else {
val intent = Intent(Intent.ACTION_VIEW)
intent.setDataAndType(Uri.fromFile(targetFile), APK_MIME_TYPE)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
activity.startActivity(intent)
}
} }
} }
} else { } else {
@ -283,6 +261,17 @@ class UpdateChecker
return targetFile return targetFile
} }
fun hasPermissions(): Boolean =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ||
ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
) == PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_EXTERNAL_STORAGE,
) == PackageManager.PERMISSION_GRANTED
/** /**
* Delete previously downloaded APKs * Delete previously downloaded APKs
*/ */