Add prompt to help troubleshoot slow notifications.
This commit is contained in:
parent
98ec2cceb4
commit
4cbcee85d6
16 changed files with 329 additions and 10 deletions
|
@ -90,6 +90,8 @@ import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
|||
import org.thoughtcrime.securesms.util.AppStartup;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags;
|
||||
import org.thoughtcrime.securesms.util.PowerManagerCompat;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
|
||||
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.app.Activity;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
@ -16,12 +17,14 @@ import androidx.lifecycle.ViewModelProvider;
|
|||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.components.DebugLogsPromptDialogFragment;
|
||||
import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment;
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaController;
|
||||
import org.thoughtcrime.securesms.components.voice.VoiceNoteMediaControllerOwner;
|
||||
import org.thoughtcrime.securesms.conversationlist.RelinkDevicesReminderBottomSheetFragment;
|
||||
import org.thoughtcrime.securesms.devicetransfer.olddevice.OldDeviceExitActivity;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.net.DeviceTransferBlockingInterceptor;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics;
|
||||
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabRepository;
|
||||
import org.thoughtcrime.securesms.stories.tabs.ConversationListTabsViewModel;
|
||||
|
@ -30,6 +33,7 @@ import org.thoughtcrime.securesms.util.CachedInflater;
|
|||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme;
|
||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||
import org.thoughtcrime.securesms.util.PowerManagerCompat;
|
||||
import org.thoughtcrime.securesms.util.SplashScreenUtil;
|
||||
import org.thoughtcrime.securesms.util.WindowUtil;
|
||||
|
||||
|
@ -139,8 +143,14 @@ public class MainActivity extends PassphraseRequiredActivity implements VoiceNot
|
|||
|
||||
updateTabVisibility();
|
||||
|
||||
if (SlowNotificationHeuristics.shouldPromptUserForLogs()) {
|
||||
DebugLogsPromptDialogFragment.show(this, getSupportFragmentManager());
|
||||
if (SlowNotificationHeuristics.isHavingDelayedNotifications()) {
|
||||
if (SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations() && Build.VERSION.SDK_INT >= 23) {
|
||||
if (SlowNotificationHeuristics.shouldPromptBatterySaver()) {
|
||||
PromptBatterySaverDialogFragment.show(this, getSupportFragmentManager());
|
||||
}
|
||||
} else if (SlowNotificationHeuristics.shouldPromptUserForLogs()) {
|
||||
DebugLogsPromptDialogFragment.show(this, getSupportFragmentManager());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import org.signal.core.util.concurrent.LifecycleDisposable
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.databinding.PromptBatterySaverBottomSheetBinding
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil
|
||||
import org.thoughtcrime.securesms.util.PowerManagerCompat
|
||||
|
||||
@RequiresApi(23)
|
||||
class PromptBatterySaverDialogFragment : FixedRoundedCornerBottomSheetDialogFragment() {
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
fun show(context: Context, fragmentManager: FragmentManager) {
|
||||
if (fragmentManager.findFragmentByTag(BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG) == null) {
|
||||
PromptBatterySaverDialogFragment().apply {
|
||||
arguments = bundleOf()
|
||||
}.show(fragmentManager, BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG)
|
||||
SignalStore.uiHints().lastBatterySaverPrompt = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val peekHeightPercentage: Float = 0.66f
|
||||
override val themeResId: Int = R.style.Widget_Signal_FixedRoundedCorners_Messages
|
||||
|
||||
private val binding by ViewBinderDelegate(PromptBatterySaverBottomSheetBinding::bind)
|
||||
|
||||
private lateinit var viewModel: PromptLogsViewModel
|
||||
|
||||
private val disposables: LifecycleDisposable = LifecycleDisposable()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
return inflater.inflate(R.layout.prompt_battery_saver_bottom_sheet, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
disposables.bindTo(viewLifecycleOwner)
|
||||
|
||||
viewModel = ViewModelProvider(this)[PromptLogsViewModel::class.java]
|
||||
binding.continueButton.setOnClickListener {
|
||||
PowerManagerCompat.requestIgnoreBatteryOptimizations(requireContext())
|
||||
}
|
||||
binding.dismissButton.setOnClickListener {
|
||||
SignalStore.uiHints().markDismissedBatterySaverPrompt()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ import androidx.preference.PreferenceManager
|
|||
import org.signal.core.util.getParcelableExtraCompat
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.components.PromptBatterySaverDialogFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLConfiguration
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsText
|
||||
|
@ -184,6 +185,16 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
|
|||
}
|
||||
)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 23 && state.messageNotificationsState.troubleshootNotifications) {
|
||||
clickPref(
|
||||
title = DSLSettingsText.from(R.string.preferences_notifications__troubleshoot),
|
||||
isEnabled = true,
|
||||
onClick = {
|
||||
PromptBatterySaverDialogFragment.show(requireContext(), childFragmentManager)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < 30) {
|
||||
if (NotificationChannels.supported()) {
|
||||
clickPref(
|
||||
|
|
|
@ -17,7 +17,8 @@ data class MessageNotificationsState(
|
|||
val inChatSoundsEnabled: Boolean,
|
||||
val repeatAlerts: Int,
|
||||
val messagePrivacy: String,
|
||||
val priority: Int
|
||||
val priority: Int,
|
||||
val troubleshootNotifications: Boolean
|
||||
)
|
||||
|
||||
data class CallNotificationsState(
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels
|
||||
import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics
|
||||
import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.livedata.Store
|
||||
|
@ -104,7 +105,8 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer
|
|||
inChatSoundsEnabled = SignalStore.settings().isMessageNotificationsInChatSoundsEnabled,
|
||||
repeatAlerts = SignalStore.settings().messageNotificationsRepeatAlerts,
|
||||
messagePrivacy = SignalStore.settings().messageNotificationsPrivacy.toString(),
|
||||
priority = TextSecurePreferences.getNotificationPriority(ApplicationDependencies.getApplication())
|
||||
priority = TextSecurePreferences.getNotificationPriority(ApplicationDependencies.getApplication()),
|
||||
troubleshootNotifications = SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations() && SlowNotificationHeuristics.isHavingDelayedNotifications()
|
||||
),
|
||||
callNotificationsState = CallNotificationsState(
|
||||
notificationsEnabled = SignalStore.settings().isCallNotificationsEnabled,
|
||||
|
|
|
@ -20,6 +20,8 @@ public class UiHints extends SignalStoreValues {
|
|||
private static final String HAS_SEEN_SAFETY_NUMBER_NUX = "uihints.has_seen_safety_number_nux";
|
||||
private static final String DECLINED_NOTIFICATION_LOGS_PROMPT = "uihints.declined_notification_logs";
|
||||
private static final String LAST_NOTIFICATION_LOGS_PROMPT_TIME = "uihints.last_notification_logs_prompt";
|
||||
private static final String DISMISSED_BATTERY_SAVER_PROMPT = "uihints.declined_battery_saver_prompt";
|
||||
private static final String LAST_BATTERY_SAVER_PROMPT = "uihints.last_battery_saver_prompt";
|
||||
|
||||
UiHints(@NonNull KeyValueStore store) {
|
||||
super(store);
|
||||
|
@ -136,4 +138,20 @@ public class UiHints extends SignalStoreValues {
|
|||
public boolean hasDeclinedToShareNotificationLogs() {
|
||||
return getBoolean(DECLINED_NOTIFICATION_LOGS_PROMPT, false);
|
||||
}
|
||||
|
||||
public void markDismissedBatterySaverPrompt() {
|
||||
putBoolean(DISMISSED_BATTERY_SAVER_PROMPT, true);
|
||||
}
|
||||
|
||||
public boolean hasDismissedBatterySaverPrompt() {
|
||||
return getBoolean(DISMISSED_BATTERY_SAVER_PROMPT, false);
|
||||
}
|
||||
|
||||
public long getLastBatterySaverPrompt() {
|
||||
return getLong(LAST_BATTERY_SAVER_PROMPT, 0);
|
||||
}
|
||||
|
||||
public void setLastBatterySaverPrompt(long time) {
|
||||
putLong(LAST_BATTERY_SAVER_PROMPT, time);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
|||
import org.thoughtcrime.securesms.emoji.EmojiFiles;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.net.StandardUserAgentInterceptor;
|
||||
import org.thoughtcrime.securesms.notifications.SlowNotificationHeuristics;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.service.webrtc.AndroidTelecomUtil;
|
||||
import org.thoughtcrime.securesms.util.AppSignatureUtil;
|
||||
|
@ -88,7 +89,10 @@ public class LogSectionSystemInfo implements LogSection {
|
|||
builder.append("Server Time Offset: ").append(SignalStore.misc().getLastKnownServerTimeOffset()).append(" ms (last updated: ").append(SignalStore.misc().getLastKnownServerTimeOffsetUpdateTime()).append(")").append("\n");
|
||||
builder.append("Telecom : ").append(AndroidTelecomUtil.getTelecomSupported()).append("\n");
|
||||
builder.append("User-Agent : ").append(StandardUserAgentInterceptor.USER_AGENT).append("\n");
|
||||
builder.append("SlowNotifications : ").append(SlowNotificationHeuristics.isHavingDelayedNotifications()).append("\n");
|
||||
builder.append("PotentiallyBattery: ").append(SlowNotificationHeuristics.isPotentiallyCausedByBatteryOptimizations()).append("\n");
|
||||
builder.append("App : ");
|
||||
|
||||
try {
|
||||
builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0)))
|
||||
.append(" ")
|
||||
|
|
|
@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.InAppDona
|
|||
import org.thoughtcrime.securesms.database.model.MegaphoneRecord;
|
||||
import org.thoughtcrime.securesms.database.model.RemoteMegaphoneRecord;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.exporter.flow.SmsExportActivity;
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint;
|
||||
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
|
|
|
@ -5,14 +5,17 @@
|
|||
|
||||
package org.thoughtcrime.securesms.notifications
|
||||
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.database.LocalMetricsDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.util.DeviceProperties
|
||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||
import org.thoughtcrime.securesms.util.JsonUtils
|
||||
import org.thoughtcrime.securesms.util.LocaleFeatureFlags
|
||||
import org.thoughtcrime.securesms.util.PowerManagerCompat
|
||||
import org.thoughtcrime.securesms.util.SignalLocalMetrics
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
@ -63,12 +66,34 @@ object SlowNotificationHeuristics {
|
|||
if (System.currentTimeMillis() - SignalStore.uiHints().lastNotificationLogsPrompt < TimeUnit.DAYS.toMillis(7)) {
|
||||
return false
|
||||
}
|
||||
val configuration = getConfiguration()
|
||||
|
||||
return isHavingDelayedNotifications(configuration)
|
||||
return true
|
||||
}
|
||||
|
||||
fun isHavingDelayedNotifications(configuration: Configuration): Boolean {
|
||||
@JvmStatic
|
||||
fun shouldPromptBatterySaver(): Boolean {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return false
|
||||
}
|
||||
if (!LocaleFeatureFlags.isBatterySaverPromptEnabled() || SignalStore.uiHints().hasDismissedBatterySaverPrompt()) {
|
||||
return false
|
||||
}
|
||||
if (System.currentTimeMillis() - SignalStore.uiHints().lastBatterySaverPrompt < TimeUnit.DAYS.toMillis(7)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isHavingDelayedNotifications(): Boolean {
|
||||
if (!SignalStore.settings().isMessageNotificationsEnabled ||
|
||||
!NotificationChannels.getInstance().areNotificationsEnabled()
|
||||
) {
|
||||
// If user does not have notifications enabled, we shouldn't bother them about delayed notifications
|
||||
return false
|
||||
}
|
||||
val configuration = getConfiguration()
|
||||
val db = LocalMetricsDatabase.getInstance(ApplicationDependencies.getApplication())
|
||||
|
||||
val metrics = db.getMetrics()
|
||||
|
@ -84,6 +109,32 @@ object SlowNotificationHeuristics {
|
|||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the delayed notifications may be due to battery saver optimizations.
|
||||
*
|
||||
* Some OEMs over-optimize this battery restrictions and remove network even after receiving a
|
||||
* high priority push.
|
||||
*
|
||||
* We consider a scenario where removing battery optimizations can fix delayed notifications:
|
||||
* - Data saver must be off (or white listed), otherwise it can be causing delayed notifications
|
||||
* - App must not already be exempted from battery optimizations
|
||||
*
|
||||
* We do not need to check if {ActivityManager#isBackgroundRestricted} is true, because if the app
|
||||
* is set to "Optimized" this will be false (and can be culprit to delayed notifications) or if
|
||||
* true can most definitely be at fault.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun isPotentiallyCausedByBatteryOptimizations(): Boolean {
|
||||
val applicationContext = ApplicationDependencies.getApplication()
|
||||
if (DeviceProperties.getDataSaverState(applicationContext) == DeviceProperties.DataSaverState.ENABLED) {
|
||||
return false
|
||||
}
|
||||
if (PowerManagerCompat.isIgnoringBatteryOptimizations(applicationContext)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun hasRepeatedFailedServiceStarts(metrics: List<LocalMetricsDatabase.EventMetrics>, minimumEventAgeMs: Long, minimumEventCount: Int, failurePercentage: Float): Boolean {
|
||||
if (!haveEnoughData(SignalLocalMetrics.FcmServiceStartSuccess.NAME, minimumEventAgeMs) && !haveEnoughData(SignalLocalMetrics.FcmServiceStartFailure.NAME, minimumEventAgeMs)) {
|
||||
Log.d(TAG, "insufficient data for service starts")
|
||||
|
|
|
@ -111,6 +111,7 @@ public final class FeatureFlags {
|
|||
private static final String SAFETY_NUMBER_ACI = "global.safetyNumberAci";
|
||||
public static final String PROMPT_FOR_NOTIFICATION_LOGS = "android.logs.promptNotifications";
|
||||
private static final String PROMPT_FOR_NOTIFICATION_CONFIG = "android.logs.promptNotificationsConfig";
|
||||
public static final String PROMPT_BATTERY_SAVER = "android.promptBatterySaver";
|
||||
|
||||
/**
|
||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||
|
@ -173,7 +174,8 @@ public final class FeatureFlags {
|
|||
SAFETY_NUMBER_ACI,
|
||||
FCM_MAY_HAVE_MESSAGES_KILL_SWITCH,
|
||||
PROMPT_FOR_NOTIFICATION_LOGS,
|
||||
PROMPT_FOR_NOTIFICATION_CONFIG
|
||||
PROMPT_FOR_NOTIFICATION_CONFIG,
|
||||
PROMPT_BATTERY_SAVER
|
||||
);
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -242,7 +244,8 @@ public final class FeatureFlags {
|
|||
SAFETY_NUMBER_ACI,
|
||||
FCM_MAY_HAVE_MESSAGES_KILL_SWITCH,
|
||||
PROMPT_FOR_NOTIFICATION_LOGS,
|
||||
PROMPT_FOR_NOTIFICATION_CONFIG
|
||||
PROMPT_FOR_NOTIFICATION_CONFIG,
|
||||
PROMPT_BATTERY_SAVER
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -631,6 +634,11 @@ public final class FeatureFlags {
|
|||
public static String delayedNotificationsPromptConfig() {
|
||||
return getString(PROMPT_FOR_NOTIFICATION_CONFIG, "");
|
||||
}
|
||||
|
||||
public static String promptBatterySaver() {
|
||||
return getString(PROMPT_BATTERY_SAVER, "*");
|
||||
}
|
||||
|
||||
/** Only for rendering debug info. */
|
||||
public static synchronized @NonNull Map<String, Object> getMemoryValues() {
|
||||
return new TreeMap<>(REMOTE_VALUES);
|
||||
|
|
|
@ -68,6 +68,11 @@ public final class LocaleFeatureFlags {
|
|||
public static boolean isDelayedNotificationPromptEnabled() {
|
||||
return isEnabled(FeatureFlags.PROMPT_FOR_NOTIFICATION_LOGS, FeatureFlags.promptForDelayedNotificationLogs());
|
||||
}
|
||||
|
||||
public static boolean isBatterySaverPromptEnabled() {
|
||||
return isEnabled(FeatureFlags.PROMPT_BATTERY_SAVER, FeatureFlags.PROMPT_BATTERY_SAVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a comma-separated list of country codes colon-separated from how many buckets out of 1 million
|
||||
* should be enabled to see this megaphone in that country code. At the end of the list, an optional
|
||||
|
|
|
@ -19,6 +19,13 @@ public class PowerManagerCompat {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean isIgnoringBatteryOptimizations(@NonNull Context context) {
|
||||
if (Build.VERSION.SDK_INT < 23) {
|
||||
return true;
|
||||
}
|
||||
return ServiceUtil.getPowerManager(context).isIgnoringBatteryOptimizations(context.getPackageName());
|
||||
}
|
||||
|
||||
@RequiresApi(api = 23)
|
||||
public static void requestIgnoreBatteryOptimizations(@NonNull Context context) {
|
||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
|
|
36
app/src/main/res/drawable/ic_troubleshoot_notification.xml
Normal file
36
app/src/main/res/drawable/ic_troubleshoot_notification.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="72dp"
|
||||
android:height="72dp"
|
||||
android:viewportWidth="72"
|
||||
android:viewportHeight="72">
|
||||
<path
|
||||
android:pathData="M24.75,56.25C24.75,62.625 29.625,67.5 36,67.5C42.375,67.5 47.25,62.625 47.25,56.25"
|
||||
android:fillColor="#C0CBE2"/>
|
||||
<path
|
||||
android:pathData="M5.625,51.242C5.625,54.008 7.83,56.25 10.551,56.25H61.449C64.17,56.25 66.375,54.008 66.375,51.242C66.375,44.247 57.372,48.738 56.524,24.532C56.113,12.824 46.882,4.5 36,4.5C25.118,4.5 15.887,12.824 15.476,24.532C14.628,48.738 5.625,44.247 5.625,51.242Z"
|
||||
android:fillColor="#DDE7FF"/>
|
||||
<path
|
||||
android:pathData="M10.551,56.25C7.83,56.25 5.625,54.008 5.625,51.242C5.625,48.77 6.749,47.732 8.241,46.356C10.643,44.139 13.998,41.042 15.141,29.656C21.026,25.465 28.225,23 36,23C43.775,23 50.974,25.465 56.859,29.656C58.002,41.042 61.357,44.139 63.759,46.356C65.251,47.732 66.375,48.77 66.375,51.242C66.375,54.008 64.17,56.25 61.449,56.25H10.551Z"
|
||||
android:fillColor="#C0CBE2"/>
|
||||
<path
|
||||
android:pathData="M36,69C28.797,69 23.25,63.453 23.25,56.25H26.25C26.25,61.797 30.453,66 36,66C41.547,66 45.75,61.797 45.75,56.25H48.75C48.75,63.453 43.203,69 36,69Z"
|
||||
android:fillColor="#6C7B9D"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M36,6C25.908,6 17.357,13.703 16.975,24.585C16.545,36.869 14.035,42.179 11.376,45.312C10.729,46.074 10.095,46.682 9.539,47.198C9.436,47.294 9.337,47.386 9.242,47.473C8.808,47.874 8.461,48.195 8.154,48.532C7.51,49.239 7.125,49.92 7.125,51.242C7.125,53.203 8.682,54.75 10.551,54.75H61.449C63.318,54.75 64.875,53.203 64.875,51.242C64.875,49.92 64.49,49.239 63.846,48.532C63.539,48.195 63.192,47.874 62.758,47.473C62.663,47.386 62.564,47.294 62.461,47.198C61.905,46.682 61.271,46.074 60.624,45.312C57.965,42.179 55.455,36.869 55.025,24.585C54.643,13.703 46.092,6 36,6ZM13.977,24.48C14.417,11.946 24.329,3 36,3C47.671,3 57.583,11.946 58.023,24.48C58.44,36.402 60.856,40.949 62.911,43.37C63.442,43.996 63.973,44.509 64.503,45C64.587,45.079 64.674,45.159 64.761,45.239C65.198,45.642 65.661,46.069 66.064,46.511C67.134,47.686 67.875,49.066 67.875,51.242C67.875,54.813 65.021,57.75 61.449,57.75H10.551C6.979,57.75 4.125,54.813 4.125,51.242C4.125,49.066 4.866,47.686 5.936,46.511C6.339,46.069 6.802,45.642 7.239,45.239C7.326,45.159 7.413,45.079 7.497,45C8.027,44.509 8.558,43.996 9.089,43.37C11.144,40.949 13.559,36.402 13.977,24.48Z"
|
||||
android:fillColor="#6C7B9D"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M53,18m-15,0a15,15 0,1 1,30 0a15,15 0,1 1,-30 0"
|
||||
android:fillColor="#FA902E"/>
|
||||
<path
|
||||
android:pathData="M53,4.5C45.544,4.5 39.5,10.544 39.5,18C39.5,25.456 45.544,31.5 53,31.5C60.456,31.5 66.5,25.456 66.5,18C66.5,10.544 60.456,4.5 53,4.5ZM36.5,18C36.5,8.887 43.887,1.5 53,1.5C62.113,1.5 69.5,8.887 69.5,18C69.5,27.113 62.113,34.5 53,34.5C43.887,34.5 36.5,27.113 36.5,18Z"
|
||||
android:fillColor="#CE711B"
|
||||
android:fillType="evenOdd"/>
|
||||
<path
|
||||
android:pathData="M54.972,11.695C55.062,10.545 54.153,9.563 53,9.563C51.847,9.563 50.938,10.545 51.028,11.695L51.661,19.762C51.716,20.461 52.299,21 53,21C53.701,21 54.284,20.461 54.339,19.762L54.972,11.695Z"
|
||||
android:fillColor="#FFE3A5"/>
|
||||
<path
|
||||
android:pathData="M53,22.75C51.861,22.75 50.938,23.673 50.938,24.813C50.938,25.952 51.861,26.875 53,26.875C54.139,26.875 55.063,25.952 55.063,24.813C55.063,23.673 54.139,22.75 53,22.75Z"
|
||||
android:fillColor="#FFE3A5"/>
|
||||
</vector>
|
|
@ -0,0 +1,88 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2023 Signal Messenger, LLC
|
||||
~ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="2dp"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@color/signal_icon_tint_tab_unselected"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_troubleshoot_notification"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
|
||||
<TextView
|
||||
style="@style/Signal.Text.TitleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/PromptBatterySaverBottomSheet__title"
|
||||
/>
|
||||
|
||||
<org.thoughtcrime.securesms.util.views.LearnMoreTextView
|
||||
android:id="@+id/message"
|
||||
style="@style/Signal.Text.BodyMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:text="@string/PromptBatterySaverBottomSheet__message"
|
||||
/>
|
||||
|
||||
<androidx.appcompat.widget.LinearLayoutCompat
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:minWidth="320dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_marginHorizontal="34dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/dismiss_button"
|
||||
style="@style/Signal.Widget.Button.Medium.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:minWidth="160dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/PromptBatterySaverBottomSheet__dismiss"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/continue_button"
|
||||
style="@style/Signal.Widget.Button.Medium.Tonal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:minWidth="160dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/PromptBatterySaverBottomSheet__continue"/>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||
|
||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
|
@ -910,6 +910,16 @@
|
|||
<!-- Message for dialog asking user to submit logs for debugging slow notification issues -->
|
||||
<string name="PromptLogsSlowNotificationsDialog__message">Debug logs helps us diagnose and fix the issue, and do not contain identifying information.</string>
|
||||
|
||||
<!-- Title for dialog asking user to submit logs for debugging slow notification issues -->
|
||||
<string name="PromptBatterySaverBottomSheet__title">Notifications may be delayed due to battery optimizations</string>
|
||||
<!-- Message explaining that battery saver may delay notifications -->
|
||||
<string name="PromptBatterySaverBottomSheet__message">You can disable battery optimizations for Signal to ensure that message notifications will not be delayed.</string>
|
||||
|
||||
<!-- Button to continue to try and disable battery saver -->
|
||||
<string name="PromptBatterySaverBottomSheet__continue">Continue</string>
|
||||
<!-- Button to dismiss battery saver dialog prompt-->
|
||||
<string name="PromptBatterySaverBottomSheet__dismiss">Dismiss</string>
|
||||
|
||||
<!-- PendingMembersActivity -->
|
||||
<string name="PendingMembersActivity_pending_group_invites">Pending group invites</string>
|
||||
<string name="PendingMembersActivity_requests">Requests</string>
|
||||
|
@ -3105,6 +3115,8 @@
|
|||
<string name="preferences_notifications__ringtone">Ringtone</string>
|
||||
<string name="preferences_chats__message_text_size">Message font size</string>
|
||||
<string name="preferences_notifications__priority">Priority</string>
|
||||
<!-- Option in settings to trouble shoot delayed notifications -->
|
||||
<string name="preferences_notifications__troubleshoot">Troubleshoot notifications</string>
|
||||
<!-- Heading for the \'censorship circumvention\' section of privacy preferences -->
|
||||
<string name="preferences_communication__category_censorship_circumvention">Censorship circumvention</string>
|
||||
<!-- Title of the \'censorship circumvention\' toggle switch -->
|
||||
|
|
Loading…
Add table
Reference in a new issue