Add opt-in setting for tracking
This commit is contained in:
@@ -158,6 +158,9 @@ public final class Constants {
|
|||||||
|
|
||||||
public static final String KEY_SIGNATURES_TABLE_INITIALIZED = "key_signatures_table_initialized";
|
public static final String KEY_SIGNATURES_TABLE_INITIALIZED = "key_signatures_table_initialized";
|
||||||
|
|
||||||
|
public static final String KEY_ANALYTICS_ASKED_POLITELY = "analytics_asked";
|
||||||
|
public static final String KEY_ANALYTICS_CONSENT = "analytics_consent";
|
||||||
|
|
||||||
public static final class Theme {
|
public static final class Theme {
|
||||||
public static final String LIGHT = "light";
|
public static final String LIGHT = "light";
|
||||||
public static final String DARK = "dark";
|
public static final String DARK = "dark";
|
||||||
|
|||||||
@@ -6,34 +6,31 @@ import android.app.Application;
|
|||||||
import android.app.Application.ActivityLifecycleCallbacks;
|
import android.app.Application.ActivityLifecycleCallbacks;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.piwik.sdk.Piwik;
|
import org.piwik.sdk.Piwik;
|
||||||
import org.piwik.sdk.Tracker;
|
import org.piwik.sdk.Tracker;
|
||||||
import org.piwik.sdk.TrackerConfig;
|
import org.piwik.sdk.TrackerConfig;
|
||||||
import org.piwik.sdk.extra.DownloadTracker.Extra.ApkChecksum;
|
import org.piwik.sdk.extra.DownloadTracker.Extra.ApkChecksum;
|
||||||
import org.piwik.sdk.extra.TrackHelper;
|
import org.piwik.sdk.extra.TrackHelper;
|
||||||
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
|
|
||||||
public class TrackingManager {
|
public class TrackingManager {
|
||||||
private Tracker piwikTracker;
|
private Tracker piwikTracker;
|
||||||
|
|
||||||
public static TrackingManager getInstance(Context context) {
|
public static TrackingManager getInstance(Context context) {
|
||||||
TrackerConfig trackerConfig = new TrackerConfig("https://mugenguild.com/piwik/", 1, "OpenKeychain");
|
return new TrackingManager(context);
|
||||||
Tracker tracker = Piwik.getInstance(context).newTracker(trackerConfig);
|
|
||||||
tracker.setDispatchInterval(30000);
|
|
||||||
|
|
||||||
return new TrackingManager(tracker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrackingManager(Tracker piwikTracker) {
|
private TrackingManager(Context context) {
|
||||||
this.piwikTracker = piwikTracker;
|
refreshSettings(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize(Application application) {
|
public void initialize(Application application) {
|
||||||
if (piwikTracker == null) {
|
if (piwikTracker != null) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
TrackHelper.track().download().identifier(new ApkChecksum(application)).with(piwikTracker);
|
TrackHelper.track().download().identifier(new ApkChecksum(application)).with(piwikTracker);
|
||||||
|
}
|
||||||
|
|
||||||
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||||
@Override
|
@Override
|
||||||
@@ -47,6 +44,9 @@ public class TrackingManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResumed(Activity activity) {
|
public void onActivityResumed(Activity activity) {
|
||||||
|
if (piwikTracker == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
TrackHelper.track().screen(activity.getClass().getSimpleName()).with(piwikTracker);
|
TrackHelper.track().screen(activity.getClass().getSimpleName()).with(piwikTracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,4 +99,20 @@ public class TrackingManager {
|
|||||||
.piece(currentCallingPackage.replace(".", "/"))
|
.piece(currentCallingPackage.replace(".", "/"))
|
||||||
.with(piwikTracker);
|
.with(piwikTracker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized void refreshSettings(Context context) {
|
||||||
|
boolean analyticsHasConsent = Preferences.getPreferences(context).isAnalyticsHasConsent();
|
||||||
|
boolean analyticsEnabled = piwikTracker != null;
|
||||||
|
if (analyticsHasConsent != analyticsEnabled) {
|
||||||
|
if (analyticsHasConsent) {
|
||||||
|
TrackerConfig trackerConfig = new TrackerConfig("https://piwik.openkeychain.org/", 1, "OpenKeychain");
|
||||||
|
piwikTracker = Piwik.getInstance(context).newTracker(trackerConfig);
|
||||||
|
piwikTracker.setDispatchInterval(60000);
|
||||||
|
piwikTracker.setOptOut(false);
|
||||||
|
} else {
|
||||||
|
piwikTracker.setOptOut(true);
|
||||||
|
piwikTracker = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ package org.sufficientlysecure.keychain.ui;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
import android.arch.lifecycle.LiveData;
|
import android.arch.lifecycle.LiveData;
|
||||||
import android.arch.lifecycle.ViewModelProviders;
|
import android.arch.lifecycle.ViewModelProviders;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.WorkerThread;
|
import android.support.annotation.WorkerThread;
|
||||||
@@ -51,9 +56,12 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
|
|||||||
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener;
|
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener;
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener;
|
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener;
|
||||||
import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
|
import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
|
||||||
|
import org.sufficientlysecure.keychain.BuildConfig;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||||
import org.sufficientlysecure.keychain.KeychainDatabase;
|
import org.sufficientlysecure.keychain.KeychainDatabase;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
|
import org.sufficientlysecure.keychain.TrackingManager;
|
||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
|
import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
|
||||||
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
import org.sufficientlysecure.keychain.daos.KeyRepository;
|
||||||
@@ -259,6 +267,8 @@ public class KeyListFragment extends RecyclerFragment<FlexibleAdapter<FlexibleKe
|
|||||||
GenericViewModel viewModel = ViewModelProviders.of(this).get(GenericViewModel.class);
|
GenericViewModel viewModel = ViewModelProviders.of(this).get(GenericViewModel.class);
|
||||||
LiveData<List<FlexibleKeyItem>> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems);
|
LiveData<List<FlexibleKeyItem>> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems);
|
||||||
liveData.observe(this, this::onLoadKeyItems);
|
liveData.observe(this, this::onLoadKeyItems);
|
||||||
|
|
||||||
|
maybeAskForAnalytics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
@@ -267,6 +277,45 @@ public class KeyListFragment extends RecyclerFragment<FlexibleAdapter<FlexibleKe
|
|||||||
return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo);
|
return flexibleKeyItemFactory.mapUnifiedKeyInfoToFlexibleKeyItems(unifiedKeyInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void maybeAskForAnalytics() {
|
||||||
|
Context context = getContext();
|
||||||
|
if (context == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Preferences preferences = Preferences.getPreferences(context);
|
||||||
|
if (!Constants.DEBUG && !preferences.isAnalyticsHasConsent() && preferences.isAnalyticsAskedPolitely()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
long firstInstallTime = context.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, 0).firstInstallTime;
|
||||||
|
long threeDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(3);
|
||||||
|
boolean installedLessThanThreeDaysAgo = firstInstallTime > threeDaysAgo;
|
||||||
|
if (installedLessThanThreeDaysAgo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (NameNotFoundException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackingManager trackingManager = ((KeychainApplication) requireActivity().getApplication()).getTrackingManager();
|
||||||
|
AlertDialog show = new Builder(context)
|
||||||
|
.setMessage(R.string.dialog_analytics_text)
|
||||||
|
.setPositiveButton(R.string.button_analytics_yes, (dialog, which) -> {
|
||||||
|
preferences.setAnalyticsAskedPolitely();
|
||||||
|
preferences.setAnalyticsGotUserConsent(true);
|
||||||
|
trackingManager.refreshSettings(context);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.button_analytics_no, (dialog, which) -> {
|
||||||
|
preferences.setAnalyticsAskedPolitely();
|
||||||
|
preferences.setAnalyticsGotUserConsent(false);
|
||||||
|
trackingManager.refreshSettings(context);
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
show.setCanceledOnTouchOutside(false);
|
||||||
|
}
|
||||||
|
|
||||||
private void onLoadKeyItems(List<FlexibleKeyItem> flexibleKeyItems) {
|
private void onLoadKeyItems(List<FlexibleKeyItem> flexibleKeyItems) {
|
||||||
FlexibleAdapter<FlexibleKeyItem> adapter = getAdapter();
|
FlexibleAdapter<FlexibleKeyItem> adapter = getAdapter();
|
||||||
if (adapter == null) {
|
if (adapter == null) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import android.os.Bundle;
|
|||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
|
import android.preference.Preference.OnPreferenceChangeListener;
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.preference.SwitchPreference;
|
import android.preference.SwitchPreference;
|
||||||
@@ -50,6 +51,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
|
import org.sufficientlysecure.keychain.Constants.Pref;
|
||||||
import org.sufficientlysecure.keychain.KeychainApplication;
|
import org.sufficientlysecure.keychain.KeychainApplication;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
|
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
|
||||||
@@ -584,6 +586,14 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
Activity activity = getActivity();
|
||||||
|
((KeychainApplication) activity.getApplication()).getTrackingManager().refreshSettings(activity);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
|
|||||||
@@ -353,6 +353,22 @@ public class Preferences {
|
|||||||
mSharedPreferences.edit().putBoolean(Pref.SYNC_IS_SCHEDULED, isScheduled).apply();
|
mSharedPreferences.edit().putBoolean(Pref.SYNC_IS_SCHEDULED, isScheduled).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAnalyticsAskedPolitely() {
|
||||||
|
return mSharedPreferences.getBoolean(Pref.KEY_ANALYTICS_ASKED_POLITELY, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnalyticsAskedPolitely() {
|
||||||
|
mSharedPreferences.edit().putBoolean(Pref.KEY_ANALYTICS_ASKED_POLITELY, true).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAnalyticsHasConsent() {
|
||||||
|
return mSharedPreferences.getBoolean(Pref.KEY_ANALYTICS_CONSENT, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAnalyticsGotUserConsent(boolean hasUserConsent) {
|
||||||
|
mSharedPreferences.edit().putBoolean(Pref.KEY_ANALYTICS_CONSENT, hasUserConsent).apply();
|
||||||
|
}
|
||||||
|
|
||||||
@AutoValue
|
@AutoValue
|
||||||
public static abstract class CloudSearchPrefs implements Parcelable {
|
public static abstract class CloudSearchPrefs implements Parcelable {
|
||||||
public abstract boolean isKeyserverEnabled();
|
public abstract boolean isKeyserverEnabled();
|
||||||
|
|||||||
@@ -236,6 +236,9 @@
|
|||||||
<string name="label_experimental_settings_keybase_summary">"Contact keybase.io for key proofs and show them every time a key is displayed"</string>
|
<string name="label_experimental_settings_keybase_summary">"Contact keybase.io for key proofs and show them every time a key is displayed"</string>
|
||||||
<string name="label_experimental_settings_theme_summary">"(The icons and many screens are not yet adjusted accordingly for the dark theme)"</string>
|
<string name="label_experimental_settings_theme_summary">"(The icons and many screens are not yet adjusted accordingly for the dark theme)"</string>
|
||||||
|
|
||||||
|
<string name="label_settings_analytics_title">Allow anonymous usage statistics</string>
|
||||||
|
<string name="label_settings_analytics_summary">If enabled, sends anonymous usage statistics to help improve the app</string>
|
||||||
|
|
||||||
<!-- Proxy Preferences -->
|
<!-- Proxy Preferences -->
|
||||||
<string name="pref_proxy_tor_title">"Enable Tor"</string>
|
<string name="pref_proxy_tor_title">"Enable Tor"</string>
|
||||||
<string name="pref_proxy_tor_summary">"Requires Orbot to be installed"</string>
|
<string name="pref_proxy_tor_summary">"Requires Orbot to be installed"</string>
|
||||||
@@ -2045,4 +2048,7 @@
|
|||||||
<string name="keylist_header_anonymous">Anonymous</string>
|
<string name="keylist_header_anonymous">Anonymous</string>
|
||||||
<string name="keylist_header_special">#</string>
|
<string name="keylist_header_special">#</string>
|
||||||
|
|
||||||
|
<string name="dialog_analytics_text">Allow OpenKeychain to collect anonymous usage statistics to help improve the app?</string>
|
||||||
|
<string name="button_analytics_yes">Yes, I want to help!</string>
|
||||||
|
<string name="button_analytics_no">No, thanks</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -5,6 +5,13 @@
|
|||||||
android:summary="@string/label_experimental_settings_desc_summary"
|
android:summary="@string/label_experimental_settings_desc_summary"
|
||||||
android:title="@string/label_experimental_settings_desc_title" />
|
android:title="@string/label_experimental_settings_desc_title" />
|
||||||
|
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="analytics_consent"
|
||||||
|
android:persistent="true"
|
||||||
|
android:summary="@string/label_settings_analytics_summary"
|
||||||
|
android:title="@string/label_settings_analytics_title" />
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="experimentalEnableLinkedIdentities"
|
android:key="experimentalEnableLinkedIdentities"
|
||||||
|
|||||||
Reference in New Issue
Block a user