diff --git a/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java b/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java index 004389b515..ca811b7b9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java +++ b/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java @@ -52,6 +52,7 @@ public final class AppInitialization { ApplicationDependencies.getMegaphoneRepository().onFirstEverAppLaunch(); SignalStore.onFirstEverAppLaunch(); + SignalStore.onboarding().clearAll(); ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false)); ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.BANDIT.getPackId(), BlessedPacks.BANDIT.getPackKey(), false)); ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forReference(BlessedPacks.SWOON_HANDS.getPackId(), BlessedPacks.SWOON_HANDS.getPackKey())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java deleted file mode 100644 index 276215b55e..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.thoughtcrime.securesms.components.reminder; - -import android.content.Context; -import android.view.View; -import android.view.View.OnClickListener; - -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; -import org.thoughtcrime.securesms.util.SmsUtil; -import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; - -public class DefaultSmsReminder extends Reminder { - - public DefaultSmsReminder(@NonNull Fragment fragment, short requestCode) { - super(fragment.getString(R.string.reminder_header_sms_default_title), - fragment.getString(R.string.reminder_header_sms_default_text)); - - final OnClickListener okListener = new OnClickListener() { - @Override - public void onClick(View v) { - TextSecurePreferences.setPromptedDefaultSmsProvider(fragment.requireContext(), true); - fragment.startActivityForResult(SmsUtil.getSmsRoleIntent(fragment.requireContext()), requestCode); - } - }; - final OnClickListener dismissListener = new OnClickListener() { - @Override - public void onClick(View v) { - TextSecurePreferences.setPromptedDefaultSmsProvider(fragment.requireContext(), true); - } - }; - setOkListener(okListener); - setDismissListener(dismissListener); - } - - public static boolean isEligible(Context context) { - final boolean isDefault = Util.isDefaultSmsProvider(context); - if (isDefault) { - TextSecurePreferences.setPromptedDefaultSmsProvider(context, false); - } - - return !isDefault && !TextSecurePreferences.hasPromptedDefaultSmsProvider(context) && PhoneNumberFormatter.getLocalCountryCode() != 91; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java b/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java deleted file mode 100644 index 6da847515a..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/components/reminder/ShareReminder.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.thoughtcrime.securesms.components.reminder; - -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.view.View; -import android.view.View.OnClickListener; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.InviteActivity; -import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.util.TextSecurePreferences; - -public class ShareReminder extends Reminder { - - public ShareReminder(final @NonNull Context context) { - super(context.getString(R.string.reminder_header_share_title), - context.getString(R.string.reminder_header_share_text)); - - setDismissListener(new OnClickListener() { - @Override public void onClick(View v) { - TextSecurePreferences.setPromptedShare(context, true); - } - }); - - setOkListener(new OnClickListener() { - @Override public void onClick(View v) { - TextSecurePreferences.setPromptedShare(context, true); - context.startActivity(new Intent(context, InviteActivity.class)); - } - }); - } - - public static boolean isEligible(final @NonNull Context context) { - if (!TextSecurePreferences.isPushRegistered(context) || - TextSecurePreferences.hasPromptedShare(context)) - { - return false; - } - - return DatabaseFactory.getThreadDatabase(context).getUnarchivedConversationListCount() >= 1; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 44c8f9b007..14f248ee85 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -80,7 +80,6 @@ import org.thoughtcrime.securesms.components.RatingManager; import org.thoughtcrime.securesms.components.SearchToolbar; import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator; import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton; -import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder; import org.thoughtcrime.securesms.components.reminder.DozeReminder; import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder; import org.thoughtcrime.securesms.components.reminder.OutdatedBuildReminder; @@ -88,8 +87,6 @@ import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder; import org.thoughtcrime.securesms.components.reminder.Reminder; import org.thoughtcrime.securesms.components.reminder.ReminderView; import org.thoughtcrime.securesms.components.reminder.ServiceOutageReminder; -import org.thoughtcrime.securesms.components.reminder.ShareReminder; -import org.thoughtcrime.securesms.components.reminder.SystemSmsImportReminder; import org.thoughtcrime.securesms.components.reminder.UnauthorizedReminder; import org.thoughtcrime.securesms.conversation.ConversationFragment; import org.thoughtcrime.securesms.conversationlist.model.Conversation; @@ -103,6 +100,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.events.ReminderUpdateEvent; import org.thoughtcrime.securesms.insights.InsightsLauncher; import org.thoughtcrime.securesms.jobs.ServiceOutageDetectionJob; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.lock.v2.CreateKbsPinActivity; import org.thoughtcrime.securesms.mediasend.MediaSendActivity; import org.thoughtcrime.securesms.megaphone.Megaphone; @@ -124,7 +122,6 @@ import org.thoughtcrime.securesms.util.SnapToTopDataObserver; import org.thoughtcrime.securesms.util.StickyHeaderDecoration; import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; import org.thoughtcrime.securesms.util.WindowUtil; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; @@ -156,12 +153,6 @@ public class ConversationListFragment extends MainFragment implements ActionMode private static final int MAXIMUM_PINNED_CONVERSATIONS = 4; - private static final int[] EMPTY_IMAGES = new int[] { R.drawable.empty_inbox_1, - R.drawable.empty_inbox_2, - R.drawable.empty_inbox_3, - R.drawable.empty_inbox_4, - R.drawable.empty_inbox_5 }; - private ActionMode actionMode; private RecyclerView list; private Stub reminderView; @@ -613,14 +604,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode return Optional.of(new ServiceOutageReminder(context)); } else if (OutdatedBuildReminder.isEligible()) { return Optional.of(new OutdatedBuildReminder(context)); - } else if (DefaultSmsReminder.isEligible(context)) { - return Optional.of(new DefaultSmsReminder(this, SMS_ROLE_REQUEST_CODE)); - } else if (Util.isDefaultSmsProvider(context) && SystemSmsImportReminder.isEligible(context)) { - return Optional.of((new SystemSmsImportReminder(context))); } else if (PushRegistrationReminder.isEligible(context)) { return Optional.of((new PushRegistrationReminder(context))); - } else if (ShareReminder.isEligible(context)) { - return Optional.of(new ShareReminder(context)); } else if (DozeReminder.isEligible(context)) { return Optional.of(new DozeReminder(context)); } else { @@ -858,8 +843,8 @@ public class ConversationListFragment extends MainFragment implements ActionMode fab.startPulse(3 * 1000); cameraFab.startPulse(3 * 1000); - ImageView emptyImage = emptyState.get().findViewById(R.id.empty); - emptyImage.setImageResource(EMPTY_IMAGES[(int) (Math.random() * EMPTY_IMAGES.length)]); + SignalStore.onboarding().setShowNewGroup(true); + SignalStore.onboarding().setShowInviteFriends(true); } else { list.setVisibility(View.VISIBLE); fab.stopPulse(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java new file mode 100644 index 0000000000..3f04276d24 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java @@ -0,0 +1,62 @@ +package org.thoughtcrime.securesms.keyvalue; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; +import org.thoughtcrime.securesms.util.Util; + +public final class OnboardingValues extends SignalStoreValues { + + private static final String SHOW_NEW_GROUP = "onboarding.new_group"; + private static final String SHOW_INVITE_FRIENDS = "onboarding.invite_friends"; + private static final String SHOW_SMS = "onboarding.sms"; + + OnboardingValues(@NonNull KeyValueStore store) { + super(store); + } + + @Override + void onFirstEverAppLaunch() { + putBoolean(SHOW_NEW_GROUP, true); + putBoolean(SHOW_INVITE_FRIENDS, true); + putBoolean(SHOW_SMS, true); + } + + public void clearAll() { + setShowNewGroup(false); + setShowInviteFriends(false); + setShowSms(false); + } + + public boolean hasOnboarding(@NonNull Context context) { + return shouldShowNewGroup() || + shouldShowInviteFriends() || + shouldShowSms(context); + } + + public void setShowNewGroup(boolean value) { + putBoolean(SHOW_NEW_GROUP, value); + } + + public boolean shouldShowNewGroup() { + return getBoolean(SHOW_NEW_GROUP, false); + } + + public void setShowInviteFriends(boolean value) { + putBoolean(SHOW_INVITE_FRIENDS, value); + } + + public boolean shouldShowInviteFriends() { + return getBoolean(SHOW_INVITE_FRIENDS, false); + } + + public void setShowSms(boolean value) { + putBoolean(SHOW_SMS, value); + } + + public boolean shouldShowSms(@NonNull Context context) { + return getBoolean(SHOW_SMS, false) && !Util.isDefaultSmsProvider(context) && PhoneNumberFormatter.getLocalCountryCode() != 91; + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java index 340cd9e5e2..17603ea04e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java @@ -27,6 +27,7 @@ public final class SignalStore { private final SettingsValues settingsValues; private final CertificateValues certificateValues; private final PhoneNumberPrivacyValues phoneNumberPrivacyValues; + private final OnboardingValues onboardingValues; private SignalStore() { this.store = new KeyValueStore(ApplicationDependencies.getApplication()); @@ -43,6 +44,7 @@ public final class SignalStore { this.settingsValues = new SettingsValues(store); this.certificateValues = new CertificateValues(store); this.phoneNumberPrivacyValues = new PhoneNumberPrivacyValues(store); + this.onboardingValues = new OnboardingValues(store); } public static void onFirstEverAppLaunch() { @@ -58,6 +60,7 @@ public final class SignalStore { settings().onFirstEverAppLaunch(); certificateValues().onFirstEverAppLaunch(); phoneNumberPrivacy().onFirstEverAppLaunch(); + onboarding().onFirstEverAppLaunch(); } public static @NonNull KbsValues kbsValues() { @@ -112,6 +115,10 @@ public final class SignalStore { return INSTANCE.phoneNumberPrivacyValues; } + public static @NonNull OnboardingValues onboarding() { + return INSTANCE.onboardingValues; + } + public static @NonNull GroupsV2AuthorizationSignalStoreCache groupsV2AuthorizationCache() { return new GroupsV2AuthorizationSignalStoreCache(getStore()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java index da90fd9e96..a6cdd69b8f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphone.java @@ -200,6 +200,9 @@ public class Megaphone { /** Specialized style for announcing link previews. */ LINK_PREVIEWS, + /** Specialized style for onboarding. */ + ONBOARDING, + /** Basic bottom of the screen megaphone with optional snooze and action buttons. */ BASIC, diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java index 9f6cba6889..4923185349 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/MegaphoneViewBuilder.java @@ -24,6 +24,8 @@ public class MegaphoneViewBuilder { return buildReactionsMegaphone(context, megaphone, listener); case LINK_PREVIEWS: return buildLinkPreviewsMegaphone(context, megaphone, listener); + case ONBOARDING: + return buildOnboardingMegaphone(context, megaphone, listener); case POPUP: return buildPopupMegaphone(context, megaphone, listener); default: @@ -58,6 +60,15 @@ public class MegaphoneViewBuilder { return view; } + private static @NonNull View buildOnboardingMegaphone(@NonNull Context context, + @NonNull Megaphone megaphone, + @NonNull MegaphoneActionController listener) + { + OnboardingMegaphoneView view = new OnboardingMegaphoneView(context); + view.present(megaphone, listener); + return view; + } + private static @NonNull View buildPopupMegaphone(@NonNull Context context, @NonNull Megaphone megaphone, @NonNull MegaphoneActionController listener) diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java index cdb48bf941..3467a94278 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/Megaphones.java @@ -93,6 +93,7 @@ public final class Megaphones { put(Event.RESEARCH, shouldShowResearchMegaphone(context) ? ShowForDurationSchedule.showForDays(7) : NEVER); put(Event.DONATE, shouldShowDonateMegaphone(context) ? ShowForDurationSchedule.showForDays(7) : NEVER); put(Event.GROUP_CALLING, shouldShowGroupCallingMegaphone() ? ALWAYS : NEVER); + put(Event.ONBOARDING, shouldShowOnboardingMegaphone(context) ? ALWAYS : NEVER); }}; } @@ -116,6 +117,8 @@ public final class Megaphones { return buildDonateMegaphone(context); case GROUP_CALLING: return buildGroupCallingMegaphone(context); + case ONBOARDING: + return buildOnboardingMegaphone(); default: throw new IllegalArgumentException("Event not handled!"); } @@ -255,6 +258,12 @@ public final class Megaphones { .build(); } + private static @NonNull Megaphone buildOnboardingMegaphone() { + return new Megaphone.Builder(Event.ONBOARDING, Megaphone.Style.ONBOARDING) + .setPriority(Megaphone.Priority.DEFAULT) + .build(); + } + private static boolean shouldShowMessageRequestsMegaphone() { return Recipient.self().getProfileName() == ProfileName.EMPTY; } @@ -275,6 +284,10 @@ public final class Megaphones { return FeatureFlags.groupCalling(); } + private static boolean shouldShowOnboardingMegaphone(@NonNull Context context) { + return SignalStore.onboarding().hasOnboarding(context); + } + public enum Event { REACTIONS("reactions"), PINS_FOR_ALL("pins_for_all"), @@ -284,7 +297,8 @@ public final class Megaphones { CLIENT_DEPRECATED("client_deprecated"), RESEARCH("research"), DONATE("donate"), - GROUP_CALLING("group_calling"); + GROUP_CALLING("group_calling"), + ONBOARDING("onboarding"); private final String key; diff --git a/app/src/main/java/org/thoughtcrime/securesms/megaphone/OnboardingMegaphoneView.java b/app/src/main/java/org/thoughtcrime/securesms/megaphone/OnboardingMegaphoneView.java new file mode 100644 index 0000000000..05c0494a85 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/megaphone/OnboardingMegaphoneView.java @@ -0,0 +1,262 @@ +package org.thoughtcrime.securesms.megaphone; + +import android.content.Context; +import android.content.Intent; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.InviteActivity; +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.conversationlist.ConversationListFragment; +import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity; +import org.thoughtcrime.securesms.keyvalue.SignalStore; +import org.thoughtcrime.securesms.util.SmsUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * Shows the a fun rail of cards that educate the user about some actions they can take right after + * they install the app. + */ +public class OnboardingMegaphoneView extends FrameLayout { + + private static final String TAG = Log.tag(OnboardingMegaphoneView.class); + + private RecyclerView cardList; + + public OnboardingMegaphoneView(Context context) { + super(context); + initialize(context); + } + + public OnboardingMegaphoneView(Context context, AttributeSet attrs) { + super(context, attrs); + initialize(context); + } + + private void initialize(@NonNull Context context) { + inflate(context, R.layout.onboarding_megaphone, this); + + this.cardList = findViewById(R.id.onboarding_megaphone_list); + } + + public void present(@NonNull Megaphone megaphone, @NonNull MegaphoneActionController listener) { + this.cardList.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); + this.cardList.setAdapter(new CardAdapter(getContext(), listener)); + } + + private static class CardAdapter extends RecyclerView.Adapter implements ActionClickListener { + + private static final int TYPE_GROUP = 0; + private static final int TYPE_INVITE = 1; + private static final int TYPE_SMS = 2; + + private final Context context; + private final MegaphoneActionController controller; + private final List data; + + CardAdapter(@NonNull Context context, @NonNull MegaphoneActionController controller) { + this.context = context; + this.controller = controller; + this.data = buildData(context); + + if (data.isEmpty()) { + Log.i(TAG, "Nothing to show (constructor)! Considering megaphone completed."); + controller.onMegaphoneCompleted(Megaphones.Event.ONBOARDING); + } + + setHasStableIds(true); + } + + @Override + public int getItemViewType(int position) { + return data.get(position); + } + + @Override + public long getItemId(int position) { + return data.get(position); + } + + @Override + public @NonNull CardViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.onboarding_megaphone_list_item, parent, false); + switch (viewType) { + case TYPE_GROUP: return new GroupCardViewHolder(view); + case TYPE_INVITE: return new InviteCardViewHolder(view); + case TYPE_SMS: return new SmsCardViewHolder(view); + default: throw new IllegalStateException("Invalid viewType! " + viewType); + } + } + + @Override + public void onBindViewHolder(@NonNull CardViewHolder holder, int position) { + holder.bind(this, controller); + } + + @Override + public int getItemCount() { + return data.size(); + } + + @Override + public void onClick() { + data.clear(); + data.addAll(buildData(context)); + if (data.isEmpty()) { + Log.i(TAG, "Nothing to show! Considering megaphone completed."); + controller.onMegaphoneCompleted(Megaphones.Event.ONBOARDING); + } + notifyDataSetChanged(); + } + + private static List buildData(@NonNull Context context) { + List data = new ArrayList<>(); + + if (SignalStore.onboarding().shouldShowNewGroup()) { + data.add(TYPE_GROUP); + } + + if (SignalStore.onboarding().shouldShowInviteFriends()) { + data.add(TYPE_INVITE); + } + + if (SignalStore.onboarding().shouldShowSms(context)) { + data.add(TYPE_SMS); + } + + return data; + } + } + + private interface ActionClickListener { + void onClick(); + } + + private static abstract class CardViewHolder extends RecyclerView.ViewHolder { + private final ImageView image; + private final TextView actionButton; + private final View closeButton; + + public CardViewHolder(@NonNull View itemView) { + super(itemView); + this.image = itemView.findViewById(R.id.onboarding_megaphone_item_image); + this.actionButton = itemView.findViewById(R.id.onboarding_megaphone_item_button); + this.closeButton = itemView.findViewById(R.id.onboarding_megaphone_item_close); + } + + public void bind(@NonNull ActionClickListener listener, @NonNull MegaphoneActionController controller) { + image.setImageResource(getImageRes()); + actionButton.setText(getButtonStringRes()); + actionButton.setOnClickListener(v -> { + onActionClicked(controller); + listener.onClick(); + }); + closeButton.setOnClickListener(v -> { + onCloseClicked(); + listener.onClick(); + }); + } + + abstract @StringRes int getButtonStringRes(); + abstract @DrawableRes int getImageRes(); + abstract void onActionClicked(@NonNull MegaphoneActionController controller); + abstract void onCloseClicked(); + } + + private static class GroupCardViewHolder extends CardViewHolder { + + public GroupCardViewHolder(@NonNull View itemView) { + super(itemView); + } + + @Override + int getButtonStringRes() { + return R.string.Megaphones_new_group; + } + + @Override + int getImageRes() { + return R.drawable.ic_megaphone_start_group; + } + + @Override + void onActionClicked(@NonNull MegaphoneActionController controller) { + controller.onMegaphoneNavigationRequested(CreateGroupActivity.newIntent(controller.getMegaphoneActivity())); + } + + @Override + void onCloseClicked() { + SignalStore.onboarding().setShowNewGroup(false); + } + } + + private static class InviteCardViewHolder extends CardViewHolder { + + public InviteCardViewHolder(@NonNull View itemView) { + super(itemView); + } + + @Override + int getButtonStringRes() { + return R.string.Megaphones_invite_friends; + } + + @Override + int getImageRes() { + return R.drawable.ic_megaphone_invite_friends; + } + + @Override + void onActionClicked(@NonNull MegaphoneActionController controller) { + controller.onMegaphoneNavigationRequested(new Intent(controller.getMegaphoneActivity(), InviteActivity.class)); + } + + @Override + void onCloseClicked() { + SignalStore.onboarding().setShowInviteFriends(false); + } + } + + private static class SmsCardViewHolder extends CardViewHolder { + + public SmsCardViewHolder(@NonNull View itemView) { + super(itemView); + } + + @Override + int getButtonStringRes() { + return R.string.Megaphones_use_sms; + } + + @Override + int getImageRes() { + return R.drawable.ic_megaphone_use_sms; + } + + @Override + void onActionClicked(@NonNull MegaphoneActionController controller) { + Intent intent = SmsUtil.getSmsRoleIntent(controller.getMegaphoneActivity()); + controller.onMegaphoneNavigationRequested(intent, ConversationListFragment.SMS_ROLE_REQUEST_CODE); + SignalStore.onboarding().setShowSms(false); + } + + @Override + void onCloseClicked() { + SignalStore.onboarding().setShowSms(false); + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java index 356b106d3e..424cc97764 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/pin/PinRestoreEntryFragment.java @@ -29,6 +29,7 @@ import org.thoughtcrime.securesms.MainActivity; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobs.ProfileUploadJob; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.lock.v2.KbsConstants; import org.thoughtcrime.securesms.lock.v2.PinKeyboardType; import org.thoughtcrime.securesms.profiles.AvatarHelper; @@ -227,6 +228,7 @@ public class PinRestoreEntryFragment extends LoggingFragment { private void handleSuccess() { cancelSpinning(pinButton); + SignalStore.onboarding().clearAll(); Activity activity = requireActivity(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java index e159bc82f5..7314ddd39b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/registration/fragments/RegistrationLockFragment.java @@ -304,6 +304,7 @@ public final class RegistrationLockFragment extends BaseRegistrationFragment { long startTime = System.currentTimeMillis(); SimpleTask.run(() -> { + SignalStore.onboarding().clearAll(); return ApplicationDependencies.getJobManager().runSynchronously(new StorageAccountRestoreJob(), StorageAccountRestoreJob.LIFESPAN); }, result -> { long elapsedTime = System.currentTimeMillis() - startTime; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 8fb4247088..1d50618a10 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -78,9 +78,7 @@ public class TextSecurePreferences { private static final String GCM_PASSWORD_PREF = "pref_gcm_password"; private static final String SEEN_WELCOME_SCREEN_PREF = "pref_seen_welcome_screen"; private static final String PROMPTED_PUSH_REGISTRATION_PREF = "pref_prompted_push_registration"; - private static final String PROMPTED_DEFAULT_SMS_PREF = "pref_prompted_default_sms"; private static final String PROMPTED_OPTIMIZE_DOZE_PREF = "pref_prompted_optimize_doze"; - private static final String PROMPTED_SHARE_PREF = "pref_prompted_share"; private static final String SIGNALING_KEY_PREF = "pref_signaling_key"; private static final String DIRECTORY_FRESH_TIME_PREF = "pref_directory_refresh_time"; private static final String UPDATE_APK_REFRESH_TIME_PREF = "pref_update_apk_refresh_time"; @@ -890,14 +888,6 @@ public class TextSecurePreferences { setBooleanPreference(context, PROMPTED_PUSH_REGISTRATION_PREF, value); } - public static boolean hasPromptedDefaultSmsProvider(Context context) { - return getBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, false); - } - - public static void setPromptedDefaultSmsProvider(Context context, boolean value) { - setBooleanPreference(context, PROMPTED_DEFAULT_SMS_PREF, value); - } - public static void setPromptedOptimizeDoze(Context context, boolean value) { setBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, value); } @@ -906,14 +896,6 @@ public class TextSecurePreferences { return getBooleanPreference(context, PROMPTED_OPTIMIZE_DOZE_PREF, false); } - public static boolean hasPromptedShare(Context context) { - return getBooleanPreference(context, PROMPTED_SHARE_PREF, false); - } - - public static void setPromptedShare(Context context, boolean value) { - setBooleanPreference(context, PROMPTED_SHARE_PREF, value); - } - public static boolean isInterceptAllMmsEnabled(Context context) { return getBooleanPreference(context, ALL_MMS_PREF, true); } diff --git a/app/src/main/res/drawable-hdpi/conversation_list_empty_state.webp b/app/src/main/res/drawable-hdpi/conversation_list_empty_state.webp deleted file mode 100644 index 60405ed02d..0000000000 Binary files a/app/src/main/res/drawable-hdpi/conversation_list_empty_state.webp and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/empty_inbox_1.webp b/app/src/main/res/drawable-hdpi/empty_inbox_1.webp deleted file mode 100644 index a036db47a2..0000000000 Binary files a/app/src/main/res/drawable-hdpi/empty_inbox_1.webp and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/empty_inbox_2.webp b/app/src/main/res/drawable-hdpi/empty_inbox_2.webp deleted file mode 100644 index b3f1f34beb..0000000000 Binary files a/app/src/main/res/drawable-hdpi/empty_inbox_2.webp and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/empty_inbox_3.webp b/app/src/main/res/drawable-hdpi/empty_inbox_3.webp deleted file mode 100644 index 68c4ce7d28..0000000000 Binary files a/app/src/main/res/drawable-hdpi/empty_inbox_3.webp and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/empty_inbox_4.webp b/app/src/main/res/drawable-hdpi/empty_inbox_4.webp deleted file mode 100644 index ea118359fa..0000000000 Binary files a/app/src/main/res/drawable-hdpi/empty_inbox_4.webp and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/empty_inbox_5.webp b/app/src/main/res/drawable-hdpi/empty_inbox_5.webp deleted file mode 100644 index ca4042a44f..0000000000 Binary files a/app/src/main/res/drawable-hdpi/empty_inbox_5.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/conversation_list_empty_state.webp b/app/src/main/res/drawable-mdpi/conversation_list_empty_state.webp deleted file mode 100644 index 760af21233..0000000000 Binary files a/app/src/main/res/drawable-mdpi/conversation_list_empty_state.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/empty_inbox_1.webp b/app/src/main/res/drawable-mdpi/empty_inbox_1.webp deleted file mode 100644 index 09630f8dda..0000000000 Binary files a/app/src/main/res/drawable-mdpi/empty_inbox_1.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/empty_inbox_2.webp b/app/src/main/res/drawable-mdpi/empty_inbox_2.webp deleted file mode 100644 index 1d10226aff..0000000000 Binary files a/app/src/main/res/drawable-mdpi/empty_inbox_2.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/empty_inbox_3.webp b/app/src/main/res/drawable-mdpi/empty_inbox_3.webp deleted file mode 100644 index 6a83ed50f5..0000000000 Binary files a/app/src/main/res/drawable-mdpi/empty_inbox_3.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/empty_inbox_4.webp b/app/src/main/res/drawable-mdpi/empty_inbox_4.webp deleted file mode 100644 index fab4ffd579..0000000000 Binary files a/app/src/main/res/drawable-mdpi/empty_inbox_4.webp and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/empty_inbox_5.webp b/app/src/main/res/drawable-mdpi/empty_inbox_5.webp deleted file mode 100644 index 1e2dbb62d8..0000000000 Binary files a/app/src/main/res/drawable-mdpi/empty_inbox_5.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/conversation_list_empty_state.webp b/app/src/main/res/drawable-xhdpi/conversation_list_empty_state.webp deleted file mode 100644 index 2b962bfafa..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/conversation_list_empty_state.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/empty_inbox_1.webp b/app/src/main/res/drawable-xhdpi/empty_inbox_1.webp deleted file mode 100644 index a269e53159..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/empty_inbox_1.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/empty_inbox_2.webp b/app/src/main/res/drawable-xhdpi/empty_inbox_2.webp deleted file mode 100644 index 13d6f3e967..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/empty_inbox_2.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/empty_inbox_3.webp b/app/src/main/res/drawable-xhdpi/empty_inbox_3.webp deleted file mode 100644 index b3514f8bab..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/empty_inbox_3.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/empty_inbox_4.webp b/app/src/main/res/drawable-xhdpi/empty_inbox_4.webp deleted file mode 100644 index 443ebe6dbb..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/empty_inbox_4.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/empty_inbox_5.webp b/app/src/main/res/drawable-xhdpi/empty_inbox_5.webp deleted file mode 100644 index a4d8f5c510..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/empty_inbox_5.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/conversation_list_empty_state.webp b/app/src/main/res/drawable-xxhdpi/conversation_list_empty_state.webp deleted file mode 100644 index 29c966a783..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/conversation_list_empty_state.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/empty_inbox_1.webp b/app/src/main/res/drawable-xxhdpi/empty_inbox_1.webp deleted file mode 100644 index 113207fe17..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/empty_inbox_1.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/empty_inbox_2.webp b/app/src/main/res/drawable-xxhdpi/empty_inbox_2.webp deleted file mode 100644 index 9a0e9ebef6..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/empty_inbox_2.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/empty_inbox_3.webp b/app/src/main/res/drawable-xxhdpi/empty_inbox_3.webp deleted file mode 100644 index 63e39d99ad..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/empty_inbox_3.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/empty_inbox_4.webp b/app/src/main/res/drawable-xxhdpi/empty_inbox_4.webp deleted file mode 100644 index 652cdba178..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/empty_inbox_4.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/empty_inbox_5.webp b/app/src/main/res/drawable-xxhdpi/empty_inbox_5.webp deleted file mode 100644 index eb5d002a8c..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/empty_inbox_5.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.webp b/app/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.webp deleted file mode 100644 index 6af8f06b7a..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/conversation_list_empty_state.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/empty_inbox_1.webp b/app/src/main/res/drawable-xxxhdpi/empty_inbox_1.webp deleted file mode 100644 index 8b3b8a9441..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/empty_inbox_1.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/empty_inbox_2.webp b/app/src/main/res/drawable-xxxhdpi/empty_inbox_2.webp deleted file mode 100644 index 0580e59d2e..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/empty_inbox_2.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/empty_inbox_3.webp b/app/src/main/res/drawable-xxxhdpi/empty_inbox_3.webp deleted file mode 100644 index 874ab96827..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/empty_inbox_3.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/empty_inbox_4.webp b/app/src/main/res/drawable-xxxhdpi/empty_inbox_4.webp deleted file mode 100644 index 26acaa6c27..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/empty_inbox_4.webp and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/empty_inbox_5.webp b/app/src/main/res/drawable-xxxhdpi/empty_inbox_5.webp deleted file mode 100644 index a791e5e804..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/empty_inbox_5.webp and /dev/null differ diff --git a/app/src/main/res/drawable/ic_close_14.xml b/app/src/main/res/drawable/ic_close_14.xml new file mode 100644 index 0000000000..14f37e2d83 --- /dev/null +++ b/app/src/main/res/drawable/ic_close_14.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_megaphone_invite_friends.xml b/app/src/main/res/drawable/ic_megaphone_invite_friends.xml new file mode 100644 index 0000000000..b45c333bea --- /dev/null +++ b/app/src/main/res/drawable/ic_megaphone_invite_friends.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_megaphone_start_group.xml b/app/src/main/res/drawable/ic_megaphone_start_group.xml new file mode 100644 index 0000000000..9c5e5ac791 --- /dev/null +++ b/app/src/main/res/drawable/ic_megaphone_start_group.xml @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_megaphone_use_sms.xml b/app/src/main/res/drawable/ic_megaphone_use_sms.xml new file mode 100644 index 0000000000..d7c2057e1e --- /dev/null +++ b/app/src/main/res/drawable/ic_megaphone_use_sms.xml @@ -0,0 +1,23 @@ + + + + + + diff --git a/app/src/main/res/layout/basic_megaphone_view.xml b/app/src/main/res/layout/basic_megaphone_view.xml index 3cf58407ec..66065bf0e2 100644 --- a/app/src/main/res/layout/basic_megaphone_view.xml +++ b/app/src/main/res/layout/basic_megaphone_view.xml @@ -5,82 +5,96 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> - + android:layout_margin="8dp" + android:clipChildren="false" + android:clipToPadding="false" + app:cardBackgroundColor="@color/megaphone_background_color" + app:cardCornerRadius="8dp" + app:cardElevation="6dp" + app:cardPreventCornerOverlap="false" + app:cardUseCompatPadding="true" + app:contentPadding="0dp"> - - - + android:clickable="true" + android:paddingStart="8dp" + android:paddingTop="16dp" + android:paddingEnd="8dp" + android:paddingBottom="8dp"> - + - + -