Add prompt to re-enable full screen intent notifications.

This commit is contained in:
Cody Henthorne 2023-10-20 13:52:39 -04:00
parent d866646f66
commit d9ecfeadc0
5 changed files with 138 additions and 23 deletions

View file

@ -106,7 +106,7 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
textId = R.string.NotificationSettingsFragment__to_enable_notifications,
actionId = R.string.NotificationSettingsFragment__turn_on,
onClick = {
TurnOnNotificationsBottomSheet().show(childFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
TurnOnNotificationsBottomSheet.turnOnSystemNotificationsFragment(requireContext()).show(childFragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
}
)
)

View file

@ -8,6 +8,7 @@ import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import androidx.core.app.NotificationManagerCompat;
import com.annimon.stream.Stream;
@ -109,6 +110,7 @@ public final class Megaphones {
put(Event.PINS_FOR_ALL, new PinsForAllSchedule());
put(Event.CLIENT_DEPRECATED, SignalStore.misc().isClientDeprecated() ? ALWAYS : NEVER);
put(Event.NOTIFICATIONS, shouldShowNotificationsMegaphone(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(30)) : NEVER);
put(Event.GRANT_FULL_SCREEN_INTENT, shouldShowGrantFullScreenIntentPermission(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(3)) : NEVER);
put(Event.SMS_EXPORT, new SmsExportReminderSchedule(context));
put(Event.BACKUP_SCHEDULE_PERMISSION, shouldShowBackupSchedulePermissionMegaphone(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(3)) : NEVER);
put(Event.ONBOARDING, shouldShowOnboardingMegaphone(context) ? ALWAYS : NEVER);
@ -151,6 +153,8 @@ public final class Megaphones {
return buildSmsExportMegaphone(context);
case SET_UP_YOUR_USERNAME:
return buildSetUpYourUsernameMegaphone(context);
case GRANT_FULL_SCREEN_INTENT:
return buildGrantFullScreenIntentPermission(context);
default:
throw new IllegalArgumentException("Event not handled!");
@ -233,7 +237,7 @@ public final class Megaphones {
.setImage(R.drawable.megaphone_notifications_64)
.setActionButton(R.string.NotificationsMegaphone_turn_on, (megaphone, controller) -> {
if (Build.VERSION.SDK_INT >= 26) {
controller.onMegaphoneDialogFragmentRequested(new TurnOnNotificationsBottomSheet());
controller.onMegaphoneDialogFragmentRequested(TurnOnNotificationsBottomSheet.turnOnSystemNotificationsFragment(context));
} else {
controller.onMegaphoneNavigationRequested(AppSettingsActivity.notifications(context));
}
@ -387,6 +391,20 @@ public final class Megaphones {
.build();
}
public static @NonNull Megaphone buildGrantFullScreenIntentPermission(@NonNull Context context) {
return new Megaphone.Builder(Event.GRANT_FULL_SCREEN_INTENT, Megaphone.Style.BASIC)
.setTitle(R.string.GrantFullScreenIntentPermission_megaphone_title)
.setBody(R.string.GrantFullScreenIntentPermission_megaphone_body)
.setImage(R.drawable.calling_64)
.setActionButton(R.string.GrantFullScreenIntentPermission_megaphone_turn_on, (megaphone, controller) -> {
controller.onMegaphoneDialogFragmentRequested(TurnOnNotificationsBottomSheet.turnOnFullScreenIntentFragment(context));
})
.setSecondaryButton(R.string.SetUpYourUsername__not_now, (megaphone, controller) -> {
controller.onMegaphoneCompleted(Event.GRANT_FULL_SCREEN_INTENT);
})
.build();
}
private static boolean shouldShowDonateMegaphone(@NonNull Context context, @NonNull Event event, @NonNull Map<Event, MegaphoneRecord> records) {
long timeSinceLastDonatePrompt = timeSinceLastDonatePrompt(event, records);
@ -461,6 +479,10 @@ public final class Megaphones {
(System.currentTimeMillis() - phoneNumberDiscoveryDisabledAt) >= TimeUnit.DAYS.toMillis(3);
}
private static boolean shouldShowGrantFullScreenIntentPermission(@NonNull Context context) {
return Build.VERSION.SDK_INT >= 34 && !NotificationManagerCompat.from(context).canUseFullScreenIntent();
}
@WorkerThread
private static boolean shouldShowRemoteMegaphone(@NonNull Map<Event, MegaphoneRecord> records) {
boolean canShowLocalDonate = timeSinceLastDonatePrompt(Event.REMOTE_MEGAPHONE, records) > MIN_TIME_BETWEEN_DONATE_MEGAPHONES;
@ -502,7 +524,8 @@ public final class Megaphones {
REMOTE_MEGAPHONE("remote_megaphone"),
BACKUP_SCHEDULE_PERMISSION("backup_schedule_permission"),
SMS_EXPORT("sms_export"),
SET_UP_YOUR_USERNAME("set_up_your_username");
SET_UP_YOUR_USERNAME("set_up_your_username"),
GRANT_FULL_SCREEN_INTENT("grant_full_screen_intent");
private final String key;

View file

@ -5,9 +5,12 @@
package org.thoughtcrime.securesms.notifications
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.annotation.RequiresApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
@ -32,6 +35,8 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.core.os.BundleCompat
import androidx.core.os.bundleOf
import org.signal.core.ui.BottomSheets
import org.signal.core.ui.Buttons
import org.signal.core.ui.theme.SignalTheme
@ -44,27 +49,67 @@ private const val PLACEHOLDER = "__TOGGLE_PLACEHOLDER__"
/**
* Sheet explaining how to turn on notifications and providing an action to do so.
*/
class TurnOnNotificationsBottomSheet : ComposeBottomSheetDialogFragment() {
class TurnOnNotificationsBottomSheet private constructor() : ComposeBottomSheetDialogFragment() {
companion object {
private const val ARG_TITLE = "argument.title_res"
private const val ARG_SUBTITLE = "argument.subtitle_res"
private const val ARG_STEP2 = "argument.step2_res"
private const val ARG_SETTINGS_INTENT = "argument.settings_intent"
@JvmStatic
fun turnOnSystemNotificationsFragment(context: Context): ComposeBottomSheetDialogFragment {
return TurnOnNotificationsBottomSheet().apply {
arguments = bundleOf(
ARG_TITLE to R.string.TurnOnNotificationsBottomSheet__turn_on_notifications,
ARG_SUBTITLE to R.string.TurnOnNotificationsBottomSheet__to_receive_notifications,
ARG_STEP2 to R.string.TurnOnNotificationsBottomSheet__2_s_turn_on_notifications,
ARG_SETTINGS_INTENT to getNotificationsSettingsIntent(context)
)
}
}
@JvmStatic
@RequiresApi(34)
fun turnOnFullScreenIntentFragment(context: Context): ComposeBottomSheetDialogFragment {
return TurnOnNotificationsBottomSheet().apply {
arguments = bundleOf(
ARG_TITLE to R.string.GrantFullScreenIntentPermission_bottomsheet_title,
ARG_SUBTITLE to R.string.GrantFullScreenIntentPermission_bottomsheet_subtitle,
ARG_STEP2 to R.string.GrantFullScreenIntentPermission_bottomsheet_step2,
ARG_SETTINGS_INTENT to Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT, Uri.parse("package:" + context.packageName))
)
}
}
private fun getNotificationsSettingsIntent(context: Context): Intent {
return if (Build.VERSION.SDK_INT >= 26 && !NotificationChannels.getInstance().isMessageChannelEnabled) {
Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_CHANNEL_ID, NotificationChannels.getInstance().messagesChannel)
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
}
} else if (Build.VERSION.SDK_INT >= 26 && (!NotificationChannels.getInstance().areNotificationsEnabled() || !NotificationChannels.getInstance().isMessagesChannelGroupEnabled)) {
Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
}
} else {
AppSettingsActivity.notifications(context)
}
}
}
@Composable
override fun SheetContent() {
TurnOnNotificationsSheetContent(this::goToSystemNotificationSettings)
TurnOnNotificationsSheetContent(
titleRes = requireArguments().getInt(ARG_TITLE),
subtitleRes = requireArguments().getInt(ARG_SUBTITLE),
step2Res = requireArguments().getInt(ARG_STEP2),
onGoToSettingsClicked = this::goToSettings
)
}
private fun goToSystemNotificationSettings() {
if (Build.VERSION.SDK_INT >= 26 && !NotificationChannels.getInstance().isMessageChannelEnabled) {
val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationChannels.getInstance().messagesChannel)
intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
startActivity(intent)
} else if (Build.VERSION.SDK_INT >= 26 && (!NotificationChannels.getInstance().areNotificationsEnabled() || !NotificationChannels.getInstance().isMessagesChannelGroupEnabled)) {
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
startActivity(intent)
} else {
startActivity(AppSettingsActivity.notifications(requireContext()))
}
private fun goToSettings() {
startActivity(BundleCompat.getParcelable(requireArguments(), ARG_SETTINGS_INTENT, Intent::class.java)!!)
dismissAllowingStateLoss()
}
}
@ -74,13 +119,20 @@ class TurnOnNotificationsBottomSheet : ComposeBottomSheetDialogFragment() {
private fun TurnOnNotificationsSheetContentPreview() {
SignalTheme(isDarkMode = false) {
Surface {
TurnOnNotificationsSheetContent {}
TurnOnNotificationsSheetContent(
titleRes = R.string.TurnOnNotificationsBottomSheet__turn_on_notifications,
subtitleRes = R.string.TurnOnNotificationsBottomSheet__to_receive_notifications,
step2Res = R.string.TurnOnNotificationsBottomSheet__2_s_turn_on_notifications
) {}
}
}
}
@Composable
private fun TurnOnNotificationsSheetContent(
titleRes: Int,
subtitleRes: Int,
step2Res: Int,
onGoToSettingsClicked: () -> Unit
) {
Column(
@ -93,7 +145,7 @@ private fun TurnOnNotificationsSheetContent(
)
Text(
text = stringResource(R.string.TurnOnNotificationsBottomSheet__turn_on_notifications),
text = stringResource(titleRes),
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier
.align(Alignment.CenterHorizontally)
@ -101,7 +153,7 @@ private fun TurnOnNotificationsSheetContent(
)
Text(
text = stringResource(R.string.TurnOnNotificationsBottomSheet__to_receive_notifications),
text = stringResource(subtitleRes),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.align(Alignment.CenterHorizontally)
@ -113,7 +165,7 @@ private fun TurnOnNotificationsSheetContent(
modifier = Modifier.padding(bottom = 32.dp)
)
val step2String = stringResource(id = R.string.TurnOnNotificationsBottomSheet__2_s_turn_on_notifications, PLACEHOLDER)
val step2String = stringResource(id = step2Res, PLACEHOLDER)
val (step2Text, step2InlineContent) = remember(step2String) {
val parts = step2String.split(PLACEHOLDER)
val annotatedString = buildAnnotatedString {

View file

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<group>
<clip-path
android:pathData="M0,0h64v64h-64z"/>
<path
android:pathData="M32,64C49.673,64 64,49.673 64,32C64,14.327 49.673,0 32,0C14.327,0 0,14.327 0,32V60C0,62.209 1.791,64 4,64H32Z"
android:fillColor="#DCE5F9"
android:fillType="evenOdd"/>
<path
android:pathData="M23.847,15.103C22.359,13.145 19.486,12.95 17.747,14.689L16.876,15.56C14.09,18.346 12.701,22.354 13.978,26.082C15.823,31.464 18.892,36.519 23.186,40.814C27.481,45.108 32.536,48.177 37.918,50.022C41.646,51.299 45.654,49.91 48.44,47.124L49.311,46.253C51.05,44.514 50.855,41.641 48.897,40.153L43.191,35.816C41.575,34.588 39.301,34.742 37.866,36.177L35.382,38.661C34.589,39.454 31.699,37.848 28.925,35.075C26.152,32.301 24.546,29.411 25.338,28.618L27.823,26.134C29.258,24.699 29.412,22.425 28.184,20.809L23.847,15.103Z"
android:fillColor="#FFEABC"/>
<path
android:pathData="M28.083,25.848C29.272,24.41 29.333,22.321 28.184,20.809L23.847,15.103C22.432,13.24 19.762,12.973 18.008,14.449C18.221,20.222 22.511,24.953 28.083,25.848Z"
android:fillColor="#E9C576"/>
<path
android:pathData="M37.524,36.519L37.866,36.177C39.301,34.742 41.575,34.588 43.191,35.816L48.897,40.153C50.417,41.307 50.874,43.296 50.191,44.942C49.799,44.98 49.402,45 49,45C43.597,45 39.028,41.429 37.524,36.519Z"
android:fillColor="#E9C576"/>
<path
android:pathData="M23.051,15.708C21.93,14.232 19.764,14.085 18.454,15.396L17.583,16.267C14.998,18.852 13.799,22.475 14.924,25.757C16.72,30.999 19.709,35.922 23.893,40.106C28.078,44.291 33.001,47.28 38.242,49.076C41.525,50.201 45.148,49.002 47.733,46.417L48.604,45.546C49.915,44.236 49.768,42.07 48.292,40.949L42.586,36.612C41.368,35.687 39.654,35.803 38.573,36.884L36.089,39.369C35.532,39.925 34.769,39.91 34.238,39.807C33.667,39.696 33.04,39.429 32.411,39.079C31.14,38.374 29.651,37.215 28.218,35.782C26.785,34.348 25.626,32.86 24.921,31.589C24.571,30.96 24.304,30.333 24.193,29.761C24.09,29.231 24.074,28.468 24.631,27.911L27.116,25.427C28.197,24.346 28.313,22.632 27.388,21.414L23.051,15.708ZM17.04,13.982C19.207,11.814 22.789,12.057 24.643,14.498L28.98,20.204C30.511,22.218 30.318,25.053 28.53,26.841L26.136,29.235C26.138,29.268 26.144,29.316 26.156,29.381C26.211,29.661 26.369,30.079 26.669,30.618C27.261,31.684 28.292,33.027 29.632,34.368C30.973,35.708 32.316,36.739 33.382,37.331C33.921,37.631 34.339,37.789 34.619,37.844C34.684,37.856 34.732,37.862 34.765,37.864L37.159,35.47C38.947,33.682 41.782,33.489 43.796,35.02L49.502,39.356C51.943,41.211 52.186,44.793 50.018,46.96L49.147,47.831C46.16,50.819 41.767,52.398 37.594,50.968C32.071,49.075 26.884,45.925 22.479,41.521C18.075,37.116 14.925,31.929 13.032,26.406C11.602,22.233 13.181,17.84 16.169,14.853L17.04,13.982L17.747,14.689L17.04,13.982ZM34.828,37.862C34.828,37.862 34.825,37.863 34.817,37.864C34.824,37.862 34.828,37.862 34.828,37.862ZM26.138,29.172C26.138,29.172 26.138,29.176 26.136,29.183C26.137,29.175 26.138,29.172 26.138,29.172Z"
android:fillColor="#9C7A2D"
android:fillType="evenOdd"/>
</group>
</vector>

View file

@ -6366,5 +6366,18 @@
<!-- Content description for confirming a user -->
<string name="PendingParticipantsBottomSheet__approve">Approve</string>
<!-- Title of a megaphone shown at the bottom of the chat list when a user has disable the system setting for showing full screen notifications used showing incoming calls -->
<string name="GrantFullScreenIntentPermission_megaphone_title">Turn on full screen notifications?</string>
<!-- Body of a megaphone shown at the bottom of the chat list when a user has disable the system setting for showing full screen notifications used showing incoming calls -->
<string name="GrantFullScreenIntentPermission_megaphone_body">Never miss a call from your contacts and groups.</string>
<!-- Button on the megaphone megaphone shown at the bottom of the chat list when a user has disable the system setting for showing full screen notifications used showing incoming calls that starts the fix process -->
<string name="GrantFullScreenIntentPermission_megaphone_turn_on">Turn on</string>
<!-- Title of bottom sheet shown after tapping "Turn on" from the megaphone to re-enable full screen notifications for incoming call notifications -->
<string name="GrantFullScreenIntentPermission_bottomsheet_title">Turn on full screen notifications</string>
<!-- Subtitle of bottom sheet shown after tapping "Turn on" from the megaphone to re-enable full screen notifications for incoming call notifications -->
<string name="GrantFullScreenIntentPermission_bottomsheet_subtitle">To receive call notifications from your contacts and groups:</string>
<!-- Step 2 of bottom sheet shown after tapping "Turn on" from the megaphone to re-enable full screen notifications for incoming call notifications, it indicates the name of the setting that needs to be re-enabled -->
<string name="GrantFullScreenIntentPermission_bottomsheet_step2">2. %1$s Allow full screen notifications</string>
<!-- EOF -->
</resources>