Add new QS Ui EVents

This CL adds events for:
* QS interactions
* StatusBarState changes
* User Switcher interaction
* DND panel interaction

The new QS events that refer to particular tiles attach the following:
* spec (if framework tile) or package (if CustomTile)
* instanceId associated with the QSTile object on creation

Test: atest SystemUITests (including new tests)
Test: manual using statsd_testdrive
Fixes: 147508235
Change-Id: I43d8fe1fdb2aec1f16032da61a599ebc29809afc
This commit is contained in:
Fabian Kozynski
2020-04-24 12:00:49 -04:00
parent 8e47f58e6b
commit 2ff6df973b
36 changed files with 650 additions and 86 deletions

View File

@@ -19,6 +19,7 @@ import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@ProvidesInterface(version = DetailAdapter.VERSION)
@@ -44,4 +45,18 @@ public interface DetailAdapter {
default boolean hasHeader() {
return true;
}
default UiEventLogger.UiEventEnum openDetailEvent() {
return INVALID;
}
default UiEventLogger.UiEventEnum closeDetailEvent() {
return INVALID;
}
default UiEventLogger.UiEventEnum moreSettingsEvent() {
return INVALID;
}
UiEventLogger.UiEventEnum INVALID = () -> 0;
}

View File

@@ -20,6 +20,7 @@ import android.graphics.drawable.Drawable;
import android.metrics.LogMaker;
import android.service.quicksettings.Tile;
import com.android.internal.logging.InstanceId;
import com.android.systemui.plugins.annotations.DependsOn;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.qs.QSTile.Callback;
@@ -81,6 +82,18 @@ public interface QSTile {
return logMaker;
}
/**
* Return a string to be used to identify the tile in UiEvents.
*/
default String getMetricsSpec() {
return getClass().getSimpleName();
}
/**
* Return an {@link InstanceId} to be used to identify the tile in UiEvents.
*/
InstanceId getInstanceId();
@ProvidesInterface(version = Callback.VERSION)
public interface Callback {
public static final int VERSION = 1;

View File

@@ -33,6 +33,8 @@ import android.view.LayoutInflater;
import android.view.WindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.ViewMediatorCallback;
@@ -218,4 +220,11 @@ public class DependencyProvider {
public Choreographer providesChoreographer() {
return Choreographer.getInstance();
}
/** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
@Singleton
@Provides
static UiEventLogger provideUiEventLogger() {
return new UiEventLoggerImpl();
}
}

View File

@@ -20,10 +20,14 @@ import android.content.Context
import android.content.res.Configuration
import android.view.View
import android.view.ViewGroup
import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.qs.TileLayout.exactly
class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTileLayout {
class DoubleLineTileLayout(
context: Context,
private val uiEventLogger: UiEventLogger
) : ViewGroup(context), QSPanel.QSTileLayout {
companion object {
private const val NUM_LINES = 2
@@ -86,6 +90,13 @@ class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTil
for (record in mRecords) {
record.tile.setListening(this, listening)
}
if (listening) {
for (i in 0 until numVisibleTiles) {
val tile = mRecords[i].tile
uiEventLogger.logWithInstanceId(
QSEvent.QQS_TILE_VISIBLE, 0, tile.metricsSpec, tile.instanceId)
}
}
}
override fun getNumVisibleTiles() = tilesToShow

View File

@@ -22,7 +22,9 @@ import android.widget.Scroller;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSPanel.QSTileLayout;
import com.android.systemui.qs.QSPanel.TileRecord;
@@ -63,7 +65,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
private int mLayoutDirection;
private int mHorizontalClipBound;
private final Rect mClippingRect;
private int mLastMaxHeight = -1;
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -75,6 +77,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
mLayoutDirection = getLayoutDirection();
mClippingRect = new Rect();
}
private int mLastMaxHeight = -1;
public void saveInstanceState(Bundle outState) {
outState.putInt(CURRENT_PAGE, getCurrentItem());
@@ -126,6 +129,15 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
return page;
}
// This will dump to the ui log all the tiles that are visible in this page
private void logVisibleTiles(TilePage page) {
for (int i = 0; i < page.mRecords.size(); i++) {
QSTile t = page.mRecords.get(i).tile;
mUiEventLogger.logWithInstanceId(QSEvent.QS_TILE_VISIBLE, 0, t.getMetricsSpec(),
t.getInstanceId());
}
}
@Override
public void setListening(boolean listening) {
if (mListening == listening) return;
@@ -218,7 +230,11 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
int currentItem = getCurrentPageNumber();
for (int i = 0; i < mPages.size(); i++) {
mPages.get(i).setSelected(i == currentItem ? selected : false);
TilePage page = mPages.get(i);
page.setSelected(i == currentItem ? selected : false);
if (page.isSelected()) {
logVisibleTiles(page);
}
}
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
@@ -419,6 +435,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
: position == 0);
}
}
@Override

View File

@@ -39,6 +39,7 @@ import android.widget.Switch;
import android.widget.TextView;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
@@ -53,6 +54,7 @@ public class QSDetail extends LinearLayout {
private static final long FADE_DURATION = 300;
private final SparseArray<View> mDetailViews = new SparseArray<>();
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
private ViewGroup mDetailContent;
protected TextView mDetailSettingsButton;
@@ -205,6 +207,7 @@ public class QSDetail extends LinearLayout {
mDetailContent.addView(detailView);
mDetailViews.put(viewCacheIndex, detailView);
Dependency.get(MetricsLogger.class).visible(adapter.getMetricsCategory());
mUiEventLogger.log(adapter.openDetailEvent());
announceForAccessibility(mContext.getString(
R.string.accessibility_quick_settings_detail,
adapter.getTitle()));
@@ -214,6 +217,7 @@ public class QSDetail extends LinearLayout {
} else {
if (mDetailAdapter != null) {
Dependency.get(MetricsLogger.class).hidden(mDetailAdapter.getMetricsCategory());
mUiEventLogger.log(mDetailAdapter.closeDetailEvent());
}
mClosingDetail = true;
mDetailAdapter = null;
@@ -249,6 +253,7 @@ public class QSDetail extends LinearLayout {
mDetailSettingsButton.setOnClickListener(v -> {
Dependency.get(MetricsLogger.class).action(ACTION_QS_MORE_SETTINGS,
adapter.getMetricsCategory());
mUiEventLogger.log(adapter.moreSettingsEvent());
Dependency.get(ActivityStarter.class)
.postStartActivityDismissingKeyguard(settingsIntent, 0);
});

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2020 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 com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.UiEventLoggerImpl
import com.android.internal.logging.testing.UiEventLoggerFake
object QSEvents {
var qsUiEventsLogger: UiEventLogger = UiEventLoggerImpl()
private set
fun setLoggerForTesting(): UiEventLoggerFake {
return UiEventLoggerFake().also {
qsUiEventsLogger = it
}
}
fun resetLogger() {
qsUiEventsLogger = UiEventLoggerImpl()
}
}
enum class QSEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "Tile clicked. It has an instance id and a spec (or packageName)")
QS_ACTION_CLICK(387),
@UiEvent(doc = "Tile secondary button clicked. " +
"It has an instance id and a spec (or packageName)")
QS_ACTION_SECONDARY_CLICK(388),
@UiEvent(doc = "Tile long clicked. It has an instance id and a spec (or packageName)")
QS_ACTION_LONG_PRESS(389),
@UiEvent(doc = "Quick Settings panel expanded")
QS_PANEL_EXPANDED(390),
@UiEvent(doc = "Quick Settings panel collapsed")
QS_PANEL_COLLAPSED(391),
@UiEvent(doc = "Tile visible in Quick Settings panel. The tile may be in a different page. " +
"It has an instance id and a spec (or packageName)")
QS_TILE_VISIBLE(392),
@UiEvent(doc = "Quick Quick Settings panel expanded")
QQS_PANEL_EXPANDED(393),
@UiEvent(doc = "Quick Quick Settings panel collapsed")
QQS_PANEL_COLLAPSED(394),
@UiEvent(doc = "Tile visible in Quick Quick Settings panel. " +
"It has an instance id and a spec (or packageName)")
QQS_TILE_VISIBLE(395);
override fun getId() = _id
}
enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "Tile removed from current tiles")
QS_EDIT_REMOVE(210),
@UiEvent(doc = "Tile added to current tiles")
QS_EDIT_ADD(211),
@UiEvent(doc = "Tile moved")
QS_EDIT_MOVE(212),
@UiEvent(doc = "QS customizer open")
QS_EDIT_OPEN(213),
@UiEvent(doc = "QS customizer closed")
QS_EDIT_CLOSED(214),
@UiEvent(doc = "QS tiles reset")
QS_EDIT_RESET(215);
override fun getId() = _id
}
enum class QSDndEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "TODO(beverlyt)")
QS_DND_CONDITION_SELECT(420),
@UiEvent(doc = "TODO(beverlyt)")
QS_DND_TIME_UP(422),
@UiEvent(doc = "TODO(beverlyt)")
QS_DND_TIME_DOWN(423);
override fun getId() = _id
}
enum class QSUserSwitcherEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "The current user has been switched in the detail panel")
QS_USER_SWITCH(424),
@UiEvent(doc = "User switcher QS detail panel open")
QS_USER_DETAIL_OPEN(425),
@UiEvent(doc = "User switcher QS detail panel closed")
QS_USER_DETAIL_CLOSE(426),
@UiEvent(doc = "User switcher QS detail panel more settings pressed")
QS_USER_MORE_SETTINGS(427);
override fun getId() = _id
}

View File

@@ -16,6 +16,8 @@ package com.android.systemui.qs;
import android.content.Context;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
@@ -30,6 +32,7 @@ public interface QSHost {
Context getContext();
Context getUserContext();
QSLogger getQSLogger();
UiEventLogger getUiEventLogger();
Collection<QSTile> getTiles();
void addCallback(Callback callback);
void removeCallback(Callback callback);
@@ -39,6 +42,8 @@ public interface QSHost {
int indexOf(String tileSpec);
InstanceId getNewInstanceId();
interface Callback {
void onTilesChanged();
}

View File

@@ -52,6 +52,7 @@ import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.settingslib.Utils;
@@ -129,6 +130,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private BrightnessController mBrightnessController;
private final DumpManager mDumpManager;
private final QSLogger mQSLogger;
protected final UiEventLogger mUiEventLogger;
protected QSTileHost mHost;
protected QSSecurityFooter mFooter;
@@ -176,7 +178,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
@Background DelayableExecutor backgroundExecutor,
@Nullable LocalBluetoothManager localBluetoothManager,
ActivityStarter activityStarter,
NotificationEntryManager entryManager
NotificationEntryManager entryManager,
UiEventLogger uiEventLogger
) {
super(context, attrs);
mContext = context;
@@ -188,6 +191,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mBroadcastDispatcher = broadcastDispatcher;
mActivityStarter = activityStarter;
mNotificationEntryManager = entryManager;
mUiEventLogger = uiEventLogger;
setOrientation(VERTICAL);
@@ -678,8 +682,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
mMetricsLogger.visibility(MetricsEvent.QS_PANEL, mExpanded);
if (!mExpanded) {
mUiEventLogger.log(closePanelEvent());
closeDetail();
} else {
mUiEventLogger.log(openPanelEvent());
logTiles();
}
}
@@ -786,6 +792,18 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
return mHost.createTileView(tile, collapsedView);
}
protected QSEvent openPanelEvent() {
return QSEvent.QS_PANEL_EXPANDED;
}
protected QSEvent closePanelEvent() {
return QSEvent.QS_PANEL_COLLAPSED;
}
protected QSEvent tileVisibleEvent() {
return QSEvent.QS_TILE_VISIBLE;
}
protected boolean shouldShowDetail() {
return mExpanded;
}

View File

@@ -31,6 +31,9 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -73,6 +76,7 @@ import javax.inject.Singleton;
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MAX_QS_INSTANCE_ID = 1 << 20;
public static final String TILES_SETTING = Secure.QS_TILES;
@@ -85,6 +89,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
private final DumpManager mDumpManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final QSLogger mQSLogger;
private final UiEventLogger mUiEventLogger;
private final InstanceIdSequence mInstanceIdSequence;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -106,7 +112,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher,
Optional<StatusBar> statusBarOptional,
QSLogger qsLogger) {
QSLogger qsLogger,
UiEventLogger uiEventLogger) {
mIconController = iconController;
mContext = context;
mUserContext = context;
@@ -114,8 +121,10 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
mPluginManager = pluginManager;
mDumpManager = dumpManager;
mQSLogger = qsLogger;
mUiEventLogger = uiEventLogger;
mBroadcastDispatcher = broadcastDispatcher;
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
mStatusBarOptional = statusBarOptional;
@@ -137,6 +146,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
return mIconController;
}
@Override
public InstanceId getNewInstanceId() {
return mInstanceIdSequence.newInstanceId();
}
public void destroy() {
mTiles.values().forEach(tile -> tile.destroy());
mAutoTiles.destroy();
@@ -169,6 +183,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
return mQSLogger;
}
@Override
public UiEventLogger getUiEventLogger() {
return mUiEventLogger;
}
@Override
public void addCallback(Callback callback) {
mCallbacks.add(callback);

View File

@@ -27,6 +27,7 @@ import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -88,11 +89,12 @@ public class QuickQSPanel extends QSPanel {
@Background DelayableExecutor backgroundExecutor,
@Nullable LocalBluetoothManager localBluetoothManager,
ActivityStarter activityStarter,
NotificationEntryManager entryManager
NotificationEntryManager entryManager,
UiEventLogger uiEventLogger
) {
super(context, attrs, dumpManager, broadcastDispatcher, qsLogger,
foregroundExecutor, backgroundExecutor, localBluetoothManager, activityStarter,
entryManager);
entryManager, uiEventLogger);
if (mFooter != null) {
removeView(mFooter.getView());
}
@@ -118,9 +120,9 @@ public class QuickQSPanel extends QSPanel {
lp2.setMarginStart(0);
mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp2);
mTileLayout = new DoubleLineTileLayout(context);
mTileLayout = new DoubleLineTileLayout(context, mUiEventLogger);
mMediaTileLayout = mTileLayout;
mRegularTileLayout = new HeaderTileLayout(context);
mRegularTileLayout = new HeaderTileLayout(context, mUiEventLogger);
LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
lp.setMarginEnd(0);
lp.setMarginStart(marginSize);
@@ -135,7 +137,7 @@ public class QuickQSPanel extends QSPanel {
super.setPadding(0, 0, 0, 0);
} else {
sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
mTileLayout = new HeaderTileLayout(context);
mTileLayout = new HeaderTileLayout(context, mUiEventLogger);
mTileLayout.setListening(mListening);
addView((View) mTileLayout, 0 /* Between brightness and footer */);
super.setPadding(0, 0, 0, 0);
@@ -312,13 +314,30 @@ public class QuickQSPanel extends QSPanel {
super.setVisibility(visibility);
}
@Override
protected QSEvent openPanelEvent() {
return QSEvent.QQS_PANEL_EXPANDED;
}
@Override
protected QSEvent closePanelEvent() {
return QSEvent.QQS_PANEL_COLLAPSED;
}
@Override
protected QSEvent tileVisibleEvent() {
return QSEvent.QQS_TILE_VISIBLE;
}
private static class HeaderTileLayout extends TileLayout {
private boolean mListening;
private final UiEventLogger mUiEventLogger;
private Rect mClippingBounds = new Rect();
public HeaderTileLayout(Context context) {
public HeaderTileLayout(Context context, UiEventLogger uiEventLogger) {
super(context);
mUiEventLogger = uiEventLogger;
setClipChildren(false);
setClipToPadding(false);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
@@ -443,5 +462,18 @@ public class QuickQSPanel extends QSPanel {
}
return getPaddingStart() + column * (mCellWidth + mCellMarginHorizontal);
}
@Override
public void setListening(boolean listening) {
boolean startedListening = !mListening && listening;
super.setListening(listening);
if (startedListening) {
for (int i = 0; i < getNumVisibleTiles(); i++) {
QSTile tile = mRecords.get(i).tile;
mUiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0,
tile.getMetricsSpec(), tile.getInstanceId());
}
}
}
}
}

View File

@@ -31,7 +31,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
private int mCellMarginTop;
private boolean mListening;
protected boolean mListening;
protected int mMaxAllowedRows = 3;
// Prototyping with less rows

View File

@@ -44,6 +44,7 @@ import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
@@ -93,7 +94,8 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
LightBarController lightBarController,
KeyguardStateController keyguardStateController,
ScreenLifecycle screenLifecycle,
TileQueryHelper tileQueryHelper) {
TileQueryHelper tileQueryHelper,
UiEventLogger uiEventLogger) {
super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
@@ -115,7 +117,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mToolbar.setTitle(R.string.qs_edit);
mRecyclerView = findViewById(android.R.id.list);
mTransparentView = findViewById(R.id.customizer_transparent_view);
mTileAdapter = new TileAdapter(getContext());
mTileAdapter = new TileAdapter(getContext(), uiEventLogger);
mTileQueryHelper = tileQueryHelper;
mTileQueryHelper.setListener(mTileAdapter);
mRecyclerView.setAdapter(mTileAdapter);

View File

@@ -1,38 +0,0 @@
/*
* Copyright (C) 2020 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.customize
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
enum class QSEditEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
@UiEvent(doc = "Tile removed from current tiles")
QS_EDIT_REMOVE(210),
@UiEvent(doc = "Tile added to current tiles")
QS_EDIT_ADD(211),
@UiEvent(doc = "Tile moved")
QS_EDIT_MOVE(212),
@UiEvent(doc = "QS customizer open")
QS_EDIT_OPEN(213),
@UiEvent(doc = "QS customizer closed")
QS_EDIT_CLOSED(214),
@UiEvent(doc = "QS tiles reset")
QS_EDIT_RESET(215);
override fun getId() = _id
}

View File

@@ -41,8 +41,8 @@ import androidx.recyclerview.widget.RecyclerView.State;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.customize.TileAdapter.Holder;
import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
@@ -92,10 +92,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
private int mAccessibilityFromIndex;
private CharSequence mAccessibilityFromLabel;
private QSTileHost mHost;
private UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
private final UiEventLogger mUiEventLogger;
public TileAdapter(Context context) {
public TileAdapter(Context context, UiEventLogger uiEventLogger) {
mContext = context;
mUiEventLogger = uiEventLogger;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mItemTouchHelper = new ItemTouchHelper(mCallbacks);
mDecoration = new TileItemDecoration(context);

View File

@@ -371,6 +371,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
return MetricsEvent.QS_CUSTOM;
}
@Override
public final String getMetricsSpec() {
return mComponent.getPackageName();
}
public void startUnlockAndRun() {
Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
try {

View File

@@ -48,7 +48,9 @@ import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
@@ -62,6 +64,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.State;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.qs.logging.QSLogger;
@@ -97,12 +100,14 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
private final StatusBarStateController
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
private final Object mStaleListener = new Object();
protected TState mState;
private TState mTmpState;
private final InstanceId mInstanceId;
private boolean mAnnounceNextStateChange;
private String mTileSpec;
@@ -156,10 +161,12 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
protected QSTileImpl(QSHost host) {
mHost = host;
mContext = host.getContext();
mInstanceId = host.getNewInstanceId();
mState = newTileState();
mTmpState = newTileState();
mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
mQSLogger = host.getQSLogger();
mUiEventLogger = host.getUiEventLogger();
}
protected final void resetStates() {
@@ -173,6 +180,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
return mLifecycle;
}
@Override
public InstanceId getInstanceId() {
return mInstanceId;
}
/**
* Adds or removes a listening client for the tile. If the tile has one or more
* listening client it will go into the listening state.
@@ -247,6 +259,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
.addTaggedData(FIELD_STATUS_BAR_STATE,
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_CLICK, 0, getMetricsSpec(),
getInstanceId());
mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
mHandler.sendEmptyMessage(H.CLICK);
}
@@ -255,6 +269,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
.addTaggedData(FIELD_STATUS_BAR_STATE,
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_SECONDARY_CLICK, 0, getMetricsSpec(),
getInstanceId());
mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
mState.state);
mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
@@ -264,6 +280,8 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
.addTaggedData(FIELD_STATUS_BAR_STATE,
mStatusBarStateController.getState())));
mUiEventLogger.logWithInstanceId(QSEvent.QS_ACTION_LONG_PRESS, 0, getMetricsSpec(),
getInstanceId());
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
mHandler.sendEmptyMessage(H.LONG_CLICK);
@@ -483,6 +501,11 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
}
}
@Override
public String getMetricsSpec() {
return mTileSpec;
}
/**
* Provides a default label for the tile.
* @return default label for the tile.

View File

@@ -26,10 +26,12 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtils;
import com.android.systemui.R;
import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.statusbar.policy.UserSwitcherController;
/**
@@ -48,8 +50,9 @@ public class UserDetailView extends PseudoGridView {
R.layout.qs_user_detail, parent, attach);
}
public void createAndSetAdapter(UserSwitcherController controller) {
mAdapter = new Adapter(mContext, controller);
public void createAndSetAdapter(UserSwitcherController controller,
UiEventLogger uiEventLogger) {
mAdapter = new Adapter(mContext, controller, uiEventLogger);
ViewGroupAdapterBridge.link(this, mAdapter);
}
@@ -63,11 +66,14 @@ public class UserDetailView extends PseudoGridView {
private final Context mContext;
protected UserSwitcherController mController;
private View mCurrentUserView;
private final UiEventLogger mUiEventLogger;
public Adapter(Context context, UserSwitcherController controller) {
public Adapter(Context context, UserSwitcherController controller,
UiEventLogger uiEventLogger) {
super(controller);
mContext = context;
mController = controller;
mUiEventLogger = uiEventLogger;
}
@Override
@@ -127,6 +133,7 @@ public class UserDetailView extends PseudoGridView {
mController.startActivity(intent);
} else if (tag.isSwitchToEnabled) {
MetricsLogger.action(mContext, MetricsEvent.QS_SWITCH_USER);
mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_SWITCH);
if (!tag.isAddUser && !tag.isRestricted && !tag.isDisabledByAdmin) {
if (mCurrentUserView != null) {
mCurrentUserView.setActivated(false);

View File

@@ -24,6 +24,7 @@ import android.util.Log;
import android.view.animation.Interpolator;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
@@ -69,6 +70,7 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
};
private final ArrayList<RankedListener> mListeners = new ArrayList<>();
private final UiEventLogger mUiEventLogger;
private int mState;
private int mLastState;
private boolean mLeaveOpenOnKeyguardHide;
@@ -119,7 +121,8 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
private Interpolator mDozeInterpolator = Interpolators.FAST_OUT_SLOW_IN;
@Inject
public StatusBarStateControllerImpl() {
public StatusBarStateControllerImpl(UiEventLogger uiEventLogger) {
mUiEventLogger = uiEventLogger;
for (int i = 0; i < HISTORY_SIZE; i++) {
mHistoricalRecords[i] = new HistoricalState();
}
@@ -155,6 +158,7 @@ public class StatusBarStateControllerImpl implements SysuiStatusBarStateControll
}
mLastState = mState;
mState = state;
mUiEventLogger.log(StatusBarStateEvent.fromState(mState));
for (RankedListener rl : new ArrayList<>(mListeners)) {
rl.mListener.onStateChanged(mState);
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (C) 2020 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.statusbar;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
/**
* Events for changes in the {@link StatusBarState}.
*/
public enum StatusBarStateEvent implements UiEventLogger.UiEventEnum {
@UiEvent(doc = "StatusBarState changed to unknown state")
STATUS_BAR_STATE_UNKNOWN(428),
@UiEvent(doc = "StatusBarState changed to SHADE state")
STATUS_BAR_STATE_SHADE(429),
@UiEvent(doc = "StatusBarState changed to KEYGUARD state")
STATUS_BAR_STATE_KEYGUARD(430),
@UiEvent(doc = "StatusBarState changed to SHADE_LOCKED state")
STATUS_BAR_STATE_SHADE_LOCKED(431),
@UiEvent(doc = "StatusBarState changed to FULLSCREEN_USER_SWITCHER state")
STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER(432);
private int mId;
StatusBarStateEvent(int id) {
mId = id;
}
@Override
public int getId() {
return mId;
}
/**
* Return the event associated with the state.
*/
public static StatusBarStateEvent fromState(int state) {
switch(state) {
case StatusBarState.SHADE:
return STATUS_BAR_STATE_SHADE;
case StatusBarState.KEYGUARD:
return STATUS_BAR_STATE_KEYGUARD;
case StatusBarState.SHADE_LOCKED:
return STATUS_BAR_STATE_SHADE_LOCKED;
case StatusBarState.FULLSCREEN_USER_SWITCHER:
return STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER;
default:
return STATUS_BAR_STATE_UNKNOWN;
}
}
}

View File

@@ -24,8 +24,6 @@ import android.os.Handler;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -161,13 +159,6 @@ public interface NotificationsModule {
return new NotificationPanelLoggerImpl();
}
/** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */
@Singleton
@Provides
static UiEventLogger provideUiEventLogger() {
return new UiEventLoggerImpl();
}
/** Provides an instance of {@link NotificationBlockingHelperManager} */
@Singleton
@Provides

View File

@@ -48,6 +48,7 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.UserIcons;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -61,6 +62,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.statusbar.phone.SystemUIDialog;
@@ -108,13 +110,15 @@ public class UserSwitcherController implements Dumpable {
private int mSecondaryUser = UserHandle.USER_NULL;
private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
private final UiEventLogger mUiEventLogger;
@Inject
public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
@Main Handler handler, ActivityStarter activityStarter,
BroadcastDispatcher broadcastDispatcher) {
BroadcastDispatcher broadcastDispatcher, UiEventLogger uiEventLogger) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mUiEventLogger = uiEventLogger;
if (!UserManager.isGuestUserEphemeral()) {
mGuestResumeSessionReceiver.register(mBroadcastDispatcher);
}
@@ -801,7 +805,7 @@ public class UserSwitcherController implements Dumpable {
UserDetailView v;
if (!(convertView instanceof UserDetailView)) {
v = UserDetailView.inflate(context, parent, false);
v.createAndSetAdapter(UserSwitcherController.this);
v.createAndSetAdapter(UserSwitcherController.this, mUiEventLogger);
} else {
v = (UserDetailView) convertView;
}
@@ -827,6 +831,21 @@ public class UserSwitcherController implements Dumpable {
public int getMetricsCategory() {
return MetricsEvent.QS_USERDETAIL;
}
@Override
public UiEventLogger.UiEventEnum openDetailEvent() {
return QSUserSwitcherEvent.QS_USER_DETAIL_OPEN;
}
@Override
public UiEventLogger.UiEventEnum closeDetailEvent() {
return QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE;
}
@Override
public UiEventLogger.UiEventEnum moreSettingsEvent() {
return QSUserSwitcherEvent.QS_USER_MORE_SETTINGS;
}
};
private final KeyguardStateController.Callback mCallback =

View File

@@ -56,9 +56,12 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.qs.QSDndEvent;
import com.android.systemui.qs.QSEvents;
import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.FileDescriptor;
@@ -103,6 +106,7 @@ public class ZenModePanel extends FrameLayout {
private final TransitionHelper mTransitionHelper = new TransitionHelper();
private final Uri mForeverId;
private final ConfigurableTexts mConfigurableTexts;
private final UiEventLogger mUiEventLogger = QSEvents.INSTANCE.getQsUiEventsLogger();
private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
@@ -662,6 +666,7 @@ public class ZenModePanel extends FrameLayout {
tag.rb.setChecked(true);
if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId);
MetricsLogger.action(mContext, MetricsEvent.QS_DND_CONDITION_SELECT);
mUiEventLogger.log(QSDndEvent.QS_DND_CONDITION_SELECT);
select(tag.condition);
announceConditionSelection(tag);
}
@@ -767,6 +772,7 @@ public class ZenModePanel extends FrameLayout {
private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
MetricsLogger.action(mContext, MetricsEvent.QS_DND_TIME, up);
mUiEventLogger.log(up ? QSDndEvent.QS_DND_TIME_UP : QSDndEvent.QS_DND_TIME_DOWN);
Condition newCondition = null;
final int N = MINUTE_BUCKETS.length;
if (mBucketIndex == -1) {

View File

@@ -27,6 +27,7 @@ import android.util.DisplayMetrics;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
@@ -65,7 +66,8 @@ public class FalsingManagerProxyTest extends SysuiTestCase {
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private DockManager mDockManager = new DockManagerFake();
private StatusBarStateController mStatusBarStateController = new StatusBarStateControllerImpl();
private StatusBarStateController mStatusBarStateController =
new StatusBarStateControllerImpl(new UiEventLoggerFake());
@Before
public void setup() {

View File

@@ -25,6 +25,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.DisplayMetrics;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
@@ -67,7 +68,7 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase {
FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
DockManager dockManager = new DockManagerFake();
mStatusBarStateController = new StatusBarStateControllerImpl();
mStatusBarStateController = new StatusBarStateControllerImpl(new UiEventLoggerFake());
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager,

View File

@@ -16,6 +16,7 @@ package com.android.systemui.qs;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_QS_MORE_SETTINGS;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
@@ -33,11 +34,13 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
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;
@@ -54,10 +57,13 @@ public class QSDetailTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
private DetailAdapter mMockDetailAdapter;
private TestableLooper mTestableLooper;
private UiEventLoggerFake mUiEventLogger;
@Before
public void setup() throws Exception {
mTestableLooper = TestableLooper.get(this);
mUiEventLogger = QSEvents.INSTANCE.setLoggerForTesting();
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class);
@@ -70,6 +76,19 @@ public class QSDetailTest extends SysuiTestCase {
when(mMockDetailAdapter.createDetailView(any(), any(), any()))
.thenReturn(mock(View.class));
});
// Only detail in use is the user detail
when(mMockDetailAdapter.openDetailEvent())
.thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN);
when(mMockDetailAdapter.closeDetailEvent())
.thenReturn(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE);
when(mMockDetailAdapter.moreSettingsEvent())
.thenReturn(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS);
}
@After
public void tearDown() {
QSEvents.INSTANCE.resetLogger();
}
@Test
@@ -79,9 +98,16 @@ public class QSDetailTest extends SysuiTestCase {
mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
verify(mMetricsLogger).visible(eq(mMockDetailAdapter.getMetricsCategory()));
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_OPEN.getId(), mUiEventLogger.eventId(0));
mUiEventLogger.getLogs().clear();
mQsDetail.handleShowingDetail(null, 0, 0, false);
verify(mMetricsLogger).hidden(eq(mMockDetailAdapter.getMetricsCategory()));
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSUserSwitcherEvent.QS_USER_DETAIL_CLOSE.getId(), mUiEventLogger.eventId(0));
ViewUtils.detachView(mQsDetail);
mTestableLooper.processAllMessages();
}
@@ -92,10 +118,13 @@ public class QSDetailTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mQsDetail.handleShowingDetail(mMockDetailAdapter, 0, 0, false);
mQsDetail.findViewById(android.R.id.button2).performClick();
mUiEventLogger.getLogs().clear();
mQsDetail.requireViewById(android.R.id.button2).performClick();
int metricsCategory = mMockDetailAdapter.getMetricsCategory();
verify(mMetricsLogger).action(eq(ACTION_QS_MORE_SETTINGS), eq(metricsCategory));
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSUserSwitcherEvent.QS_USER_MORE_SETTINGS.getId(), mUiEventLogger.eventId(0));
verify(mActivityStarter).postStartActivityDismissingKeyguard(any(), anyInt());
@@ -105,7 +134,9 @@ public class QSDetailTest extends SysuiTestCase {
@Test
public void testNullAdapterClick() {
mQsDetail.setupDetailFooter(mock(DetailAdapter.class));
mQsDetail.findViewById(android.R.id.button2).performClick();
DetailAdapter mock = mock(DetailAdapter.class);
when(mock.moreSettingsEvent()).thenReturn(DetailAdapter.INVALID);
mQsDetail.setupDetailFooter(mock);
mQsDetail.requireViewById(android.R.id.button2).performClick();
}
}

View File

@@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -106,7 +107,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
mock(PluginManager.class), mock(TunerService.class),
() -> mock(AutoTileManager.class), mock(DumpManager.class),
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class));
mock(QSLogger.class), mock(UiEventLogger.class));
qs.setHost(host);
qs.setListening(true);

View File

@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -94,17 +95,19 @@ public class QSPanelTest extends SysuiTestCase {
private ActivityStarter mActivityStarter;
@Mock
private NotificationEntryManager mEntryManager;
private UiEventLoggerFake mUiEventLogger;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
mUiEventLogger = new UiEventLoggerFake();
mTestableLooper.runWithLooper(() -> {
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher,
mQSLogger, mForegroundExecutor, mBackgroundExecutor,
mLocalBluetoothManager, mActivityStarter, mEntryManager);
mLocalBluetoothManager, mActivityStarter, mEntryManager, mUiEventLogger);
// Provides a parent with non-zero size for QSPanel
mParentView = new FrameLayout(mContext);
mParentView.addView(mQsPanel);
@@ -124,9 +127,17 @@ public class QSPanelTest extends SysuiTestCase {
mQsPanel.setExpanded(true);
verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag());
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0));
mUiEventLogger.getLogs().clear();
mQsPanel.setExpanded(false);
verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag());
assertEquals(1, mUiEventLogger.numLogs());
assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0));
mUiEventLogger.getLogs().clear();
}
@Test

View File

@@ -41,6 +41,7 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.CollectionUtils;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -50,7 +51,6 @@ import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
@@ -105,6 +105,8 @@ public class QSTileHostTest extends SysuiTestCase {
private QSLogger mQSLogger;
@Mock
private CustomTile mCustomTile;
@Mock
private UiEventLogger mUiEventLogger;
private Handler mHandler;
private TestableLooper mLooper;
@@ -117,7 +119,7 @@ public class QSTileHostTest extends SysuiTestCase {
mHandler = new Handler(mLooper.getLooper());
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger);
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger);
setUpTileFactory();
// Override this config so there are no unexpected tiles
@@ -298,10 +300,11 @@ public class QSTileHostTest extends SysuiTestCase {
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger) {
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger);
Optional.of(statusBar), qsLogger, uiEventLogger);
}
@Override

View File

@@ -24,6 +24,7 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSTileHost;
@@ -42,7 +43,8 @@ public class TileAdapterTest extends SysuiTestCase {
@Before
public void setup() throws Exception {
TestableLooper.get(this).runWithLooper(() -> mTileAdapter = new TileAdapter(mContext));
TestableLooper.get(this).runWithLooper(() -> mTileAdapter =
new TileAdapter(mContext, new UiEventLoggerFake()));
}
@Test

View File

@@ -30,6 +30,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
@@ -81,6 +82,8 @@ public class TileServicesTest extends SysuiTestCase {
private StatusBar mStatusBar;
@Mock
private QSLogger mQSLogger;
@Mock
private UiEventLogger mUiEventLogger;
@Before
public void setUp() throws Exception {
@@ -98,7 +101,8 @@ public class TileServicesTest extends SysuiTestCase {
mDumpManager,
mBroadcastDispatcher,
Optional.of(mStatusBar),
mQSLogger);
mQSLogger,
mUiEventLogger);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher);
}

View File

@@ -46,12 +46,16 @@ import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSEvent;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
@@ -83,6 +87,8 @@ public class QSTileImplTest extends SysuiTestCase {
private QSTileHost mHost;
private MetricsLogger mMetricsLogger;
private StatusBarStateController mStatusBarStateController;
private UiEventLoggerFake mUiEventLoggerFake;
private InstanceId mInstanceId = InstanceId.fakeInstanceId(5);
@Captor
private ArgumentCaptor<LogMaker> mLogCaptor;
@@ -94,12 +100,15 @@ public class QSTileImplTest extends SysuiTestCase {
mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
mDependency.injectMockDependency(ActivityStarter.class);
mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
mUiEventLoggerFake = new UiEventLoggerFake();
mStatusBarStateController =
mDependency.injectMockDependency(StatusBarStateController.class);
mHost = mock(QSTileHost.class);
when(mHost.indexOf(SPEC)).thenReturn(POSITION);
when(mHost.getContext()).thenReturn(mContext.getBaseContext());
when(mHost.getQSLogger()).thenReturn(mQsLogger);
when(mHost.getUiEventLogger()).thenReturn(mUiEventLoggerFake);
when(mHost.getNewInstanceId()).thenReturn(mInstanceId);
mTile = spy(new TileImpl(mHost));
mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
@@ -110,6 +119,9 @@ public class QSTileImplTest extends SysuiTestCase {
public void testClick_Metrics() {
mTile.click();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_CLICK)));
assertEquals(1, mUiEventLoggerFake.numLogs());
UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
assertEvent(QSEvent.QS_ACTION_CLICK, event);
}
@Test
@@ -133,6 +145,9 @@ public class QSTileImplTest extends SysuiTestCase {
public void testSecondaryClick_Metrics() {
mTile.secondaryClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
assertEquals(1, mUiEventLoggerFake.numLogs());
UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
assertEvent(QSEvent.QS_ACTION_SECONDARY_CLICK, event);
}
@Test
@@ -156,6 +171,9 @@ public class QSTileImplTest extends SysuiTestCase {
public void testLongClick_Metrics() {
mTile.longClick();
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
assertEquals(1, mUiEventLoggerFake.numLogs());
UiEventLoggerFake.FakeUiEvent event = mUiEventLoggerFake.get(0);
assertEvent(QSEvent.QS_ACTION_LONG_PRESS, event);
}
@@ -249,6 +267,13 @@ public class QSTileImplTest extends SysuiTestCase {
verify(mQsLogger).logTileChangeListening(SPEC, false);
}
private void assertEvent(UiEventLogger.UiEventEnum eventType,
UiEventLoggerFake.FakeUiEvent fakeEvent) {
assertEquals(eventType.getId(), fakeEvent.eventId);
assertEquals(SPEC, fakeEvent.packageName);
assertEquals(mInstanceId, fakeEvent.instanceId);
}
private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
private final int mCategory;

View File

@@ -24,8 +24,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSUserSwitcherEvent
import com.android.systemui.statusbar.policy.UserSwitcherController
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -50,15 +53,17 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
@Mock private lateinit var mPicture: Bitmap
@Mock private lateinit var mLayoutInflater: LayoutInflater
private lateinit var adapter: UserDetailView.Adapter
private lateinit var uiEventLogger: UiEventLoggerFake
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
uiEventLogger = UiEventLoggerFake()
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater)
`when`(mLayoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
.thenReturn(mInflatedUserDetailItemView)
adapter = UserDetailView.Adapter(mContext, mUserSwitcherController)
adapter = UserDetailView.Adapter(mContext, mUserSwitcherController, uiEventLogger)
}
private fun clickableTest(
@@ -76,6 +81,17 @@ class UserDetailViewAdapterTest : SysuiTestCase() {
}
}
@Test
fun testUserSwitchLog() {
val user = createUserRecord(false /* current */, false /* guest */)
val v = adapter.createUserDetailItemView(View(mContext), mParent, user)
`when`(v.tag).thenReturn(user)
adapter.onClick(v)
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_SWITCH.id, uiEventLogger.eventId(0))
}
@Test
fun testGuestIsClickable_differentViews_notCurrent() {
clickableTest(false, true, mOtherView, true)

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2020 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.statusbar
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class StatusBarStateControllerImplTest : SysuiTestCase() {
private lateinit var controller: StatusBarStateControllerImpl
private lateinit var uiEventLogger: UiEventLoggerFake
@Before
fun setUp() {
uiEventLogger = UiEventLoggerFake()
controller = StatusBarStateControllerImpl(uiEventLogger)
}
@Test
fun testChangeState_logged() {
TestableLooper.get(this).runWithLooper {
controller.state = StatusBarState.FULLSCREEN_USER_SWITCHER
controller.state = StatusBarState.KEYGUARD
controller.state = StatusBarState.SHADE
controller.state = StatusBarState.SHADE_LOCKED
}
val logs = uiEventLogger.logs
assertEquals(4, logs.size)
val ids = logs.map(UiEventLoggerFake.FakeUiEvent::eventId)
assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER.id, ids[0])
assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[1])
assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[2])
assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[3])
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2020 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.statusbar
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidTestingRunner::class)
class StatusBarStateEventTest : SysuiTestCase() {
@Test
fun testFromState() {
val events = listOf(
StatusBarStateEvent.STATUS_BAR_STATE_SHADE,
StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED,
StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD,
StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER,
StatusBarStateEvent.STATUS_BAR_STATE_UNKNOWN
)
val states = listOf(
StatusBarState.SHADE,
StatusBarState.SHADE_LOCKED,
StatusBarState.KEYGUARD,
StatusBarState.FULLSCREEN_USER_SWITCHER,
-1
)
events.zip(states).forEach { (event, state) ->
assertEquals(event, StatusBarStateEvent.fromState(state))
}
}
}

View File

@@ -47,6 +47,7 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -207,7 +208,7 @@ public class NotificationPanelViewTest extends SysuiTestCase {
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(),
new StatusBarStateControllerImpl(new UiEventLoggerFake()),
mKeyguardBypassController,
mDozeParameters);
PulseExpansionHandler expansionHandler = new PulseExpansionHandler(