OSDN Git Service

Add logging for SettingsPanels
authorMatthew Fritze <mfritze@google.com>
Fri, 8 Feb 2019 22:09:35 +0000 (14:09 -0800)
committerMatthew Fritze <mfritze@google.com>
Thu, 14 Feb 2019 00:06:24 +0000 (16:06 -0800)
The 3 main categories are:
- Panel Opened
- Panel Closed
- Panel Slice interaction

There are 3 close events:
- See more button
- Done button
- Clicking outside the panel

Slice interactions will log the Uri of the Slice and the ActionType,
such as Toggle, Slider, Content (intent).

Change-Id: Iecad948f39f50dd12ae1bcb23a5d523e0df8bb98
Fixes: 117804231
Test: robotests

15 files changed:
src/com/android/settings/panel/InternetConnectivityPanel.java
src/com/android/settings/panel/MediaOutputPanel.java
src/com/android/settings/panel/NfcPanel.java
src/com/android/settings/panel/PanelContent.java
src/com/android/settings/panel/PanelFeatureProvider.java
src/com/android/settings/panel/PanelFeatureProviderImpl.java
src/com/android/settings/panel/PanelFragment.java
src/com/android/settings/panel/PanelLoggingContract.java [new file with mode: 0644]
src/com/android/settings/panel/PanelSlicesAdapter.java
src/com/android/settings/panel/SettingsPanelActivity.java
src/com/android/settings/panel/VolumePanel.java
tests/robotests/src/com/android/settings/panel/FakePanelContent.java
tests/robotests/src/com/android/settings/panel/PanelFragmentTest.java
tests/robotests/src/com/android/settings/panel/PanelSlicesAdapterTest.java
tests/robotests/src/com/android/settings/panel/SettingsPanelActivityTest.java

index d20b75a..4e4fc0c 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.settings.panel;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -64,4 +65,9 @@ public class InternetConnectivityPanel implements PanelContent {
     public Intent getSeeMoreIntent() {
         return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
     }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.PANEL_INTERNET_CONNECTIVITY;
+    }
 }
index f7639d9..c42906d 100644 (file)
@@ -19,6 +19,7 @@ package com.android.settings.panel;
 import static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME;
 import static com.android.settings.slices.CustomSliceRegistry.MEDIA_OUTPUT_SLICE_URI;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -71,4 +72,9 @@ public class MediaOutputPanel implements PanelContent {
     public Intent getSeeMoreIntent() {
         return null;
     }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.PANEL_MEDIA_OUTPUT;
+    }
 }
index 70452a3..8fa41d9 100644 (file)
@@ -50,4 +50,9 @@ public class NfcPanel implements PanelContent {
         intent.setClassName(mContext.getPackageName(), SubSettings.class.getName());
         return intent;
     }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.PANEL_NFC;
+    }
 }
index bd84c2f..496bac3 100644 (file)
@@ -19,12 +19,14 @@ package com.android.settings.panel;
 import android.content.Intent;
 import android.net.Uri;
 
+import com.android.settingslib.core.instrumentation.Instrumentable;
+
 import java.util.List;
 
 /**
  * Represents the data class needed to create a Settings Panel. See {@link PanelFragment}.
  */
-public interface PanelContent {
+public interface PanelContent extends Instrumentable {
 
     /**
      * @return a string for the title of the Panel.
index 5af5ac8..85e098d 100644 (file)
@@ -21,7 +21,8 @@ import android.content.Context;
 public interface PanelFeatureProvider {
 
     /**
-     * Returns {@link PanelContent} as specified by the {@code panelType} and {@code packageName}.
+     * Returns {@link PanelContent} as specified by the {@param panelType}, and
+     * {@param mediaPackageName}.
      */
-    PanelContent getPanel(Context context, String panelType, String packageName);
+    PanelContent getPanel(Context context, String panelType, String mediaPackageName);
 }
index c3d611d..6b096a1 100644 (file)
@@ -24,7 +24,7 @@ import android.provider.Settings;
 public class PanelFeatureProviderImpl implements PanelFeatureProvider {
 
     @Override
-    public PanelContent getPanel(Context context, String panelType, String packageName) {
+    public PanelContent getPanel(Context context, String panelType, String mediaPackageName) {
         switch (panelType) {
             case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
                 return InternetConnectivityPanel.create(context);
@@ -33,7 +33,7 @@ public class PanelFeatureProviderImpl implements PanelFeatureProvider {
             case Settings.Panel.ACTION_NFC:
                 return NfcPanel.create(context);
             case ACTION_MEDIA_OUTPUT:
-                return MediaOutputPanel.create(context, packageName);
+                return MediaOutputPanel.create(context, mediaPackageName);
         }
 
         throw new IllegalStateException("No matching panel for: "  + panelType);
index db0bf0e..a222d36 100644 (file)
@@ -16,9 +16,9 @@
 
 package com.android.settings.panel;
 
-import android.content.Intent;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
 import android.os.Bundle;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,6 +35,8 @@ import androidx.recyclerview.widget.RecyclerView;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 
 public class PanelFragment extends Fragment {
 
@@ -45,15 +47,15 @@ public class PanelFragment extends Fragment {
     private Button mDoneButton;
     private RecyclerView mPanelSlices;
 
+    private PanelContent mPanel;
+    private final MetricsFeatureProvider mMetricsProvider;
+
     @VisibleForTesting
     PanelSlicesAdapter mAdapter;
 
-    private View.OnClickListener mDoneButtonListener = (v) -> {
-        Log.d(TAG, "Closing dialog");
-        getActivity().finish();
-    };
-
     public PanelFragment() {
+        final Context context = getActivity();
+        mMetricsProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
     }
 
     @Nullable
@@ -69,38 +71,70 @@ public class PanelFragment extends Fragment {
         mTitleView = view.findViewById(R.id.panel_title);
 
         final Bundle arguments = getArguments();
-        final String panelType = arguments.getString(SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT);
-        final String packageName =
-                arguments.getString(SettingsPanelActivity.KEY_PANEL_PACKAGE_NAME);
-
-        final PanelContent panel = FeatureFactory.getFactory(activity)
+        final String panelType =
+                arguments.getString(SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT);
+        final String callingPackageName =
+                arguments.getString(SettingsPanelActivity.KEY_CALLING_PACKAGE_NAME);
+        final String mediaPackageName =
+                arguments.getString(SettingsPanelActivity.KEY_MEDIA_PACKAGE_NAME);
+
+        // TODO (b/124399577) transform interface to take a context and bundle.
+        mPanel = FeatureFactory.getFactory(activity)
                 .getPanelFeatureProvider()
-                .getPanel(activity, panelType, packageName);
+                .getPanel(activity, panelType, mediaPackageName);
 
-        mAdapter = new PanelSlicesAdapter(this, panel.getSlices());
+        // Log panel opened.
+        mMetricsProvider.action(
+                0 /* attribution */,
+                SettingsEnums.PAGE_VISIBLE /* opened panel - Action */,
+                mPanel.getMetricsCategory(),
+                callingPackageName,
+                0 /* value */);
+
+        mAdapter = new PanelSlicesAdapter(this, mPanel);
 
         mPanelSlices.setHasFixedSize(true);
         mPanelSlices.setLayoutManager(new LinearLayoutManager((activity)));
         mPanelSlices.setAdapter(mAdapter);
 
-        mTitleView.setText(panel.getTitle());
+        mTitleView.setText(mPanel.getTitle());
 
-        mSeeMoreButton.setOnClickListener(getSeeMoreListener(panel.getSeeMoreIntent()));
-        mDoneButton.setOnClickListener(mDoneButtonListener);
+        mSeeMoreButton.setOnClickListener(getSeeMoreListener());
+        mDoneButton.setOnClickListener(getCloseListener());
 
         //If getSeeMoreIntent() is null, hide the mSeeMoreButton.
-        if (panel.getSeeMoreIntent() == null) {
+        if (mPanel.getSeeMoreIntent() == null) {
             mSeeMoreButton.setVisibility(View.GONE);
         }
 
         return view;
     }
 
-    private View.OnClickListener getSeeMoreListener(final Intent intent) {
+    @VisibleForTesting
+    View.OnClickListener getSeeMoreListener() {
         return (v) -> {
+            mMetricsProvider.action(
+                    0 /* attribution */,
+                    SettingsEnums.PAGE_HIDE ,
+                    mPanel.getMetricsCategory(),
+                    PanelClosedKeys.KEY_SEE_MORE,
+                    0 /* value */);
             final FragmentActivity activity = getActivity();
-            activity.startActivity(intent);
+            activity.startActivityForResult(mPanel.getSeeMoreIntent(), 0);
             activity.finish();
         };
     }
+
+    @VisibleForTesting
+    View.OnClickListener getCloseListener() {
+        return (v) -> {
+            mMetricsProvider.action(
+                    0 /* attribution */,
+                    SettingsEnums.PAGE_HIDE,
+                    mPanel.getMetricsCategory(),
+                    PanelClosedKeys.KEY_DONE,
+                    0 /* value */);
+            getActivity().finish();
+        };
+    }
 }
diff --git a/src/com/android/settings/panel/PanelLoggingContract.java b/src/com/android/settings/panel/PanelLoggingContract.java
new file mode 100644 (file)
index 0000000..e149186
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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.settings.panel;
+
+/**
+ * Simple contract class to track keys in Panel logging.
+ *
+ * <p>
+ *    Constants should only be removed if underlying panel, or use case is removed.
+ * </p>
+ */
+public class PanelLoggingContract {
+
+    /**
+     * Keys tracking different ways users exit Panels.
+     */
+    interface PanelClosedKeys {
+        /**
+         * The user clicked the See More button linking deeper into Settings.
+         */
+        String KEY_SEE_MORE = "see_more";
+
+        /**
+         * The user clicked the Done button, closing the Panel.
+         */
+        String KEY_DONE = "done";
+
+        /**
+         * The user clicked outside the dialog, closing the Panel.
+         */
+        String KEY_CLICKED_OUT = "clicked_out";
+    }
+}
index f688512..c1cb049 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.settings.panel;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.net.Uri;
 import android.view.LayoutInflater;
@@ -31,8 +32,8 @@ import androidx.slice.widget.SliceLiveData;
 import androidx.slice.widget.SliceView;
 
 import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -43,10 +44,12 @@ public class PanelSlicesAdapter
 
     private final List<Uri> mSliceUris;
     private final PanelFragment mPanelFragment;
+    private final PanelContent mPanelContent;
 
-    public PanelSlicesAdapter(PanelFragment fragment, List<Uri> sliceUris) {
+    public PanelSlicesAdapter(PanelFragment fragment, PanelContent panel) {
         mPanelFragment = fragment;
-        mSliceUris = new ArrayList<>(sliceUris);
+        mSliceUris = panel.getSlices();
+        mPanelContent = panel;
     }
 
     @NonNull
@@ -56,7 +59,7 @@ public class PanelSlicesAdapter
         final LayoutInflater inflater = LayoutInflater.from(context);
         final View view = inflater.inflate(R.layout.panel_slice_row, viewGroup, false);
 
-        return new SliceRowViewHolder(view);
+        return new SliceRowViewHolder(view, mPanelContent);
     }
 
     @Override
@@ -79,22 +82,38 @@ public class PanelSlicesAdapter
      */
     public static class SliceRowViewHolder extends RecyclerView.ViewHolder {
 
+        private final PanelContent mPanelContent;
+
         @VisibleForTesting
         LiveData<Slice> sliceLiveData;
 
         @VisibleForTesting
         final SliceView sliceView;
 
-        public SliceRowViewHolder(View view) {
+        public SliceRowViewHolder(View view, PanelContent panelContent) {
             super(view);
             sliceView = view.findViewById(R.id.slice_view);
             sliceView.setMode(SliceView.MODE_LARGE);
+            mPanelContent = panelContent;
         }
 
         public void onBind(PanelFragment fragment, Uri sliceUri) {
             final Context context = sliceView.getContext();
             sliceLiveData = SliceLiveData.fromUri(context, sliceUri);
             sliceLiveData.observe(fragment.getViewLifecycleOwner(), sliceView);
+
+            // Log Panel interaction
+            sliceView.setOnSliceActionListener(
+                    ((eventInfo, sliceItem) -> {
+                        FeatureFactory.getFactory(context)
+                                .getMetricsFeatureProvider()
+                                .action(0 /* attribution */,
+                                        SettingsEnums.ACTION_PANEL_INTERACTION,
+                                        mPanelContent.getMetricsCategory(),
+                                        sliceUri.toString() /* log key */,
+                                        eventInfo.actionType /* value */);
+                    })
+            );
         }
     }
 }
index 4cf535e..3819c80 100644 (file)
@@ -19,11 +19,13 @@ package com.android.settings.panel;
 import static com.android.settingslib.media.MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT;
 import static com.android.settingslib.media.MediaOutputSliceConstants.EXTRA_PACKAGE_NAME;
 
+import android.app.settings.SettingsEnums;
 import android.content.Intent;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Gravity;
+import android.view.MotionEvent;
 import android.view.Window;
 import android.view.WindowManager;
 
@@ -34,6 +36,8 @@ import androidx.fragment.app.FragmentManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.R;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.panel.PanelLoggingContract.PanelClosedKeys;
 
 /**
  * Dialog Activity to host Settings Slices.
@@ -49,7 +53,16 @@ public class SettingsPanelActivity extends FragmentActivity {
      * Key specifying which Panel the app is requesting.
      */
     public static final String KEY_PANEL_TYPE_ARGUMENT = "PANEL_TYPE_ARGUMENT";
-    public static final String KEY_PANEL_PACKAGE_NAME = "PANEL_PACKAGE_NAME";
+
+    /**
+     * Key specifying the package which requested the Panel.
+     */
+    public static final String KEY_CALLING_PACKAGE_NAME = "PANEL_CALLING_PACKAGE_NAME";
+
+    /**
+     * Key specifying the package name for which the
+     */
+    public static final String KEY_MEDIA_PACKAGE_NAME = "PANEL_MEDIA_PACKAGE_NAME";
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -62,12 +75,12 @@ public class SettingsPanelActivity extends FragmentActivity {
             return;
         }
 
-        final String packageName =
+        final String mediaPackageName =
                 callingIntent.getStringExtra(EXTRA_PACKAGE_NAME);
 
         if (TextUtils.equals(ACTION_MEDIA_OUTPUT, callingIntent.getAction())
-                && TextUtils.isEmpty(packageName)) {
-            Log.e(TAG, "Null package name, closing Panel Activity");
+                && TextUtils.isEmpty(mediaPackageName)) {
+            Log.e(TAG, "Missing EXTRA_PACKAGE_NAME, closing Panel Activity");
             finish();
             return;
         }
@@ -81,7 +94,8 @@ public class SettingsPanelActivity extends FragmentActivity {
                 WindowManager.LayoutParams.WRAP_CONTENT);
 
         mBundle.putString(KEY_PANEL_TYPE_ARGUMENT, callingIntent.getAction());
-        mBundle.putString(KEY_PANEL_PACKAGE_NAME, packageName);
+        mBundle.putString(KEY_CALLING_PACKAGE_NAME, getCallingPackage());
+        mBundle.putString(KEY_MEDIA_PACKAGE_NAME, mediaPackageName);
 
         final PanelFragment panelFragment = new PanelFragment();
         panelFragment.setArguments(mBundle);
@@ -92,4 +106,21 @@ public class SettingsPanelActivity extends FragmentActivity {
             fragmentManager.beginTransaction().add(R.id.main_content, panelFragment).commit();
         }
     }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+            final PanelContent panelContent = FeatureFactory.getFactory(this)
+                    .getPanelFeatureProvider()
+                    .getPanel(this, getIntent().getAction(), null /* Media Package Name */);
+            FeatureFactory.getFactory(this)
+                    .getMetricsFeatureProvider()
+                    .action(0 /* attribution */,
+                            SettingsEnums.PAGE_HIDE,
+                            panelContent.getMetricsCategory(),
+                            PanelClosedKeys.KEY_CLICKED_OUT,
+                            0 /* value */);
+        }
+        return super.onTouchEvent(event);
+    }
 }
index 352fed5..20c2272 100644 (file)
@@ -21,6 +21,7 @@ import static com.android.settings.slices.CustomSliceRegistry.VOLUME_CALL_URI;
 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_MEDIA_URI;
 import static com.android.settings.slices.CustomSliceRegistry.VOLUME_RINGER_URI;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -62,4 +63,9 @@ public class VolumePanel implements PanelContent {
     public Intent getSeeMoreIntent() {
         return new Intent(Settings.ACTION_SOUND_SETTINGS);
     }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.PANEL_VOLUME;
+    }
 }
\ No newline at end of file
index 1d79952..8f54115 100644 (file)
@@ -18,6 +18,7 @@ package com.android.settings.panel;
 
 import static com.android.settings.slices.CustomSliceRegistry.WIFI_SLICE_URI;
 
+import android.app.settings.SettingsEnums;
 import android.content.Intent;
 import android.net.Uri;
 
@@ -53,4 +54,9 @@ public class FakePanelContent implements PanelContent {
     public Intent getSeeMoreIntent() {
         return INTENT;
     }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.TESTING;
+    }
 }
index 389c31e..be8d8bc 100644 (file)
@@ -22,10 +22,13 @@ import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
+import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.os.Bundle;
 import android.view.LayoutInflater;
+import android.view.View;
 import android.widget.LinearLayout;
 
 import com.android.settings.R;
@@ -71,13 +74,14 @@ public class PanelFragmentTest {
                                 .get()
                                 .getSupportFragmentManager()
                                 .findFragmentById(R.id.main_content));
-    }
 
-    @Test
-    public void onCreateView_adapterGetsDataset() {
         final Bundle bundle = new Bundle();
         bundle.putString(SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT, FAKE_EXTRA);
         doReturn(bundle).when(mPanelFragment).getArguments();
+    }
+
+    @Test
+    public void onCreateView_adapterGetsDataset() {
         mPanelFragment.onCreateView(LayoutInflater.from(mContext),
                 new LinearLayout(mContext), null);
         PanelSlicesAdapter adapter = mPanelFragment.mAdapter;
@@ -85,4 +89,43 @@ public class PanelFragmentTest {
         assertThat(adapter.getData()).containsAllIn(mFakePanelContent.getSlices());
     }
 
+    @Test
+    public void onCreate_logsOpenEvent() {
+        verify(mFakeFeatureFactory.metricsFeatureProvider).action(
+                0,
+                SettingsEnums.PAGE_VISIBLE,
+                mFakePanelContent.getMetricsCategory(),
+                null,
+                0);
+    }
+
+    @Test
+    public void panelSeeMoreClick_logsCloseEvent() {
+        final View.OnClickListener listener = mPanelFragment.getSeeMoreListener();
+
+        listener.onClick(null);
+
+        verify(mFakeFeatureFactory.metricsFeatureProvider).action(
+                0,
+                SettingsEnums.PAGE_HIDE,
+                mFakePanelContent.getMetricsCategory(),
+                PanelLoggingContract.PanelClosedKeys.KEY_SEE_MORE,
+                0
+        );
+    }
+
+    @Test
+    public void panelDoneClick_logsCloseEvent() {
+        final View.OnClickListener listener = mPanelFragment.getCloseListener();
+
+        listener.onClick(null);
+
+        verify(mFakeFeatureFactory.metricsFeatureProvider).action(
+                0,
+                SettingsEnums.PAGE_HIDE,
+                mFakePanelContent.getMetricsCategory(),
+                PanelLoggingContract.PanelClosedKeys.KEY_DONE,
+                0
+        );
+    }
 }
\ No newline at end of file
index abefa67..4dce15e 100644 (file)
@@ -71,7 +71,7 @@ public class PanelSlicesAdapterTest {
                                 .getSupportFragmentManager()
                                 .findFragmentById(R.id.main_content));
 
-        mAdapter = new PanelSlicesAdapter(mPanelFragment, mFakePanelContent.getSlices());
+        mAdapter = new PanelSlicesAdapter(mPanelFragment, mFakePanelContent);
     }
 
     @Test
index 359cf5d..a51b7b0 100644 (file)
 
 package com.android.settings.panel;
 
-import static com.android.settings.panel.SettingsPanelActivity.KEY_PANEL_PACKAGE_NAME;
+import static com.android.settings.panel.SettingsPanelActivity.KEY_MEDIA_PACKAGE_NAME;
 import static com.android.settings.panel.SettingsPanelActivity.KEY_PANEL_TYPE_ARGUMENT;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.settings.SettingsEnums;
 import android.content.Intent;
+import android.view.MotionEvent;
+
+import com.android.settings.testutils.FakeFeatureFactory;
 
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
@@ -31,6 +43,22 @@ import org.robolectric.RobolectricTestRunner;
 @RunWith(RobolectricTestRunner.class)
 public class SettingsPanelActivityTest {
 
+    private FakeFeatureFactory mFakeFeatureFactory;
+    private FakeSettingsPanelActivity mSettingsPanelActivity;
+    private PanelFeatureProvider mPanelFeatureProvider;
+    private FakePanelContent mFakePanelContent;
+
+    @Before
+    public void setUp() {
+        mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
+        mSettingsPanelActivity = Robolectric.buildActivity(FakeSettingsPanelActivity.class)
+                .create().get();
+        mPanelFeatureProvider = spy(new PanelFeatureProviderImpl());
+        mFakeFeatureFactory.panelFeatureProvider = mPanelFeatureProvider;
+        mFakePanelContent = new FakePanelContent();
+        doReturn(mFakePanelContent).when(mPanelFeatureProvider).getPanel(any(), any(), any());
+    }
+
     @Test
     public void startMediaOutputSlice_withPackageName_bundleShouldHaveValue() {
         final Intent intent = new Intent()
@@ -41,7 +69,7 @@ public class SettingsPanelActivityTest {
         final SettingsPanelActivity activity =
                 Robolectric.buildActivity(SettingsPanelActivity.class, intent).create().get();
 
-        assertThat(activity.mBundle.getString(KEY_PANEL_PACKAGE_NAME))
+        assertThat(activity.mBundle.getString(KEY_MEDIA_PACKAGE_NAME))
                 .isEqualTo("com.google.android.music");
         assertThat(activity.mBundle.getString(KEY_PANEL_TYPE_ARGUMENT))
                 .isEqualTo("com.android.settings.panel.action.MEDIA_OUTPUT");
@@ -55,7 +83,23 @@ public class SettingsPanelActivityTest {
         final SettingsPanelActivity activity =
                 Robolectric.buildActivity(SettingsPanelActivity.class, intent).create().get();
 
-        assertThat(activity.mBundle.containsKey(KEY_PANEL_PACKAGE_NAME)).isFalse();
+        assertThat(activity.mBundle.containsKey(KEY_MEDIA_PACKAGE_NAME)).isFalse();
         assertThat(activity.mBundle.containsKey(KEY_PANEL_TYPE_ARGUMENT)).isFalse();
     }
+
+    @Test
+    public void onTouchEvent_outsideAction_logsPanelClosed() {
+        final MotionEvent event = mock(MotionEvent.class);
+        when(event.getAction()).thenReturn(MotionEvent.ACTION_OUTSIDE);
+
+        mSettingsPanelActivity.onTouchEvent(event);
+
+        verify(mFakeFeatureFactory.metricsFeatureProvider).action(
+                0,
+                SettingsEnums.PAGE_HIDE,
+                SettingsEnums.TESTING,
+                PanelLoggingContract.PanelClosedKeys.KEY_CLICKED_OUT,
+                0
+        );
+    }
 }