summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/media/framework/java/android/media/MediaParser.java3
-rw-r--r--apex/statsd/framework/java/android/util/StatsEvent.java50
-rw-r--r--apex/statsd/framework/test/src/android/util/StatsEventTest.java160
-rw-r--r--core/res/res/values-television/themes_device_defaults.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/values/themes_leanback.xml4
-rw-r--r--media/java/android/media/tv/TvRecordingClient.java2
-rw-r--r--packages/SystemUI/res/layout/controls_detail_dialog.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java215
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java129
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java5
18 files changed, 590 insertions, 53 deletions
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 070b13b9e592..7cbb98e1bb4e 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -219,7 +219,8 @@ public final class MediaParser {
* duration is unknown.
*/
public long getDurationMicros() {
- return mExoPlayerSeekMap.getDurationUs();
+ long durationUs = mExoPlayerSeekMap.getDurationUs();
+ return durationUs != C.TIME_UNSET ? durationUs : UNKNOWN_DURATION;
}
/**
diff --git a/apex/statsd/framework/java/android/util/StatsEvent.java b/apex/statsd/framework/java/android/util/StatsEvent.java
index 8bd36a516b12..8be5c63f31e3 100644
--- a/apex/statsd/framework/java/android/util/StatsEvent.java
+++ b/apex/statsd/framework/java/android/util/StatsEvent.java
@@ -26,6 +26,8 @@ import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Arrays;
+
/**
* StatsEvent builds and stores the buffer sent over the statsd socket.
* This class defines and encapsulates the socket protocol.
@@ -224,7 +226,9 @@ public final class StatsEvent {
// Max payload size is 4 bytes less as 4 bytes are reserved for statsEventTag.
// See android_util_StatsLog.cpp.
- private static final int MAX_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+ private static final int MAX_PUSH_PAYLOAD_SIZE = LOGGER_ENTRY_MAX_PAYLOAD - 4;
+
+ private static final int MAX_PULL_PAYLOAD_SIZE = 50 * 1024; // 50 KB
private final int mAtomId;
private final byte[] mPayload;
@@ -619,6 +623,7 @@ public final class StatsEvent {
@NonNull
public Builder usePooledBuffer() {
mUsePooledBuffer = true;
+ mBuffer.setMaxSize(MAX_PUSH_PAYLOAD_SIZE, mPos);
return this;
}
@@ -694,8 +699,9 @@ public final class StatsEvent {
@GuardedBy("sLock")
private static Buffer sPool;
- private final byte[] mBytes = new byte[MAX_PAYLOAD_SIZE];
+ private byte[] mBytes = new byte[MAX_PUSH_PAYLOAD_SIZE];
private boolean mOverflow = false;
+ private int mMaxSize = MAX_PULL_PAYLOAD_SIZE;
@NonNull
private static Buffer obtain() {
@@ -717,15 +723,26 @@ public final class StatsEvent {
}
private void release() {
- synchronized (sLock) {
- if (null == sPool) {
- sPool = this;
+ // Recycle this Buffer if its size is MAX_PUSH_PAYLOAD_SIZE or under.
+ if (mBytes.length <= MAX_PUSH_PAYLOAD_SIZE) {
+ synchronized (sLock) {
+ if (null == sPool) {
+ sPool = this;
+ }
}
}
}
private void reset() {
mOverflow = false;
+ mMaxSize = MAX_PULL_PAYLOAD_SIZE;
+ }
+
+ private void setMaxSize(final int maxSize, final int numBytesWritten) {
+ mMaxSize = maxSize;
+ if (numBytesWritten > maxSize) {
+ mOverflow = true;
+ }
}
private boolean hasOverflowed() {
@@ -740,11 +757,28 @@ public final class StatsEvent {
* @return true if space is available, false otherwise.
**/
private boolean hasEnoughSpace(final int index, final int numBytes) {
- final boolean result = index + numBytes < MAX_PAYLOAD_SIZE;
- if (!result) {
+ final int totalBytesNeeded = index + numBytes;
+
+ if (totalBytesNeeded > mMaxSize) {
mOverflow = true;
+ return false;
}
- return result;
+
+ // Expand buffer if needed.
+ if (mBytes.length < mMaxSize && totalBytesNeeded > mBytes.length) {
+ int newSize = mBytes.length;
+ do {
+ newSize *= 2;
+ } while (newSize <= totalBytesNeeded);
+
+ if (newSize > mMaxSize) {
+ newSize = mMaxSize;
+ }
+
+ mBytes = Arrays.copyOf(mBytes, newSize);
+ }
+
+ return true;
}
/**
diff --git a/apex/statsd/framework/test/src/android/util/StatsEventTest.java b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
index 7b511553a26f..8d263699d9c8 100644
--- a/apex/statsd/framework/test/src/android/util/StatsEventTest.java
+++ b/apex/statsd/framework/test/src/android/util/StatsEventTest.java
@@ -33,6 +33,7 @@ import org.junit.runner.RunWith;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
+import java.util.Random;
/**
* Internal tests for {@link StatsEvent}.
@@ -644,6 +645,165 @@ public class StatsEventTest {
statsEvent.release();
}
+ @Test
+ public void testLargePulledEvent() {
+ final int expectedAtomId = 10_020;
+ byte[] field1 = new byte[10 * 1024];
+ new Random().nextBytes(field1);
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent =
+ StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get())
+ .isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong())
+ .isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not byte array")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_BYTE_ARRAY);
+
+ final byte[] field1Actual = getByteArrayFromByteBuffer(buffer);
+ assertWithMessage("Incorrect field 1").that(field1Actual).isEqualTo(field1);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
+ @Test
+ public void testPulledEventOverflow() {
+ final int expectedAtomId = 10_020;
+ byte[] field1 = new byte[50 * 1024];
+ new Random().nextBytes(field1);
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent =
+ StatsEvent.newBuilder().setAtomId(expectedAtomId).writeByteArray(field1).build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get())
+ .isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong())
+ .isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not errors type")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_ERRORS);
+
+ final int errorMask = buffer.getInt();
+
+ assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
+ .that(errorMask)
+ .isEqualTo(StatsEvent.ERROR_OVERFLOW);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
+ @Test
+ public void testPushedEventOverflow() {
+ final int expectedAtomId = 10_020;
+ byte[] field1 = new byte[10 * 1024];
+ new Random().nextBytes(field1);
+
+ final long minTimestamp = SystemClock.elapsedRealtimeNanos();
+ final StatsEvent statsEvent = StatsEvent.newBuilder()
+ .setAtomId(expectedAtomId)
+ .writeByteArray(field1)
+ .usePooledBuffer()
+ .build();
+ final long maxTimestamp = SystemClock.elapsedRealtimeNanos();
+
+ assertThat(statsEvent.getAtomId()).isEqualTo(expectedAtomId);
+
+ final ByteBuffer buffer =
+ ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
+
+ assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_OBJECT);
+
+ assertWithMessage("Incorrect number of elements in root object")
+ .that(buffer.get())
+ .isEqualTo(3);
+
+ assertWithMessage("First element is not timestamp")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_LONG);
+
+ assertWithMessage("Incorrect timestamp")
+ .that(buffer.getLong())
+ .isIn(Range.closed(minTimestamp, maxTimestamp));
+
+ assertWithMessage("Second element is not atom id")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_INT);
+
+ assertWithMessage("Incorrect atom id").that(buffer.getInt()).isEqualTo(expectedAtomId);
+
+ assertWithMessage("Third element is not errors type")
+ .that(buffer.get())
+ .isEqualTo(StatsEvent.TYPE_ERRORS);
+
+ final int errorMask = buffer.getInt();
+
+ assertWithMessage("ERROR_OVERFLOW should be the only error in the error mask")
+ .that(errorMask)
+ .isEqualTo(StatsEvent.ERROR_OVERFLOW);
+
+ assertThat(statsEvent.getNumBytes()).isEqualTo(buffer.position());
+
+ statsEvent.release();
+ }
+
private static byte[] getByteArrayFromByteBuffer(final ByteBuffer buffer) {
final int numBytes = buffer.getInt();
byte[] bytes = new byte[numBytes];
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index cb3d32815ea9..d6bdeee00a9f 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -33,5 +33,6 @@
<style name="Theme.DeviceDefault.Autofill.Light" parent="Theme.DeviceDefault.Autofill"/>
<style name="Theme.DeviceDefault.Light.Autofill.Save" parent="Theme.DeviceDefault.Autofill.Save"/>
- <style name="Theme.DeviceDefault.Resolver" parent="Theme.Leanback.Resolver" />
+ <style name="Theme.DeviceDefault.ResolverCommon" parent="Theme.Leanback.Resolver" />
+ <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f3345b015c87..051bf7cca2d4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1785,6 +1785,7 @@
<java-symbol type="string" name="faceunlock_multiple_failures" />
<java-symbol type="string" name="global_actions" />
<java-symbol type="string" name="global_action_power_off" />
+ <java-symbol type="string" name="global_action_power_options" />
<java-symbol type="string" name="global_action_restart" />
<java-symbol type="string" name="global_actions_airplane_mode_off_status" />
<java-symbol type="string" name="global_actions_airplane_mode_on_status" />
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index a80725c2758b..9dca9128e362 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -128,6 +128,10 @@
<!-- Toolbar attributes -->
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
+
+ <!-- Icon sizes -->
+ <item name="iconfactoryIconSize">@dimen/resolver_icon_size</item>
+ <item name="iconfactoryBadgeSize">@dimen/resolver_badge_size</item>
</style>
<!-- @hide Special theme for the default system Activity-based Alert dialogs. -->
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 8ae98ae5e937..23fadac8a72b 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -146,6 +146,8 @@ public class TvRecordingClient {
mPendingAppPrivateCommands.clear();
if (mSession != null) {
mSession.release();
+ mIsTuned = false;
+ mIsRecordingStarted = false;
mSession = null;
}
}
diff --git a/packages/SystemUI/res/layout/controls_detail_dialog.xml b/packages/SystemUI/res/layout/controls_detail_dialog.xml
index d61122fd47dd..ee5315ad782f 100644
--- a/packages/SystemUI/res/layout/controls_detail_dialog.xml
+++ b/packages/SystemUI/res/layout/controls_detail_dialog.xml
@@ -42,6 +42,7 @@
android:layout_height="1dp" />
<ImageView
android:id="@+id/control_detail_open_in_app"
+ android:contentDescription="@string/controls_open_app"
android:src="@drawable/ic_open_in_new"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:tint="@color/control_primary_text"
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 2ceb06690da6..91f361b15945 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -270,7 +270,7 @@ public class BubbleStackView extends FrameLayout
private boolean mShowingDismiss = false;
/** The view to desaturate/darken when magneted to the dismiss target. */
- private View mDesaturateAndDarkenTargetView;
+ @Nullable private View mDesaturateAndDarkenTargetView;
private LayoutInflater mInflater;
@@ -908,7 +908,10 @@ public class BubbleStackView extends FrameLayout
// Update the paint and apply it to the bubble container.
mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
- mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
+
+ if (mDesaturateAndDarkenTargetView != null) {
+ mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
+ }
});
// If the stack itself is touched, it means none of its touchable views (bubbles, flyouts,
@@ -1894,6 +1897,10 @@ public class BubbleStackView extends FrameLayout
private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
mDesaturateAndDarkenTargetView = targetView;
+ if (mDesaturateAndDarkenTargetView == null) {
+ return;
+ }
+
if (desaturateAndDarken) {
// Use the animated paint for the bubbles.
mDesaturateAndDarkenTargetView.setLayerType(
@@ -1915,9 +1922,14 @@ public class BubbleStackView extends FrameLayout
}
private void resetDesaturationAndDarken() {
+
mDesaturateAndDarkenAnimator.removeAllListeners();
mDesaturateAndDarkenAnimator.cancel();
- mDesaturateAndDarkenTargetView.setLayerType(View.LAYER_TYPE_NONE, null);
+
+ if (mDesaturateAndDarkenTargetView != null) {
+ mDesaturateAndDarkenTargetView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mDesaturateAndDarkenTargetView = null;
+ }
}
/** Animates in the dismiss target. */
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 7e009b459ede..5e5ebe92e559 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -175,7 +175,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
private static final String GLOBAL_ACTION_KEY_USERS = "users";
private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
- private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
+ static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
static final String GLOBAL_ACTION_KEY_RESTART = "restart";
@@ -213,7 +213,9 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
@VisibleForTesting
protected final ArrayList<Action> mItems = new ArrayList<>();
@VisibleForTesting
- final ArrayList<Action> mOverflowItems = new ArrayList<>();
+ protected final ArrayList<Action> mOverflowItems = new ArrayList<>();
+ @VisibleForTesting
+ protected final ArrayList<Action> mPowerItems = new ArrayList<>();
@VisibleForTesting
protected ActionsDialog mDialog;
@@ -223,6 +225,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private MyAdapter mAdapter;
private MyOverflowAdapter mOverflowAdapter;
+ private MyPowerOptionsAdapter mPowerAdapter;
private boolean mKeyguardShowing = false;
private boolean mDeviceProvisioned = false;
@@ -584,14 +587,19 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mItems.clear();
mOverflowItems.clear();
-
+ mPowerItems.clear();
String[] defaultActions = getDefaultActions();
+
+ ShutDownAction shutdownAction = new ShutDownAction();
+ RestartAction restartAction = new RestartAction();
+ ArraySet<String> addedKeys = new ArraySet<String>();
+
// make sure emergency affordance action is first, if needed
if (mEmergencyAffordanceManager.needsEmergencyAffordance()) {
addActionItem(new EmergencyAffordanceAction());
+ addedKeys.add(GLOBAL_ACTION_KEY_EMERGENCY);
}
- ArraySet<String> addedKeys = new ArraySet<String>();
for (int i = 0; i < defaultActions.length; i++) {
String actionKey = defaultActions[i];
if (addedKeys.contains(actionKey)) {
@@ -599,7 +607,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
- addActionItem(new PowerAction());
+ addActionItem(shutdownAction);
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
addActionItem(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
@@ -618,10 +626,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
addActionItem(getSettingsAction());
} else if (GLOBAL_ACTION_KEY_LOCKDOWN.equals(actionKey)) {
- int userId = getCurrentUser().id;
- if (Settings.Secure.getIntForUser(mContentResolver,
- Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, userId) != 0
- && shouldDisplayLockdown(userId)) {
+ if (shouldDisplayLockdown(getCurrentUser())) {
addActionItem(getLockdownAction());
}
} else if (GLOBAL_ACTION_KEY_VOICEASSIST.equals(actionKey)) {
@@ -629,7 +634,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
} else if (GLOBAL_ACTION_KEY_ASSIST.equals(actionKey)) {
addActionItem(getAssistAction());
} else if (GLOBAL_ACTION_KEY_RESTART.equals(actionKey)) {
- addActionItem(new RestartAction());
+ addActionItem(restartAction);
} else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
addActionItem(new ScreenshotAction());
} else if (GLOBAL_ACTION_KEY_LOGOUT.equals(actionKey)) {
@@ -638,15 +643,32 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
addActionItem(new LogoutAction());
}
} else if (GLOBAL_ACTION_KEY_EMERGENCY.equals(actionKey)) {
- if (!mEmergencyAffordanceManager.needsEmergencyAffordance()) {
- addActionItem(new EmergencyDialerAction());
- }
+ addActionItem(new EmergencyDialerAction());
} else {
Log.e(TAG, "Invalid global action key " + actionKey);
}
// Add here so we don't add more than one.
addedKeys.add(actionKey);
}
+
+ // replace power and restart with a single power options action, if needed
+ if (mItems.contains(shutdownAction) && mItems.contains(restartAction)
+ && mOverflowItems.size() > 0) {
+ // transfer shutdown and restart to their own list of power actions
+ mItems.remove(shutdownAction);
+ mItems.remove(restartAction);
+ mPowerItems.add(shutdownAction);
+ mPowerItems.add(restartAction);
+
+ // add the PowerOptionsAction after Emergency, if present
+ int powerIndex = addedKeys.contains(GLOBAL_ACTION_KEY_EMERGENCY) ? 1 : 0;
+ mItems.add(powerIndex, new PowerOptionsAction());
+
+ // transfer the first overflow action to the main set of items
+ Action firstOverflowAction = mOverflowItems.get(0);
+ mOverflowItems.remove(0);
+ mItems.add(firstOverflowAction);
+ }
}
private void onRotate() {
@@ -664,6 +686,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mAdapter = new MyAdapter();
mOverflowAdapter = new MyOverflowAdapter();
+ mPowerAdapter = new MyPowerOptionsAdapter();
mDepthController.setShowingHomeControls(true);
GlobalActionsPanelPlugin.PanelViewController walletViewController =
@@ -676,7 +699,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
walletViewController, mDepthController, mSysuiColorExtractor,
mStatusBarService, mNotificationShadeWindowController,
controlsAvailable(), uiController,
- mSysUiState, this::onRotate, mKeyguardShowing);
+ mSysUiState, this::onRotate, mKeyguardShowing, mPowerAdapter);
boolean walletViewAvailable = walletViewController != null
&& walletViewController.getPanelContent() != null;
if (shouldShowLockMessage(walletViewAvailable)) {
@@ -689,7 +712,20 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return dialog;
}
- private boolean shouldDisplayLockdown(int userId) {
+ @VisibleForTesting
+ protected boolean shouldDisplayLockdown(UserInfo user) {
+ if (user == null) {
+ return false;
+ }
+
+ int userId = user.id;
+
+ // No lockdown option if it's not turned on in Settings
+ if (Settings.Secure.getIntForUser(mContentResolver,
+ Settings.Secure.LOCKDOWN_IN_POWER_MENU, 0, userId) == 0) {
+ return false;
+ }
+
// Lockdown is meaningless without a place to go.
if (!mKeyguardStateController.isMethodSecure()) {
return false;
@@ -740,8 +776,32 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mActivityStarter.startPendingIntentDismissingKeyguard(pendingIntent);
}
- private final class PowerAction extends SinglePressAction implements LongPressAction {
- private PowerAction() {
+ @VisibleForTesting
+ protected final class PowerOptionsAction extends SinglePressAction {
+ private PowerOptionsAction() {
+ super(R.drawable.ic_lock_power_off, R.string.global_action_power_options);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ if (mDialog != null) {
+ mDialog.showPowerOptionsMenu();
+ }
+ }
+ }
+
+ private final class ShutDownAction extends SinglePressAction implements LongPressAction {
+ private ShutDownAction() {
super(R.drawable.ic_lock_power_off,
R.string.global_action_power_off);
}
@@ -772,7 +832,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
- private abstract class EmergencyAction extends SinglePressAction {
+ @VisibleForTesting
+ protected abstract class EmergencyAction extends SinglePressAction {
EmergencyAction(int iconResId, int messageResId) {
super(iconResId, messageResId);
}
@@ -1317,7 +1378,10 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Action item = mAdapter.getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
- mDialog.dismiss();
+ // don't dismiss the dialog if we're opening the power options menu
+ if (!(item instanceof PowerOptionsAction)) {
+ mDialog.dismiss();
+ }
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
}
@@ -1334,6 +1398,70 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
/**
* The adapter used for items in the overflow menu.
*/
+ public class MyPowerOptionsAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return mPowerItems.size();
+ }
+
+ @Override
+ public Action getItem(int position) {
+ return mPowerItems.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ Action action = getItem(position);
+ if (action == null) {
+ Log.w(TAG, "No power options action found at position: " + position);
+ return null;
+ }
+ int viewLayoutResource = com.android.systemui.R.layout.controls_more_item;
+ View view = convertView != null ? convertView
+ : LayoutInflater.from(mContext).inflate(viewLayoutResource, parent, false);
+ TextView textView = (TextView) view;
+ if (action.getMessageResId() != 0) {
+ textView.setText(action.getMessageResId());
+ } else {
+ textView.setText(action.getMessage());
+ }
+ return textView;
+ }
+
+ private boolean onLongClickItem(int position) {
+ final Action action = getItem(position);
+ if (action instanceof LongPressAction) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ } else {
+ Log.w(TAG, "Action long-clicked while mDialog is null.");
+ }
+ return ((LongPressAction) action).onLongPress();
+ }
+ return false;
+ }
+
+ private void onClickItem(int position) {
+ Action item = getItem(position);
+ if (!(item instanceof SilentModeTriStateAction)) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ } else {
+ Log.w(TAG, "Action clicked while mDialog is null.");
+ }
+ item.onPress();
+ }
+ }
+ }
+
+ /**
+ * The adapter used for items in the power options menu, triggered by the PowerOptionsAction.
+ */
public class MyOverflowAdapter extends BaseAdapter {
@Override
public int getCount() {
@@ -1373,7 +1501,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
final Action action = getItem(position);
if (action instanceof LongPressAction) {
if (mDialog != null) {
- mDialog.hidePowerOverflowMenu();
mDialog.dismiss();
} else {
Log.w(TAG, "Action long-clicked while mDialog is null.");
@@ -1387,7 +1514,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
Action item = getItem(position);
if (!(item instanceof SilentModeTriStateAction)) {
if (mDialog != null) {
- mDialog.hidePowerOverflowMenu();
mDialog.dismiss();
} else {
Log.w(TAG, "Action clicked while mDialog is null.");
@@ -1495,7 +1621,6 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
-
public int getMessageResId() {
return mMessageResId;
}
@@ -1846,6 +1971,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
mAirplaneState = inAirplaneMode ? ToggleState.On : ToggleState.Off;
mAirplaneModeOn.updateState(mAirplaneState);
mAdapter.notifyDataSetChanged();
+ mOverflowAdapter.notifyDataSetChanged();
+ mPowerAdapter.notifyDataSetChanged();
}
};
@@ -1928,6 +2055,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final Context mContext;
private final MyAdapter mAdapter;
private final MyOverflowAdapter mOverflowAdapter;
+ private final MyPowerOptionsAdapter mPowerOptionsAdapter;
private final IStatusBarService mStatusBarService;
private final IBinder mToken = new Binder();
private MultiListLayout mGlobalActionsLayout;
@@ -1943,6 +2071,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
private final NotificationShadeDepthController mDepthController;
private final SysUiState mSysUiState;
private ListPopupWindow mOverflowPopup;
+ private ListPopupWindow mPowerOptionsPopup;
private final Runnable mOnRotateCallback;
private final boolean mControlsAvailable;
@@ -1958,11 +2087,13 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
NotificationShadeWindowController notificationShadeWindowController,
boolean controlsAvailable, @Nullable ControlsUiController controlsUiController,
- SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing) {
+ SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
+ MyPowerOptionsAdapter powerAdapter) {
super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
mContext = context;
mAdapter = adapter;
mOverflowAdapter = overflowAdapter;
+ mPowerOptionsAdapter = powerAdapter;
mDepthController = depthController;
mColorExtractor = sysuiColorExtractor;
mStatusBarService = statusBarService;
@@ -2076,6 +2207,21 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
+ private ListPopupWindow createPowerOptionsPopup() {
+ GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
+ new ContextThemeWrapper(
+ mContext,
+ com.android.systemui.R.style.Control_ListPopupWindow
+ ), false /* isDropDownMode */);
+ popup.setOnItemClickListener(
+ (parent, view, position, id) -> mPowerOptionsAdapter.onClickItem(position));
+ popup.setOnItemLongClickListener(
+ (parent, view, position, id) -> mPowerOptionsAdapter.onLongClickItem(position));
+ popup.setAnchorView(mGlobalActionsLayout);
+ popup.setAdapter(mPowerOptionsAdapter);
+ return popup;
+ }
+
private ListPopupWindow createPowerOverflowPopup() {
GlobalActionsPopupMenu popup = new GlobalActionsPopupMenu(
new ContextThemeWrapper(
@@ -2093,16 +2239,16 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
return popup;
}
+ public void showPowerOptionsMenu() {
+ mPowerOptionsPopup = createPowerOptionsPopup();
+ mPowerOptionsPopup.show();
+ }
+
private void showPowerOverflowMenu() {
mOverflowPopup = createPowerOverflowPopup();
mOverflowPopup.show();
}
- private void hidePowerOverflowMenu() {
- mOverflowPopup.dismiss();
- mOverflowPopup = null;
- }
-
private void initializeLayout() {
setContentView(com.android.systemui.R.layout.global_actions_grid_v2);
fixNavBarClipping();
@@ -2278,6 +2424,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
// close first, as popup windows will not fade during the animation
dismissOverflow(false);
+ dismissPowerOptions(false);
if (mControlsUiController != null) mControlsUiController.closeDialogs(false);
});
}
@@ -2302,6 +2449,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
resetOrientation();
dismissWallet();
dismissOverflow(true);
+ dismissPowerOptions(true);
if (mControlsUiController != null) mControlsUiController.hide();
mNotificationShadeWindowController.setForceHasTopUi(mHadTopUi);
mDepthController.updateGlobalDialogVisibility(0, null /* view */);
@@ -2326,6 +2474,16 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
}
}
+ private void dismissPowerOptions(boolean immediate) {
+ if (mPowerOptionsPopup != null) {
+ if (immediate) {
+ mPowerOptionsPopup.dismissImmediate();
+ } else {
+ mPowerOptionsPopup.dismiss();
+ }
+ }
+ }
+
private void setRotationSuggestionsEnabled(boolean enabled) {
try {
final int userId = Binder.getCallingUserHandle().getIdentifier();
@@ -2369,6 +2527,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
// ensure dropdown menus are dismissed before re-initializing the dialog
dismissWallet();
dismissOverflow(true);
+ dismissPowerOptions(true);
if (mControlsUiController != null) {
mControlsUiController.hide();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 4008918e267c..65d3572d04a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -355,10 +355,23 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
}
public void addTile(ComponentName tile) {
+ addTile(tile, /* end */ false);
+ }
+
+ /**
+ * Adds a custom tile to the set of current tiles.
+ * @param tile the component name of the {@link android.service.quicksettings.TileService}
+ * @param end if true, the tile will be added at the end. If false, at the beginning.
+ */
+ public void addTile(ComponentName tile, boolean end) {
String spec = CustomTile.toSpec(tile);
if (!mTileSpecs.contains(spec)) {
List<String> newSpecs = new ArrayList<>(mTileSpecs);
- newSpecs.add(0, spec);
+ if (end) {
+ newSpecs.add(spec);
+ } else {
+ newSpecs.add(0, spec);
+ }
changeTiles(mTileSpecs, newSpecs);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
index b163818f68a8..93db9cdf85ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
@@ -341,7 +341,6 @@ class ChannelEditorDialogController @Inject constructor(
}
private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
- or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
index 88c325880241..c88f0bdc2acb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PriorityOnboardingDialogController.kt
@@ -184,7 +184,6 @@ class PriorityOnboardingDialogController @Inject constructor(
}
private val wmFlags = (WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
- or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 79515415f1c3..fc8c8dbba7fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -274,7 +274,7 @@ public class AutoTileManager {
}
if (value != 0) {
if (mSpec.startsWith(CustomTile.PREFIX)) {
- mHost.addTile(CustomTile.getComponentFromSpec(mSpec));
+ mHost.addTile(CustomTile.getComponentFromSpec(mSpec), /* end */ true);
} else {
mHost.addTile(mSpec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index ce032e2ceaec..3455ff47de8d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -212,7 +212,6 @@ public class VolumeDialogImpl implements VolumeDialog,
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 32546333aac3..329af2b7f62b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -19,6 +19,7 @@ package com.android.systemui.globalactions;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -244,7 +245,7 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
}
@Test
- public void testCreateActionItems_maxThree() {
+ public void testCreateActionItems_maxThree_noOverflow() {
mGlobalActionsDialog = spy(mGlobalActionsDialog);
// allow 3 items to be shown
doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
@@ -254,13 +255,129 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ };
+ doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
+ mGlobalActionsDialog.createActionItems();
+
+ assertEquals(3, mGlobalActionsDialog.mItems.size());
+ assertEquals(0, mGlobalActionsDialog.mOverflowItems.size());
+ assertEquals(0, mGlobalActionsDialog.mPowerItems.size());
+ }
+
+ @Test
+ public void testCreateActionItems_maxThree_condensePower() {
+ mGlobalActionsDialog = spy(mGlobalActionsDialog);
+ // allow 3 items to be shown
+ doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
+ // ensure items are not blocked by keyguard or device provisioning
+ doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
+ // make sure lockdown action will be shown
+ doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ };
+ doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
+ mGlobalActionsDialog.createActionItems();
+
+ assertEquals(3, mGlobalActionsDialog.mItems.size());
+ assertEquals(0, mGlobalActionsDialog.mOverflowItems.size());
+ assertEquals(2, mGlobalActionsDialog.mPowerItems.size());
+
+ // PowerOptionsAction should appear immediately after the Emergency action
+
+ GlobalActionsDialog.Action firstItem = mGlobalActionsDialog.mItems.get(0);
+ GlobalActionsDialog.Action secondItem = mGlobalActionsDialog.mItems.get(1);
+
+ assertTrue(firstItem instanceof GlobalActionsDialog.EmergencyAction);
+ assertTrue(secondItem instanceof GlobalActionsDialog.PowerOptionsAction);
+ }
+
+ @Test
+ public void testCreateActionItems_maxThree_condensePower_noEmergency() {
+ mGlobalActionsDialog = spy(mGlobalActionsDialog);
+ // allow 3 items to be shown
+ doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
+ // make sure lockdown action will be shown
+ doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
+ // ensure items are not blocked by keyguard or device provisioning
+ doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ };
+ doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
+ mGlobalActionsDialog.createActionItems();
+
+ assertEquals(3, mGlobalActionsDialog.mItems.size());
+ assertEquals(0, mGlobalActionsDialog.mOverflowItems.size());
+ assertEquals(2, mGlobalActionsDialog.mPowerItems.size());
+
+ // When Emergency isn't used, PowerOptionsAction should be first
+
+ GlobalActionsDialog.Action firstItem = mGlobalActionsDialog.mItems.get(0);
+ GlobalActionsDialog.Action secondItem = mGlobalActionsDialog.mItems.get(1);
+
+ assertTrue(firstItem instanceof GlobalActionsDialog.PowerOptionsAction);
+ assertTrue(secondItem instanceof GlobalActionsDialog.ScreenshotAction);
+ }
+
+ @Test
+ public void testCreateActionItems_maxFour_condensePower() {
+ mGlobalActionsDialog = spy(mGlobalActionsDialog);
+ // allow 3 items to be shown
+ doReturn(4).when(mGlobalActionsDialog).getMaxShownPowerItems();
+ // make sure lockdown action will be shown
+ doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
+ // ensure items are not blocked by keyguard or device provisioning
+ doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT
+ };
+ doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
+ mGlobalActionsDialog.createActionItems();
+
+ assertEquals(4, mGlobalActionsDialog.mItems.size());
+ assertEquals(0, mGlobalActionsDialog.mOverflowItems.size());
+ assertEquals(2, mGlobalActionsDialog.mPowerItems.size());
+
+ // with four items, make sure power still shows up immediately after Emergency
+ GlobalActionsDialog.Action firstItem = mGlobalActionsDialog.mItems.get(0);
+ GlobalActionsDialog.Action secondItem = mGlobalActionsDialog.mItems.get(1);
+
+ assertTrue(firstItem instanceof GlobalActionsDialog.EmergencyAction);
+ assertTrue(secondItem instanceof GlobalActionsDialog.PowerOptionsAction);
+ }
+
+ @Test
+ public void testCreateActionItems_maxThree_doNotCondensePower() {
+ mGlobalActionsDialog = spy(mGlobalActionsDialog);
+ // allow 3 items to be shown
+ doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
+ // make sure lockdown action will be shown
+ doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
+ // ensure items are not blocked by keyguard or device provisioning
+ doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
+ String[] actions = {
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
};
doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
mGlobalActionsDialog.createActionItems();
assertEquals(3, mGlobalActionsDialog.mItems.size());
assertEquals(1, mGlobalActionsDialog.mOverflowItems.size());
+ assertEquals(0, mGlobalActionsDialog.mPowerItems.size());
}
@Test
@@ -270,11 +387,13 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
doReturn(Integer.MAX_VALUE).when(mGlobalActionsDialog).getMaxShownPowerItems();
// ensure items are not blocked by keyguard or device provisioning
doReturn(true).when(mGlobalActionsDialog).shouldShowAction(any());
+ // make sure lockdown action will be shown
+ doReturn(true).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
String[] actions = {
GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
- GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
};
doReturn(actions).when(mGlobalActionsDialog).getDefaultActions();
mGlobalActionsDialog.createActionItems();
@@ -288,10 +407,12 @@ public class GlobalActionsDialogTest extends SysuiTestCase {
mGlobalActionsDialog = spy(mGlobalActionsDialog);
// allow only 3 items to be shown
doReturn(3).when(mGlobalActionsDialog).getMaxShownPowerItems();
+ // make sure lockdown action will NOT be shown
+ doReturn(false).when(mGlobalActionsDialog).shouldDisplayLockdown(any());
String[] actions = {
GlobalActionsDialog.GLOBAL_ACTION_KEY_EMERGENCY,
- // screenshot blocked because device not provisioned
- GlobalActionsDialog.GLOBAL_ACTION_KEY_SCREENSHOT,
+ // lockdown action not allowed
+ GlobalActionsDialog.GLOBAL_ACTION_KEY_LOCKDOWN,
GlobalActionsDialog.GLOBAL_ACTION_KEY_POWER,
GlobalActionsDialog.GLOBAL_ACTION_KEY_RESTART,
};
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 11477395a781..5d4ef550b36c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -219,13 +219,43 @@ public class QSTileHostTest extends SysuiTestCase {
public void testNoRepeatedSpecs_customTile() {
mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);
- mQSTileHost.addTile(CUSTOM_TILE);
+ mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
assertEquals(1, mQSTileHost.mTileSpecs.size());
assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
}
@Test
+ public void testAddedAtBeginningOnDefault_customTile() {
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1"); // seed
+
+ mQSTileHost.addTile(CUSTOM_TILE);
+
+ assertEquals(2, mQSTileHost.mTileSpecs.size());
+ assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
+ }
+
+ @Test
+ public void testAddedAtBeginning_customTile() {
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1"); // seed
+
+ mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
+
+ assertEquals(2, mQSTileHost.mTileSpecs.size());
+ assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
+ }
+
+ @Test
+ public void testAddedAtEnd_customTile() {
+ mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1"); // seed
+
+ mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
+
+ assertEquals(2, mQSTileHost.mTileSpecs.size());
+ assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(1));
+ }
+
+ @Test
public void testLoadTileSpec_repeated() {
List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 1a6921a1d136..05cdd802167a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -172,11 +172,12 @@ public class AutoTileManagerTest extends SysuiTestCase {
}
@Test
- public void testSettingTileAddedComponent_onChanged() {
+ public void testSettingTileAddedComponentAtEnd_onChanged() {
changeValue(TEST_SETTING_COMPONENT, 1);
waitForIdleSync();
verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC);
- verify(mQsTileHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT));
+ verify(mQsTileHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT)
+ , /* end */ true);
}
@Test