Merge pull request #1660 from oxen-io/feature/strings-fixes

SES-2688 SES-2689
This commit is contained in:
ThomasSession 2024-09-10 11:14:47 +10:00 committed by GitHub
commit 2ebd20c31e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 114 additions and 85 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

@ -26,14 +26,7 @@ fun showMuteDialog(
context.getSubbedString(entry.stringRes, TIME_LARGE_KEY to largeTimeUnitString)
}
}.toTypedArray()) {
// Note: We add the current timestamp to the mute duration to get the un-mute timestamp
// that gets stored in the database via ConversationMenuHelper.mute().
// Also: This is a kludge, but we ADD one second to the mute duration because otherwise by
// the time the view for how long the conversation is muted for gets set then it's actually
// less than the entire duration - so 1 hour becomes 59 minutes, 1 day becomes 23 hours etc.
// As we really want to see the actual set time (1 hour / 1 day etc.) then we'll bump it by
// 1 second which is neither here nor there in the grand scheme of things.
onMuteDuration(Option.entries[it].getTime() + System.currentTimeMillis() + 1.seconds.inWholeMilliseconds)
onMuteDuration(Option.entries[it].getTime())
}
}
@ -44,5 +37,12 @@ private enum class Option(@StringRes val stringRes: Int, val getTime: () -> Long
SEVEN_DAYS(R.string.notificationsMuteFor, duration = TimeUnit.DAYS.toMillis(7)),
FOREVER(R.string.notificationsMute, getTime = { Long.MAX_VALUE } );
constructor(@StringRes stringRes: Int, duration: Long): this(stringRes, { duration } )
// Note: We add the current timestamp to the mute duration to get the un-mute timestamp
// that gets stored in the database via ConversationMenuHelper.mute().
// Also: This is a kludge, but we ADD one second to the mute duration because otherwise by
// the time the view for how long the conversation is muted for gets set then it's actually
// less than the entire duration - so 1 hour becomes 59 minutes, 1 day becomes 23 hours etc.
// As we really want to see the actual set time (1 hour / 1 day etc.) then we'll bump it by
// 1 second which is neither here nor there in the grand scheme of things.
constructor(@StringRes stringRes: Int, duration: Long): this(stringRes, { duration + System.currentTimeMillis() + 1.seconds.inWholeMilliseconds } )
}

View File

@ -125,9 +125,7 @@ class ConversationActionBarView @JvmOverloads constructor(
settings += ConversationSetting(
recipient.mutedUntil.takeUnless { it == Long.MAX_VALUE }
?.let {
val mutedDuration = (it - System.currentTimeMillis()).milliseconds
val durationString = LocalisedTimeUtil.getDurationWithSingleLargestTimeUnit(context, mutedDuration)
context.getSubbedString(R.string.notificationsMuteFor, TIME_LARGE_KEY to durationString)
context.getString(R.string.notificationsHeaderMute)
}
?: context.getString(R.string.notificationsMuted),
ConversationSettingType.NOTIFICATION,

View File

@ -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)
@ -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

@ -547,6 +547,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
} else {
showMuteDialog(this) { until ->
lifecycleScope.launch(Dispatchers.IO) {
Log.d("", "**** until: $until")
recipientDatabase.setMuted(thread.recipient, until)
withContext(Dispatchers.Main) {
binding.recyclerView.adapter!!.notifyDataSetChanged()

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