Show a megaphone when notifications are disabled.
This commit is contained in:
parent
4f01bacb49
commit
8f6ff215aa
11 changed files with 120 additions and 6 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
BIN
app/src/main/res/drawable-hdpi/megaphone_notifications_64.webp
Normal file
BIN
app/src/main/res/drawable-hdpi/megaphone_notifications_64.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/main/res/drawable-mdpi/megaphone_notifications_64.webp
Normal file
BIN
app/src/main/res/drawable-mdpi/megaphone_notifications_64.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/drawable-xhdpi/megaphone_notifications_64.webp
Normal file
BIN
app/src/main/res/drawable-xhdpi/megaphone_notifications_64.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
BIN
app/src/main/res/drawable-xxhdpi/megaphone_notifications_64.webp
Normal file
BIN
app/src/main/res/drawable-xxhdpi/megaphone_notifications_64.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -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>
|
||||
|
|
Loading…
Add table
Reference in a new issue