Skip to content

Commit

Permalink
Improve UI for FCM service alerts
Browse files Browse the repository at this point in the history
  • Loading branch information
valldrac committed Oct 18, 2024
1 parent ff247d8 commit 4665f08
Show file tree
Hide file tree
Showing 40 changed files with 57 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;

import com.bumptech.glide.Glide;
Expand Down Expand Up @@ -91,7 +90,6 @@
import org.thoughtcrime.securesms.mms.SignalGlideModule;
import org.thoughtcrime.securesms.net.NetworkManager;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.notifications.NotificationIds;
import org.thoughtcrime.securesms.providers.BlobProvider;
import org.thoughtcrime.securesms.ratelimit.RateLimitUtil;
import org.thoughtcrime.securesms.recipients.Recipient;
Expand All @@ -113,7 +111,6 @@
import org.thoughtcrime.securesms.util.DynamicTheme;
import org.thoughtcrime.securesms.util.RemoteConfig;
import org.thoughtcrime.securesms.util.FileUtils;
import org.thoughtcrime.securesms.util.PlayServicesUtil;
import org.thoughtcrime.securesms.util.SignalLocalMetrics;
import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler;
import org.thoughtcrime.securesms.util.StorageUtil;
Expand Down Expand Up @@ -504,22 +501,19 @@ public void updatePushNotificationServices() {
return;
}

PlayServicesUtil.PlayServicesStatus fcmStatus = PlayServicesUtil.getPlayServicesStatus(this);

boolean fcmEnabled = SignalStore.account().isFcmEnabled();
boolean forceWebSocket = SignalStore.internal().isWebsocketModeForced();
boolean forceFcm = SignalStore.internal().isFcmModeForced();

if (forceWebSocket || fcmStatus == PlayServicesUtil.PlayServicesStatus.DISABLED) {
if (forceWebSocket || !BuildConfig.USE_PLAY_SERVICES) {
if (fcmEnabled) {
Log.i(TAG, "Play Services are disabled. Disabling FCM.");
Log.i(TAG, "Play Services not allowed. Disabling FCM.");
updateFcmStatus(false);
} else {
Log.d(TAG, "FCM is disabled.");
SignalStore.account().setFcmTokenLastSetTime(-1);
}
} else if (fcmStatus == PlayServicesUtil.PlayServicesStatus.SUCCESS && !fcmEnabled &&
SignalStore.account().getFcmTokenLastSetTime() < 0) {
Log.i(TAG, "Play Services are newly-available. Updating to use FCM.");
} else if (forceFcm && !fcmEnabled && BuildConfig.USE_PLAY_SERVICES) {
Log.i(TAG, "FCM preferred. Updating to use FCM.");
updateFcmStatus(true);
} else {
long lastSetTime = SignalStore.account().getFcmTokenLastSetTime();
Expand All @@ -541,7 +535,7 @@ public void updatePushNotificationServices() {
private void updateFcmStatus(boolean fcmEnabled) {
SignalStore.account().setFcmEnabled(fcmEnabled);
if (!fcmEnabled) {
NotificationManagerCompat.from(this).cancel(NotificationIds.FCM_FAILURE);
FcmRefreshJob.cancelFcmFailureNotification(this);
}
AppDependencies.getJobManager().startChain(new FcmRefreshJob())
.then(new RefreshAttributesJob())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package org.thoughtcrime.securesms.banner.banners

import android.content.Context
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
Expand All @@ -15,11 +14,11 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import org.signal.core.ui.Previews
import org.signal.core.ui.SignalPreview
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.banner.Banner
import org.thoughtcrime.securesms.banner.ui.compose.Action
import org.thoughtcrime.securesms.banner.ui.compose.DefaultBanner
import org.thoughtcrime.securesms.banner.ui.compose.Importance
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.PowerManagerCompat
import org.thoughtcrime.securesms.util.ServiceUtil
Expand All @@ -28,7 +27,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
class DozeBanner(private val context: Context) : Banner<Unit>() {

override val enabled: Boolean
get() = !SignalStore.account.pushAvailable && !TextSecurePreferences.hasPromptedOptimizeDoze(context) && !ServiceUtil.getPowerManager(context).isIgnoringBatteryOptimizations(context.packageName)
get() = !SignalStore.account.fcmEnabled && !TextSecurePreferences.hasPromptedOptimizeDoze(context) && !ServiceUtil.getPowerManager(context).isIgnoringBatteryOptimizations(context.packageName)

override val dataFlow: Flow<Unit>
get() = flowOf(Unit)
Expand All @@ -51,32 +50,19 @@ class DozeBanner(private val context: Context) : Banner<Unit>() {
@Composable
private fun Banner(contentPadding: PaddingValues, onDismissListener: () -> Unit = {}, onOkListener: () -> Unit = {}) {
DefaultBanner(
title = stringResource(id = dozeTitle),
body = stringResource(id = dozeBody),
title = null,
body = stringResource(id = R.string.DozeReminder_allow_molly_to_deliver_timely_notifications),
importance = Importance.ERROR,
onDismissListener = onDismissListener,
actions = listOf(
Action(android.R.string.ok) {
Action(R.string.DozeReminder_disable_battery_restrictions) {
onOkListener()
}
),
paddingValues = contentPadding
)
}

@StringRes
private val dozeTitle: Int = if (BuildConfig.USE_PLAY_SERVICES) {
R.string.DozeReminder_optimize_for_missing_play_services
} else {
R.string.DozeReminder_optimize_for_timely_notifications
}

@StringRes
private val dozeBody: Int = if (BuildConfig.USE_PLAY_SERVICES) {
R.string.DozeReminder_this_device_does_not_support_play_services_tap_to_disable_system_battery
} else {
R.string.DozeReminder_tap_to_allow_molly_to_retrieve_messages_while_the_device_is_in_standby
}

@SignalPreview
@Composable
private fun BannerPreview() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,6 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
onToggle = { isChecked ->
if (isChecked && !state.canEnableNotifyWhileLocked) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.NotificationsSettingsFragment__push_notifications_unavailable)
.setMessage(R.string.NotificationsSettingsFragment__sorry_this_feature_requires_push_notifications_delivered_via_fcm_or_unifiedpush)
.setPositiveButton(android.R.string.ok, null)
.show()
Expand All @@ -341,19 +340,19 @@ class NotificationsSettingsFragment : DSLSettingsFragment(R.string.preferences__
sectionHeaderPref(R.string.NotificationsSettingsFragment__push_notifications)

textPref(
summary = DSLSettingsText.from(R.string.NotificationsSettingsFragment__preferred_method_for_receiving_notifications_from_the_signal_service)
summary = DSLSettingsText.from(R.string.NotificationsSettingsFragment__select_your_preferred_service_for_push_notifications)
)

val showAlertIcon = when (state.preferredNotificationMethod) {
NotificationDeliveryMethod.FCM -> !state.canReceiveFcm
NotificationDeliveryMethod.WEBSOCKET -> false
}
radioListPref(
title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__delivery_method),
title = DSLSettingsText.from(R.string.NotificationsSettingsFragment__delivery_service),
listItems = notificationMethodLabels,
selected = notificationMethodValues.indexOf(state.preferredNotificationMethod),
isEnabled = !state.isLinkedDevice, // MOLLY: TODO
iconEnd = if (showAlertIcon) DSLSettingsIcon.from(R.drawable.ic_alert) else null,
iconEnd = if (showAlertIcon) DSLSettingsIcon.from(R.drawable.ic_alert, R.color.signal_alert_primary) else null,
onSelected = {
onNotificationMethodChanged(notificationMethodValues[it], state.preferredNotificationMethod)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,9 @@ class NotificationsSettingsViewModel(private val sharedPreferences: SharedPrefer
SignalStore.settings.preferredNotificationMethod = method
ApplicationContext.getInstance().updatePushNotificationServices()
AppDependencies.resetNetwork(true)
refresh()
// Avoid calling refresh() here to prevent updating canReceiveFcm
// while the FCM token is still being refreshed.
store.update { it.copy(preferredNotificationMethod = method) }
}

val fcmState get() = PlayServicesUtil.getPlayServicesStatus(AppDependencies.application)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
Expand Down Expand Up @@ -86,10 +87,9 @@ public void onRun() throws Exception {

if (!SignalStore.account().isFcmEnabled()) {
if (oldToken != null) {
Log.i(TAG, "FCM not allowed: clearing existing token...");
Log.i(TAG, "FCM is disabled: clearing existing token...");
AppDependencies.getSignalServiceAccountManager().setGcmId(Optional.empty());
SignalStore.account().setFcmToken(null);
SignalStore.account().setFcmTokenLastSetTime(-1);
}
return;
}
Expand All @@ -104,6 +104,8 @@ public void onRun() throws Exception {
Optional<String> token = FcmUtil.getToken(context);

if (token.isPresent()) {
cancelFcmFailureNotification(context);

if (!token.get().equals(oldToken)) {
int oldLength = oldToken != null ? oldToken.length() : -1;
Log.i(TAG, "Token changed. oldLength: " + oldLength + " newLength: " + token.get().length());
Expand All @@ -116,11 +118,12 @@ public void onRun() throws Exception {
if (oldToken == null) {
AppDependencies.resetNetwork(true);
}
EventBus.getDefault().post(PushServiceEvent.INSTANCE);
} else {
throw new RetryLaterException(new IOException("Failed to retrieve a token."));
}
}

EventBus.getDefault().post(PushServiceEvent.INSTANCE);
}

@Override
Expand All @@ -142,16 +145,20 @@ private void notifyFcmFailure() {
builder.setSmallIcon(R.drawable.ic_notification);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
R.drawable.symbol_error_triangle_fill_32));
builder.setContentTitle(context.getString(R.string.GcmRefreshJob_Permanent_Signal_communication_failure));
builder.setContentText(context.getString(R.string.GcmRefreshJob_Signal_was_unable_to_register_with_Google_Play_Services));
builder.setTicker(context.getString(R.string.GcmRefreshJob_Permanent_Signal_communication_failure));
builder.setContentTitle(context.getString(R.string.GcmRefreshJob_unable_to_register_with_Google_Play_Services));
builder.setContentText(context.getString(R.string.GcmRefreshJob_switch_to_an_alternative_push_service_in_settings_notifications));
builder.setTicker(context.getString(R.string.GcmRefreshJob_unable_to_register_with_Google_Play_Services));
builder.setVibrate(new long[] {0, 1000});
builder.setContentIntent(pendingIntent);

((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(NotificationIds.FCM_FAILURE, builder.build());
}

static public void cancelFcmFailureNotification(@NonNull Context context) {
NotificationManagerCompat.from(context).cancel(NotificationIds.FCM_FAILURE);
}

public static final class Factory implements Job.Factory<FcmRefreshJob> {
@Override
public @NonNull FcmRefreshJob create(@NonNull Parameters parameters, @Nullable byte[] serializedData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void onRun() throws IOException {
}

int registrationId = SignalStore.account().getRegistrationId();
boolean fetchesMessages = !SignalStore.account().isFcmEnabled() || SignalStore.internal().isWebsocketModeForced();
boolean fetchesMessages = !SignalStore.account().isFcmEnabled();
byte[] unidentifiedAccessKey = UnidentifiedAccess.deriveAccessKeyFrom(ProfileKeyUtil.getSelfProfileKey());
boolean universalUnidentifiedAccess = TextSecurePreferences.isUniversalUnidentifiedAccess(context);
String registrationLockV2 = null;
Expand All @@ -105,6 +105,7 @@ public void onRun() throws IOException {

AccountAttributes.Capabilities capabilities = AppCapabilities.getCapabilities((svrValues.hasPin() && !svrValues.hasOptedOut()) || SignalStore.storageService().hasStorageKeyFromPrimary());
Log.i(TAG, "Calling setAccountAttributes() reglockV2? " + !TextUtils.isEmpty(registrationLockV2) + ", pin? " + svrValues.hasPin() +
", fetchesMessages? " + fetchesMessages +
"\n Recovery password? " + !TextUtils.isEmpty(recoveryPassword) +
"\n Phone number discoverable : " + phoneNumberDiscoverable +
"\n Device Name : " + (encryptedDeviceName != null) +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,8 @@ class AccountValues internal constructor(store: KeyValueStore, context: Context)
}

/** When we last set the [fcmToken] */
var fcmTokenLastSetTime: Long
val fcmTokenLastSetTime: Long
get() = getLong(KEY_FCM_TOKEN_LAST_SET_TIME, 0)
set(value) = putLong(KEY_FCM_TOKEN_LAST_SET_TIME, value)

/** Whether or not the user is registered with the Signal service. */
val isRegistered: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ public boolean isWebsocketModeForced() {
return SignalStore.settings().getPreferredNotificationMethod() == SettingsValues.NotificationDeliveryMethod.WEBSOCKET;
}

public boolean isFcmModeForced() {
return SignalStore.settings().getPreferredNotificationMethod() == SettingsValues.NotificationDeliveryMethod.FCM;
}

public void setHevcEncoding(boolean enabled) {
putBoolean(ENCODE_HEVC, enabled);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import androidx.lifecycle.LiveData;

import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.BuildConfig;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.mms.SentMediaQuality;
Expand Down Expand Up @@ -479,7 +480,13 @@ public boolean getUseCompactNavigationBar() {
}

public NotificationDeliveryMethod getPreferredNotificationMethod() {
return NotificationDeliveryMethod.deserialize(getString(MOLLY_NOTIFICATION_METHOD, NotificationDeliveryMethod.FCM.serialize()));
final NotificationDeliveryMethod method = NotificationDeliveryMethod.deserialize(
getString(MOLLY_NOTIFICATION_METHOD, NotificationDeliveryMethod.FCM.serialize())
);
if (!BuildConfig.USE_PLAY_SERVICES && method == NotificationDeliveryMethod.FCM) {
return NotificationDeliveryMethod.WEBSOCKET;
}
return method;
}

public void setPreferredNotificationMethod(NotificationDeliveryMethod method) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ class IncomingMessageObserver(private val context: Application) {
init {
MessageRetrievalThread().start()

// MOLLY: Foreground service startup is handled inside the connection loop
// MOLLY: Ensure the foreground service is stopped, it will be started if needed in the connection loop
ForegroundService.stop(context)

AppForegroundObserver.addListener(object : AppForegroundObserver.Listener {
override fun onForeground() {
Expand Down Expand Up @@ -458,9 +459,6 @@ class IncomingMessageObserver(private val context: Application) {
Log.i(TAG, "Looping...")
}
Log.w(TAG, "Terminated! (${this.hashCode()})")

// MOLLY: Stop foreground service, it will be restarted if needed
ForegroundService.stop(context)
}

override fun uncaughtException(t: Thread, e: Throwable) {
Expand Down
3 changes: 0 additions & 3 deletions app/src/main/res/values-ar/strings2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
<string name="preferences__block_users_youve_never_been_in_contact_with_and_who_are_not_saved_in_your_contacts">احظر المستخدمين الذين لم تتواصل معهم مطلقًا والذين لم يتم حفظهم كجهات اتصال.</string>
<string name="DonateMegaphone_molly_is_free_software">مولي هو برنامج مجاني</string>
<string name="BackupsPreferenceFragment__change_schedule">تغيير الجدولة</string>
<string name="DozeReminder_tap_to_allow_molly_to_retrieve_messages_while_the_device_is_in_standby">انقر للسماح لمولي باسترداد الرسائل عندما يكون الجهاز في وضع الاستعداد.</string>
<string name="RegistrationActivity_link_retry">أعد محاولة الربط</string>
<string name="DeviceListItem_device_d">الجهاز #%d</string>
<string name="DeviceListItem_primary">الرئيسي</string>
Expand All @@ -70,9 +69,7 @@
<string name="WipeMemoryService_clearing_unencrypted_data_from_ram">يمسح البيانات غير المُعمَّاة من ذاكرة الوصول العشوائي</string>
<string name="BioTextPreference_no_linked_devices">لا توجد أجهزة مرتبطة</string>
<string name="NotificationsSettingsFragment__new_activity_while_locked">نشاط جديد أثناء القفل</string>
<string name="NotificationsSettingsFragment__push_notifications_unavailable">إشعارات Push غير متوفرة</string>
<string name="NotificationsSettingsFragment__sorry_this_feature_requires_push_notifications_delivered_via_fcm_or_unifiedpush">عذرًا، تتطلب هذه الميزة إرسال إشعارات Push عبر FCM أو UnifiedPush، وهي غير متوفرة حاليًا.</string>
<string name="DozeReminder_optimize_for_timely_notifications">تحسين الإشعارات في الوقت المناسب</string>
<string name="arrays__weekly">أسبوعيًا</string>
<string name="MessageRetrievalService_ready_to_receive_messages">جاهز لتلقي الرسائل</string>
<string name="HelpSettingsFragment__molly_im_website">موقع Molly.im</string>
Expand Down
3 changes: 0 additions & 3 deletions app/src/main/res/values-bs/strings2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
<string name="HelpSettingsFragment__molly_im_website">Molly.im internet-stranica</string>
<string name="DeviceAddFragment__link_without_scanning">Poveži bez skeniranja</string>
<string name="HelpSettingsFragment_disable_and_delete_debug_log">Izbrisati i deaktivirati izvještaj o greškama\?</string>
<string name="DozeReminder_tap_to_allow_molly_to_retrieve_messages_while_the_device_is_in_standby">Pritisnite kako biste dopustili Molly da preuzme poruke dok je uređaj u pripravnom stanju.</string>
<string name="DozeReminder_optimize_for_timely_notifications">Optimiziraj za pravovremena obavještenja</string>
<string name="arrays__tor_via_orbot">Tor preko Orbota</string>
<string name="preferences_network__connection">Konekcija</string>
<string name="preferences__map_satellite">Satelitska</string>
Expand Down Expand Up @@ -104,7 +102,6 @@
<string name="RegistrationActivity_link_timeout">Isteklo je vrijeme čekanja za primarni uređaj. Pripremi uređaj za snimanje QR koda, zatim pokušaj ponovo</string>
<string name="RegistrationActivity_link_retry">Ponovi povezivanje</string>
<string name="NotificationsSettingsFragment__new_activity_while_locked">Nova aktivnost dok je aplikacija zaljučana</string>
<string name="NotificationsSettingsFragment__push_notifications_unavailable">Push obavještenja nisu dostupna</string>
<string name="NotificationsSettingsFragment__sorry_this_feature_requires_push_notifications_delivered_via_fcm_or_unifiedpush">Žao nam je, ova funkcija zahtijeva push obavještenja koja se isporučuju putem FCM ili UnifiedPush usluga koje trenutno nisu dostupne.</string>
<string name="RegistrationActivity_provider_declined">Pružatelj usluge je odbio poslati kod za verifikaciju, vjerovatno zbog pravila za sprječavanje prijevara.</string>
<string name="NotificationsSettingsFragment__receive_notifications_for_messages_or_missed_calls_when_the_app_is_locked">Primaj obavještenja o porukama ili propuštenim pozivima kada je aplikacija zaključana.</string>
Expand Down
Loading

0 comments on commit 4665f08

Please sign in to comment.