From c0a1fc84eb32e1d332906f48515b2fbe301ab897 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 14 Jun 2018 22:18:38 +0200 Subject: [PATCH] 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