From c0a1fc84eb32e1d332906f48515b2fbe301ab897 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 14 Jun 2018 22:18:38 +0200 Subject: [PATCH 1/9] add simple tracking (no opt-in yet!) --- OpenKeychain/build.gradle | 3 + .../keychain/KeychainApplication.java | 9 ++ .../keychain/TrackingManager.java | 102 ++++++++++++++++++ .../keychain/remote/OpenPgpService.java | 7 ++ .../keychain/service/KeychainServiceTask.java | 19 +++- .../keychain/ui/MainActivity.java | 7 ++ .../keychain/ui/ViewKeyAdvActivity.java | 49 +++++---- 7 files changed, 172 insertions(+), 24 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index aed98e357..38ecdb5f4 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -50,6 +50,9 @@ dependencies { // Nordpol compile 'com.fidesmo:nordpol-android:0.1.22' + // piwik + implementation 'org.piwik.sdk:piwik-sdk:3.0.3' + // libs as submodules implementation project(':libkeychain') implementation project(':openpgp-api-lib') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index b51071e3e..139bf6475 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -26,6 +26,7 @@ import android.accounts.AccountManager; import android.app.Application; import android.content.Context; import android.graphics.Bitmap; +import android.os.Handler; import android.support.annotation.Nullable; import android.widget.Toast; @@ -41,6 +42,7 @@ import timber.log.Timber.DebugTree; public class KeychainApplication extends Application { + TrackingManager trackingManager; /** * Called when the application is starting, before any activity, service, or receiver objects @@ -105,6 +107,9 @@ public class KeychainApplication extends Application { KeyserverSyncManager.updateKeyserverSyncScheduleAsync(this, Constants.DEBUG_KEYSERVER_SYNC); TemporaryFileProvider.scheduleCleanupImmediately(); + + trackingManager = TrackingManager.getInstance(getApplicationContext()); + trackingManager.initialize(this); } /** @@ -152,4 +157,8 @@ public class KeychainApplication extends Application { Timber.plant(new DebugTree()); } } + + public TrackingManager getTrackingManager() { + return trackingManager; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java new file mode 100644 index 000000000..1be0e19da --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java @@ -0,0 +1,102 @@ +package org.sufficientlysecure.keychain; + + +import android.app.Activity; +import android.app.Application; +import android.app.Application.ActivityLifecycleCallbacks; +import android.content.Context; +import android.os.Bundle; + +import org.piwik.sdk.Piwik; +import org.piwik.sdk.Tracker; +import org.piwik.sdk.TrackerConfig; +import org.piwik.sdk.extra.DownloadTracker.Extra.ApkChecksum; +import org.piwik.sdk.extra.TrackHelper; + + +public class TrackingManager { + private Tracker piwikTracker; + + public static TrackingManager getInstance(Context context) { + TrackerConfig trackerConfig = new TrackerConfig("https://mugenguild.com/piwik/", 1, "OpenKeychain"); + Tracker tracker = Piwik.getInstance(context).newTracker(trackerConfig); + tracker.setDispatchInterval(30000); + + return new TrackingManager(tracker); + } + + private TrackingManager(Tracker piwikTracker) { + this.piwikTracker = piwikTracker; + } + + public void initialize(Application application) { + if (piwikTracker == null) { + return; + } + TrackHelper.track().download().identifier(new ApkChecksum(application)).with(piwikTracker); + + application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + } + + @Override + public void onActivityStarted(Activity activity) { + + } + + @Override + public void onActivityResumed(Activity activity) { + TrackHelper.track().screen(activity.getClass().getSimpleName()).with(piwikTracker); + } + + @Override + public void onActivityPaused(Activity activity) { + + } + + @Override + public void onActivityStopped(Activity activity) { + + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override + public void onActivityDestroyed(Activity activity) { + + } + }); + } + + public void trackFragmentImpression(String opClassName, String fragmentName) { + if (piwikTracker == null) { + return; + } + + TrackHelper.track().screen(opClassName + "/" + fragmentName).with(piwikTracker); + } + + public void trackInternalServiceCall(String opClassName) { + if (piwikTracker == null) { + return; + } + TrackHelper.track() + .interaction("internalApiCall", opClassName) + .with(piwikTracker); + } + + public void trackApiServiceCall(String opClassName, String currentCallingPackage) { + if (piwikTracker == null) { + return; + } + + TrackHelper.track() + .interaction("externalApiCall", opClassName) + .piece(currentCallingPackage.replace(".", "/")) + .with(piwikTracker); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 39e405a7b..4773c042a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -53,6 +53,8 @@ import org.openintents.openpgp.OpenPgpSignatureResult.AutocryptPeerResult; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; +import org.sufficientlysecure.keychain.KeychainApplication; +import org.sufficientlysecure.keychain.TrackingManager; import org.sufficientlysecure.keychain.operations.BackupOperation; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; @@ -99,6 +101,7 @@ public class OpenPgpService extends Service { private ApiAppDao mApiAppDao; private OpenPgpServiceKeyIdExtractor mKeyIdExtractor; private ApiPendingIntentFactory mApiPendingIntentFactory; + private TrackingManager trackingManager; @Override public void onCreate() { @@ -108,6 +111,8 @@ public class OpenPgpService extends Service { mApiPermissionHelper = new ApiPermissionHelper(this, mApiAppDao); mApiPendingIntentFactory = new ApiPendingIntentFactory(getBaseContext()); mKeyIdExtractor = OpenPgpServiceKeyIdExtractor.getInstance(getContentResolver(), mApiPendingIntentFactory); + + trackingManager = ((KeychainApplication) getApplication()).getTrackingManager(); } private Intent signImpl(Intent data, InputStream inputStream, @@ -1025,6 +1030,8 @@ public class OpenPgpService extends Service { return errorResult; } + trackingManager.trackApiServiceCall(data.getAction(), mApiPermissionHelper.getCurrentCallingPackage()); + Progressable progressable = null; if (data.hasExtra(OpenPgpApi.EXTRA_PROGRESS_MESSENGER)) { Messenger messenger = data.getParcelableExtra(OpenPgpApi.EXTRA_PROGRESS_MESSENGER); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java index 305a566a1..5fa9437b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java @@ -21,11 +21,14 @@ package org.sufficientlysecure.keychain.service; import java.util.concurrent.atomic.AtomicBoolean; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; import android.os.AsyncTask; import android.os.Parcelable; import android.support.v4.os.CancellationSignal; +import org.sufficientlysecure.keychain.KeychainApplication; +import org.sufficientlysecure.keychain.TrackingManager; import org.sufficientlysecure.keychain.daos.KeyWritableRepository; import org.sufficientlysecure.keychain.operations.BackupOperation; import org.sufficientlysecure.keychain.operations.BaseOperation; @@ -52,13 +55,19 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; public class KeychainServiceTask { - public static KeychainServiceTask create(Context context) { - return new KeychainServiceTask(context.getApplicationContext()); + private final TrackingManager trackingManager; + + public static KeychainServiceTask create(Activity activity) { + Context context = activity.getApplicationContext(); + TrackingManager trackingManager = ((KeychainApplication) activity.getApplication()).getTrackingManager(); + + return new KeychainServiceTask(context, KeyWritableRepository.create(context), trackingManager); } - private KeychainServiceTask(Context context) { + private KeychainServiceTask(Context context, KeyWritableRepository keyRepository, TrackingManager trackingManager) { this.context = context; - this.keyRepository = KeyWritableRepository.create(context); + this.keyRepository = keyRepository; + this.trackingManager = trackingManager; } private final Context context; @@ -121,6 +130,8 @@ public class KeychainServiceTask { return null; } + trackingManager.trackInternalServiceCall(op.getClass().getSimpleName()); + // noinspection unchecked, we make sure it's the correct op above return op.execute(inputParcel, cryptoInput); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 9471c644d..e98e15abd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -37,7 +37,9 @@ import com.mikepenz.materialdrawer.model.DividerDrawerItem; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; +import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.TrackingManager; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; @@ -62,6 +64,7 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai public Drawer mDrawer; private Toolbar mToolbar; + private TrackingManager trackingManager; @Override public void onCreate(Bundle savedInstanceState) { @@ -72,6 +75,8 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai mToolbar.setTitle(R.string.app_name); setSupportActionBar(mToolbar); + trackingManager = ((KeychainApplication) getApplication()).getTrackingManager(); + mDrawer = new DrawerBuilder() .withActivity(this) .withHeader(R.layout.main_drawer_header) @@ -200,6 +205,8 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai private void setFragment(Fragment frag) { FragmentManager fragmentManager = getSupportFragmentManager(); + trackingManager.trackFragmentImpression(getClass().getSimpleName(), frag.getClass().getSimpleName()); + FragmentTransaction ft = fragmentManager.beginTransaction(); ft.replace(R.id.main_fragment_container, frag); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index 71eb650c7..25f16f672 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -42,11 +42,13 @@ import android.view.ViewPropertyAnimator; import android.view.animation.OvershootInterpolator; import com.astuetz.PagerSlidingTabStrip; +import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.model.UserPacket.UserId; +import org.sufficientlysecure.keychain.TrackingManager; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.daos.KeyRepository; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; @@ -62,12 +64,13 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList KeyRepository keyRepository; // view - private ViewPager mViewPager; - private PagerSlidingTabStrip mSlidingTabLayout; + private ViewPager viewPager; + private PagerSlidingTabStrip slidingTabLayout; - private ActionMode mActionMode; + private ActionMode actionMode; private boolean hasSecret; - private boolean mActionIconShown; + private boolean actionIconShown; + private PagerTabStripAdapter tabAdapter; enum ViewKeyAdvTab { START(ViewKeyAdvStartFragment.class, R.string.key_view_tab_start, false), @@ -86,6 +89,8 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList } } + private TrackingManager trackingManager; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -93,9 +98,10 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList setFullScreenDialogClose(v -> finish()); keyRepository = KeyRepository.create(this); + trackingManager = ((KeychainApplication) getApplication()).getTrackingManager(); - mViewPager = findViewById(R.id.pager); - mSlidingTabLayout = findViewById(R.id.sliding_tab_layout); + viewPager = findViewById(R.id.pager); + slidingTabLayout = findViewById(R.id.sliding_tab_layout); if (!getIntent().hasExtra(EXTRA_MASTER_KEY_ID)) { throw new IllegalArgumentException("Missing required extra master_key_id"); @@ -185,7 +191,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList } mToolbar.setBackgroundColor(color); mStatusBar.setBackgroundColor(ViewKeyActivity.getStatusBarBackgroundColor(color)); - mSlidingTabLayout.setBackgroundColor(color); + slidingTabLayout.setBackgroundColor(color); invalidateOptionsMenu(); } @@ -196,21 +202,21 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList } private void initTabs() { - PagerTabStripAdapter tabAdapter = new PagerTabStripAdapter(this); - mViewPager.setAdapter(tabAdapter); + tabAdapter = new PagerTabStripAdapter(this); + viewPager.setAdapter(tabAdapter); for (ViewKeyAdvTab tab : ViewKeyAdvTab.values()) { tabAdapter.addTab(tab.fragmentClass, null, getString(tab.titleRes)); } // update layout after operations - mSlidingTabLayout.setViewPager(mViewPager); - mSlidingTabLayout.setOnPageChangeListener(this); + slidingTabLayout.setViewPager(viewPager); + slidingTabLayout.setOnPageChangeListener(this); // switch to tab selected by extra Intent intent = getIntent(); int switchToTab = intent.getIntExtra(EXTRA_SELECTED_TAB, 0); - mViewPager.setCurrentItem(switchToTab); + viewPager.setCurrentItem(switchToTab); } @Override @@ -234,15 +240,15 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList getMenuInflater().inflate(R.menu.action_mode_edit, menu); final MenuItem vActionModeItem = menu.findItem(R.id.menu_action_mode_edit); - boolean isCurrentActionFragment = ViewKeyAdvTab.values()[mViewPager.getCurrentItem()].hasActionMode; + boolean isCurrentActionFragment = ViewKeyAdvTab.values()[viewPager.getCurrentItem()].hasActionMode; // if the state is as it should be, never mind - if (isCurrentActionFragment == mActionIconShown) { + if (isCurrentActionFragment == actionIconShown) { return isCurrentActionFragment; } // show or hide accordingly - mActionIconShown = isCurrentActionFragment; + actionIconShown = isCurrentActionFragment; vActionModeItem.setEnabled(isCurrentActionFragment); animateMenuItem(vActionModeItem, isCurrentActionFragment); @@ -273,13 +279,13 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList @Override public void onActionModeStarted(final ActionMode mode) { super.onActionModeStarted(mode); - mActionMode = mode; + actionMode = mode; } @Override public void onActionModeFinished(ActionMode mode) { super.onActionModeFinished(mode); - mActionMode = null; + actionMode = null; } @Override @@ -289,11 +295,14 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList @Override public void onPageSelected(int position) { - if (mActionMode != null) { - mActionMode.finish(); - mActionMode = null; + if (actionMode != null) { + actionMode.finish(); + actionMode = null; } invalidateOptionsMenu(); + + String fragmentName = tabAdapter.getItem(position).getClass().getSimpleName(); + trackingManager.trackFragmentImpression(getClass().getSimpleName(), fragmentName); } @Override From c5d7e482e0381266dba93f0705ac0d3552c0980b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 14 Jun 2018 23:18:27 +0200 Subject: [PATCH 2/9] Add opt-in setting for tracking --- .../keychain/Constants.java | 3 ++ .../keychain/TrackingManager.java | 36 ++++++++++---- .../keychain/ui/KeyListFragment.java | 49 +++++++++++++++++++ .../keychain/ui/SettingsActivity.java | 10 ++++ .../keychain/util/Preferences.java | 16 ++++++ OpenKeychain/src/main/res/values/strings.xml | 6 +++ .../main/res/xml/experimental_preferences.xml | 7 +++ 7 files changed, 117 insertions(+), 10 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 4d30d115d..6da7283a1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -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_ANALYTICS_ASKED_POLITELY = "analytics_asked"; + public static final String KEY_ANALYTICS_CONSENT = "analytics_consent"; + public static final class Theme { public static final String LIGHT = "light"; public static final String DARK = "dark"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java index 1be0e19da..dc42d0237 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java @@ -6,34 +6,31 @@ import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.content.Context; import android.os.Bundle; +import android.support.annotation.NonNull; import org.piwik.sdk.Piwik; import org.piwik.sdk.Tracker; import org.piwik.sdk.TrackerConfig; import org.piwik.sdk.extra.DownloadTracker.Extra.ApkChecksum; import org.piwik.sdk.extra.TrackHelper; +import org.sufficientlysecure.keychain.util.Preferences; public class TrackingManager { private Tracker piwikTracker; public static TrackingManager getInstance(Context context) { - TrackerConfig trackerConfig = new TrackerConfig("https://mugenguild.com/piwik/", 1, "OpenKeychain"); - Tracker tracker = Piwik.getInstance(context).newTracker(trackerConfig); - tracker.setDispatchInterval(30000); - - return new TrackingManager(tracker); + return new TrackingManager(context); } - private TrackingManager(Tracker piwikTracker) { - this.piwikTracker = piwikTracker; + private TrackingManager(Context context) { + refreshSettings(context); } public void initialize(Application application) { - if (piwikTracker == null) { - return; + if (piwikTracker != null) { + TrackHelper.track().download().identifier(new ApkChecksum(application)).with(piwikTracker); } - TrackHelper.track().download().identifier(new ApkChecksum(application)).with(piwikTracker); application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override @@ -47,6 +44,9 @@ public class TrackingManager { @Override public void onActivityResumed(Activity activity) { + if (piwikTracker == null) { + return; + } TrackHelper.track().screen(activity.getClass().getSimpleName()).with(piwikTracker); } @@ -99,4 +99,20 @@ public class TrackingManager { .piece(currentCallingPackage.replace(".", "/")) .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; + } + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 82a93632e..3ed742297 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -20,12 +20,17 @@ package org.sufficientlysecure.keychain.ui; import java.io.IOException; import java.util.List; +import java.util.concurrent.TimeUnit; import android.animation.ObjectAnimator; import android.app.Activity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.ViewModelProviders; +import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; import android.os.Bundle; 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.OnItemLongClickListener; import eu.davidea.flexibleadapter.SelectableAdapter.Mode; +import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.KeychainDatabase; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.TrackingManager; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager; import org.sufficientlysecure.keychain.daos.KeyRepository; @@ -259,6 +267,8 @@ public class KeyListFragment extends RecyclerFragment> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems); liveData.observe(this, this::onLoadKeyItems); + + maybeAskForAnalytics(); } @WorkerThread @@ -267,6 +277,45 @@ public class KeyListFragment extends RecyclerFragment 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 flexibleKeyItems) { FlexibleAdapter adapter = getAdapter(); if (adapter == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index f12943f14..37042fdb9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -37,6 +37,7 @@ import android.os.Bundle; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.preference.SwitchPreference; @@ -50,6 +51,7 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.R; 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 public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 38773c116..f73baf6c3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -353,6 +353,22 @@ public class Preferences { 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 public static abstract class CloudSearchPrefs implements Parcelable { public abstract boolean isKeyserverEnabled(); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 5a7d2ac92..6349870dc 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -236,6 +236,9 @@ "Contact keybase.io for key proofs and show them every time a key is displayed" "(The icons and many screens are not yet adjusted accordingly for the dark theme)" + Allow anonymous usage statistics + If enabled, sends anonymous usage statistics to help improve the app + "Enable Tor" "Requires Orbot to be installed" @@ -2045,4 +2048,7 @@ Anonymous # + Allow OpenKeychain to collect anonymous usage statistics to help improve the app? + Yes, I want to help! + No, thanks diff --git a/OpenKeychain/src/main/res/xml/experimental_preferences.xml b/OpenKeychain/src/main/res/xml/experimental_preferences.xml index e557f06e6..fd21a546c 100644 --- a/OpenKeychain/src/main/res/xml/experimental_preferences.xml +++ b/OpenKeychain/src/main/res/xml/experimental_preferences.xml @@ -5,6 +5,13 @@ android:summary="@string/label_experimental_settings_desc_summary" android:title="@string/label_experimental_settings_desc_title" /> + + Date: Mon, 16 Jul 2018 11:58:26 +0200 Subject: [PATCH 3/9] don't ask for 24h if user cancels dialog (eg clicking outside) --- .../org/sufficientlysecure/keychain/Constants.java | 1 + .../keychain/ui/KeyListFragment.java | 11 ++++++++++- .../sufficientlysecure/keychain/util/Preferences.java | 9 +++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 6da7283a1..0cc871a3a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -160,6 +160,7 @@ public final class Constants { public static final String KEY_ANALYTICS_ASKED_POLITELY = "analytics_asked"; public static final String KEY_ANALYTICS_CONSENT = "analytics_consent"; + public static final String KEY_ANALYTICS_LAST_ASKED = "analytics_last_asked"; public static final class Theme { public static final String LIGHT = "light"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 3ed742297..289d492c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -284,7 +284,8 @@ public class KeyListFragment extends RecyclerFragment twentyFourHoursAgo; + if (askedLessThan24HoursAgo) { + return; + } + + preferences.setAnalyticsLastAskedNow(); + TrackingManager trackingManager = ((KeychainApplication) requireActivity().getApplication()).getTrackingManager(); AlertDialog show = new Builder(context) .setMessage(R.string.dialog_analytics_text) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index f73baf6c3..26a8d05d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.util; import java.net.Proxy; import java.util.ArrayList; +import java.util.Date; import java.util.ListIterator; import android.annotation.SuppressLint; @@ -369,6 +370,14 @@ public class Preferences { mSharedPreferences.edit().putBoolean(Pref.KEY_ANALYTICS_CONSENT, hasUserConsent).apply(); } + public void setAnalyticsLastAskedNow() { + mSharedPreferences.edit().putLong(Pref.KEY_ANALYTICS_LAST_ASKED, System.currentTimeMillis()).apply(); + } + + public long getAnalyticsLastAsked() { + return mSharedPreferences.getLong(Pref.KEY_ANALYTICS_LAST_ASKED, 0); + } + @AutoValue public static abstract class CloudSearchPrefs implements Parcelable { public abstract boolean isKeyserverEnabled(); From a277a3767607479e09f608561175946d6fd08b70 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jul 2018 12:11:45 +0200 Subject: [PATCH 4/9] Show snackbar after tracking dialog --- .../keychain/ui/KeyListFragment.java | 26 +++++++++++++++++-- OpenKeychain/src/main/res/values/strings.xml | 4 +++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 289d492c4..20e48b19f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -33,6 +33,7 @@ import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; import android.os.Bundle; +import android.preference.PreferenceActivity; import android.support.annotation.WorkerThread; import android.support.v4.app.FragmentActivity; import android.support.v4.view.MenuItemCompat; @@ -73,6 +74,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.service.BenchmarkInputParcel; +import org.sufficientlysecure.keychain.ui.SettingsActivity.ExperimentalPrefsFragment; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDetailsItem; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDummyItem; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyHeader; @@ -284,6 +286,10 @@ public class KeyListFragment extends RecyclerFragment threeDaysAgo; - if (installedLessThanThreeDaysAgo) { + if (!Constants.DEBUG && installedLessThanThreeDaysAgo) { return; } } catch (NameNotFoundException e) { @@ -302,7 +308,7 @@ public class KeyListFragment extends RecyclerFragment twentyFourHoursAgo; - if (askedLessThan24HoursAgo) { + if (!Constants.DEBUG && askedLessThan24HoursAgo) { return; } @@ -315,16 +321,32 @@ public class KeyListFragment extends RecyclerFragment { preferences.setAnalyticsAskedPolitely(); preferences.setAnalyticsGotUserConsent(false); trackingManager.refreshSettings(context); + Notify.create(requireActivity(), R.string.snack_analytics_reject, Style.OK, + this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); }) .show(); show.setCanceledOnTouchOutside(false); } + private void startExperimentalSettingsActivity() { + Activity activity = getActivity(); + if (activity == null) { + return; + } + + Intent resultIntent = new Intent(activity, SettingsActivity.class); + String experimentalPrefsName = ExperimentalPrefsFragment.class.getName(); + resultIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, experimentalPrefsName); + startActivity(resultIntent); + } + private void onLoadKeyItems(List flexibleKeyItems) { FlexibleAdapter adapter = getAdapter(); if (adapter == null) { diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 6349870dc..274059e46 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -2051,4 +2051,8 @@ Allow OpenKeychain to collect anonymous usage statistics to help improve the app? Yes, I want to help! No, thanks + + "Thanks for helping out! You can change this preference in the settings." + "That's alright, we won't ask again. You can change your mind in the settings." + "Settings" From 10466101c028a3cabbb3df8434875c81f42ca132 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jul 2018 13:38:02 +0200 Subject: [PATCH 5/9] move tracking consent logic into TrackingConsentRequester --- .../keychain/TrackingManager.java | 1 - .../analytics/TrackingConsentRequester.java | 91 +++++++++++++++++++ .../keychain/ui/KeyListFragment.java | 81 +---------------- 3 files changed, 93 insertions(+), 80 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java index dc42d0237..d57b077e3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java @@ -6,7 +6,6 @@ import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; import org.piwik.sdk.Piwik; import org.piwik.sdk.Tracker; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java new file mode 100644 index 000000000..59370ba58 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java @@ -0,0 +1,91 @@ +package org.sufficientlysecure.keychain; + + +import java.util.concurrent.TimeUnit; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; +import android.preference.PreferenceActivity; + +import org.sufficientlysecure.keychain.ui.SettingsActivity; +import org.sufficientlysecure.keychain.ui.SettingsActivity.ExperimentalPrefsFragment; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.util.Preferences; + + +public class TrackingConsentRequester { + private final Activity activity; + + public static TrackingConsentRequester getInstance(Activity activity) { + return new TrackingConsentRequester(activity); + } + + private TrackingConsentRequester(Activity activity) { + this.activity = activity; + } + + public void maybeAskForAnalytics() { + Preferences preferences = Preferences.getPreferences(activity); + if (preferences.isAnalyticsHasConsent()) { + return; + } + + boolean askedBeforeAndWasRejected = + preferences.isAnalyticsAskedPolitely() && !preferences.isAnalyticsHasConsent(); + if (!Constants.DEBUG && askedBeforeAndWasRejected) { + return; + } + + try { + long firstInstallTime = + activity.getPackageManager().getPackageInfo(BuildConfig.APPLICATION_ID, 0).firstInstallTime; + long threeDaysAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(3); + boolean installedLessThanThreeDaysAgo = firstInstallTime > threeDaysAgo; + if (!Constants.DEBUG && installedLessThanThreeDaysAgo) { + return; + } + } catch (NameNotFoundException e) { + return; + } + + long twentyFourHoursAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1); + boolean askedLessThan24HoursAgo = preferences.getAnalyticsLastAsked() > twentyFourHoursAgo; + if (!Constants.DEBUG && askedLessThan24HoursAgo) { + return; + } + + preferences.setAnalyticsLastAskedNow(); + + TrackingManager trackingManager = ((KeychainApplication) activity.getApplication()).getTrackingManager(); + AlertDialog show = new Builder(activity) + .setMessage(R.string.dialog_analytics_text) + .setPositiveButton(R.string.button_analytics_yes, (dialog, which) -> { + preferences.setAnalyticsAskedPolitely(); + preferences.setAnalyticsGotUserConsent(true); + trackingManager.refreshSettings(activity); + Notify.create(activity, R.string.snack_analytics_accept, Style.OK, + this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); + }) + .setNegativeButton(R.string.button_analytics_no, (dialog, which) -> { + preferences.setAnalyticsAskedPolitely(); + preferences.setAnalyticsGotUserConsent(false); + trackingManager.refreshSettings(activity); + Notify.create(activity, R.string.snack_analytics_reject, Style.OK, + this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); + }) + .show(); + show.setCanceledOnTouchOutside(false); + } + + private void startExperimentalSettingsActivity() { + Intent resultIntent = new Intent(activity, SettingsActivity.class); + String experimentalPrefsName = ExperimentalPrefsFragment.class.getName(); + resultIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, experimentalPrefsName); + activity.startActivity(resultIntent); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 20e48b19f..a3afc393d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -20,20 +20,14 @@ package org.sufficientlysecure.keychain.ui; import java.io.IOException; import java.util.List; -import java.util.concurrent.TimeUnit; import android.animation.ObjectAnimator; import android.app.Activity; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; import android.arch.lifecycle.LiveData; import android.arch.lifecycle.ViewModelProviders; -import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; import android.os.Bundle; -import android.preference.PreferenceActivity; import android.support.annotation.WorkerThread; import android.support.v4.app.FragmentActivity; import android.support.v4.view.MenuItemCompat; @@ -57,12 +51,10 @@ import eu.davidea.flexibleadapter.FlexibleAdapter; import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener; import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener; import eu.davidea.flexibleadapter.SelectableAdapter.Mode; -import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.KeychainDatabase; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.TrackingManager; +import org.sufficientlysecure.keychain.TrackingConsentRequester; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager; import org.sufficientlysecure.keychain.daos.KeyRepository; @@ -74,7 +66,6 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.service.BenchmarkInputParcel; -import org.sufficientlysecure.keychain.ui.SettingsActivity.ExperimentalPrefsFragment; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDetailsItem; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDummyItem; import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyHeader; @@ -270,7 +261,7 @@ public class KeyListFragment extends RecyclerFragment> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems); liveData.observe(this, this::onLoadKeyItems); - maybeAskForAnalytics(); + TrackingConsentRequester.getInstance(activity).maybeAskForAnalytics(); } @WorkerThread @@ -279,74 +270,6 @@ public class KeyListFragment extends RecyclerFragment threeDaysAgo; - if (!Constants.DEBUG && installedLessThanThreeDaysAgo) { - return; - } - } catch (NameNotFoundException e) { - return; - } - - long twentyFourHoursAgo = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1); - boolean askedLessThan24HoursAgo = preferences.getAnalyticsLastAsked() > twentyFourHoursAgo; - if (!Constants.DEBUG && askedLessThan24HoursAgo) { - return; - } - - preferences.setAnalyticsLastAskedNow(); - - 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); - Notify.create(requireActivity(), R.string.snack_analytics_accept, Style.OK, - this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); - }) - .setNegativeButton(R.string.button_analytics_no, (dialog, which) -> { - preferences.setAnalyticsAskedPolitely(); - preferences.setAnalyticsGotUserConsent(false); - trackingManager.refreshSettings(context); - Notify.create(requireActivity(), R.string.snack_analytics_reject, Style.OK, - this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); - }) - .show(); - show.setCanceledOnTouchOutside(false); - } - - private void startExperimentalSettingsActivity() { - Activity activity = getActivity(); - if (activity == null) { - return; - } - - Intent resultIntent = new Intent(activity, SettingsActivity.class); - String experimentalPrefsName = ExperimentalPrefsFragment.class.getName(); - resultIntent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, experimentalPrefsName); - startActivity(resultIntent); - } - private void onLoadKeyItems(List flexibleKeyItems) { FlexibleAdapter adapter = getAdapter(); if (adapter == null) { From fef79c7f73f2859baf1d61e3a3656a7f69332de0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jul 2018 13:42:03 +0200 Subject: [PATCH 6/9] rename "Tracking" to "Analytics" --- .../keychain/KeychainApplication.java | 12 +++++------ ...er.java => AnalyticsConsentRequester.java} | 21 +++++++++++-------- .../AnalyticsManager.java} | 10 ++++----- .../keychain/remote/OpenPgpService.java | 8 +++---- .../keychain/service/KeychainServiceTask.java | 15 ++++++------- .../keychain/ui/KeyListFragment.java | 4 ++-- .../keychain/ui/MainActivity.java | 9 ++++---- .../keychain/ui/SettingsActivity.java | 4 +--- .../keychain/ui/ViewKeyAdvActivity.java | 8 +++---- 9 files changed, 46 insertions(+), 45 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/{TrackingConsentRequester.java => AnalyticsConsentRequester.java} (82%) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/{TrackingManager.java => analytics/AnalyticsManager.java} (93%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index 139bf6475..c8149ba76 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -26,11 +26,11 @@ import android.accounts.AccountManager; import android.app.Application; import android.content.Context; import android.graphics.Bitmap; -import android.os.Handler; import android.support.annotation.Nullable; import android.widget.Toast; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.sufficientlysecure.keychain.analytics.AnalyticsManager; import org.sufficientlysecure.keychain.network.TlsCertificatePinning; import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; @@ -42,7 +42,7 @@ import timber.log.Timber.DebugTree; public class KeychainApplication extends Application { - TrackingManager trackingManager; + AnalyticsManager analyticsManager; /** * Called when the application is starting, before any activity, service, or receiver objects @@ -108,8 +108,8 @@ public class KeychainApplication extends Application { TemporaryFileProvider.scheduleCleanupImmediately(); - trackingManager = TrackingManager.getInstance(getApplicationContext()); - trackingManager.initialize(this); + analyticsManager = AnalyticsManager.getInstance(getApplicationContext()); + analyticsManager.initialize(this); } /** @@ -158,7 +158,7 @@ public class KeychainApplication extends Application { } } - public TrackingManager getTrackingManager() { - return trackingManager; + public AnalyticsManager getAnalyticsManager() { + return analyticsManager; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java similarity index 82% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java index 59370ba58..1a6173555 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/TrackingConsentRequester.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java @@ -1,4 +1,4 @@ -package org.sufficientlysecure.keychain; +package org.sufficientlysecure.keychain.analytics; import java.util.concurrent.TimeUnit; @@ -6,11 +6,14 @@ import java.util.concurrent.TimeUnit; import android.app.Activity; import android.app.AlertDialog; import android.app.AlertDialog.Builder; -import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.preference.PreferenceActivity; +import org.sufficientlysecure.keychain.BuildConfig; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.KeychainApplication; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.SettingsActivity; import org.sufficientlysecure.keychain.ui.SettingsActivity.ExperimentalPrefsFragment; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -18,14 +21,14 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.Preferences; -public class TrackingConsentRequester { +public class AnalyticsConsentRequester { private final Activity activity; - public static TrackingConsentRequester getInstance(Activity activity) { - return new TrackingConsentRequester(activity); + public static AnalyticsConsentRequester getInstance(Activity activity) { + return new AnalyticsConsentRequester(activity); } - private TrackingConsentRequester(Activity activity) { + private AnalyticsConsentRequester(Activity activity) { this.activity = activity; } @@ -61,20 +64,20 @@ public class TrackingConsentRequester { preferences.setAnalyticsLastAskedNow(); - TrackingManager trackingManager = ((KeychainApplication) activity.getApplication()).getTrackingManager(); + AnalyticsManager analyticsManager = ((KeychainApplication) activity.getApplication()).getAnalyticsManager(); AlertDialog show = new Builder(activity) .setMessage(R.string.dialog_analytics_text) .setPositiveButton(R.string.button_analytics_yes, (dialog, which) -> { preferences.setAnalyticsAskedPolitely(); preferences.setAnalyticsGotUserConsent(true); - trackingManager.refreshSettings(activity); + analyticsManager.refreshSettings(activity); Notify.create(activity, R.string.snack_analytics_accept, Style.OK, this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); }) .setNegativeButton(R.string.button_analytics_no, (dialog, which) -> { preferences.setAnalyticsAskedPolitely(); preferences.setAnalyticsGotUserConsent(false); - trackingManager.refreshSettings(activity); + analyticsManager.refreshSettings(activity); Notify.create(activity, R.string.snack_analytics_reject, Style.OK, this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); }) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java similarity index 93% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java index d57b077e3..5e494dd19 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/TrackingManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java @@ -1,4 +1,4 @@ -package org.sufficientlysecure.keychain; +package org.sufficientlysecure.keychain.analytics; import android.app.Activity; @@ -15,14 +15,14 @@ import org.piwik.sdk.extra.TrackHelper; import org.sufficientlysecure.keychain.util.Preferences; -public class TrackingManager { +public class AnalyticsManager { private Tracker piwikTracker; - public static TrackingManager getInstance(Context context) { - return new TrackingManager(context); + public static AnalyticsManager getInstance(Context context) { + return new AnalyticsManager(context); } - private TrackingManager(Context context) { + private AnalyticsManager(Context context) { refreshSettings(context); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 4773c042a..b0e6eab6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -54,7 +54,7 @@ import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.KeychainApplication; -import org.sufficientlysecure.keychain.TrackingManager; +import org.sufficientlysecure.keychain.analytics.AnalyticsManager; import org.sufficientlysecure.keychain.operations.BackupOperation; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; @@ -101,7 +101,7 @@ public class OpenPgpService extends Service { private ApiAppDao mApiAppDao; private OpenPgpServiceKeyIdExtractor mKeyIdExtractor; private ApiPendingIntentFactory mApiPendingIntentFactory; - private TrackingManager trackingManager; + private AnalyticsManager analyticsManager; @Override public void onCreate() { @@ -112,7 +112,7 @@ public class OpenPgpService extends Service { mApiPendingIntentFactory = new ApiPendingIntentFactory(getBaseContext()); mKeyIdExtractor = OpenPgpServiceKeyIdExtractor.getInstance(getContentResolver(), mApiPendingIntentFactory); - trackingManager = ((KeychainApplication) getApplication()).getTrackingManager(); + analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager(); } private Intent signImpl(Intent data, InputStream inputStream, @@ -1030,7 +1030,7 @@ public class OpenPgpService extends Service { return errorResult; } - trackingManager.trackApiServiceCall(data.getAction(), mApiPermissionHelper.getCurrentCallingPackage()); + analyticsManager.trackApiServiceCall(data.getAction(), mApiPermissionHelper.getCurrentCallingPackage()); Progressable progressable = null; if (data.hasExtra(OpenPgpApi.EXTRA_PROGRESS_MESSENGER)) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java index 5fa9437b5..2065dc1cc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainServiceTask.java @@ -28,7 +28,7 @@ import android.os.Parcelable; import android.support.v4.os.CancellationSignal; import org.sufficientlysecure.keychain.KeychainApplication; -import org.sufficientlysecure.keychain.TrackingManager; +import org.sufficientlysecure.keychain.analytics.AnalyticsManager; import org.sufficientlysecure.keychain.daos.KeyWritableRepository; import org.sufficientlysecure.keychain.operations.BackupOperation; import org.sufficientlysecure.keychain.operations.BaseOperation; @@ -55,19 +55,20 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; public class KeychainServiceTask { - private final TrackingManager trackingManager; + private final AnalyticsManager analyticsManager; public static KeychainServiceTask create(Activity activity) { Context context = activity.getApplicationContext(); - TrackingManager trackingManager = ((KeychainApplication) activity.getApplication()).getTrackingManager(); + KeyWritableRepository keyRepository = KeyWritableRepository.create(context); + AnalyticsManager analyticsManager = ((KeychainApplication) activity.getApplication()).getAnalyticsManager(); - return new KeychainServiceTask(context, KeyWritableRepository.create(context), trackingManager); + return new KeychainServiceTask(context, keyRepository, analyticsManager); } - private KeychainServiceTask(Context context, KeyWritableRepository keyRepository, TrackingManager trackingManager) { + private KeychainServiceTask(Context context, KeyWritableRepository keyRepository, AnalyticsManager analyticsManager) { this.context = context; this.keyRepository = keyRepository; - this.trackingManager = trackingManager; + this.analyticsManager = analyticsManager; } private final Context context; @@ -130,7 +131,7 @@ public class KeychainServiceTask { return null; } - trackingManager.trackInternalServiceCall(op.getClass().getSimpleName()); + analyticsManager.trackInternalServiceCall(op.getClass().getSimpleName()); // noinspection unchecked, we make sure it's the correct op above return op.execute(inputParcel, cryptoInput); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index a3afc393d..32ce010d3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -54,7 +54,7 @@ import eu.davidea.flexibleadapter.SelectableAdapter.Mode; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.KeychainDatabase; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.TrackingConsentRequester; +import org.sufficientlysecure.keychain.analytics.AnalyticsConsentRequester; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager; import org.sufficientlysecure.keychain.daos.KeyRepository; @@ -261,7 +261,7 @@ public class KeyListFragment extends RecyclerFragment> liveData = viewModel.getGenericLiveData(requireContext(), this::loadFlexibleKeyItems); liveData.observe(this, this::onLoadKeyItems); - TrackingConsentRequester.getInstance(activity).maybeAskForAnalytics(); + AnalyticsConsentRequester.getInstance(activity).maybeAskForAnalytics(); } @WorkerThread diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index e98e15abd..358d9aafa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -23,7 +23,6 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentManager.OnBackStackChangedListener; import android.support.v4.app.FragmentTransaction; import android.support.v7.widget.Toolbar; import android.view.View; @@ -39,7 +38,7 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.TrackingManager; +import org.sufficientlysecure.keychain.analytics.AnalyticsManager; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; import org.sufficientlysecure.keychain.ui.base.BaseSecurityTokenActivity; @@ -64,7 +63,7 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai public Drawer mDrawer; private Toolbar mToolbar; - private TrackingManager trackingManager; + private AnalyticsManager analyticsManager; @Override public void onCreate(Bundle savedInstanceState) { @@ -75,7 +74,7 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai mToolbar.setTitle(R.string.app_name); setSupportActionBar(mToolbar); - trackingManager = ((KeychainApplication) getApplication()).getTrackingManager(); + analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager(); mDrawer = new DrawerBuilder() .withActivity(this) @@ -205,7 +204,7 @@ public class MainActivity extends BaseSecurityTokenActivity implements FabContai private void setFragment(Fragment frag) { FragmentManager fragmentManager = getSupportFragmentManager(); - trackingManager.trackFragmentImpression(getClass().getSimpleName(), frag.getClass().getSimpleName()); + analyticsManager.trackFragmentImpression(getClass().getSimpleName(), frag.getClass().getSimpleName()); FragmentTransaction ft = fragmentManager.beginTransaction(); ft.replace(R.id.main_fragment_container, frag); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 37042fdb9..09994ebb0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -37,7 +37,6 @@ import android.os.Bundle; import android.preference.EditTextPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.preference.SwitchPreference; @@ -51,7 +50,6 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity; @@ -591,7 +589,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { super.onPause(); Activity activity = getActivity(); - ((KeychainApplication) activity.getApplication()).getTrackingManager().refreshSettings(activity); + ((KeychainApplication) activity.getApplication()).getAnalyticsManager().refreshSettings(activity); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index 25f16f672..457d02354 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -48,7 +48,7 @@ import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.SubKey; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.model.UserPacket.UserId; -import org.sufficientlysecure.keychain.TrackingManager; +import org.sufficientlysecure.keychain.analytics.AnalyticsManager; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.daos.KeyRepository; import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter; @@ -89,7 +89,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList } } - private TrackingManager trackingManager; + private AnalyticsManager analyticsManager; @Override protected void onCreate(Bundle savedInstanceState) { @@ -98,7 +98,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList setFullScreenDialogClose(v -> finish()); keyRepository = KeyRepository.create(this); - trackingManager = ((KeychainApplication) getApplication()).getTrackingManager(); + analyticsManager = ((KeychainApplication) getApplication()).getAnalyticsManager(); viewPager = findViewById(R.id.pager); slidingTabLayout = findViewById(R.id.sliding_tab_layout); @@ -302,7 +302,7 @@ public class ViewKeyAdvActivity extends BaseActivity implements OnPageChangeList invalidateOptionsMenu(); String fragmentName = tabAdapter.getItem(position).getClass().getSimpleName(); - trackingManager.trackFragmentImpression(getClass().getSimpleName(), fragmentName); + analyticsManager.trackFragmentImpression(getClass().getSimpleName(), fragmentName); } @Override From 15229854a6a36add4e02147dec7f6661d634911c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jul 2018 13:56:44 +0200 Subject: [PATCH 7/9] Use correct id for piwik instance --- .../sufficientlysecure/keychain/analytics/AnalyticsManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java index 5e494dd19..f47f6c12a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java @@ -104,7 +104,7 @@ public class AnalyticsManager { boolean analyticsEnabled = piwikTracker != null; if (analyticsHasConsent != analyticsEnabled) { if (analyticsHasConsent) { - TrackerConfig trackerConfig = new TrackerConfig("https://piwik.openkeychain.org/", 1, "OpenKeychain"); + TrackerConfig trackerConfig = new TrackerConfig("https://piwik.openkeychain.org/", 2, "OpenKeychain"); piwikTracker = Piwik.getInstance(context).newTracker(trackerConfig); piwikTracker.setDispatchInterval(60000); piwikTracker.setOptOut(false); From fcf6abfec3de198f7c0bb929d95a76489c4cc6df Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 16 Jul 2018 14:15:23 +0200 Subject: [PATCH 8/9] link to privacy policy in consent dialog --- .../keychain/analytics/AnalyticsConsentRequester.java | 9 ++++++--- OpenKeychain/src/main/res/values/strings.xml | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java index 1a6173555..acf643ba6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsConsentRequester.java @@ -9,6 +9,8 @@ import android.app.AlertDialog.Builder; import android.content.Intent; import android.content.pm.PackageManager.NameNotFoundException; import android.preference.PreferenceActivity; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; @@ -65,8 +67,8 @@ public class AnalyticsConsentRequester { preferences.setAnalyticsLastAskedNow(); AnalyticsManager analyticsManager = ((KeychainApplication) activity.getApplication()).getAnalyticsManager(); - AlertDialog show = new Builder(activity) - .setMessage(R.string.dialog_analytics_text) + AlertDialog alertDialog = new Builder(activity) + .setMessage(R.string.dialog_analytics_consent) .setPositiveButton(R.string.button_analytics_yes, (dialog, which) -> { preferences.setAnalyticsAskedPolitely(); preferences.setAnalyticsGotUserConsent(true); @@ -82,7 +84,8 @@ public class AnalyticsConsentRequester { this::startExperimentalSettingsActivity, R.string.snackbutton_analytics_settings).show(); }) .show(); - show.setCanceledOnTouchOutside(false); + alertDialog.findViewById(android.R.id.message).setMovementMethod(LinkMovementMethod.getInstance()); + alertDialog.setCanceledOnTouchOutside(false); } private void startExperimentalSettingsActivity() { diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 274059e46..5e457eded 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -2048,9 +2048,9 @@ Anonymous # - Allow OpenKeychain to collect anonymous usage statistics to help improve the app? - Yes, I want to help! - No, thanks + "To improve the experience for all users, may OpenKeychain collect anonymous usage statistics?\n\nTo find out more, see our Privacy Policy." + "Yes, I want to help!" + "No, thanks" "Thanks for helping out! You can change this preference in the settings." "That's alright, we won't ask again. You can change your mind in the settings." From 3f4de8b9c643bfc73f60b322db65064d9e19e844 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 17 Jul 2018 15:44:14 +0200 Subject: [PATCH 9/9] add (coarse!) analytics for preferences --- .../keychain/Constants.java | 11 +++++ .../keychain/analytics/AnalyticsManager.java | 43 ++++++++++++++++++- .../keychain/util/Preferences.java | 6 ++- 3 files changed, 58 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 0cc871a3a..317d9f5cd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -28,6 +28,9 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import java.io.File; import java.net.Proxy; +import java.util.Arrays; +import java.util.List; + public final class Constants { @@ -172,6 +175,14 @@ public final class Constants { public static final String TYPE_HTTP = "proxyHttp"; public static final String TYPE_SOCKS = "proxySocks"; } + + // we generally only track booleans. never snoop around in the user's string settings!! + public static final List ANALYTICS_PREFS = Arrays.asList(USE_NORMAL_PROXY, USE_TOR_PROXY, THEME, + SYNC_CONTACTS, SYNC_KEYSERVER, ENABLE_WIFI_SYNC_ONLY, EXPERIMENTAL_ENABLE_KEYBASE, + EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, EXPERIMENTAL_USB_ALLOW_UNTESTED, + PASSPHRASE_CACHE_SUBS, SEARCH_KEYSERVER, SEARCH_KEYBASE, SEARCH_WEB_KEY_DIRECTORY, + TEXT_USE_COMPRESSION, TEXT_SELF_ENCRYPT, FILE_USE_COMPRESSION, FILE_SELF_ENCRYPT, USE_ARMOR, + USE_NUMKEYPAD_FOR_SECURITY_TOKEN_PIN, ENCRYPT_FILENAMES); } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java index f47f6c12a..bb3574d56 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/analytics/AnalyticsManager.java @@ -5,6 +5,8 @@ import android.app.Activity; import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.os.Bundle; import org.piwik.sdk.Piwik; @@ -12,10 +14,13 @@ import org.piwik.sdk.Tracker; import org.piwik.sdk.TrackerConfig; import org.piwik.sdk.extra.DownloadTracker.Extra.ApkChecksum; import org.piwik.sdk.extra.TrackHelper; +import org.sufficientlysecure.keychain.Constants.Defaults; +import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.util.Preferences; +import timber.log.Timber; -public class AnalyticsManager { +public class AnalyticsManager implements OnSharedPreferenceChangeListener { private Tracker piwikTracker; public static AnalyticsManager getInstance(Context context) { @@ -69,6 +74,42 @@ public class AnalyticsManager { } }); + + Preferences preferences = Preferences.getPreferences(application); + preferences.getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + // we generally only track booleans. never snoop around in the user's string settings!! + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + // small exception: check if the user uses a custom keyserver, or one of the well-known ones + if (Pref.KEY_SERVERS.equals(key)) { + Timber.d("Tracking pref %s", key); + String keyServers = sharedPreferences.getString(Pref.KEY_SERVERS, Defaults.KEY_SERVERS); + String current = keyServers.substring(keyServers.indexOf(',')); + + String coarseGranularityKeyserver; + if (current.contains("keyserver.ubuntu.com")) { + coarseGranularityKeyserver = "ubuntu"; + } else if (current.contains("pgp.mit.edu")) { + coarseGranularityKeyserver = "mit"; + } else if (current.contains("pool.sks-keyservers.net")) { + coarseGranularityKeyserver = "pool"; + } else { + coarseGranularityKeyserver = "custom"; + } + TrackHelper.track().interaction("pref_" + Pref.KEY_SERVERS, coarseGranularityKeyserver).with(piwikTracker); + return; + } + if (Pref.ANALYTICS_PREFS.contains(key)) { + Timber.d("Tracking pref %s", key); + if (!sharedPreferences.contains(key)) { + TrackHelper.track().interaction("pref_" + key, "empty").with(piwikTracker); + return; + } + boolean value = sharedPreferences.getBoolean(key, false); + TrackHelper.track().interaction("pref_" + key, value ? "true" : "false").with(piwikTracker); + } } public void trackFragmentImpression(String opClassName, String fragmentName) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 26a8d05d7..790f0e845 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.util; import java.net.Proxy; import java.util.ArrayList; -import java.util.Date; import java.util.ListIterator; import android.annotation.SuppressLint; @@ -34,6 +33,7 @@ import android.support.annotation.Nullable; import com.google.auto.value.AutoValue; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.Pref; +import org.sufficientlysecure.keychain.analytics.AnalyticsManager; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; import timber.log.Timber; @@ -82,6 +82,10 @@ public class Preferences { mSharedPreferences = context.getSharedPreferences(PREF_FILE_NAME, PREF_FILE_MODE); } + public SharedPreferences getSharedPreferences() { + return mSharedPreferences; + } + public String getLanguage() { return mSharedPreferences.getString(Constants.Pref.LANGUAGE, ""); }