From 0efdada92847e3d3351681ac36533cf94ec80863 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 31 Mar 2015 12:34:49 -0700 Subject: [PATCH] Prompt user to rate app Closes #2841 // FREEBIE --- res/values/strings.xml | 7 ++ .../securesms/ConversationListActivity.java | 2 + .../securesms/components/RatingManager.java | 85 +++++++++++++++++++ .../securesms/util/TextSecurePreferences.java | 18 ++++ 4 files changed, 112 insertions(+) create mode 100644 src/org/thoughtcrime/securesms/components/RatingManager.java diff --git a/res/values/strings.xml b/res/values/strings.xml index 223afe0f62..9834706517 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -279,6 +279,13 @@ Submit passphrase Invalid passphrase! + + Rate this app + If you enjoy using this app, please take a moment to help us by rating it. + Rate now! + No thanks + Later + The signature on this key exchange is different than what you\'ve previously received from this diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java index 44f23dcca9..861cc7c4ff 100644 --- a/src/org/thoughtcrime/securesms/ConversationListActivity.java +++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java @@ -30,6 +30,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import org.thoughtcrime.securesms.components.RatingManager; import org.thoughtcrime.securesms.crypto.MasterSecret; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.notifications.MessageNotifier; @@ -69,6 +70,7 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit initializeContactUpdatesReceiver(); DirectoryRefreshListener.schedule(this); + RatingManager.showRatingDialogIfNecessary(this); } @Override diff --git a/src/org/thoughtcrime/securesms/components/RatingManager.java b/src/org/thoughtcrime/securesms/components/RatingManager.java new file mode 100644 index 0000000000..e359ef2dbc --- /dev/null +++ b/src/org/thoughtcrime/securesms/components/RatingManager.java @@ -0,0 +1,85 @@ +package org.thoughtcrime.securesms.components; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.util.Log; + +import com.afollestad.materialdialogs.MaterialDialog; + +import org.thoughtcrime.securesms.R; +import org.thoughtcrime.securesms.util.TextSecurePreferences; + +import java.util.concurrent.TimeUnit; + +public class RatingManager { + + private static final int DAYS_SINCE_INSTALL_THRESHOLD = 7; + private static final int DAYS_UNTIL_REPROMPT_THRESHOLD = 4; + + private static final String TAG = RatingManager.class.getSimpleName(); + + public static void showRatingDialogIfNecessary(Context context) { + if (!TextSecurePreferences.isRatingEnabled(context)) return; + + long daysSinceInstall = getDaysSinceInstalled(context); + long laterTimestamp = TextSecurePreferences.getRatingLaterTimestamp(context); + + if (daysSinceInstall >= DAYS_SINCE_INSTALL_THRESHOLD && + System.currentTimeMillis() >= laterTimestamp) + { + showRatingDialog(context); + } + } + + private static void showRatingDialog(final Context context) { + new MaterialDialog.Builder(context) + .title(context.getString(R.string.RatingManager_rate_this_app)) + .content(context.getString(R.string.RatingManager_if_you_enjoy_using_this_app_please_take_a_moment)) + .positiveText(context.getString(R.string.RatingManager_rate_now)) + .negativeText(context.getString(R.string.RatingManager_no_thanks)) + .neutralText(context.getString(R.string.RatingManager_later)) + .callback(new MaterialDialog.ButtonCallback() { + @Override + public void onPositive(MaterialDialog dialog) { + TextSecurePreferences.setRatingEnabled(context, false); + startPlayStore(context); + super.onPositive(dialog); + } + + @Override + public void onNegative(MaterialDialog dialog) { + TextSecurePreferences.setRatingEnabled(context, false); + super.onNegative(dialog); + } + + @Override + public void onNeutral(MaterialDialog dialog) { + long waitUntil = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(DAYS_UNTIL_REPROMPT_THRESHOLD); + TextSecurePreferences.setRatingLaterTimestamp(context, waitUntil); + super.onNeutral(dialog); + } + }) + .show(); + } + + private static void startPlayStore(Context context) { + Uri uri = Uri.parse("market://details?id=" + context.getPackageName()); + context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + } + + private static long getDaysSinceInstalled(Context context) { + try { + long installTimestamp = context.getPackageManager() + .getPackageInfo(context.getPackageName(), 0) + .firstInstallTime; + + return TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - installTimestamp); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, e); + return 0; + } + } + +} diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 1ae209bf6b..31cf71475a 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -66,9 +66,27 @@ public class TextSecurePreferences { private static final String GCM_REGISTRATION_ID_PREF = "pref_gcm_registration_id"; private static final String GCM_REGISTRATION_ID_VERSION_PREF = "pref_gcm_registration_id_version"; private static final String WEBSOCKET_REGISTERED_PREF = "pref_websocket_registered"; + private static final String RATING_LATER_PREF = "pref_rating_later"; + private static final String RATING_ENABLED_PREF = "pref_rating_enabled"; public static final String REPEAT_ALERTS_PREF = "pref_repeat_alerts"; + public static long getRatingLaterTimestamp(Context context) { + return getLongPreference(context, RATING_LATER_PREF, 0); + } + + public static void setRatingLaterTimestamp(Context context, long timestamp) { + setLongPreference(context, RATING_LATER_PREF, timestamp); + } + + public static boolean isRatingEnabled(Context context) { + return getBooleanPreference(context, RATING_ENABLED_PREF, true); + } + + public static void setRatingEnabled(Context context, boolean enabled) { + setBooleanPreference(context, RATING_ENABLED_PREF, enabled); + } + public static boolean isWebsocketRegistered(Context context) { return getBooleanPreference(context, WEBSOCKET_REGISTERED_PREF, false); }