SES-2688 SES-2689

Reworking permission logic
Added the ability to copy build number from debug menu (requested by Miki)
This commit is contained in:
ThomasSession 2024-09-09 18:13:25 +10:00
parent fe7d3ef7fe
commit 150208cc56
8 changed files with 101 additions and 71 deletions

View File

@ -412,14 +412,9 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
Permissions.with(this)
.request(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P)
.withPermanentDenialDialog(Phrase.from(getApplicationContext(), R.string.permissionsStorageSaveDenied)
.put(APP_NAME_KEY, getString(R.string.app_name))
.format().toString())
.withPermanentDenialDialog(getPermanentlyDeniedStorageText())
.onAnyDenied(() -> {
String txt = Phrase.from(getApplicationContext(), R.string.permissionsStorageSaveDenied)
.put(APP_NAME_KEY, getString(R.string.app_name))
.format().toString();
Toast.makeText(this, txt, Toast.LENGTH_LONG).show();
Toast.makeText(this, getPermanentlyDeniedStorageText(), Toast.LENGTH_LONG).show();
})
.onAllGranted(() -> {
SaveAttachmentTask saveTask = new SaveAttachmentTask(MediaPreviewActivity.this);
@ -436,6 +431,12 @@ public class MediaPreviewActivity extends PassphraseRequiredActionBarActivity im
});
}
private String getPermanentlyDeniedStorageText(){
return Phrase.from(getApplicationContext(), R.string.permissionsStorageDeniedLegacy)
.put(APP_NAME_KEY, getString(R.string.app_name))
.format().toString();
}
private void sendMediaSavedNotificationIfNeeded() {
if (conversationRecipient.isGroupRecipient()) return;
DataExtractionNotification message = new DataExtractionNotification(new DataExtractionNotification.Kind.MediaSaved(SnodeAPI.getNowWithOffset()));

View File

@ -2279,7 +2279,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P) // P is 28
.withPermanentDenialDialog(Phrase.from(applicationContext, R.string.permissionsStorageSaveDenied)
.withPermanentDenialDialog(Phrase.from(applicationContext, R.string.permissionsStorageDeniedLegacy)
.put(APP_NAME_KEY, getString(R.string.app_name))
.format().toString())
.onAnyDenied {
@ -2289,7 +2289,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
showSessionDialog {
title(R.string.permissionsRequired)
val txt = Phrase.from(applicationContext, R.string.permissionsStorageSaveDenied)
val txt = Phrase.from(applicationContext, R.string.permissionsStorageDeniedLegacy)
.put(APP_NAME_KEY, getString(R.string.app_name))
.format().toString()
text(txt)

View File

@ -245,51 +245,58 @@ public class AttachmentManager {
public static void selectDocument(Activity activity, int requestCode) {
Permissions.PermissionsBuilder builder = Permissions.with(activity);
Context c = activity.getApplicationContext();
// The READ_EXTERNAL_STORAGE permission is deprecated (and will AUTO-FAIL if requested!) on
// Android 13 and above (API 33 - 'Tiramisu') we must ask for READ_MEDIA_VIDEO/IMAGES/AUDIO instead.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
builder = builder.request(Manifest.permission.READ_MEDIA_VIDEO)
.request(Manifest.permission.READ_MEDIA_IMAGES)
.request(Manifest.permission.READ_MEDIA_AUDIO);
.request(Manifest.permission.READ_MEDIA_AUDIO)
.withRationaleDialog(
Phrase.from(c, R.string.permissionsStorageSend)
.put(APP_NAME_KEY, c.getString(R.string.app_name)).format().toString()
)
.withPermanentDenialDialog(
Phrase.from(c, R.string.permissionMusicAudioDenied)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString()
);
} else {
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE);
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE)
.withPermanentDenialDialog(
Phrase.from(c, R.string.permissionsStorageDeniedLegacy)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString()
);
}
Context c = activity.getApplicationContext();
String needStoragePermissionTxt = Phrase.from(c, R.string.permissionsStorageSend).put(APP_NAME_KEY, c.getString(R.string.app_name)).format().toString();
String storagePermissionDeniedTxt = Phrase.from(c, R.string.permissionsStorageSaveDenied)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString();
builder.withPermanentDenialDialog(storagePermissionDeniedTxt)
.withRationaleDialog(needStoragePermissionTxt, R.drawable.ic_baseline_photo_library_24)
.onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) // Note: We can use startActivityForResult w/ the ACTION_OPEN_DOCUMENT or ACTION_OPEN_DOCUMENT_TREE intent if we need to modernise this.
builder.onAllGranted(() -> selectMediaType(activity, "*/*", null, requestCode)) // Note: We can use startActivityForResult w/ the ACTION_OPEN_DOCUMENT or ACTION_OPEN_DOCUMENT_TREE intent if we need to modernise this.
.execute();
}
public static void selectGallery(Activity activity, int requestCode, @NonNull Recipient recipient, @NonNull String body) {
Context c = activity.getApplicationContext();
String needStoragePermissionTxt = Phrase.from(c, R.string.permissionsStorageSend)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString();
String cameraPermissionDeniedTxt = Phrase.from(c, R.string.cameraGrantAccessDenied)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString();
Permissions.PermissionsBuilder builder = Permissions.with(activity);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
builder = builder.request(Manifest.permission.READ_MEDIA_VIDEO)
.request(Manifest.permission.READ_MEDIA_IMAGES);
.request(Manifest.permission.READ_MEDIA_IMAGES)
.withPermanentDenialDialog(
Phrase.from(c, R.string.permissionsStorageDenied)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString()
);
} else {
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE);
builder = builder.request(Manifest.permission.READ_EXTERNAL_STORAGE)
.withPermanentDenialDialog(
Phrase.from(c, R.string.permissionsStorageDeniedLegacy)
.put(APP_NAME_KEY, c.getString(R.string.app_name))
.format().toString()
);
}
builder.withPermanentDenialDialog(cameraPermissionDeniedTxt)
.withRationaleDialog(needStoragePermissionTxt, R.drawable.ic_baseline_photo_library_24)
.onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body), requestCode))
builder.onAllGranted(() -> activity.startActivityForResult(MediaSendActivity.buildGalleryIntent(activity, recipient, body), requestCode))
.execute();
}

View File

@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.debugmenu
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer
@ -19,6 +20,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import network.loki.messenger.BuildConfig
import network.loki.messenger.R
@ -45,7 +48,7 @@ fun DebugMenu(
sendCommand: (DebugMenuViewModel.Commands) -> Unit,
modifier: Modifier = Modifier,
onClose: () -> Unit
){
) {
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
@ -56,7 +59,7 @@ fun DebugMenu(
) { contentPadding ->
// display a snackbar when required
LaunchedEffect(uiState.snackMessage) {
if(!uiState.snackMessage.isNullOrEmpty()){
if (!uiState.snackMessage.isNullOrEmpty()) {
snackbarHostState.showSnackbar(uiState.snackMessage)
}
}
@ -102,13 +105,22 @@ fun DebugMenu(
.verticalScroll(rememberScrollState())
) {
// Info pane
DebugCell("App Info") {
val clipboardManager = LocalClipboardManager.current
val appVersion = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE} - ${
BuildConfig.GIT_HASH.take(
6
)
})"
DebugCell(
modifier = Modifier.clickable {
// clicking the cell copies the version number to the clipboard
clipboardManager.setText(AnnotatedString(appVersion))
},
title = "App Info"
) {
Text(
text = "Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE} - ${
BuildConfig.GIT_HASH.take(
6
)
})",
text = "Version: $appVersion",
style = LocalType.current.base
)
}
@ -155,7 +167,7 @@ fun ColumnScope.DebugCell(
@Preview
@Composable
fun PreviewDebugMenu(){
fun PreviewDebugMenu() {
PreviewTheme {
DebugMenu(
uiState = DebugMenuViewModel.UIState(

View File

@ -73,7 +73,7 @@ public class Permissions {
return this;
}
public PermissionsBuilder withRationaleDialog(@NonNull String message, @NonNull @DrawableRes int... headers) {
public PermissionsBuilder withRationaleDialog(@NonNull String message, @DrawableRes int... headers) {
this.rationalDialogHeader = headers;
this.rationaleDialogMessage = message;
return this;
@ -143,7 +143,7 @@ public class Permissions {
if (!isInTargetSDKRange || permissionObject.hasAll(requestedPermissions)) {
executePreGrantedPermissionsRequest(request);
} else if (rationaleDialogMessage != null && rationalDialogHeader != null) {
} else if (rationaleDialogMessage != null) {
executePermissionsRequestWithRationale(request);
} else {
executePermissionsRequest(request);

View File

@ -4,6 +4,7 @@ import android.content.Context
import android.graphics.Color
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
@ -25,34 +26,44 @@ object RationaleDialog {
onNegative: Runnable,
@DrawableRes vararg drawables: Int
): AlertDialog {
val view = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null)
.apply { clipToOutline = true }
val header = view.findViewById<ViewGroup>(R.id.header_container)
view.findViewById<TextView>(R.id.message).text = message
var customView: View? = null
if (!drawables.isEmpty()) {
customView = LayoutInflater.from(context).inflate(R.layout.permissions_rationale_dialog, null)
.apply { clipToOutline = true }
val header = customView.findViewById<ViewGroup>(R.id.header_container)
fun addIcon(id: Int) {
ImageView(context).apply {
setImageDrawable(ResourcesCompat.getDrawable(context.resources, id, context.theme))
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
}.also(header::addView)
customView.findViewById<TextView>(R.id.message).text = message
fun addIcon(id: Int) {
ImageView(context).apply {
setImageDrawable(ResourcesCompat.getDrawable(context.resources, id, context.theme))
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
}.also(header::addView)
}
fun addPlus() {
TextView(context).apply {
text = "+"
setTextSize(TypedValue.COMPLEX_UNIT_SP, 40f)
setTextColor(Color.WHITE)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
ViewUtil.dpToPx(context, 20).let { setMargins(it, 0, it, 0) }
}
}.also(header::addView)
}
drawables.firstOrNull()?.let(::addIcon)
drawables.drop(1).forEach { addPlus(); addIcon(it) }
}
fun addPlus() {
TextView(context).apply {
text = "+"
setTextSize(TypedValue.COMPLEX_UNIT_SP, 40f)
setTextColor(Color.WHITE)
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
ViewUtil.dpToPx(context, 20).let { setMargins(it, 0, it, 0) }
}
}.also(header::addView)
}
drawables.firstOrNull()?.let(::addIcon)
drawables.drop(1).forEach { addPlus(); addIcon(it) }
return context.showSessionDialog {
view(view)
// show the generic title when there are no icons
if(customView != null){
view(customView)
} else {
title(R.string.permissionsRequired)
text(message)
}
button(R.string.theContinue) { onPositive.run() }
button(R.string.notNow) { onNegative.run() }
}

View File

@ -98,10 +98,10 @@ class HelpSettingsFragment: CorrectedPreferenceFragment() {
Permissions.with(this)
.request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.maxSdkVersion(Build.VERSION_CODES.P)
.withPermanentDenialDialog(requireContext().getSubbedString(R.string.permissionsStorageSaveDenied, APP_NAME_KEY to getString(R.string.app_name)))
.withPermanentDenialDialog(requireContext().getSubbedString(R.string.permissionsStorageDeniedLegacy, APP_NAME_KEY to getString(R.string.app_name)))
.onAnyDenied {
val c = requireContext()
val txt = c.getSubbedString(R.string.permissionsStorageSaveDenied, APP_NAME_KEY to getString(R.string.app_name))
val txt = c.getSubbedString(R.string.permissionsStorageDeniedLegacy, APP_NAME_KEY to getString(R.string.app_name))
Toast.makeText(c, txt, Toast.LENGTH_LONG).show()
}
.onAllGranted {

View File

@ -63,7 +63,6 @@ import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.StringSubstitutionConstants.APP_NAME_KEY
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.mediasend.MediaSendActivity
import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.ui.AlertDialog
import org.thoughtcrime.securesms.ui.DialogButtonModel