Show a megaphone when notifications are disabled.

This commit is contained in:
Greyson Parrelli 2021-02-25 12:54:23 -05:00 committed by GitHub
parent 4f01bacb49
commit 8f6ff215aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 120 additions and 6 deletions

View file

@ -69,6 +69,7 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
public static final String LAUNCH_TO_BACKUPS_FRAGMENT = "launch.to.backups.fragment";
public static final String LAUNCH_TO_HELP_FRAGMENT = "launch.to.help.fragment";
public static final String LAUNCH_TO_PROXY_FRAGMENT = "launch.to.proxy.fragment";
public static final String LAUNCH_TO_NOTIFICATIONS_FRAGMENT = "launch.to.notifications.fragment";
@SuppressWarnings("unused")
private static final String TAG = ApplicationPreferencesActivity.class.getSimpleName();
@ -112,6 +113,8 @@ public class ApplicationPreferencesActivity extends PassphraseRequiredActivity
initFragment(android.R.id.content, new HelpFragment());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_PROXY_FRAGMENT, false)) {
initFragment(android.R.id.content, EditProxyFragment.newInstance());
} else if (getIntent() != null && getIntent().getBooleanExtra(LAUNCH_TO_NOTIFICATIONS_FRAGMENT, false)) {
initFragment(android.R.id.content, new NotificationsPreferenceFragment());
} else if (icicle == null) {
initFragment(android.R.id.content, new ApplicationPreferenceFragment());
} else {

View file

@ -56,7 +56,10 @@ public class BasicMegaphoneView extends FrameLayout {
this.megaphone = megaphone;
this.megaphoneListener = megaphoneListener;
if (megaphone.getImageRequest() != null) {
if (megaphone.getImageRes() != 0) {
image.setVisibility(VISIBLE);
image.setImageResource(megaphone.getImageRes());
} else if (megaphone.getImageRequest() != null) {
image.setVisibility(VISIBLE);
megaphone.getImageRequest().into(image);
} else {

View file

@ -24,6 +24,7 @@ public class Megaphone {
private final boolean canSnooze;
private final int titleRes;
private final int bodyRes;
private final int imageRes;
private final GlideRequest<Drawable> imageRequest;
private final int buttonTextRes;
private final EventListener buttonListener;
@ -39,6 +40,7 @@ public class Megaphone {
this.canSnooze = builder.canSnooze;
this.titleRes = builder.titleRes;
this.bodyRes = builder.bodyRes;
this.imageRes = builder.imageRes;
this.imageRequest = builder.imageRequest;
this.buttonTextRes = builder.buttonTextRes;
this.buttonListener = builder.buttonListener;
@ -72,6 +74,10 @@ public class Megaphone {
return bodyRes;
}
public @DrawableRes int getImageRes() {
return imageRes;
}
public @Nullable GlideRequest<Drawable> getImageRequest() {
return imageRequest;
}
@ -117,6 +123,7 @@ public class Megaphone {
private boolean canSnooze;
private int titleRes;
private int bodyRes;
private int imageRes;
private GlideRequest<Drawable> imageRequest;
private int buttonTextRes;
private EventListener buttonListener;
@ -163,7 +170,8 @@ public class Megaphone {
}
public @NonNull Builder setImage(@DrawableRes int imageRes) {
return setImageRequest(GlideApp.with(ApplicationDependencies.getApplication()).load(imageRes));
this.imageRes = imageRes;
return this;
}
public @NonNull Builder setImageRequest(@Nullable GlideRequest<Drawable> imageRequest) {

View file

@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.megaphone;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -9,6 +11,7 @@ import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversationlist.ConversationListFragment;
import org.thoughtcrime.securesms.database.model.MegaphoneRecord;
@ -20,6 +23,7 @@ import org.thoughtcrime.securesms.lock.SignalPinReminders;
import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity;
import org.thoughtcrime.securesms.lock.v2.KbsMigrationActivity;
import org.thoughtcrime.securesms.messagerequests.MessageRequestMegaphoneActivity;
import org.thoughtcrime.securesms.notifications.NotificationChannels;
import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.CommunicationActions;
@ -32,6 +36,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Creating a new megaphone:
@ -94,6 +99,7 @@ public final class Megaphones {
put(Event.DONATE, shouldShowDonateMegaphone(context) ? ShowForDurationSchedule.showForDays(7) : NEVER);
put(Event.GROUP_CALLING, shouldShowGroupCallingMegaphone() ? ALWAYS : NEVER);
put(Event.ONBOARDING, shouldShowOnboardingMegaphone(context) ? ALWAYS : NEVER);
put(Event.NOTIFICATIONS, shouldShowNotificationsMegaphone(context) ? RecurringSchedule.every(TimeUnit.DAYS.toMillis(30)) : NEVER);
}};
}
@ -119,6 +125,8 @@ public final class Megaphones {
return buildGroupCallingMegaphone(context);
case ONBOARDING:
return buildOnboardingMegaphone();
case NOTIFICATIONS:
return buildNotificationsMegaphone(context);
default:
throw new IllegalArgumentException("Event not handled!");
}
@ -264,6 +272,34 @@ public final class Megaphones {
.build();
}
private static @NonNull Megaphone buildNotificationsMegaphone(@NonNull Context context) {
return new Megaphone.Builder(Event.NOTIFICATIONS, Megaphone.Style.BASIC)
.setTitle(R.string.NotificationsMegaphone_turn_on_notifications)
.setBody(R.string.NotificationsMegaphone_never_miss_a_message)
.setImage(R.drawable.megaphone_notifications_64)
.setActionButton(R.string.NotificationsMegaphone_turn_on, (megaphone, controller) -> {
controller.onMegaphoneSnooze(Event.NOTIFICATIONS);
if (Build.VERSION.SDK_INT >= 26 && !NotificationChannels.isMessageChannelEnabled(context)) {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationChannels.getMessagesChannel(context));
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
controller.onMegaphoneNavigationRequested(intent);
} else if (Build.VERSION.SDK_INT >= 26 && !NotificationChannels.areNotificationsEnabled(context)) {
Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
controller.onMegaphoneNavigationRequested(intent);
} else {
Intent intent = new Intent(context, ApplicationPreferencesActivity.class);
intent.putExtra(ApplicationPreferencesActivity.LAUNCH_TO_NOTIFICATIONS_FRAGMENT, true);
controller.onMegaphoneNavigationRequested(intent);
}
})
.setSecondaryButton(R.string.NotificationsMegaphone_not_now, (megaphone, controller) -> controller.onMegaphoneSnooze(Event.NOTIFICATIONS))
.setPriority(Megaphone.Priority.DEFAULT)
.build();
}
private static boolean shouldShowMessageRequestsMegaphone() {
return Recipient.self().getProfileName() == ProfileName.EMPTY;
}
@ -288,6 +324,12 @@ public final class Megaphones {
return SignalStore.onboarding().hasOnboarding(context);
}
private static boolean shouldShowNotificationsMegaphone(@NonNull Context context) {
return !TextSecurePreferences.isNotificationsEnabled(context) ||
!NotificationChannels.isMessageChannelEnabled(context) ||
!NotificationChannels.areNotificationsEnabled(context);
}
public enum Event {
REACTIONS("reactions"),
PINS_FOR_ALL("pins_for_all"),
@ -298,7 +340,8 @@ public final class Megaphones {
RESEARCH("research"),
DONATE("donate"),
GROUP_CALLING("group_calling"),
ONBOARDING("onboarding");
ONBOARDING("onboarding"),
NOTIFICATIONS("notifications");
private final String key;

View file

@ -1,13 +1,34 @@
package org.thoughtcrime.securesms.megaphone;
import androidx.annotation.NonNull;
/**
* A schedule that provides a high level of control, allowing you to specify an amount of time to
* wait based on how many times a user has seen the megaphone.
*/
class RecurringSchedule implements MegaphoneSchedule {
private final long[] gaps;
/**
* How long to wait after each time a user has seen the megaphone. Index 0 corresponds to how long
* to wait to show it again after the user has seen it once, index 1 is for after the user has
* seen it twice, etc. If the seen count is greater than the number of provided intervals, it will
* continue to use the last interval provided indefinitely.
*
* The schedule will always show the megaphone if the user has never seen it.
*/
RecurringSchedule(long... durationGaps) {
this.gaps = durationGaps;
}
/**
* Shortcut for a recurring schedule with a single interval.
*/
public static @NonNull MegaphoneSchedule every(long interval) {
return new RecurringSchedule(interval);
}
@Override
public boolean shouldDisplay(int seenCount, long lastSeen, long firstVisible, long currentTime) {
if (seenCount == 0) {

View file

@ -376,6 +376,36 @@ public class NotificationChannels {
ensureCustomChannelConsistency(context);
}
/**
* Whether or not the default messages notification channel is enabled. Note that "enabled" just
* means receiving notifications in some capacity -- a user could have it enabled, but set it to a
* lower importance.
*
* This could also return true if the specific channnel is enabled, but notifications *overall*
* are disabled. Check {@link #areNotificationsEnabled(Context)} to be safe.
*/
public static synchronized boolean isMessageChannelEnabled(@NonNull Context context) {
if (!supported()) {
return true;
}
NotificationManager notificationManager = ServiceUtil.getNotificationManager(context);
NotificationChannel channel = notificationManager.getNotificationChannel(getMessagesChannel(context));
return channel != null && channel.getImportance() != NotificationManager.IMPORTANCE_NONE;
}
/**
* Whether or not notifications for the entire app are enabled.
*/
public static synchronized boolean areNotificationsEnabled(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= 24) {
return ServiceUtil.getNotificationManager(context).areNotificationsEnabled();
} else {
return true;
}
}
/**
* Updates the name of an existing channel to match the recipient's current name. Will have no
* effect if the recipient doesn't have an existing valid channel.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -973,6 +973,12 @@
<string name="NotificationBarManager__end_call">End call</string>
<string name="NotificationBarManager__cancel_call">Cancel call</string>
<!-- NotificationsMegaphone -->
<string name="NotificationsMegaphone_turn_on_notifications">Turn on Notifications?</string>
<string name="NotificationsMegaphone_never_miss_a_message">Never miss a message from your contacts and groups.</string>
<string name="NotificationsMegaphone_turn_on">Turn on</string>
<string name="NotificationsMegaphone_not_now">Not now</string>
<!-- NotificationMmsMessageRecord -->
<string name="NotificationMmsMessageRecord_multimedia_message">Multimedia message</string>
<string name="NotificationMmsMessageRecord_downloading_mms_message">Downloading MMS message</string>