From 599bb5ab0ff26c86384b8d82134737b4420fc82d Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 15 Nov 2019 16:33:54 -0400 Subject: [PATCH] Update insights copy and queries. --- lint.xml | 2 +- res/values/strings.xml | 12 +++---- .../securesms/ApplicationContext.java | 2 ++ .../securesms/database/MessagingDatabase.java | 28 +++++---------- .../securesms/database/MmsSmsDatabase.java | 14 ++++---- .../securesms/database/RecipientDatabase.java | 35 ++++--------------- .../securesms/insights/InsightsConstants.java | 13 +++++++ .../InsightsDashboardDialogFragment.java | 12 ++++--- .../securesms/insights/InsightsOptOut.java | 7 ++-- .../insights/InsightsRepository.java | 11 +++--- .../invites/InviteReminderRepository.java | 12 ++----- 11 files changed, 62 insertions(+), 86 deletions(-) create mode 100644 src/org/thoughtcrime/securesms/insights/InsightsConstants.java diff --git a/lint.xml b/lint.xml index 23bcb40c45..5009276e2a 100644 --- a/lint.xml +++ b/lint.xml @@ -15,7 +15,7 @@ - + diff --git a/res/values/strings.xml b/res/values/strings.xml index c1c89beab4..2363d388ff 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1544,15 +1544,13 @@ % Insights Insights - %1$d%% of your outgoing messages in the past 7 days were end-to-end encrypted with Signal Protocol. - Boost your Signal - Your Signal is Strong - No Signal (yet) - You\'re just getting started. Insights will be displayed after you send a few messages. + Signal Protocol automatically protected %1$d%% of your outgoing messages over the past %2$d days. Conversations between Signal users are always end-to-end encrypted. + Signal boost + Not enough data + Your Insights percentage is calculated based on outgoing messages within the past %1$d days that have not disappeared or been deleted. Start a conversation - Signal\'s advanced privacy-preserving technology automatically protected all of your recent outgoing messages. Start communicating securely and enable new features that go beyond the limitations of unencrypted SMS messages by inviting more contacts to join Signal. - This stat was locally generated on your device and can only be seen by you. It is never transmitted anywhere. + These statistics were locally generated on your device and can only be seen by you. They are never transmitted anywhere. Encrypted messages Cancel Send diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 9b1d90e0a9..bae68728e1 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -42,6 +42,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.dependencies.ApplicationDependencyProvider; import org.thoughtcrime.securesms.gcm.FcmJobService; +import org.thoughtcrime.securesms.insights.InsightsOptOut; import org.thoughtcrime.securesms.jobmanager.JobManager; import org.thoughtcrime.securesms.jobmanager.JobMigrator; import org.thoughtcrime.securesms.jobmanager.impl.JsonDataSerializer; @@ -239,6 +240,7 @@ public class ApplicationContext extends MultiDexApplication implements DefaultLi if (!SQLCipherOpenHelper.databaseFileExists(this)) { Log.i(TAG, "First ever app launch!"); + InsightsOptOut.userRequestedOptOut(this); TextSecurePreferences.setAppMigrationVersion(this, ApplicationMigrations.CURRENT_VERSION); TextSecurePreferences.setJobManagerVersion(this, JobManager.CURRENT_VERSION); } diff --git a/src/org/thoughtcrime/securesms/database/MessagingDatabase.java b/src/org/thoughtcrime/securesms/database/MessagingDatabase.java index f84e51b6d0..82dfd3317a 100644 --- a/src/org/thoughtcrime/securesms/database/MessagingDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MessagingDatabase.java @@ -7,18 +7,16 @@ import android.text.TextUtils; import androidx.annotation.NonNull; -import com.annimon.stream.Stream; - import net.sqlcipher.database.SQLiteDatabase; import org.thoughtcrime.securesms.database.documents.Document; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch; import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatchList; import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; +import org.thoughtcrime.securesms.insights.InsightsConstants; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.JsonUtils; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.IdentityKey; import java.io.IOException; @@ -49,7 +47,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn SQLiteDatabase db = databaseHelper.getReadableDatabase(); String[] projection = new String[]{"COUNT(*)"}; String query = THREAD_ID + " = ? AND " + getOutgoingInsecureMessageClause() + " AND " + getDateSentColumnName() + " > ?"; - String[] args = new String[]{String.valueOf(threadId), String.valueOf(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7))}; + String[] args = new String[]{String.valueOf(threadId), String.valueOf(System.currentTimeMillis() - InsightsConstants.PERIOD_IN_MILLIS)}; try (Cursor cursor = db.query(getTableName(), projection, query, args, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { @@ -60,28 +58,20 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn } } - final int getInsecureMessageCountForRecipients(List recipients) { - return getMessageCountForRecipientsAndType(recipients, getOutgoingInsecureMessageClause()); + final int getInsecureMessageCountForInsights() { + return getMessageCountForRecipientsAndType(getOutgoingInsecureMessageClause()); } - final int getSecureMessageCountForRecipients(List recipients) { - return getMessageCountForRecipientsAndType(recipients, getOutgoingSecureMessageClause()); + final int getSecureMessageCountForInsights() { + return getMessageCountForRecipientsAndType(getOutgoingSecureMessageClause()); } - private int getMessageCountForRecipientsAndType(List recipients, String typeClause) { - if (recipients.size() == 0) return 0; + private int getMessageCountForRecipientsAndType(String typeClause) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String placeholders = Util.join(Stream.of(recipients).map(r -> "?").toList(), ","); String[] projection = new String[] {"COUNT(*)"}; - String query = RECIPIENT_ID + " IN ( " + placeholders + " ) AND " + typeClause + " AND " + getDateSentColumnName() + " > ?"; - String[] args = new String[recipients.size() + 1]; - - for (int i = 0; i < recipients.size(); i++) { - args[i] = recipients.get(i).serialize(); - } - - args[args.length - 1] = String.valueOf(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)); + String query = typeClause + " AND " + getDateSentColumnName() + " > ?"; + String[] args = new String[]{String.valueOf(System.currentTimeMillis() - InsightsConstants.PERIOD_IN_MILLIS)}; try (Cursor cursor = db.query(getTableName(), projection, query, args, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { diff --git a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index 0cb9dff8e8..898e17c963 100644 --- a/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/src/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -18,6 +18,7 @@ package org.thoughtcrime.securesms.database; import android.content.Context; import android.database.Cursor; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -31,7 +32,6 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import java.util.HashSet; -import java.util.List; import java.util.Set; public class MmsSmsDatabase extends Database { @@ -163,16 +163,16 @@ public class MmsSmsDatabase extends Database { return count; } - public int getInsecureMessageCountForRecipients(List recipients) { - int count = DatabaseFactory.getSmsDatabase(context).getInsecureMessageCountForRecipients(recipients); - count += DatabaseFactory.getMmsDatabase(context).getInsecureMessageCountForRecipients(recipients); + public int getInsecureMessageCountForInsights() { + int count = DatabaseFactory.getSmsDatabase(context).getInsecureMessageCountForInsights(); + count += DatabaseFactory.getMmsDatabase(context).getInsecureMessageCountForInsights(); return count; } - public int getSecureMessageCountForRecipients(List recipients) { - int count = DatabaseFactory.getSmsDatabase(context).getSecureMessageCountForRecipients(recipients); - count += DatabaseFactory.getMmsDatabase(context).getSecureMessageCountForRecipients(recipients); + public int getSecureMessageCountForInsights() { + int count = DatabaseFactory.getSmsDatabase(context).getSecureMessageCountForInsights(); + count += DatabaseFactory.getMmsDatabase(context).getSecureMessageCountForInsights(); return count; } diff --git a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java index b532793430..1bafa01d3f 100644 --- a/src/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/src/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -1,6 +1,5 @@ package org.thoughtcrime.securesms.database; -import android.annotation.SuppressLint; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; @@ -37,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeUnit; public class RecipientDatabase extends Database { @@ -205,8 +205,9 @@ public class RecipientDatabase extends Database { TABLE_NAME + "." + GROUP_ID + " IS NULL AND " + TABLE_NAME + "." + REGISTERED + " = " + RegisteredState.NOT_REGISTERED.id + " AND " + TABLE_NAME + "." + SEEN_INVITE_REMINDER + " < " + InsightsBannerTier.TIER_TWO.id + " AND " + - ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.HAS_SENT + - " ORDER BY " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.DATE + " DESC"; + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.HAS_SENT + " AND " + + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.DATE + " > ?" + + " ORDER BY " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.DATE + " DESC LIMIT 50"; public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) { super(context, databaseHelper); @@ -618,33 +619,9 @@ public class RecipientDatabase extends Database { public @NonNull List getUninvitedRecipientsForInsights() { SQLiteDatabase db = databaseHelper.getReadableDatabase(); List results = new LinkedList<>(); + final String[] args = new String[]{String.valueOf(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31))}; - try (Cursor cursor = db.rawQuery(INSIGHTS_INVITEE_LIST, null)) { - while (cursor != null && cursor.moveToNext()) { - results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))); - } - } - - return results; - } - - public @NonNull List getNotRegisteredForInsights() { - return getRecipientsForInsights(REGISTERED + " = ?", new String[]{String.valueOf(RegisteredState.NOT_REGISTERED.id)}); - } - - public @NonNull List getRegisteredForInsights() { - final String selfId = Recipient.self().getId().serialize(); - final String query = REGISTERED + " = ? AND " + ID + " != ?"; - final String[] args = new String[]{String.valueOf(RegisteredState.REGISTERED.id), selfId}; - - return getRecipientsForInsights(query, args); - } - - private @NonNull List getRecipientsForInsights(@NonNull String query, @NonNull String[] args) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - List results = new LinkedList<>(); - - try (Cursor cursor = db.query(TABLE_NAME, ID_PROJECTION, query + " AND " + GROUP_ID + " IS NULL", args, null, null, null)) { + try (Cursor cursor = db.rawQuery(INSIGHTS_INVITEE_LIST, args)) { while (cursor != null && cursor.moveToNext()) { results.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ID)))); } diff --git a/src/org/thoughtcrime/securesms/insights/InsightsConstants.java b/src/org/thoughtcrime/securesms/insights/InsightsConstants.java new file mode 100644 index 0000000000..40435b33d5 --- /dev/null +++ b/src/org/thoughtcrime/securesms/insights/InsightsConstants.java @@ -0,0 +1,13 @@ +package org.thoughtcrime.securesms.insights; + +import java.util.concurrent.TimeUnit; + +public final class InsightsConstants { + + public static final long PERIOD_IN_DAYS = 7L; + public static final long PERIOD_IN_MILLIS = TimeUnit.DAYS.toMillis(PERIOD_IN_DAYS); + + private InsightsConstants() { + } + +} diff --git a/src/org/thoughtcrime/securesms/insights/InsightsDashboardDialogFragment.java b/src/org/thoughtcrime/securesms/insights/InsightsDashboardDialogFragment.java index 323794bc96..c279b8c561 100644 --- a/src/org/thoughtcrime/securesms/insights/InsightsDashboardDialogFragment.java +++ b/src/org/thoughtcrime/securesms/insights/InsightsDashboardDialogFragment.java @@ -182,16 +182,18 @@ public final class InsightsDashboardDialogFragment extends DialogFragment { progressContainer.setVisibility(View.VISIBLE); insecureRecipients.setVisibility(View.VISIBLE); encryptedMessages.setText(R.string.InsightsDashboardFragment__encrypted_messages); - tagline.setText(getString(R.string.InsightsDashboardFragment__tagline, 100 - insecurePercent)); + tagline.setText(getString(R.string.InsightsDashboardFragment__signal_protocol_automatically_protected, 100 - insecurePercent, InsightsConstants.PERIOD_IN_DAYS)); if (insecurePercent == 0) { lottieAnimationView.setVisibility(View.VISIBLE); - title.setText(R.string.InsightsDashboardFragment__100_title); - description.setText(R.string.InsightsDashboardFragment__100_description); + title.setVisibility(View.GONE); + description.setVisibility(View.GONE); } else { lottieAnimationView.setVisibility(View.GONE); title.setText(R.string.InsightsDashboardFragment__boost_your_signal); description.setText(R.string.InsightsDashboardFragment__invite_your_contacts); + title.setVisibility(View.VISIBLE); + description.setVisibility(View.VISIBLE); } } @@ -199,8 +201,8 @@ public final class InsightsDashboardDialogFragment extends DialogFragment { startAConversation.setVisibility(View.VISIBLE); progressContainer.setVisibility(View.INVISIBLE); insecureRecipients.setVisibility(View.GONE); - encryptedMessages.setText(R.string.InsightsDashboardFragment__no_signal_yet); - tagline.setText(R.string.InsightsDashboardFragment__youre_just_getting_started); + encryptedMessages.setText(R.string.InsightsDashboardFragment__not_enough_data); + tagline.setText(getString(R.string.InsightsDashboardFragment__your_insights_percentage_is_calculated_based_on, InsightsConstants.PERIOD_IN_DAYS)); } private void animateNotEnoughData() { diff --git a/src/org/thoughtcrime/securesms/insights/InsightsOptOut.java b/src/org/thoughtcrime/securesms/insights/InsightsOptOut.java index 80a9e398ac..c3d90f227f 100644 --- a/src/org/thoughtcrime/securesms/insights/InsightsOptOut.java +++ b/src/org/thoughtcrime/securesms/insights/InsightsOptOut.java @@ -6,14 +6,17 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.util.TextSecurePreferences; -class InsightsOptOut { +public final class InsightsOptOut { private static final String INSIGHTS_OPT_OUT_PREFERENCE = "insights.opt.out"; + private InsightsOptOut() { + } + static boolean userHasOptedOut(@NonNull Context context) { return TextSecurePreferences.getBooleanPreference(context, INSIGHTS_OPT_OUT_PREFERENCE, false); } - static void userRequestedOptOut(@NonNull Context context) { + public static void userRequestedOptOut(@NonNull Context context) { TextSecurePreferences.setBooleanPreference(context, INSIGHTS_OPT_OUT_PREFERENCE, true); } } diff --git a/src/org/thoughtcrime/securesms/insights/InsightsRepository.java b/src/org/thoughtcrime/securesms/insights/InsightsRepository.java index 281767a216..41d8920045 100644 --- a/src/org/thoughtcrime/securesms/insights/InsightsRepository.java +++ b/src/org/thoughtcrime/securesms/insights/InsightsRepository.java @@ -16,11 +16,11 @@ import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.OutgoingTextMessage; +import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.concurrent.SimpleTask; @@ -39,12 +39,9 @@ public class InsightsRepository implements InsightsDashboardViewModel.Repository @Override public void getInsightsData(@NonNull Consumer insightsDataConsumer) { SimpleTask.run(() -> { - RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); - List unregisteredRecipients = recipientDatabase.getNotRegisteredForInsights(); - List registeredRecipients = recipientDatabase.getRegisteredForInsights(); - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); - int insecure = mmsSmsDatabase.getInsecureMessageCountForRecipients(unregisteredRecipients); - int secure = mmsSmsDatabase.getSecureMessageCountForRecipients(registeredRecipients); + MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); + int insecure = mmsSmsDatabase.getInsecureMessageCountForInsights(); + int secure = mmsSmsDatabase.getSecureMessageCountForInsights(); if (insecure + secure == 0) { return new InsightsData(false, 0); diff --git a/src/org/thoughtcrime/securesms/invites/InviteReminderRepository.java b/src/org/thoughtcrime/securesms/invites/InviteReminderRepository.java index c57bc374c3..1c9276a283 100644 --- a/src/org/thoughtcrime/securesms/invites/InviteReminderRepository.java +++ b/src/org/thoughtcrime/securesms/invites/InviteReminderRepository.java @@ -6,9 +6,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.MmsSmsDatabase; import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.recipients.Recipient; -import org.thoughtcrime.securesms.recipients.RecipientId; - -import java.util.List; public final class InviteReminderRepository implements InviteReminderModel.Repository { @@ -32,12 +29,9 @@ public final class InviteReminderRepository implements InviteReminderModel.Repos @Override public int getPercentOfInsecureMessages(int insecureCount) { - RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); - List registeredRecipients = recipientDatabase.getRegisteredForInsights(); - List unregisteredRecipients = recipientDatabase.getNotRegisteredForInsights(); - MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); - int insecure = mmsSmsDatabase.getInsecureMessageCountForRecipients(unregisteredRecipients); - int secure = mmsSmsDatabase.getSecureMessageCountForRecipients(registeredRecipients); + MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context); + int insecure = mmsSmsDatabase.getInsecureMessageCountForInsights(); + int secure = mmsSmsDatabase.getSecureMessageCountForInsights(); if (insecure + secure == 0) return 0; return Math.round(100f * (insecureCount / (float) (insecure + secure)));