Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SES-2688 SES-2689 #1660

Merged
merged 3 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -412,14 +412,9 @@ private void saveToDisk() {
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);
Expand All @@ -436,6 +431,12 @@ private void saveToDisk() {
});
}

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()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2257,8 +2257,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
// that we've warned the user just _once_ that any attachments they save can be accessed by other apps.
val haveWarned = TextSecurePreferences.getHaveWarnedUserAboutSavingAttachments(this)
if (haveWarned) {
// On Android versions below 30 we require the WRITE_EXTERNAL_STORAGE permission to save attachments.
if (Build.VERSION.SDK_INT < 30) {
// On Android versions below 29 we require the WRITE_EXTERNAL_STORAGE permission to save attachments.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
// Save the attachment(s) then bail if we already have permission to do so
if (hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
saveAttachments(message)
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,51 +245,58 @@ SlideDeck buildSlideDeck() {

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();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -45,7 +48,7 @@ fun DebugMenu(
sendCommand: (DebugMenuViewModel.Commands) -> Unit,
modifier: Modifier = Modifier,
onClose: () -> Unit
){
) {
val snackbarHostState = remember { SnackbarHostState() }

Scaffold(
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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
)
}
Expand Down Expand Up @@ -155,7 +167,7 @@ fun ColumnScope.DebugCell(

@Preview
@Composable
fun PreviewDebugMenu(){
fun PreviewDebugMenu() {
PreviewTheme {
DebugMenu(
uiState = DebugMenuViewModel.UIState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public PermissionsBuilder request(String... requestedPermissions) {
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;
Expand Down Expand Up @@ -143,7 +143,7 @@ public void execute() {

if (!isInTargetSDKRange || permissionObject.hasAll(requestedPermissions)) {
executePreGrantedPermissionsRequest(request);
} else if (rationaleDialogMessage != null && rationalDialogHeader != null) {
} else if (rationaleDialogMessage != null) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this safe?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SessionHero01 I modified the rationale dialog to now be able to display without a header, since we now have designs that require it. You can see in the RationaleDialog I added some code to display a generic title when no icons are provided for the header.

executePermissionsRequestWithRationale(request);
} else {
executePermissionsRequest(request);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 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)
}
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)
}

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() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down