OSDN Git Service

Clean up and cover more QS metrics
authorJason Monk <jmonk@google.com>
Thu, 16 Mar 2017 15:53:40 +0000 (11:53 -0400)
committerJason Monk <jmonk@google.com>
Mon, 27 Mar 2017 16:36:25 +0000 (12:36 -0400)
Change-Id: I4ab5e54398024c8b16ca8223f0639aadcdef4377
Fixes: 34801532
Test: runtest systemui

33 files changed:
core/java/android/metrics/LogMaker.java
packages/SystemUI/src/com/android/systemui/Dependency.java
packages/SystemUI/src/com/android/systemui/qs/QSDetail.java
packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
packages/SystemUI/src/com/android/systemui/qs/QSHost.java
packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenGestureLogger.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java [new file with mode: 0644]
packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java [new file with mode: 0644]
packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java [new file with mode: 0644]
packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
proto/src/metrics_constants.proto
services/core/java/com/android/server/am/ActivityMetricsLogger.java

index 60b27b4..0448221 100644 (file)
@@ -16,6 +16,7 @@
 package android.metrics;
 
 import android.annotation.SystemApi;
+import android.content.ComponentName;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -118,6 +119,16 @@ public class LogMaker {
         return this;
     }
 
+    /**
+     * @param component to replace the existing setting.
+     * @hide
+     */
+    public LogMaker setComponentName(ComponentName component) {
+        entries.put(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME, component.getPackageName());
+        entries.put(MetricsEvent.FIELD_CLASS_NAME, component.getClassName());
+        return this;
+    }
+
     /** Remove the package name property. */
     public LogMaker clearPackageName() {
         entries.remove(MetricsEvent.RESERVED_FOR_LOGBUILDER_PACKAGENAME);
index d058e78..79190cb 100644 (file)
@@ -24,6 +24,7 @@ import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.NightDisplayController;
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.Preconditions;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.assist.AssistManager;
@@ -257,6 +258,8 @@ public class Dependency extends SystemUI {
         mProviders.put(VolumeDialogController.class, () ->
                 new VolumeDialogControllerImpl(mContext));
 
+        mProviders.put(MetricsLogger.class, () -> new MetricsLogger());
+
         // Put all dependencies above here so the factory can override them if it wants.
         SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
     }
index 1709718..9efe224 100644 (file)
@@ -14,6 +14,8 @@
 
 package com.android.systemui.qs;
 
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
+
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
@@ -197,7 +199,7 @@ public class QSDetail extends LinearLayout {
             mDetailContent.removeAllViews();
             mDetailContent.addView(detailView);
             mDetailViews.put(viewCacheIndex, detailView);
-            MetricsLogger.visible(mContext, adapter.getMetricsCategory());
+            Dependency.get(MetricsLogger.class).visible(adapter.getMetricsCategory());
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_quick_settings_detail,
                     adapter.getTitle()));
@@ -206,7 +208,7 @@ public class QSDetail extends LinearLayout {
             setVisibility(View.VISIBLE);
         } else {
             if (mDetailAdapter != null) {
-                MetricsLogger.hidden(mContext, mDetailAdapter.getMetricsCategory());
+                Dependency.get(MetricsLogger.class).hidden(mDetailAdapter.getMetricsCategory());
             }
             mClosingDetail = true;
             mDetailAdapter = null;
@@ -238,8 +240,12 @@ public class QSDetail extends LinearLayout {
     protected void setupDetailFooter(DetailAdapter adapter) {
         final Intent settingsIntent = adapter.getSettingsIntent();
         mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
-        mDetailSettingsButton.setOnClickListener(v -> Dependency.get(ActivityStarter.class)
-                .postStartActivityDismissingKeyguard(settingsIntent, 0));
+        mDetailSettingsButton.setOnClickListener(v -> {
+            Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS,
+                    mDetailAdapter.getMetricsCategory());
+            Dependency.get(ActivityStarter.class)
+                    .postStartActivityDismissingKeyguard(settingsIntent, 0);
+        });
     }
 
     protected void setupDetailHeader(final DetailAdapter adapter) {
index 2202b58..a84138d 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs;
 
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_DATE;
+
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -358,6 +360,8 @@ public class QSFooter extends LinearLayout implements
                 startSettingsActivity();
             }
         } else if (v == mDateTimeGroup) {
+            Dependency.get(MetricsLogger.class).action(ACTION_QS_DATE,
+                    mNextAlarm != null);
             if (mNextAlarm != null) {
                 PendingIntent showIntent = mNextAlarm.getShowIntent();
                 mActivityStarter.startPendingIntentDismissingKeyguard(showIntent);
index 29d547c..8596b57 100644 (file)
@@ -32,6 +32,8 @@ public interface QSHost {
     TileServices getTileServices();
     void removeTile(String tileSpec);
 
+    int indexOf(String tileSpec);
+
     interface Callback {
         void onTilesChanged();
     }
index 8298cbb..2e6116d 100644 (file)
@@ -59,6 +59,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback {
     protected final View mBrightnessView;
     private final H mHandler = new H();
     private final View mPageIndicator;
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private int mPanelPaddingBottom;
     private int mBrightnessPaddingTop;
@@ -259,7 +260,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback {
         if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
             ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
         }
-        MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, mExpanded);
+        mMetricsLogger.visibility(MetricsEvent.QS_PANEL, mExpanded);
         if (!mExpanded) {
             closeDetail();
         } else {
@@ -475,7 +476,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback {
         int newVis = visible ? VISIBLE : INVISIBLE;
         setVisibility(newVis);
         if (mGridContentVisible != visible) {
-            MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, newVis);
+            mMetricsLogger.visibility(MetricsEvent.QS_PANEL, newVis);
         }
         mGridContentVisible = visible;
     }
@@ -483,7 +484,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback {
     private void logTiles() {
         for (int i = 0; i < mRecords.size(); i++) {
             TileRecord tileRecord = mRecords.get(i);
-            MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
+            mMetricsLogger.visible(tileRecord.tile.getMetricsCategory());
         }
     }
 
index 0ca115e..9330541 100644 (file)
@@ -157,6 +157,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
         return mServices;
     }
 
+    public int indexOf(String spec) {
+        return mTileSpecs.indexOf(spec);
+    }
+
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (!TILES_SETTING.equals(key)) {
index 6f35017..b5c1bd9 100644 (file)
@@ -22,6 +22,7 @@ import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
@@ -155,6 +156,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
         return mComponent;
     }
 
+    @Override
+    protected LogMaker populate(LogMaker logMaker) {
+        return super.populate(logMaker).setComponentName(mComponent);
+    }
+
     public Tile getQsTile() {
         return mTile;
     }
index 948954c..1aa51b1 100644 (file)
 
 package com.android.systemui.qs.tileimpl;
 
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -29,7 +36,6 @@ import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
@@ -58,6 +64,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile {
     protected final H mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
     protected final Handler mUiHandler = new Handler(Looper.getMainLooper());
     private final ArraySet<Object> mListeners = new ArraySet<>();
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     protected TState mState = newTileState();
@@ -76,7 +83,7 @@ public abstract class QSTileImpl<TState extends State> implements QSTile {
     /**
      * Declare the category of this tile.
      *
-     * Categories are defined in {@link com.android.internal.logging.MetricsProto.MetricsEvent}
+     * Categories are defined in {@link com.android.internal.logging.nano.MetricsProto.MetricsEvent}
      * by editing frameworks/base/proto/src/metrics_constants.proto.
      */
     abstract public int getMetricsCategory();
@@ -152,17 +159,28 @@ public abstract class QSTileImpl<TState extends State> implements QSTile {
     }
 
     public void click() {
+        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)));
         mHandler.sendEmptyMessage(H.CLICK);
     }
 
     public void secondaryClick() {
+        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)));
         mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
     }
 
     public void longClick() {
+        mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
         mHandler.sendEmptyMessage(H.LONG_CLICK);
     }
 
+    protected LogMaker populate(LogMaker logMaker) {
+        if (mState instanceof BooleanState) {
+            logMaker.addTaggedData(FIELD_QS_VALUE, ((BooleanState) mState).value ? 1 : 0);
+        }
+        return logMaker.setSubtype(getMetricsCategory())
+                .addTaggedData(FIELD_QS_POSITION, mHost.indexOf(mTileSpec));
+    }
+
     public void showDetail(boolean show) {
         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0).sendToTarget();
     }
@@ -224,7 +242,6 @@ public abstract class QSTileImpl<TState extends State> implements QSTile {
     }
 
     protected void handleLongClick() {
-        MetricsLogger.action(mContext, MetricsEvent.ACTION_QS_LONG_PRESS, getTileSpec());
         Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
                 getLongClickIntent(), 0);
     }
index ed6e6ef..4e4de15 100644 (file)
@@ -83,7 +83,6 @@ public class BluetoothTile extends QSTileImpl<BooleanState> {
     protected void handleClick() {
         // Secondary clicks are header clicks, just toggle.
         final boolean isEnabled = (Boolean)mState.value;
-        MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
         mController.setBluetoothEnabled(!isEnabled);
     }
 
index a1d3d26..22b6a63 100644 (file)
@@ -108,13 +108,11 @@ public class CastTile extends QSTileImpl<BooleanState> {
     protected void handleClick() {
         if (mKeyguard.isSecure() && !mKeyguard.canSkipBouncer()) {
             mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
-                MetricsLogger.action(mContext, getMetricsCategory());
                 showDetail(true);
                 mHost.openPanels();
             });
             return;
         }
-        MetricsLogger.action(mContext, getMetricsCategory());
         showDetail(true);
     }
 
index 4351b2c..04be7de 100644 (file)
@@ -98,7 +98,6 @@ public class CellularTile extends QSTileImpl<SignalState> {
 
     @Override
     protected void handleSecondaryClick() {
-        MetricsLogger.action(mContext, getMetricsCategory());
         if (mDataController.isMobileDataSupported()) {
             showDetail(true);
         } else {
index e33b680..5b374b1 100644 (file)
@@ -84,7 +84,6 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
 
     @Override
     protected void handleClick() {
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         mSetting.setValue(mState.value ? 0 : 1);
     }
 
index 7a25140..b796451 100644 (file)
@@ -87,7 +87,6 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
 
     private void toggleDataSaver() {
         mState.value = !mDataSaverController.isDataSaverEnabled();
-        MetricsLogger.action(mContext, getMetricsCategory(), mState.value);
         mDataSaverController.setDataSaverEnabled(mState.value);
         refreshState(mState.value);
     }
index f35de68..3c2e897 100644 (file)
@@ -146,7 +146,6 @@ public class DndTile extends QSTileImpl<BooleanState> {
                     Toast.LENGTH_LONG).show();
             return;
         }
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         showDetail(true);
         int zen = Prefs.getInt(mContext, Prefs.Key.DND_FAVORITE_ZEN, Global.ZEN_MODE_ALARMS);
         mController.setZen(zen, null, TAG);
index 7b0fd73..6d2aa90 100644 (file)
@@ -87,7 +87,6 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
         if (ActivityManager.isUserAMonkey()) {
             return;
         }
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         boolean newState = !mState.value;
         refreshState(newState);
         mFlashlightController.setFlashlight(newState);
index 6662937..5c3f65c 100644 (file)
@@ -106,7 +106,6 @@ public class HotspotTile extends QSTileImpl<AirplaneBooleanState> {
         if (!isEnabled && mAirplaneMode.getValue() != 0) {
             return;
         }
-        MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
         mController.setHotspotEnabled(!isEnabled);
     }
 
index c953363..00cfbfa 100644 (file)
@@ -92,7 +92,6 @@ public class IntentTile extends QSTileImpl<State> {
 
     @Override
     protected void handleClick() {
-        MetricsLogger.action(mContext, getMetricsCategory(), mIntentPackage);
         sendIntent("click", mOnClick, mOnClickUri);
     }
 
index b5c02cb..b11b15a 100644 (file)
@@ -81,13 +81,11 @@ public class LocationTile extends QSTileImpl<BooleanState> {
             Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
                 final boolean wasEnabled = mState.value;
                 mHost.openPanels();
-                MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
                 mController.setLocationEnabled(!wasEnabled);
             });
             return;
         }
         final boolean wasEnabled = mState.value;
-        MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
         mController.setLocationEnabled(!wasEnabled);
     }
 
index 3299339..d147b69 100644 (file)
@@ -85,7 +85,6 @@ public class NfcTile extends QSTileImpl<BooleanState> {
     @Override
     protected void handleClick() {
         if (mAdapter == null) return;
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         if (!mAdapter.isEnabled()) {
             mAdapter.enable();
         } else {
index 8b47216..8aa1e43 100644 (file)
@@ -54,7 +54,6 @@ public class NightDisplayTile extends QSTileImpl<BooleanState>
     @Override
     protected void handleClick() {
         final boolean activated = !mState.value;
-        MetricsLogger.action(mContext, getMetricsCategory(), activated);
         mController.setActivated(activated);
     }
 
index 130304f..fb937bd 100644 (file)
@@ -79,7 +79,6 @@ public class RotationLockTile extends QSTileImpl<BooleanState> {
     @Override
     protected void handleClick() {
         if (mController == null) return;
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         final boolean newState = !mState.value;
         mController.setRotationLocked(!newState);
         refreshState(newState);
index fde2e04..79b4c4a 100644 (file)
@@ -114,7 +114,6 @@ public class WifiTile extends QSTileImpl<SignalState> {
     protected void handleClick() {
         // Secondary clicks are header clicks, just toggle.
         mState.copyTo(mStateBeforeClick);
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         mController.setWifiEnabled(!mState.value);
     }
 
index 5086091..6c89241 100644 (file)
@@ -68,7 +68,6 @@ public class WorkModeTile extends QSTileImpl<BooleanState> implements
 
     @Override
     public void handleClick() {
-        MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
         mProfileController.setWorkModeEnabled(!mState.value);
     }
 
index b5f56c3..4d99a46 100644 (file)
@@ -22,6 +22,7 @@ import android.util.ArrayMap;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
 
@@ -33,7 +34,7 @@ public class LockscreenGestureLogger {
     private ArrayMap<Integer, Integer> mLegacyMap;
     private LogMaker mLogMaker = new LogMaker(MetricsEvent.VIEW_UNKNOWN)
             .setType(MetricsEvent.TYPE_ACTION);
-    private MetricsLogger mMetricsLogger = new MetricsLogger();
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     public LockscreenGestureLogger() {
         mLegacyMap = new ArrayMap<>(EventLogConstants.METRICS_GESTURE_TYPE_MAP.length);
@@ -58,9 +59,4 @@ public class LockscreenGestureLogger {
         }
         return value;
     }
-
-    @VisibleForTesting
-    void setMetricsLogger(MetricsLogger metricsLogger) {
-        mMetricsLogger = metricsLogger;
-    }
 }
index b82b113..bfe55bc 100644 (file)
@@ -484,7 +484,7 @@ public class StatusBar extends SystemUI implements DemoMode,
 
     private ScreenPinningRequest mScreenPinningRequest;
 
-    MetricsLogger mMetricsLogger = new MetricsLogger();
+    private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     // ensure quick settings is disabled until the current user makes it through the setup wizard
     private boolean mUserSetup = false;
@@ -748,12 +748,6 @@ public class StatusBar extends SystemUI implements DemoMode,
     private NavigationBarFragment mNavigationBar;
     private View mNavigationBarView;
 
-    @VisibleForTesting
-    void setMetricsLogger(MetricsLogger metricsLogger) {
-        mMetricsLogger = metricsLogger;
-        mLockscreenGestureLogger.setMetricsLogger(metricsLogger);
-    }
-
     @Override
     public void start() {
         mNetworkController = Dependency.get(NetworkController.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
new file mode 100644 (file)
index 0000000..c67cccc
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.DetailAdapter;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class QSDetailTest extends SysuiTestCase {
+
+    private MetricsLogger mMetricsLogger;
+    private QSDetail mQsDetail;
+    private QSPanel mQsPanel;
+    private QuickStatusBarHeader mQuickHeader;
+    private ActivityStarter mActivityStarter;
+    private DetailAdapter mMockDetailAdapter;
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setup() throws Exception {
+        mTestableLooper = TestableLooper.get(this);
+        mTestableLooper.runWithLooper(() -> {
+            mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+            mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
+            mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null);
+            mQsPanel = mock(QSPanel.class);
+            mQuickHeader = mock(QuickStatusBarHeader.class);
+            mQsDetail.setQsPanel(mQsPanel, mQuickHeader);
+
+            mMockDetailAdapter = mock(DetailAdapter.class);
+            when(mMockDetailAdapter.createDetailView(any(), any(), any()))
+                    .thenReturn(mock(View.class));
+        });
+    }
+
+    @Test
+    public void testShowDetail_Metrics() {
+        ViewUtils.attachView(mQsDetail);
+        mTestableLooper.processAllMessages();
+
+        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
+        verify(mMetricsLogger).visible(eq(mMockDetailAdapter.getMetricsCategory()));
+        mQsDetail.handleShowingDetail(null, 0, 0, false);
+        verify(mMetricsLogger).hidden(eq(mMockDetailAdapter.getMetricsCategory()));
+
+        ViewUtils.detachView(mQsDetail);
+        mTestableLooper.processAllMessages();
+    }
+
+    @Test
+    public void testMoreSettingsButton() {
+        ViewUtils.attachView(mQsDetail);
+        mTestableLooper.processAllMessages();
+
+        mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
+        mQsDetail.findViewById(android.R.id.button2).performClick();
+
+        int metricsCategory = mMockDetailAdapter.getMetricsCategory();
+        verify(mMetricsLogger).action(eq(ACTION_QS_MORE_SETTINGS), eq(metricsCategory));
+
+        verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt());
+
+        ViewUtils.detachView(mQsDetail);
+        mTestableLooper.processAllMessages();
+    }
+}
index deb31da..d77ed3d 100644 (file)
@@ -19,6 +19,7 @@ import static org.mockito.Mockito.mock;
 
 import android.os.Looper;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -44,12 +45,15 @@ import android.widget.FrameLayout;
 @RunWithLooper(setAsMainLooper = true)
 public class QSFragmentTest extends SysuiBaseFragmentTest {
 
+    private MetricsLogger mMockMetricsLogger;
+
     public QSFragmentTest() {
         super(QSFragment.class);
     }
 
     @Before
     public void addLeakCheckDependencies() {
+        mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
                 new LayoutInflaterBuilder(mContext)
                         .replace("com.android.systemui.statusbar.policy.SplitClockView",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
new file mode 100644 (file)
index 0000000..4979684
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.customize.QSCustomizer;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class QSPanelTest extends SysuiTestCase {
+
+    private MetricsLogger mMetricsLogger;
+    private QSPanel mQsPanel;
+    private QSTileHost mHost;
+    private QSCustomizer mCustomizer;
+
+    @Before
+    public void setup() throws Exception {
+        TestableLooper.get(this).runWithLooper(() -> {
+            mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+            mQsPanel = new QSPanel(mContext, null);
+            mHost = mock(QSTileHost.class);
+            when(mHost.getTiles()).thenReturn(Collections.emptyList());
+            mCustomizer = mock(QSCustomizer.class);
+            mQsPanel.setHost(mHost, mCustomizer);
+        });
+    }
+
+    @Test
+    public void testSetExpanded_Metrics() {
+        mQsPanel.setExpanded(true);
+        verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
+        mQsPanel.setExpanded(false);
+        verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
new file mode 100644 (file)
index 0000000..9ed9d28
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_CLICK;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_LONG_PRESS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_SECONDARY_CLICK;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_POSITION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_QS_VALUE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.metrics.LogMaker;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.Dependency;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSTileHost;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class QSTileImplTest extends SysuiTestCase {
+
+    public static final int POSITION = 14;
+    private TestableLooper mTestableLooper;
+    private TileImpl mTile;
+    private QSTileHost mHost;
+    private MetricsLogger mMetricsLogger;
+
+    @Before
+    public void setup() throws Exception {
+        String spec = "spec";
+        mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
+        mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
+        mHost = mock(QSTileHost.class);
+        when(mHost.indexOf(spec)).thenReturn(POSITION);
+        mTestableLooper.runWithLooper(() -> {
+            mTile = new TileImpl(mHost);
+            mTile.setTileSpec(spec);
+        });
+    }
+
+    @Test
+    public void testClick_Metrics() {
+        mTile.click();
+        verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_CLICK)));
+    }
+
+    @Test
+    public void testSecondaryClick_Metrics() {
+        mTile.secondaryClick();
+        verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
+    }
+
+    @Test
+    public void testLongClick_Metrics() {
+        mTile.longClick();
+        verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
+    }
+
+    @Test
+    public void testPopulate() {
+        LogMaker maker = mock(LogMaker.class);
+        when(maker.setSubtype(anyInt())).thenReturn(maker);
+        mTile.getState().value = true;
+        mTile.populate(maker);
+        verify(maker).addTaggedData(eq(FIELD_QS_VALUE), eq(1));
+        verify(maker).addTaggedData(eq(FIELD_QS_POSITION), eq(POSITION));
+    }
+
+    private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
+
+        private final int mCategory;
+        public String mInvalid;
+
+        public TileLogMatcher(int category) {
+            mCategory = category;
+        }
+
+        @Override
+        public boolean matches(LogMaker arg) {
+            if (arg.getCategory() != mCategory) {
+                mInvalid = "Expected category " + mCategory + " but was " + arg.getCategory();
+                return false;
+            }
+            if (arg.getType() != TYPE_ACTION) {
+                mInvalid = "Expected type " + TYPE_ACTION + " but was " + arg.getType();
+                return false;
+            }
+            if (arg.getSubtype() != mTile.getMetricsCategory()) {
+                mInvalid = "Expected subtype " + mTile.getMetricsCategory() + " but was "
+                        + arg.getSubtype();
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return mInvalid;
+        }
+    }
+
+    private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
+        protected TileImpl(QSHost host) {
+            super(host);
+        }
+
+        @Override
+        public BooleanState newTileState() {
+            return new BooleanState();
+        }
+
+        @Override
+        protected void handleClick() {
+
+        }
+
+        @Override
+        protected void handleUpdateState(BooleanState state, Object arg) {
+
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return 42;
+        }
+
+        @Override
+        public Intent getLongClickIntent() {
+            return null;
+        }
+
+        @Override
+        protected void setListening(boolean listening) {
+
+        }
+
+        @Override
+        public CharSequence getTileLabel() {
+            return null;
+        }
+    }
+}
index f48af75..bf6b394 100644 (file)
@@ -62,9 +62,9 @@ public class StatusBarTest extends SysuiTestCase {
         mKeyguardIndicationController = mock(KeyguardIndicationController.class);
         mStackScroller = mock(NotificationStackScrollLayout.class);
         mMetricsLogger = new FakeMetricsLogger();
+        mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller);
-        mStatusBar.setMetricsLogger(mMetricsLogger);
 
         doAnswer(invocation -> {
             OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0];
index 783aae7..da441f5 100644 (file)
@@ -3656,7 +3656,7 @@ message MetricsEvent {
     ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870;
 
     // The name of the activity being launched in an app transition event.
-    APP_TRANSITION_ACTIVITY_NAME = 871;
+    FIELD_CLASS_NAME = 871;
 
     // ACTION: Settings > App detail > Uninstall
     ACTION_SETTINGS_UNINSTALL_APP = 872;
@@ -3874,6 +3874,24 @@ message MetricsEvent {
     // OPEN: Settings -> System -> Reset options
     RESET_DASHBOARD = 924;
 
+    // ACTION: QS -> Tile clicked
+    ACTION_QS_CLICK = 925;
+
+    // ACTION: QS -> Secondary click
+    ACTION_QS_SECONDARY_CLICK = 926;
+
+    // FIELD: Position info in QS clicks
+    FIELD_QS_POSITION = 927;
+
+    // FIELD: The value of a QS tile when clicked (if applicable)
+    FIELD_QS_VALUE = 928;
+
+    // ACTION: QS -> Detail panel -> more settings
+    ACTION_QS_MORE_SETTINGS = 929;
+
+    // ACTION: QS -> Click date
+    ACTION_QS_DATE = 930;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
index 04a09fe..5edfb06 100644 (file)
@@ -10,8 +10,8 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
-import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_ACTIVITY_NAME;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CALLING_PACKAGE_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_IS_EPHEMERAL;
@@ -313,7 +313,7 @@ class ActivityMetricsLogger {
             final LogMaker builder = new LogMaker(APP_TRANSITION);
             builder.setPackageName(info.launchedActivity.packageName);
             builder.setType(type);
-            builder.addTaggedData(APP_TRANSITION_ACTIVITY_NAME, info.launchedActivity.info.name);
+            builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
             if (info.launchedActivity.launchedFromPackage != null) {
                 builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
                         info.launchedActivity.launchedFromPackage);