Merge "Add dumpable to QSTile. Dump QSTileHost, QSPanel, QuickQSPanel" into qt-dev

This commit is contained in:
Fabian Kozynski
2019-04-24 18:05:15 +00:00
committed by Android (Google) Code Review
13 changed files with 331 additions and 13 deletions

View File

@@ -17,6 +17,7 @@
package com.android.systemui.qs;
import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -37,6 +38,8 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSTile;
@@ -51,11 +54,17 @@ import com.android.systemui.statusbar.policy.BrightnessMirrorController.Brightne
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Named;
/** View that represents the quick settings tile panel (when expanded/pulled down). **/
public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener,
Dumpable {
public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
public static final String QS_SHOW_HEADER = "qs_show_header";
@@ -74,6 +83,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
private QSDetail.Callback mCallback;
private BrightnessController mBrightnessController;
private DumpController mDumpController;
protected QSTileHost mHost;
protected QSSecurityFooter mFooter;
@@ -93,6 +103,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
public QSPanel(Context context, AttributeSet attrs) {
this(context, attrs, null);
}
@Inject
public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
DumpController dumpController) {
super(context, attrs);
mContext = context;
@@ -119,6 +135,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_slider));
mDumpController = dumpController;
}
protected void addDivider() {
@@ -170,6 +187,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.addCallback(this);
}
if (mDumpController != null) mDumpController.addListener(this);
}
@Override
@@ -184,6 +202,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.removeCallback(this);
}
if (mDumpController != null) mDumpController.removeListener(this);
super.onDetachedFromWindow();
}
@@ -649,6 +668,18 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(getClass().getSimpleName() + ":");
pw.println(" Tile records:");
for (TileRecord record : mRecords) {
if (record.tile instanceof Dumpable) {
pw.print(" "); ((Dumpable) record.tile).dump(fd, pw, args);
pw.print(" "); pw.println(record.tileView.toString());
}
}
}
protected static class Record {
DetailAdapter detailAdapter;
int x;

View File

@@ -30,6 +30,8 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.plugins.PluginListener;
@@ -47,6 +49,8 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -61,7 +65,7 @@ import javax.inject.Singleton;
/** Platform implementation of the quick settings tile host **/
@Singleton
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
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);
@@ -73,6 +77,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
private final TileServices mServices;
private final TunerService mTunerService;
private final PluginManager mPluginManager;
private final DumpController mDumpController;
private final List<Callback> mCallbacks = new ArrayList<>();
private AutoTileManager mAutoTiles;
@@ -89,17 +94,20 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
@Named(Dependency.BG_LOOPER_NAME) Looper bgLooper,
PluginManager pluginManager,
TunerService tunerService,
Provider<AutoTileManager> autoTiles) {
Provider<AutoTileManager> autoTiles,
DumpController dumpController) {
mIconController = iconController;
mContext = context;
mTunerService = tunerService;
mPluginManager = pluginManager;
mDumpController = dumpController;
mServices = new TileServices(this, bgLooper);
defaultFactory.setHost(this);
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
mDumpController.addListener(this);
mainHandler.post(() -> {
// This is technically a hack to avoid circular dependency of
@@ -121,6 +129,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
mTunerService.removeTunable(this);
mServices.destroy();
mPluginManager.removePluginListener(this);
mDumpController.removeListener(this);
}
@Override
@@ -363,4 +372,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
}
return tiles;
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("QSTileHost:");
mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
.forEach(o -> ((Dumpable) o).dump(fd, pw, args));
}
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.qs;
import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -25,6 +27,7 @@ import android.view.View;
import android.widget.LinearLayout;
import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTile.SignalState;
@@ -36,6 +39,9 @@ import com.android.systemui.tuner.TunerService.Tunable;
import java.util.ArrayList;
import java.util.Collection;
import javax.inject.Inject;
import javax.inject.Named;
/**
* Version of QSPanel that only shows N Quick Tiles in the QS Header.
*/
@@ -49,8 +55,10 @@ public class QuickQSPanel extends QSPanel {
private int mMaxTiles;
protected QSPanel mFullPanel;
public QuickQSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@Inject
public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
DumpController dumpController) {
super(context, attrs, dumpController);
if (mFooter != null) {
removeView(mFooter.getView());
}

View File

@@ -47,6 +47,7 @@ public class QSIconViewImpl extends QSIconView {
private boolean mAnimationEnabled = true;
private int mState = -1;
private int mTint;
private QSTile.Icon mLastIcon;
public QSIconViewImpl(Context context) {
super(context);
@@ -74,6 +75,16 @@ public class QSIconViewImpl extends QSIconView {
setMeasuredDimension(w, mIcon.getMeasuredHeight());
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
sb.append("state=" + mState);
sb.append(", tint=" + mTint);
if (mLastIcon != null) sb.append(", lastIcon=" + mLastIcon.toString());
sb.append("]");
return sb.toString();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int w = getMeasuredWidth();
@@ -91,6 +102,7 @@ public class QSIconViewImpl extends QSIconView {
if (!Objects.equals(icon, iv.getTag(R.id.qs_icon_tag))
|| !Objects.equals(state.slash, iv.getTag(R.id.qs_slash_tag))) {
boolean shouldAnimate = allowAnimations && shouldAnimate(iv);
mLastIcon = icon;
Drawable d = icon != null
? shouldAnimate ? icon.getDrawable(mContext)
: icon.getInvisibleDrawable(mContext) : null;

View File

@@ -318,6 +318,16 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView {
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(getClass().getSimpleName()).append('[');
sb.append("locInScreen=(" + mLocInScreen[0] + ", " + mLocInScreen[1] + ")");
sb.append(", iconView=" + mIcon.toString());
sb.append(", tileState=" + mTileState);
sb.append("]");
return sb.toString();
}
private class H extends Handler {
private static final int STATE_CHANGED = 1;

View File

@@ -52,6 +52,7 @@ import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.Dumpable;
import com.android.systemui.Prefs;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.DetailAdapter;
@@ -63,6 +64,8 @@ import com.android.systemui.qs.PagedTileLayout.TilePage;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QuickStatusBarHeader;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
@@ -74,7 +77,7 @@ import java.util.ArrayList;
*
* @param <TState> see above
*/
public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner {
public abstract class QSTileImpl<TState extends State> implements QSTile, LifecycleOwner, Dumpable {
protected final String TAG = "Tile." + getClass().getSimpleName();
protected static final boolean DEBUG = Log.isLoggable("Tile", Log.DEBUG);
@@ -592,4 +595,10 @@ public abstract class QSTileImpl<TState extends State> implements QSTile, Lifecy
return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
}
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(this.getClass().getSimpleName() + ":");
pw.print(" "); pw.println(getState().toString());
}
}

View File

@@ -29,6 +29,8 @@ import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.qs.QSCarrierGroup;
import com.android.systemui.qs.QSFooterImpl;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QuickQSPanel;
import com.android.systemui.qs.QuickStatusBarHeader;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.LockIcon;
@@ -154,6 +156,16 @@ public class InjectionInflationController {
* Creates the keyguard LockIcon.
*/
LockIcon createLockIcon();
/**
* Creates the QSPanel.
*/
QSPanel createQSPanel();
/**
* Creates the QuickQSPanel.
*/
QuickQSPanel createQuickQSPanel();
}
/**

View File

@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.keyguard.CarrierText;
import com.android.systemui.Dependency;
import com.android.systemui.DumpController;
import com.android.systemui.R;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.SysuiBaseFragmentTest;
@@ -95,7 +96,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
QSTileHost host = new QSTileHost(mContext, mock(StatusBarIconController.class),
mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
mock(PluginManager.class), mock(TunerService.class),
() -> mock(AutoTileManager.class));
() -> mock(AutoTileManager.class), mock(DumpController.class));
qs.setHost(host);
qs.setListening(true);

View File

@@ -14,10 +14,12 @@
package com.android.systemui.qs;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -34,9 +36,9 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizer;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import org.junit.Before;
import org.junit.Test;
@@ -44,6 +46,9 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collections;
@RunWith(AndroidTestingRunner.class)
@@ -59,10 +64,12 @@ public class QSPanelTest extends SysuiTestCase {
@Mock
private QSCustomizer mCustomizer;
@Mock
private QSTile dndTile;
private QSTileImpl dndTile;
private ViewGroup mParentView;
@Mock
private QSDetail.Callback mCallback;
@Mock
private QSTileView mQSTileView;
@Before
public void setup() throws Exception {
@@ -78,7 +85,7 @@ public class QSPanelTest extends SysuiTestCase {
when(dndTile.getTileSpec()).thenReturn("dnd");
when(mHost.getTiles()).thenReturn(Collections.emptyList());
when(mHost.createTileView(any(), anyBoolean())).thenReturn(mock(QSTileView.class));
when(mHost.createTileView(any(), anyBoolean())).thenReturn(mQSTileView);
mQsPanel.setHost(mHost, mCustomizer);
mQsPanel.addTile(dndTile, true);
@@ -120,4 +127,27 @@ public class QSPanelTest extends SysuiTestCase {
verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt());
}
@Test
public void testDump() {
String mockTileViewString = "Mock Tile View";
String mockTileString = "Mock Tile";
doAnswer(invocation -> {
PrintWriter pw = invocation.getArgument(1);
pw.println(mockTileString);
return null;
}).when(dndTile).dump(any(FileDescriptor.class), any(PrintWriter.class),
any(String[].class));
when(mQSTileView.toString()).thenReturn(mockTileViewString);
StringWriter w = new StringWriter();
PrintWriter pw = new PrintWriter(w);
mQsPanel.dump(mock(FileDescriptor.class), pw, new String[]{});
String expected = "QSPanel:\n"
+ " Tile records:\n"
+ " " + mockTileString + "\n"
+ " " + mockTileViewString + "\n";
assertEquals(expected, w.getBuffer().toString());
}
}

View File

@@ -17,23 +17,100 @@
package com.android.systemui.qs;
import static junit.framework.Assert.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.DumpController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
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;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import javax.inject.Provider;
@RunWith(AndroidTestingRunner.class)
@SmallTest
@RunWithLooper
public class QSTileHostTest extends SysuiTestCase {
private static String MOCK_STATE_STRING = "MockState";
@Mock
private StatusBarIconController mIconController;
@Mock
private QSFactoryImpl mDefaultFactory;
@Mock
private PluginManager mPluginManager;
@Mock
private TunerService mTunerService;
@Mock
private Provider<AutoTileManager> mAutoTiles;
@Mock
private DumpController mDumpController;
@Mock
private QSTile.State mMockState;
private Handler mHandler;
private TestableLooper mLooper;
private QSTileHost mQSTileHost;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mLooper = TestableLooper.get(this);
mHandler = new Handler(mLooper.getLooper());
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(),
mPluginManager, mTunerService, mAutoTiles, mDumpController);
setUpTileFactory();
}
private void setUpTileFactory() {
when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
when(mDefaultFactory.createTile(anyString())).thenAnswer(
invocation -> {
String spec = invocation.getArgument(0);
switch (spec) {
case "spec1":
return new TestTile1(mQSTileHost);
case "spec2":
return new TestTile2(mQSTileHost);
default:
return null;
}
});
}
@Test
public void testLoadTileSpecs_emptySetting() {
List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
@@ -45,4 +122,91 @@ public class QSTileHostTest extends SysuiTestCase {
List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
assertFalse(tiles.isEmpty());
}
@Test
public void testDump() {
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
StringWriter w = new StringWriter();
PrintWriter pw = new PrintWriter(w);
mQSTileHost.dump(mock(FileDescriptor.class), pw, new String[]{});
String output = "QSTileHost:\n"
+ TestTile1.class.getSimpleName() + ":\n"
+ " " + MOCK_STATE_STRING + "\n"
+ TestTile2.class.getSimpleName() + ":\n"
+ " " + MOCK_STATE_STRING + "\n";
assertEquals(output, w.getBuffer().toString());
}
private static class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, StatusBarIconController iconController,
QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpController dumpController) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpController);
}
@Override
public void onPluginConnected(QSFactory plugin, Context pluginContext) {
}
@Override
public void onPluginDisconnected(QSFactory plugin) {
}
}
private class TestTile extends QSTileImpl<QSTile.State> {
protected TestTile(QSHost host) {
super(host);
}
@Override
public State newTileState() {
return mMockState;
}
@Override
public State getState() {
return mMockState;
}
@Override
protected void handleClick() {}
@Override
protected void handleUpdateState(State state, Object arg) {}
@Override
public int getMetricsCategory() {
return 0;
}
@Override
public Intent getLongClickIntent() {
return null;
}
@Override
protected void handleSetListening(boolean listening) {}
@Override
public CharSequence getTileLabel() {
return null;
}
}
private class TestTile1 extends TestTile {
protected TestTile1(QSHost host) {
super(host);
}
}
private class TestTile2 extends TestTile {
protected TestTile2(QSHost host) {
super(host);
}
}
}

View File

@@ -29,6 +29,7 @@ import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.DumpController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -67,7 +68,8 @@ public class TileServicesTest extends SysuiTestCase {
Looper.myLooper(),
mock(PluginManager.class),
mock(TunerService.class),
() -> mock(AutoTileManager.class));
() -> mock(AutoTileManager.class),
mock(DumpController.class));
mTileService = new TestTileServices(host, Looper.getMainLooper());
}

View File

@@ -14,6 +14,8 @@
package com.android.systemui.qs.tileimpl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
@@ -43,6 +45,7 @@ import org.junit.runner.RunWith;
public class QSIconViewImplTest extends SysuiTestCase {
private QSIconViewImpl mIconView;
private static int RES_ID = 1;
@Before
public void setup() {
@@ -81,4 +84,24 @@ public class QSIconViewImplTest extends SysuiTestCase {
mIconView.setIcon(iv, s, true);
verify(iv).setImageTintList(argThat(stateList -> stateList.getColors()[0] == desiredColor));
}
@Test
public void testStateSetCorrectly_toString() {
ImageView iv = mock(ImageView.class);
State s = new State();
s.state = Tile.STATE_ACTIVE;
int desiredColor = mIconView.getColor(s.state);
Icon i = mock(Icon.class);
s.icon = i;
when(i.toString()).thenReturn("MOCK ICON");
mIconView.setIcon(iv, s, false);
assertEquals("QSIconViewImpl[state=" + Tile.STATE_ACTIVE + ", tint=" + desiredColor
+ ", lastIcon=" + i.toString() + "]", mIconView.toString());
}
@Test
public void testIconNotSet_toString() {
assertFalse(mIconView.toString().contains("lastIcon"));
}
}

View File

@@ -34,6 +34,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static java.lang.Thread.sleep;
import android.content.Intent;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
@@ -60,8 +62,6 @@ import org.mockito.ArgumentMatcher;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import static java.lang.Thread.sleep;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest