diff --git a/res/layout/conversation_activity.xml b/res/layout/conversation_activity.xml
index fd6e1786b3..48a4716e46 100644
--- a/res/layout/conversation_activity.xml
+++ b/res/layout/conversation_activity.xml
@@ -20,6 +20,11 @@
android:paddingTop="?attr/actionBarSize"
android:gravity="bottom">
+
+
-
diff --git a/res/layout/reminder_header.xml b/res/layout/reminder_header.xml
index 2d737437e3..1af3d4ed06 100644
--- a/res/layout/reminder_header.xml
+++ b/res/layout/reminder_header.xml
@@ -1,59 +1,62 @@
-
-
+
+
+ android:layout_weight="1"
+ android:layout_margin="10dp"
+ android:orientation="vertical">
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 8a4be620d8..ba9d1c988f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1037,12 +1037,20 @@
Your build of Signal has expired!
Messages will no longer send successfully, please update to the most recent version.
+ UPGRADE
Use as default SMS app?
Tap to make Signal your default SMS app.
+ SET
Import system SMS?
Tap to copy your phone\'s SMS messages into its encrypted database.
- Enable Signal messages?
- Tap for instant delivery, stronger privacy, and no SMS fees.
+ IMPORT
+ Enable Signal?
+ Upgrade your messaging experience.
+ ENABLE
+ Invite to Signal?
+ Take your conversation with %1$s to the next level.
+ INVITE
+ CLOSE
You
diff --git a/src/org/thoughtcrime/securesms/ConversationActivity.java b/src/org/thoughtcrime/securesms/ConversationActivity.java
index 4427988c67..45496a27d8 100644
--- a/src/org/thoughtcrime/securesms/ConversationActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationActivity.java
@@ -66,6 +66,8 @@ import org.thoughtcrime.securesms.components.AnimatingToggle;
import org.thoughtcrime.securesms.components.ComposeText;
import org.thoughtcrime.securesms.components.InputAwareLayout;
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout.OnKeyboardShownListener;
+import org.thoughtcrime.securesms.components.reminder.InviteReminder;
+import org.thoughtcrime.securesms.components.reminder.ReminderView;
import org.thoughtcrime.securesms.components.SendButton;
import org.thoughtcrime.securesms.components.camera.HidingImageButton;
import org.thoughtcrime.securesms.components.camera.QuickAttachmentDrawer;
@@ -85,6 +87,7 @@ import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
import org.thoughtcrime.securesms.database.DraftDatabase.Drafts;
import org.thoughtcrime.securesms.database.GroupDatabase;
import org.thoughtcrime.securesms.database.MmsSmsColumns.Types;
+import org.thoughtcrime.securesms.database.RecipientPreferenceDatabase.RecipientsPreferences;
import org.thoughtcrime.securesms.database.ThreadDatabase;
import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.mms.AttachmentManager.MediaType;
@@ -106,6 +109,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
+import org.thoughtcrime.securesms.util.concurrent.AssertedSuccessListener;
import org.thoughtcrime.securesms.util.CharacterCalculator.CharacterState;
import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.DirectoryHelper;
@@ -117,16 +121,17 @@ import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
+import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.whispersystems.libaxolotl.InvalidMessageException;
+import org.whispersystems.libaxolotl.util.guava.Optional;
import org.whispersystems.textsecure.api.util.InvalidNumberException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import static org.thoughtcrime.securesms.TransportOption.Type;
import static org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
@@ -175,6 +180,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
private InputAwareLayout container;
private View composePanel;
private View composeBubble;
+ private ReminderView reminderView;
private AttachmentTypeSelectorAdapter attachmentAdapter;
private AttachmentManager attachmentManager;
@@ -216,16 +222,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
initializeActionBar();
initializeViews();
initializeResources();
- initializeSecurity(false, false).addListener(new ListenableFuture.Listener() {
+ initializeSecurity(false, false).addListener(new AssertedSuccessListener() {
@Override
public void onSuccess(Boolean result) {
initializeDraft();
}
-
- @Override
- public void onFailure(ExecutionException e) {
- throw new AssertionError(e);
- }
});
}
@@ -241,15 +242,11 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setIntent(intent);
initializeResources();
- initializeSecurity(false, false).addListener(new ListenableFuture.Listener() {
+ initializeSecurity(false, false).addListener(new AssertedSuccessListener() {
@Override
public void onSuccess(Boolean result) {
initializeDraft();
}
- @Override
- public void onFailure(ExecutionException e) {
- throw new AssertionError(e);
- }
});
if (fragment != null) {
@@ -804,14 +801,25 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
if (result.first != currentSecureText || result.second != currentSecureVoice) {
handleSecurityChange(result.first, result.second);
}
-
future.set(true);
+ onSecurityUpdated();
}
}.execute(recipients);
return future;
}
+ private void onSecurityUpdated() {
+ updateInviteReminder();
+ }
+
+ private void updateInviteReminder() {
+ if (TextSecurePreferences.isPushRegistered(this) && !isSecureText && recipients.isSingleRecipient()) {
+ new ShowInviteReminderTask().execute(recipients);
+ } else {
+ reminderView.hide();
+ }
+ }
private void initializeMmsEnabledCheck() {
new AsyncTask() {
@@ -828,21 +836,21 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
}
private void initializeViews() {
- titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
- buttonToggle = (AnimatingToggle) findViewById(R.id.button_toggle);
- sendButton = (SendButton) findViewById(R.id.send_button);
- attachButton = (ImageButton) findViewById(R.id.attach_button);
- composeText = (ComposeText) findViewById(R.id.embedded_text_editor);
- charactersLeft = (TextView) findViewById(R.id.space_left);
- emojiToggle = (EmojiToggle) findViewById(R.id.emoji_toggle);
- emojiDrawer = (EmojiDrawer) findViewById(R.id.emoji_drawer);
- unblockButton = (Button) findViewById(R.id.unblock_button);
- composePanel = findViewById(R.id.bottom_panel);
- composeBubble = findViewById(R.id.compose_bubble);
- container = (InputAwareLayout) findViewById(R.id.layout_container);
-
- quickAttachmentDrawer = (QuickAttachmentDrawer) findViewById(R.id.quick_attachment_drawer);
- quickAttachmentToggle = (HidingImageButton) findViewById(R.id.quick_attachment_toggle);
+ titleView = (ConversationTitleView) getSupportActionBar().getCustomView();
+ buttonToggle = ViewUtil.findById(this, R.id.button_toggle);
+ sendButton = ViewUtil.findById(this, R.id.send_button);
+ attachButton = ViewUtil.findById(this, R.id.attach_button);
+ composeText = ViewUtil.findById(this, R.id.embedded_text_editor);
+ charactersLeft = ViewUtil.findById(this, R.id.space_left);
+ emojiToggle = ViewUtil.findById(this, R.id.emoji_toggle);
+ emojiDrawer = ViewUtil.findById(this, R.id.emoji_drawer);
+ unblockButton = ViewUtil.findById(this, R.id.unblock_button);
+ composePanel = ViewUtil.findById(this, R.id.bottom_panel);
+ composeBubble = ViewUtil.findById(this, R.id.compose_bubble);
+ container = ViewUtil.findById(this, R.id.layout_container);
+ reminderView = ViewUtil.findById(this, R.id.reminder);
+ quickAttachmentDrawer = ViewUtil.findById(this, R.id.quick_attachment_drawer);
+ quickAttachmentToggle = ViewUtil.findById(this, R.id.quick_attachment_toggle);
container.addOnKeyboardShownListener(this);
@@ -944,6 +952,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
titleView.setTitle(recipients);
setBlockedUserState(recipients);
setActionBarColor(recipients.getColor());
+ updateInviteReminder();
}
});
}
@@ -1441,4 +1450,31 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
updateToggleButtonState();
}
+ private class ShowInviteReminderTask extends AsyncTask> {
+ @Override
+ protected Pair doInBackground(Recipients... recipients) {
+ if (recipients.length != 1 || recipients[0] == null) throw new AssertionError("task needs exactly one Recipients object");
+
+ Optional prefs = DatabaseFactory.getRecipientPreferenceDatabase(ConversationActivity.this)
+ .getRecipientsPreferences(recipients[0].getIds());
+ return new Pair<>(recipients[0], prefs.isPresent() && prefs.get().hasSeenInviteReminder());
+ }
+
+ @Override
+ protected void onPostExecute(Pair result) {
+ if (!result.second && result.first == recipients) {
+ InviteReminder reminder = new InviteReminder(ConversationActivity.this, result.first);
+ reminder.setOkListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ handleInviteLink();
+ reminderView.requestDismiss();
+ }
+ });
+ reminderView.showReminder(reminder);
+ } else {
+ reminderView.hide();
+ }
+ }
+ }
}
diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java
index 007b940c75..65e29ae8ef 100644
--- a/src/org/thoughtcrime/securesms/ConversationListFragment.java
+++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java
@@ -33,8 +33,6 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.RecyclerListener;
-import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -48,12 +46,12 @@ import com.afollestad.materialdialogs.AlertDialogWrapper;
import com.melnykov.fab.FloatingActionButton;
import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener;
-import org.thoughtcrime.securesms.components.DefaultSmsReminder;
-import org.thoughtcrime.securesms.components.ExpiredBuildReminder;
-import org.thoughtcrime.securesms.components.PushRegistrationReminder;
-import org.thoughtcrime.securesms.components.Reminder;
-import org.thoughtcrime.securesms.components.ReminderView;
-import org.thoughtcrime.securesms.components.SystemSmsImportReminder;
+import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder;
+import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
+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.SystemSmsImportReminder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
@@ -137,7 +135,7 @@ public class ConversationListFragment extends Fragment
@Override protected Optional extends Reminder> doInBackground(Context... params) {
final Context context = params[0];
if (ExpiredBuildReminder.isEligible(context)) {
- return Optional.of(new ExpiredBuildReminder());
+ return Optional.of(new ExpiredBuildReminder(context));
} else if (DefaultSmsReminder.isEligible(context)) {
return Optional.of(new DefaultSmsReminder(context));
} else if (SystemSmsImportReminder.isEligible(context)) {
diff --git a/src/org/thoughtcrime/securesms/components/ComposeText.java b/src/org/thoughtcrime/securesms/components/ComposeText.java
index 08e4624692..ae9b85d175 100644
--- a/src/org/thoughtcrime/securesms/components/ComposeText.java
+++ b/src/org/thoughtcrime/securesms/components/ComposeText.java
@@ -57,6 +57,7 @@ public class ComposeText extends EmojiEditText {
}
append(invite);
+ setSelection(getText().length());
}
private boolean isLandscape() {
diff --git a/src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java b/src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java
deleted file mode 100644
index 55ded55da2..0000000000
--- a/src/org/thoughtcrime/securesms/components/ExpiredBuildReminder.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.thoughtcrime.securesms.components;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.provider.Telephony;
-import android.util.Log;
-import android.view.View;
-
-import org.thoughtcrime.securesms.R;
-import org.thoughtcrime.securesms.TextSecureExpiredException;
-import org.thoughtcrime.securesms.util.TextSecurePreferences;
-import org.thoughtcrime.securesms.util.Util;
-
-public class ExpiredBuildReminder extends Reminder {
-
- private static final String TAG = ExpiredBuildReminder.class.getSimpleName();
-
- public ExpiredBuildReminder() {
- super(R.drawable.ic_warning_dark,
- R.string.reminder_header_expired_build,
- R.string.reminder_header_expired_build_details);
- }
-
- @Override
- public boolean isDismissable() {
- return false;
- }
-
- public static boolean isEligible(Context context) {
- return !Util.isBuildFresh();
- }
-
-}
diff --git a/src/org/thoughtcrime/securesms/components/Reminder.java b/src/org/thoughtcrime/securesms/components/Reminder.java
deleted file mode 100644
index 090f35d3eb..0000000000
--- a/src/org/thoughtcrime/securesms/components/Reminder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.thoughtcrime.securesms.components;
-
-import android.content.Context;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-
-import org.thoughtcrime.securesms.R;
-
-public abstract class Reminder {
- private int iconResId;
- private int titleResId;
- private int textResId;
- private OnClickListener okListener;
- private OnClickListener cancelListener;
-
- public Reminder(int iconResId, int titleResId, int textResId) {
- this.iconResId = iconResId;
- this.titleResId = titleResId;
- this.textResId = textResId;
- }
-
- public int getIconResId() {
- return iconResId;
- }
-
- public int getTitleResId() {
- return titleResId;
- }
-
- public int getTextResId() {
- return textResId;
- }
-
- public OnClickListener getOkListener() {
- return okListener;
- }
-
- public OnClickListener getCancelListener() {
- return cancelListener;
- }
-
- public void setOkListener(OnClickListener okListener) {
- this.okListener = okListener;
- }
-
- public void setCancelListener(OnClickListener cancelListener) {
- this.cancelListener = cancelListener;
- }
-
- public boolean isDismissable() {
- return true;
- }
-}
diff --git a/src/org/thoughtcrime/securesms/components/DefaultSmsReminder.java b/src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java
similarity index 78%
rename from src/org/thoughtcrime/securesms/components/DefaultSmsReminder.java
rename to src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java
index 6a2a0ea8ba..0035a42190 100644
--- a/src/org/thoughtcrime/securesms/components/DefaultSmsReminder.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/DefaultSmsReminder.java
@@ -1,9 +1,8 @@
-package org.thoughtcrime.securesms.components;
+package org.thoughtcrime.securesms.components.reminder;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.provider.Telephony;
import android.view.View;
@@ -17,9 +16,9 @@ public class DefaultSmsReminder extends Reminder {
@TargetApi(VERSION_CODES.KITKAT)
public DefaultSmsReminder(final Context context) {
- super(R.drawable.sms_selection_icon,
- R.string.reminder_header_sms_default_title,
- R.string.reminder_header_sms_default_text);
+ super(context.getString(R.string.reminder_header_sms_default_title),
+ context.getString(R.string.reminder_header_sms_default_text),
+ context.getString(R.string.reminder_header_sms_default_button));
final OnClickListener okListener = new OnClickListener() {
@Override
@@ -30,14 +29,14 @@ public class DefaultSmsReminder extends Reminder {
context.startActivity(intent);
}
};
- final OnClickListener cancelListener = new OnClickListener() {
+ final OnClickListener dismissListener = new OnClickListener() {
@Override
public void onClick(View v) {
TextSecurePreferences.setPromptedDefaultSmsProvider(context, true);
}
};
setOkListener(okListener);
- setCancelListener(cancelListener);
+ setDismissListener(dismissListener);
}
public static boolean isEligible(Context context) {
diff --git a/src/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java b/src/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java
new file mode 100644
index 0000000000..f5a5b0fc9c
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/reminder/ExpiredBuildReminder.java
@@ -0,0 +1,39 @@
+package org.thoughtcrime.securesms.components.reminder;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.util.Util;
+
+public class ExpiredBuildReminder extends Reminder {
+ private static final String TAG = ExpiredBuildReminder.class.getSimpleName();
+
+ public ExpiredBuildReminder(final Context context) {
+ super(context.getString(R.string.reminder_header_expired_build),
+ context.getString(R.string.reminder_header_expired_build_details),
+ context.getString(R.string.reminder_header_expired_build_button));
+ setOkListener(new OnClickListener() {
+ @Override public void onClick(View v) {
+ try {
+ context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + context.getPackageName())));
+ } catch (android.content.ActivityNotFoundException anfe) {
+ context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + context.getPackageName())));
+ }
+ }
+ });
+ }
+
+ @Override
+ public boolean isDismissable() {
+ return false;
+ }
+
+ public static boolean isEligible(Context context) {
+ return !Util.isBuildFresh();
+ }
+
+}
diff --git a/src/org/thoughtcrime/securesms/components/reminder/InviteReminder.java b/src/org/thoughtcrime/securesms/components/reminder/InviteReminder.java
new file mode 100644
index 0000000000..255d944771
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/reminder/InviteReminder.java
@@ -0,0 +1,34 @@
+package org.thoughtcrime.securesms.components.reminder;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.support.annotation.NonNull;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.database.DatabaseFactory;
+import org.thoughtcrime.securesms.recipients.Recipients;
+
+public class InviteReminder extends Reminder {
+
+ public InviteReminder(final @NonNull Context context,
+ final @NonNull Recipients recipients)
+ {
+ super(context.getString(R.string.reminder_header_invite_title),
+ context.getString(R.string.reminder_header_invite_text, recipients.toShortString()),
+ context.getString(R.string.reminder_header_invite_button));
+
+ setDismissListener(new OnClickListener() {
+ @Override public void onClick(View v) {
+ new AsyncTask() {
+
+ @Override protected Void doInBackground(Void... params) {
+ DatabaseFactory.getRecipientPreferenceDatabase(context).setSeenInviteReminder(recipients, true);
+ return null;
+ }
+ }.execute();
+ }
+ });
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/components/PushRegistrationReminder.java b/src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java
similarity index 80%
rename from src/org/thoughtcrime/securesms/components/PushRegistrationReminder.java
rename to src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java
index 6fbb3ef359..12843f4b1f 100644
--- a/src/org/thoughtcrime/securesms/components/PushRegistrationReminder.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/PushRegistrationReminder.java
@@ -1,4 +1,4 @@
-package org.thoughtcrime.securesms.components;
+package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import android.content.Intent;
@@ -13,9 +13,9 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
public class PushRegistrationReminder extends Reminder {
public PushRegistrationReminder(final Context context, final MasterSecret masterSecret) {
- super(R.drawable.ic_push_registration_reminder,
- R.string.reminder_header_push_title,
- R.string.reminder_header_push_text);
+ super(context.getString(R.string.reminder_header_push_title),
+ context.getString(R.string.reminder_header_push_text),
+ context.getString(R.string.reminder_header_push_button));
final OnClickListener okListener = new OnClickListener() {
@Override
diff --git a/src/org/thoughtcrime/securesms/components/reminder/Reminder.java b/src/org/thoughtcrime/securesms/components/reminder/Reminder.java
new file mode 100644
index 0000000000..652a59cecf
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/reminder/Reminder.java
@@ -0,0 +1,56 @@
+package org.thoughtcrime.securesms.components.reminder;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.view.View.OnClickListener;
+
+public abstract class Reminder {
+ private CharSequence buttonText;
+ private CharSequence title;
+ private CharSequence text;
+
+ private OnClickListener okListener;
+ private OnClickListener dismissListener;
+
+ public Reminder(@NonNull CharSequence title,
+ @NonNull CharSequence text,
+ @NonNull CharSequence buttonText)
+ {
+ this.title = title;
+ this.text = text;
+ this.buttonText = buttonText;
+ }
+
+ public CharSequence getTitle() {
+ return title;
+ }
+
+ public CharSequence getText() {
+ return text;
+ }
+
+ public CharSequence getButtonText() {
+ return buttonText;
+ }
+
+ public OnClickListener getOkListener() {
+ return okListener;
+ }
+
+ public OnClickListener getDismissListener() {
+ return dismissListener;
+ }
+
+ public void setOkListener(OnClickListener okListener) {
+ this.okListener = okListener;
+ }
+
+ public void setDismissListener(OnClickListener dismissListener) {
+ this.dismissListener = dismissListener;
+ }
+
+ public boolean isDismissable() {
+ return true;
+ }
+}
diff --git a/src/org/thoughtcrime/securesms/components/ReminderView.java b/src/org/thoughtcrime/securesms/components/reminder/ReminderView.java
similarity index 59%
rename from src/org/thoughtcrime/securesms/components/ReminderView.java
rename to src/org/thoughtcrime/securesms/components/reminder/ReminderView.java
index d543d55694..3657f2d0ed 100644
--- a/src/org/thoughtcrime/securesms/components/ReminderView.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/ReminderView.java
@@ -1,4 +1,4 @@
-package org.thoughtcrime.securesms.components;
+package org.thoughtcrime.securesms.components.reminder;
import android.annotation.TargetApi;
import android.content.Context;
@@ -7,23 +7,21 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.util.ViewUtil;
/**
* View to display actionable reminders to the user
*/
public class ReminderView extends LinearLayout {
private ViewGroup container;
- private ImageButton cancel;
+ private TextView acceptButton;
+ private TextView closeButton;
private TextView title;
private TextView text;
- private ImageView icon;
public ReminderView(Context context) {
super(context);
@@ -43,35 +41,39 @@ public class ReminderView extends LinearLayout {
private void initialize() {
LayoutInflater.from(getContext()).inflate(R.layout.reminder_header, this, true);
- container = (ViewGroup ) findViewById(R.id.container);
- cancel = (ImageButton) findViewById(R.id.cancel);
- title = (TextView ) findViewById(R.id.reminder_title);
- text = (TextView ) findViewById(R.id.reminder_text);
- icon = (ImageView ) findViewById(R.id.icon);
+ container = ViewUtil.findById(this, R.id.container);
+ acceptButton = ViewUtil.findById(this, R.id.accept);
+ closeButton = ViewUtil.findById(this, R.id.cancel);
+ title = ViewUtil.findById(this, R.id.reminder_title);
+ text = ViewUtil.findById(this, R.id.reminder_text);
}
public void showReminder(final Reminder reminder) {
- icon.setImageResource(reminder.getIconResId());
- title.setText(reminder.getTitleResId());
- text.setText(reminder.getTextResId());
+ title.setText(reminder.getTitle());
+ text.setText(reminder.getText());
+ acceptButton.setText(reminder.getButtonText());
- this.setOnClickListener(reminder.getOkListener());
+ acceptButton.setOnClickListener(reminder.getOkListener());
if (reminder.isDismissable()) {
- cancel.setOnClickListener(new OnClickListener() {
+ closeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
hide();
- if (reminder.getCancelListener() != null) reminder.getCancelListener().onClick(v);
+ if (reminder.getDismissListener() != null) reminder.getDismissListener().onClick(v);
}
});
} else {
- cancel.setVisibility(View.GONE);
+ closeButton.setVisibility(View.GONE);
}
container.setVisibility(View.VISIBLE);
}
+ public void requestDismiss() {
+ closeButton.performClick();
+ }
+
public void hide() {
container.setVisibility(View.GONE);
}
diff --git a/src/org/thoughtcrime/securesms/components/SystemSmsImportReminder.java b/src/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java
similarity index 84%
rename from src/org/thoughtcrime/securesms/components/SystemSmsImportReminder.java
rename to src/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java
index 6377162141..56e5d40d49 100644
--- a/src/org/thoughtcrime/securesms/components/SystemSmsImportReminder.java
+++ b/src/org/thoughtcrime/securesms/components/reminder/SystemSmsImportReminder.java
@@ -1,4 +1,4 @@
-package org.thoughtcrime.securesms.components;
+package org.thoughtcrime.securesms.components.reminder;
import android.content.Context;
import android.content.Intent;
@@ -14,9 +14,9 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
public class SystemSmsImportReminder extends Reminder {
public SystemSmsImportReminder(final Context context, final MasterSecret masterSecret) {
- super(R.drawable.sms_system_import_icon,
- R.string.reminder_header_sms_import_title,
- R.string.reminder_header_sms_import_text);
+ super(context.getString(R.string.reminder_header_sms_import_title),
+ context.getString(R.string.reminder_header_sms_import_text),
+ context.getString(R.string.reminder_header_sms_import_button));
final OnClickListener okListener = new OnClickListener() {
@Override
@@ -42,7 +42,7 @@ public class SystemSmsImportReminder extends Reminder {
}
};
setOkListener(okListener);
- setCancelListener(cancelListener);
+ setDismissListener(cancelListener);
}
public static boolean isEligible(Context context) {
diff --git a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
index d160c7243b..66a46ef716 100644
--- a/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
+++ b/src/org/thoughtcrime/securesms/database/DatabaseFactory.java
@@ -66,7 +66,8 @@ public class DatabaseFactory {
private static final int INTRODUCED_ENVELOPE_CONTENT_VERSION = 19;
private static final int INTRODUCED_COLOR_PREFERENCE_VERSION = 20;
private static final int INTRODUCED_DB_OPTIMIZATIONS_VERSION = 21;
- private static final int DATABASE_VERSION = 21;
+ private static final int INTRODUCED_INVITE_REMINDERS_VERSION = 22;
+ private static final int DATABASE_VERSION = 22;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@@ -768,6 +769,10 @@ public class DatabaseFactory {
db.execSQL("CREATE INDEX IF NOT EXISTS mms_thread_date_index ON mms (thread_id, date_received);");
}
+ if (oldVersion < INTRODUCED_INVITE_REMINDERS_VERSION) {
+ db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN seen_invite_reminder INTEGER DEFAULT 0");
+ }
+
db.setTransactionSuccessful();
db.endTransaction();
}
diff --git a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java
index 19f2630131..02f21f0f37 100644
--- a/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/RecipientPreferenceDatabase.java
@@ -23,14 +23,15 @@ public class RecipientPreferenceDatabase extends Database {
private static final String TAG = RecipientPreferenceDatabase.class.getSimpleName();
private static final String RECIPIENT_PREFERENCES_URI = "content://textsecure/recipients/";
- private static final String TABLE_NAME = "recipient_preferences";
- private static final String ID = "_id";
- private static final String RECIPIENT_IDS = "recipient_ids";
- private static final String BLOCK = "block";
- private static final String NOTIFICATION = "notification";
- private static final String VIBRATE = "vibrate";
- private static final String MUTE_UNTIL = "mute_until";
- private static final String COLOR = "color";
+ private static final String TABLE_NAME = "recipient_preferences";
+ private static final String ID = "_id";
+ private static final String RECIPIENT_IDS = "recipient_ids";
+ private static final String BLOCK = "block";
+ private static final String NOTIFICATION = "notification";
+ private static final String VIBRATE = "vibrate";
+ private static final String MUTE_UNTIL = "mute_until";
+ private static final String COLOR = "color";
+ private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
public enum VibrateState {
DEFAULT(0), ENABLED(1), DISABLED(2);
@@ -58,7 +59,8 @@ public class RecipientPreferenceDatabase extends Database {
NOTIFICATION + " TEXT DEFAULT NULL, " +
VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
MUTE_UNTIL + " INTEGER DEFAULT 0, " +
- COLOR + " TEXT DEFAULT NULL);";
+ COLOR + " TEXT DEFAULT NULL, " +
+ SEEN_INVITE_REMINDER + " INTEGER DEFAULT 0);";
public RecipientPreferenceDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
@@ -86,12 +88,13 @@ public class RecipientPreferenceDatabase extends Database {
null, null, null);
if (cursor != null && cursor.moveToNext()) {
- boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
- String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
- int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
- long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
- String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
- Uri notificationUri = notification == null ? null : Uri.parse(notification);
+ boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
+ String notification = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
+ int vibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
+ long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
+ String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
+ Uri notificationUri = notification == null ? null : Uri.parse(notification);
+ boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
MaterialColor color;
@@ -106,7 +109,7 @@ public class RecipientPreferenceDatabase extends Database {
return Optional.of(new RecipientsPreferences(blocked, muteUntil,
VibrateState.fromId(vibrateState),
- notificationUri, color));
+ notificationUri, color, seenInviteReminder));
}
return Optional.absent();
@@ -146,6 +149,12 @@ public class RecipientPreferenceDatabase extends Database {
updateOrInsert(recipients, values);
}
+ public void setSeenInviteReminder(Recipients recipients, boolean seen) {
+ ContentValues values = new ContentValues(1);
+ values.put(SEEN_INVITE_REMINDER, seen ? 1 : 0);
+ updateOrInsert(recipients, values);
+ }
+
private void updateOrInsert(Recipients recipients, ContentValues contentValues) {
SQLiteDatabase database = databaseHelper.getWritableDatabase();
@@ -171,17 +180,20 @@ public class RecipientPreferenceDatabase extends Database {
private final VibrateState vibrateState;
private final Uri notification;
private final MaterialColor color;
+ private final boolean seenInviteReminder;
public RecipientsPreferences(boolean blocked, long muteUntil,
@NonNull VibrateState vibrateState,
@Nullable Uri notification,
- @Nullable MaterialColor color)
+ @Nullable MaterialColor color,
+ boolean seenInviteReminder)
{
- this.blocked = blocked;
- this.muteUntil = muteUntil;
- this.vibrateState = vibrateState;
- this.notification = notification;
- this.color = color;
+ this.blocked = blocked;
+ this.muteUntil = muteUntil;
+ this.vibrateState = vibrateState;
+ this.notification = notification;
+ this.color = color;
+ this.seenInviteReminder = seenInviteReminder;
}
public @Nullable MaterialColor getColor() {
@@ -203,5 +215,9 @@ public class RecipientPreferenceDatabase extends Database {
public @Nullable Uri getRingtone() {
return notification;
}
+
+ public boolean hasSeenInviteReminder() {
+ return seenInviteReminder;
+ }
}
}
diff --git a/src/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java b/src/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java
new file mode 100644
index 0000000000..1bd4e81248
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/util/concurrent/AssertedSuccessListener.java
@@ -0,0 +1,12 @@
+package org.thoughtcrime.securesms.util.concurrent;
+
+import org.thoughtcrime.securesms.util.concurrent.ListenableFuture.Listener;
+
+import java.util.concurrent.ExecutionException;
+
+public abstract class AssertedSuccessListener implements Listener {
+ @Override
+ public void onFailure(ExecutionException e) {
+ throw new AssertionError(e);
+ }
+}
diff --git a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java
index 292f3e2051..65d94888eb 100644
--- a/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java
+++ b/test/androidTestEspresso/java/org/thoughtcrime/securesms/ConversationListActivityTest.java
@@ -23,10 +23,10 @@ import android.widget.TextView;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
-import org.thoughtcrime.securesms.components.DefaultSmsReminder;
-import org.thoughtcrime.securesms.components.ExpiredBuildReminder;
-import org.thoughtcrime.securesms.components.PushRegistrationReminder;
-import org.thoughtcrime.securesms.components.SystemSmsImportReminder;
+import org.thoughtcrime.securesms.components.reminder.DefaultSmsReminder;
+import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder;
+import org.thoughtcrime.securesms.components.reminder.PushRegistrationReminder;
+import org.thoughtcrime.securesms.components.reminder.SystemSmsImportReminder;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import static android.support.test.espresso.Espresso.onView;