Merge "Fix issue with translucent animations" into pi-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index 2ef2025..f7bfeae 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -204,6 +204,10 @@
package android.app.usage {
+ public class NetworkStatsManager {
+ method public void setPollForce(boolean);
+ }
+
public class StorageStatsManager {
method public boolean isQuotaSupported(java.util.UUID);
method public boolean isReservedSupported(java.util.UUID);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 919f714..569c2bd 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -575,6 +575,11 @@
void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
in Rect tempDockedTaskInsetBounds,
in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
+ /**
+ * Sets whether we are currently in an interactive split screen resize operation where we
+ * are changing the docked stack size.
+ */
+ void setSplitScreenResizing(boolean resizing);
int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
// Gets the URI permissions granted to an arbitrary package (or all packages if null)
// NOTE: this is different from getPersistedUriPermissions(), which returns the URIs the package
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 0b21196..9f46f20 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.usage.NetworkStats.Bucket;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -111,7 +112,9 @@
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
/** @hide */
- public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 1;
+ public static final int FLAG_POLL_FORCE = 1 << 1;
+ /** @hide */
+ public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 2;
private int mFlags;
@@ -141,6 +144,16 @@
}
/** @hide */
+ @TestApi
+ public void setPollForce(boolean pollForce) {
+ if (pollForce) {
+ mFlags |= FLAG_POLL_FORCE;
+ } else {
+ mFlags &= ~FLAG_POLL_FORCE;
+ }
+ }
+
+ /** @hide */
public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) {
if (augmentWithSubscriptionPlan) {
mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index 435bcb0..90d407c 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -498,7 +498,7 @@
@Override
public int hashCode() {
// secondaryIds and vendorIds are ignored for equality/hashing
- return Objects.hash(mProgramType, mPrimaryId);
+ return mPrimaryId.hashCode();
}
@Override
@@ -507,7 +507,8 @@
if (!(obj instanceof ProgramSelector)) return false;
ProgramSelector other = (ProgramSelector) obj;
// secondaryIds and vendorIds are ignored for equality/hashing
- return other.getProgramType() == mProgramType && mPrimaryId.equals(other.getPrimaryId());
+ // programType can be inferred from primaryId, thus not checked
+ return mPrimaryId.equals(other.getPrimaryId());
}
private ProgramSelector(Parcel in) {
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 32737c5..a7d70d0 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -807,7 +807,8 @@
* @return An array of active notifications, sorted in natural order.
*/
public StatusBarNotification[] getActiveNotifications() {
- return getActiveNotifications(null, TRIM_FULL);
+ StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
+ return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
}
/**
@@ -842,7 +843,8 @@
*/
@SystemApi
public StatusBarNotification[] getActiveNotifications(int trim) {
- return getActiveNotifications(null, trim);
+ StatusBarNotification[] activeNotifications = getActiveNotifications(null, trim);
+ return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
}
/**
@@ -858,7 +860,8 @@
* same order as the key list.
*/
public StatusBarNotification[] getActiveNotifications(String[] keys) {
- return getActiveNotifications(keys, TRIM_FULL);
+ StatusBarNotification[] activeNotifications = getActiveNotifications(keys, TRIM_FULL);
+ return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
}
/**
@@ -890,6 +893,9 @@
private StatusBarNotification[] cleanUpNotificationList(
ParceledListSlice<StatusBarNotification> parceledList) {
+ if (parceledList == null || parceledList.getList() == null) {
+ return new StatusBarNotification[0];
+ }
List<StatusBarNotification> list = parceledList.getList();
ArrayList<StatusBarNotification> corruptNotifications = null;
int N = list.size();
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index e60377b..549f8b3 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -25,6 +25,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.textclassifier.TextLinks.TextLinkSpan;
import android.widget.TextView;
/**
@@ -130,64 +131,70 @@
selStart = selEnd = -1;
switch (what) {
- case CLICK:
- if (selStart == selEnd) {
- return false;
- }
+ case CLICK:
+ if (selStart == selEnd) {
+ return false;
+ }
- ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
+ ClickableSpan[] links = buffer.getSpans(selStart, selEnd, ClickableSpan.class);
- if (link.length != 1)
- return false;
+ if (links.length != 1) {
+ return false;
+ }
- link[0].onClick(widget);
- break;
+ ClickableSpan link = links[0];
+ if (link instanceof TextLinkSpan) {
+ ((TextLinkSpan) link).onClick(widget, TextLinkSpan.INVOCATION_METHOD_KEYBOARD);
+ } else {
+ link.onClick(widget);
+ }
+ break;
- case UP:
- int bestStart, bestEnd;
+ case UP:
+ int bestStart, bestEnd;
- bestStart = -1;
- bestEnd = -1;
+ bestStart = -1;
+ bestEnd = -1;
- for (int i = 0; i < candidates.length; i++) {
- int end = buffer.getSpanEnd(candidates[i]);
+ for (int i = 0; i < candidates.length; i++) {
+ int end = buffer.getSpanEnd(candidates[i]);
- if (end < selEnd || selStart == selEnd) {
- if (end > bestEnd) {
- bestStart = buffer.getSpanStart(candidates[i]);
- bestEnd = end;
+ if (end < selEnd || selStart == selEnd) {
+ if (end > bestEnd) {
+ bestStart = buffer.getSpanStart(candidates[i]);
+ bestEnd = end;
+ }
}
}
- }
- if (bestStart >= 0) {
- Selection.setSelection(buffer, bestEnd, bestStart);
- return true;
- }
+ if (bestStart >= 0) {
+ Selection.setSelection(buffer, bestEnd, bestStart);
+ return true;
+ }
- break;
+ break;
- case DOWN:
- bestStart = Integer.MAX_VALUE;
- bestEnd = Integer.MAX_VALUE;
+ case DOWN:
+ bestStart = Integer.MAX_VALUE;
+ bestEnd = Integer.MAX_VALUE;
- for (int i = 0; i < candidates.length; i++) {
- int start = buffer.getSpanStart(candidates[i]);
+ for (int i = 0; i < candidates.length; i++) {
+ int start = buffer.getSpanStart(candidates[i]);
- if (start > selStart || selStart == selEnd) {
- if (start < bestStart) {
- bestStart = start;
- bestEnd = buffer.getSpanEnd(candidates[i]);
+ if (start > selStart || selStart == selEnd) {
+ if (start < bestStart) {
+ bestStart = start;
+ bestEnd = buffer.getSpanEnd(candidates[i]);
+ }
}
}
- }
- if (bestEnd < Integer.MAX_VALUE) {
- Selection.setSelection(buffer, bestStart, bestEnd);
- return true;
- }
+ if (bestEnd < Integer.MAX_VALUE) {
+ Selection.setSelection(buffer, bestStart, bestEnd);
+ return true;
+ }
- break;
+ break;
}
return false;
@@ -215,8 +222,14 @@
ClickableSpan[] links = buffer.getSpans(off, off, ClickableSpan.class);
if (links.length != 0) {
+ ClickableSpan link = links[0];
if (action == MotionEvent.ACTION_UP) {
- links[0].onClick(widget);
+ if (link instanceof TextLinkSpan) {
+ ((TextLinkSpan) link).onClick(
+ widget, TextLinkSpan.INVOCATION_METHOD_TOUCH);
+ } else {
+ link.onClick(widget);
+ }
} else if (action == MotionEvent.ACTION_DOWN) {
if (widget.getContext().getApplicationInfo().targetSdkVersion
>= Build.VERSION_CODES.P) {
@@ -225,8 +238,8 @@
widget.hideFloatingToolbar(HIDE_FLOATING_TOOLBAR_DELAY_MS);
}
Selection.setSelection(buffer,
- buffer.getSpanStart(links[0]),
- buffer.getSpanEnd(links[0]));
+ buffer.getSpanStart(link),
+ buffer.getSpanEnd(link));
}
return true;
} else {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6486230..8395681 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -342,12 +342,6 @@
int getDockedStackSide();
/**
- * Sets whether we are currently in a drag resize operation where we are changing the docked
- * stack size.
- */
- void setDockedStackResizing(boolean resizing);
-
- /**
* Sets the region the user can touch the divider. This region will be excluded from the region
* which is used to cause a focus switch when dispatching touch.
*/
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 851b2c9..e7faf14 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -503,6 +503,22 @@
*/
public static class TextLinkSpan extends ClickableSpan {
+ /**
+ * How the clickspan is triggered.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({INVOCATION_METHOD_UNSPECIFIED, INVOCATION_METHOD_TOUCH,
+ INVOCATION_METHOD_KEYBOARD})
+ public @interface InvocationMethod {}
+
+ /** @hide */
+ public static final int INVOCATION_METHOD_UNSPECIFIED = -1;
+ /** @hide */
+ public static final int INVOCATION_METHOD_TOUCH = 0;
+ /** @hide */
+ public static final int INVOCATION_METHOD_KEYBOARD = 1;
+
private final TextLink mTextLink;
public TextLinkSpan(@NonNull TextLink textLink) {
@@ -511,16 +527,24 @@
@Override
public void onClick(View widget) {
+ onClick(widget, INVOCATION_METHOD_UNSPECIFIED);
+ }
+
+ /** @hide */
+ public final void onClick(View widget, @InvocationMethod int invocationMethod) {
if (widget instanceof TextView) {
final TextView textView = (TextView) widget;
final Context context = textView.getContext();
if (TextClassificationManager.getSettings(context).isSmartLinkifyEnabled()) {
- if (textView.requestFocus()) {
- textView.requestActionMode(this);
- } else {
- // If textView can not take focus, then simply handle the click as it will
- // be difficult to get rid of the floating action mode.
- textView.handleClick(this);
+ switch (invocationMethod) {
+ case INVOCATION_METHOD_TOUCH:
+ textView.requestActionMode(this);
+ break;
+ case INVOCATION_METHOD_KEYBOARD:// fall though
+ case INVOCATION_METHOD_UNSPECIFIED: // fall through
+ default:
+ textView.handleClick(this);
+ break;
}
} else {
if (mTextLink.mUrlSpan != null) {
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 2ce5a0b..63c2e96 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1176,6 +1176,9 @@
final boolean showIcon = isFirstItem && menuItem.getItemId() == R.id.textAssist;
final View menuItemButton = createMenuItemButton(
mContext, menuItem, mIconTextSpacing, showIcon);
+ if (!showIcon && menuItemButton instanceof LinearLayout) {
+ ((LinearLayout) menuItemButton).setGravity(Gravity.CENTER);
+ }
// Adding additional start padding for the first button to even out button spacing.
if (isFirstItem) {
@@ -1200,57 +1203,21 @@
final int menuItemButtonWidth = Math.min(
menuItemButton.getMeasuredWidth(), toolbarWidth);
- final boolean isNewGroup = !isFirstItem && lastGroupId != menuItem.getGroupId();
- final int extraPadding = isNewGroup ? menuItemButton.getPaddingEnd() * 2 : 0;
-
// Check if we can fit an item while reserving space for the overflowButton.
final boolean canFitWithOverflow =
menuItemButtonWidth <=
- availableWidth - mOverflowButtonSize.getWidth() - extraPadding;
+ availableWidth - mOverflowButtonSize.getWidth();
final boolean canFitNoOverflow =
- isLastItem && menuItemButtonWidth <= availableWidth - extraPadding;
+ isLastItem && menuItemButtonWidth <= availableWidth;
if (canFitWithOverflow || canFitNoOverflow) {
- if (isNewGroup) {
- final View divider = createDivider(mContext);
- final int dividerWidth = divider.getLayoutParams().width;
-
- // Add extra padding to the end of the previous button.
- // Half of the extra padding (less borderWidth) goes to the previous button.
- final View previousButton = mMainPanel.getChildAt(
- mMainPanel.getChildCount() - 1);
- final int prevPaddingEnd = previousButton.getPaddingEnd()
- + extraPadding / 2 - dividerWidth;
- previousButton.setPaddingRelative(
- previousButton.getPaddingStart(),
- previousButton.getPaddingTop(),
- prevPaddingEnd,
- previousButton.getPaddingBottom());
- final ViewGroup.LayoutParams prevParams = previousButton.getLayoutParams();
- prevParams.width += extraPadding / 2 - dividerWidth;
- previousButton.setLayoutParams(prevParams);
-
- // Add extra padding to the start of this button.
- // Other half of the extra padding goes to this button.
- final int paddingStart = menuItemButton.getPaddingStart()
- + extraPadding / 2;
- menuItemButton.setPaddingRelative(
- paddingStart,
- menuItemButton.getPaddingTop(),
- menuItemButton.getPaddingEnd(),
- menuItemButton.getPaddingBottom());
-
- // Include a divider.
- mMainPanel.addView(divider);
- }
-
setButtonTagAndClickListener(menuItemButton, menuItem);
// Set tooltips for main panel items, but not overflow items (b/35726766).
menuItemButton.setTooltipText(menuItem.getTooltipText());
mMainPanel.addView(menuItemButton);
final ViewGroup.LayoutParams params = menuItemButton.getLayoutParams();
- params.width = menuItemButtonWidth + extraPadding / 2;
+ params.width = menuItemButtonWidth;
menuItemButton.setLayoutParams(params);
- availableWidth -= menuItemButtonWidth + extraPadding;
+ availableWidth -= menuItemButtonWidth;
remainingMenuItems.pop();
} else {
break;
@@ -1726,30 +1693,6 @@
return popupWindow;
}
- private static View createDivider(Context context) {
- // TODO: Inflate this instead.
- View divider = new View(context);
-
- int _1dp = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
- _1dp, ViewGroup.LayoutParams.MATCH_PARENT);
- params.setMarginsRelative(0, _1dp * 10, 0, _1dp * 10);
- divider.setLayoutParams(params);
-
- TypedArray a = context.obtainStyledAttributes(
- new TypedValue().data, new int[] { R.attr.floatingToolbarDividerColor });
- divider.setBackgroundColor(a.getColor(0, 0));
- a.recycle();
-
- divider.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- divider.setEnabled(false);
- divider.setFocusable(false);
- divider.setContentDescription(null);
-
- return divider;
- }
-
/**
* Creates an "appear" animation for the specified view.
*
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 2aa40fd..a135b28 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -536,7 +536,7 @@
<dimen name="floating_toolbar_menu_image_width">24dp</dimen>
<dimen name="floating_toolbar_menu_image_button_width">56dp</dimen>
<dimen name="floating_toolbar_menu_image_button_vertical_padding">12dp</dimen>
- <dimen name="floating_toolbar_menu_button_side_padding">11dp</dimen>
+ <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
<dimen name="floating_toolbar_overflow_image_button_width">60dp</dimen>
<dimen name="floating_toolbar_overflow_side_padding">18dp</dimen>
<dimen name="floating_toolbar_text_size">14sp</dimen>
diff --git a/core/tests/coretests/src/android/graphics/RectTest.java b/core/tests/coretests/src/android/graphics/RectTest.java
new file mode 100644
index 0000000..d31d7d5
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/RectTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 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 android.graphics;
+
+import static android.graphics.Rect.copyOrNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class RectTest {
+
+ @Test
+ public void copyOrNull_passesThroughNull() {
+ assertNull(copyOrNull(null));
+ }
+
+ @Test
+ public void copyOrNull_copiesNonNull() {
+ final Rect orig = new Rect(1, 2, 3, 4);
+ final Rect copy = copyOrNull(orig);
+
+ assertEquals(orig, copy);
+ assertNotSame(orig, copy);
+ }
+}
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index aff942d..3843cb9 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -17,6 +17,7 @@
package android.graphics;
import android.annotation.CheckResult;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -99,6 +100,16 @@
}
}
+ /**
+ * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
+ *
+ * @hide
+ */
+ @Nullable
+ public static Rect copyOrNull(@Nullable Rect r) {
+ return r == null ? null : new Rect(r);
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
new file mode 100644
index 0000000..e92b36a
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/SliceBroadcastRelay.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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.settingslib;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+
+/**
+ * Utility class that allows Settings to use SystemUI to relay broadcasts related to pinned slices.
+ */
+public class SliceBroadcastRelay {
+
+ public static final String ACTION_REGISTER
+ = "com.android.settingslib.action.REGISTER_SLICE_RECEIVER";
+ public static final String ACTION_UNREGISTER
+ = "com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER";
+ public static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+
+ public static final String EXTRA_URI = "uri";
+ public static final String EXTRA_RECEIVER = "receiver";
+ public static final String EXTRA_FILTER = "filter";
+
+ public static void registerReceiver(Context context, Uri registerKey,
+ Class<? extends BroadcastReceiver> receiver, IntentFilter filter) {
+ Intent registerBroadcast = new Intent(ACTION_REGISTER);
+ registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
+ registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+ Process.myUserHandle().getIdentifier()));
+ registerBroadcast.putExtra(EXTRA_RECEIVER,
+ new ComponentName(context.getPackageName(), receiver.getName()));
+ registerBroadcast.putExtra(EXTRA_FILTER, filter);
+
+ context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+ }
+
+ public static void unregisterReceivers(Context context, Uri registerKey) {
+ Intent registerBroadcast = new Intent(ACTION_UNREGISTER);
+ registerBroadcast.setPackage(SYSTEMUI_PACKAGE);
+ registerBroadcast.putExtra(EXTRA_URI, ContentProvider.maybeAddUserId(registerKey,
+ Process.myUserHandle().getIdentifier()));
+
+ context.sendBroadcastAsUser(registerBroadcast, UserHandle.SYSTEM);
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
index ad300f4..53f7e44 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java
@@ -50,6 +50,4 @@
public abstract void onStateChanged(State state);
public abstract int getDetailY();
-
- public void setExpansion(float expansion) {}
}
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 74c22b0..49d142a 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -76,7 +76,7 @@
android:layout_below="@id/label_group"
android:clickable="false"
android:ellipsize="marquee"
- android:maxLines="1"
+ android:singleLine="true"
android:padding="0dp"
android:visibility="gone"
android:gravity="center"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f49d3de4..7eb08c4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -350,6 +350,7 @@
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+ <item>com.android.systemui.SliceBroadcastRelayHandler</item>
</string-array>
<!-- SystemUI vender service, used in config_systemUIServiceComponents. -->
diff --git a/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
new file mode 100644
index 0000000..68f5836
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SliceBroadcastRelayHandler.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2018 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;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.SliceBroadcastRelay;
+
+/**
+ * Allows settings to register certain broadcasts to launch the settings app for pinned slices.
+ * @see SliceBroadcastRelay
+ */
+public class SliceBroadcastRelayHandler extends SystemUI {
+ private static final String TAG = "SliceBroadcastRelay";
+ private static final boolean DEBUG = false;
+
+ private final ArrayMap<Uri, BroadcastRelay> mRelays = new ArrayMap<>();
+
+ @Override
+ public void start() {
+ if (DEBUG) Log.d(TAG, "Start");
+ IntentFilter filter = new IntentFilter(SliceBroadcastRelay.ACTION_REGISTER);
+ filter.addAction(SliceBroadcastRelay.ACTION_UNREGISTER);
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ @VisibleForTesting
+ void handleIntent(Intent intent) {
+ if (SliceBroadcastRelay.ACTION_REGISTER.equals(intent.getAction())) {
+ Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+ ComponentName receiverClass =
+ intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_RECEIVER);
+ IntentFilter filter = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_FILTER);
+ if (DEBUG) Log.d(TAG, "Register " + uri + " " + receiverClass + " " + filter);
+ getOrCreateRelay(uri).register(mContext, receiverClass, filter);
+ } else if (SliceBroadcastRelay.ACTION_UNREGISTER.equals(intent.getAction())) {
+ Uri uri = intent.getParcelableExtra(SliceBroadcastRelay.EXTRA_URI);
+ if (DEBUG) Log.d(TAG, "Unregister " + uri);
+ getAndRemoveRelay(uri).unregister(mContext);
+ }
+ }
+
+ private BroadcastRelay getOrCreateRelay(Uri uri) {
+ BroadcastRelay ret = mRelays.get(uri);
+ if (ret == null) {
+ ret = new BroadcastRelay(uri);
+ mRelays.put(uri, ret);
+ }
+ return ret;
+ }
+
+ private BroadcastRelay getAndRemoveRelay(Uri uri) {
+ return mRelays.remove(uri);
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleIntent(intent);
+ }
+ };
+
+ private static class BroadcastRelay extends BroadcastReceiver {
+
+ private final ArraySet<ComponentName> mReceivers = new ArraySet<>();
+ private final UserHandle mUserId;
+
+ public BroadcastRelay(Uri uri) {
+ mUserId = new UserHandle(ContentProvider.getUserIdFromUri(uri));
+ }
+
+ public void register(Context context, ComponentName receiver, IntentFilter filter) {
+ mReceivers.add(receiver);
+ context.registerReceiver(this, filter);
+ }
+
+ public void unregister(Context context) {
+ context.unregisterReceiver(this);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ for (ComponentName receiver : mReceivers) {
+ intent.setComponent(receiver);
+ if (DEBUG) Log.d(TAG, "Forwarding " + receiver + " " + intent + " " + mUserId);
+ context.sendBroadcastAsUser(intent, mUserId);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 03a76da..b7ff984 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -114,6 +114,8 @@
mHost.dozeTimeTick();
// The first frame may arrive when the display isn't ready yet.
mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 100);
+ // The the delayed frame may arrive when the display isn't ready yet either.
+ mHandler.postDelayed(mWakeLock.wrap(mHost::dozeTimeTick), 1000);
}
scheduleTimeTick();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index d8d07c0..1fd6023 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -56,6 +56,7 @@
private AnimatorSet mBounceAnimatorSet;
private int mAnimatingToPage = -1;
+ private float mLastExpansion;
public PagedTileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -172,8 +173,19 @@
@Override
public void setExpansion(float expansion) {
- for (TileRecord tr : mTiles) {
- tr.tileView.setExpansion(expansion);
+ mLastExpansion = expansion;
+ updateSelected();
+ }
+
+ private void updateSelected() {
+ // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
+ // other expansion ratios since there is no way way to pause the marquee.
+ if (mLastExpansion > 0f && mLastExpansion < 1f) {
+ return;
+ }
+ boolean selected = mLastExpansion == 1f;
+ for (int i = 0; i < mPages.size(); i++) {
+ mPages.get(i).setSelected(i == getCurrentItem() ? selected : false);
}
}
@@ -323,6 +335,7 @@
new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
+ updateSelected();
if (mPageIndicator == null) return;
if (mPageListener != null) {
mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index d21b06f..5649f7f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -107,15 +107,6 @@
}
@Override
- public void setExpansion(float expansion) {
- // Start the marquee when fully expanded and stop when fully collapsed. Leave as is for
- // other expansion ratios since there is no way way to pause the marquee.
- boolean selected = expansion == 1f ? true : expansion == 0f ? false : mLabel.isSelected();
- mLabel.setSelected(selected);
- mSecondLine.setSelected(selected);
- }
-
- @Override
protected void handleStateChanged(QSTile.State state) {
super.handleStateChanged(state);
if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 85a6062..1e5b37c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -180,7 +180,7 @@
@Override
public void run() {
try {
- WindowManagerGlobal.getWindowManagerService().setDockedStackResizing(resizing);
+ ActivityManager.getService().setSplitScreenResizing(resizing);
} catch (RemoteException e) {
Log.w(TAG, "Error calling setDockedStackResizing: " + e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index dbd1cd4..f1e2302 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -20,7 +20,9 @@
import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.os.AsyncTask;
+import android.os.UserHandle;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -31,6 +33,7 @@
import androidx.car.widget.PagedListView;
+import com.android.internal.util.UserIcons;
import com.android.settingslib.users.UserManagerHelper;
import com.android.systemui.R;
@@ -180,13 +183,7 @@
@Override
public void onBindViewHolder(UserAdapterViewHolder holder, int position) {
UserRecord userRecord = mUsers.get(position);
- if (!userRecord.mIsAddUser) {
- holder.mUserAvatarImageView.setImageBitmap(mUserManagerHelper
- .getUserIcon(userRecord.mInfo));
- } else {
- holder.mUserAvatarImageView.setImageDrawable(mContext
- .getDrawable(R.drawable.car_add_circle_round));
- }
+ holder.mUserAvatarImageView.setImageBitmap(getUserRecordIcon(userRecord));
holder.mUserNameTextView.setText(userRecord.mInfo.name);
holder.mView.setOnClickListener(v -> {
if (userRecord == null) {
@@ -219,6 +216,20 @@
}
+ private Bitmap getUserRecordIcon(UserRecord userRecord) {
+ if (userRecord.mIsStartGuestSession) {
+ return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
+ mContext.getResources(), UserHandle.USER_NULL, false));
+ }
+
+ if (userRecord.mIsAddUser) {
+ return UserIcons.convertToBitmap(mContext
+ .getDrawable(R.drawable.car_add_circle_round));
+ }
+
+ return mUserManagerHelper.getUserIcon(userRecord.mInfo);
+ }
+
private class AddNewUserTask extends AsyncTask<String, Void, UserInfo> {
@Override
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 767a24a..1be8322 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -57,6 +57,12 @@
<service
android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService"
android:process=":killable" />
+
+ <receiver android:name="com.android.systemui.SliceBroadcastRelayHandlerTest$Receiver">
+ <intent-filter>
+ <action android:name="com.android.systemui.action.TEST_ACTION" />
+ </intent-filter>
+ </receiver>
</application>
<instrumentation android:name="android.testing.TestableInstrumentation"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
new file mode 100644
index 0000000..4abac56
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SliceBroadcastRelayHandlerTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.settingslib.SliceBroadcastRelay;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class SliceBroadcastRelayHandlerTest extends SysuiTestCase {
+
+ private static final String TEST_ACTION = "com.android.systemui.action.TEST_ACTION";
+
+ @Test
+ public void testRegister() {
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+ IntentFilter value = new IntentFilter(TEST_ACTION);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+ relayHandler.handleIntent(intent);
+ verify(relayHandler.mContext).registerReceiver(any(), eq(value));
+ }
+
+ @Test
+ public void testUnregister() {
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+ IntentFilter value = new IntentFilter(TEST_ACTION);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+ relayHandler.handleIntent(intent);
+ ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+
+ intent = new Intent(SliceBroadcastRelay.ACTION_UNREGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ relayHandler.handleIntent(intent);
+ verify(relayHandler.mContext).unregisterReceiver(eq(relay.getValue()));
+ }
+
+ @Test
+ public void testRelay() {
+ Receiver.sReceiver = mock(BroadcastReceiver.class);
+ Uri testUri = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority("something")
+ .path("test")
+ .build();
+ SliceBroadcastRelayHandler relayHandler = new SliceBroadcastRelayHandler();
+ relayHandler.mContext = spy(mContext);
+
+ Intent intent = new Intent(SliceBroadcastRelay.ACTION_REGISTER);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_URI, ContentProvider.maybeAddUserId(testUri, 0));
+ intent.putExtra(SliceBroadcastRelay.EXTRA_RECEIVER,
+ new ComponentName(mContext.getPackageName(), Receiver.class.getName()));
+ IntentFilter value = new IntentFilter(TEST_ACTION);
+ intent.putExtra(SliceBroadcastRelay.EXTRA_FILTER, value);
+
+ relayHandler.handleIntent(intent);
+ ArgumentCaptor<BroadcastReceiver> relay = ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(relayHandler.mContext).registerReceiver(relay.capture(), eq(value));
+ relay.getValue().onReceive(relayHandler.mContext, new Intent(TEST_ACTION));
+
+ verify(Receiver.sReceiver, timeout(2000)).onReceive(any(), any());
+ }
+
+ public static class Receiver extends BroadcastReceiver {
+ private static BroadcastReceiver sReceiver;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (sReceiver != null) sReceiver.onReceive(context, intent);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6718d95..62a055d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -11451,6 +11451,19 @@
}
@Override
+ public void setSplitScreenResizing(boolean resizing) {
+ enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ mStackSupervisor.setSplitScreenResizing(resizing);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e20356f..95bae2e 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1743,6 +1743,11 @@
return getDisplay().isTopStack(this);
}
+ boolean isTopActivityVisible() {
+ final ActivityRecord topActivity = getTopActivity();
+ return topActivity != null && topActivity.visible;
+ }
+
/**
* Returns true if the stack should be visible.
*
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6a3587c..0dc2445 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -43,6 +43,7 @@
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.graphics.Rect.copyOrNull;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
@@ -245,6 +246,20 @@
// the activity callback indicating that it has completed pausing
static final boolean PAUSE_IMMEDIATELY = true;
+ /** True if the docked stack is currently being resized. */
+ private boolean mDockedStackResizing;
+
+ /**
+ * True if there are pending docked bounds that need to be applied after
+ * {@link #mDockedStackResizing} is reset to false.
+ */
+ private boolean mHasPendingDockedBounds;
+ private Rect mPendingDockedBounds;
+ private Rect mPendingTempDockedTaskBounds;
+ private Rect mPendingTempDockedTaskInsetBounds;
+ private Rect mPendingTempOtherTaskBounds;
+ private Rect mPendingTempOtherTaskInsetBounds;
+
/**
* The modes which affect which tasks are returned when calling
* {@link ActivityStackSupervisor#anyTaskForIdLocked(int)}.
@@ -2708,6 +2723,28 @@
moveTasksToFullscreenStackInSurfaceTransaction(fromStack, toDisplayId, onTop));
}
+ void setSplitScreenResizing(boolean resizing) {
+ if (resizing == mDockedStackResizing) {
+ return;
+ }
+
+ mDockedStackResizing = resizing;
+ mWindowManager.setDockedStackResizing(resizing);
+
+ if (!resizing && mHasPendingDockedBounds) {
+ resizeDockedStackLocked(mPendingDockedBounds, mPendingTempDockedTaskBounds,
+ mPendingTempDockedTaskInsetBounds, mPendingTempOtherTaskBounds,
+ mPendingTempOtherTaskInsetBounds, PRESERVE_WINDOWS);
+
+ mHasPendingDockedBounds = false;
+ mPendingDockedBounds = null;
+ mPendingTempDockedTaskBounds = null;
+ mPendingTempDockedTaskInsetBounds = null;
+ mPendingTempOtherTaskBounds = null;
+ mPendingTempOtherTaskInsetBounds = null;
+ }
+ }
+
void resizeDockedStackLocked(Rect dockedBounds, Rect tempDockedTaskBounds,
Rect tempDockedTaskInsetBounds, Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds,
boolean preserveWindows) {
@@ -2731,6 +2768,15 @@
return;
}
+ if (mDockedStackResizing) {
+ mHasPendingDockedBounds = true;
+ mPendingDockedBounds = copyOrNull(dockedBounds);
+ mPendingTempDockedTaskBounds = copyOrNull(tempDockedTaskBounds);
+ mPendingTempDockedTaskInsetBounds = copyOrNull(tempDockedTaskInsetBounds);
+ mPendingTempOtherTaskBounds = copyOrNull(tempOtherTaskBounds);
+ mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
+ }
+
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
mWindowManager.deferSurfaceLayout();
try {
@@ -2765,6 +2811,11 @@
if (!current.affectedBySplitScreenResize()) {
continue;
}
+ if (mDockedStackResizing && !current.isTopActivityVisible()) {
+ // Non-visible stacks get resized once we're done with the resize
+ // interaction.
+ continue;
+ }
// Need to set windowing mode here before we try to get the dock bounds.
current.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
current.getStackDockedModeBounds(
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index ae7058d..9ef6c66 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -544,7 +544,8 @@
final int usedFlags = isRateLimitedForPoll(callingUid)
? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
: flags;
- if ((usedFlags & NetworkStatsManager.FLAG_POLL_ON_OPEN) != 0) {
+ if ((usedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
+ | NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
final long ident = Binder.clearCallingIdentity();
try {
performPoll(FLAG_PERSIST_ALL);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 4c8b91b..477b062 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -926,7 +926,11 @@
Slog.v(TAG, " disconnecting old " + getCaption() + ": " + info.service);
removeServiceLocked(i);
if (info.connection != null) {
- mContext.unbindService(info.connection);
+ try {
+ mContext.unbindService(info.connection);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "failed to unbind " + name, e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 608d0aa..68be50c 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -811,36 +811,35 @@
return;
}
mInvalidated = false;
- Canvas canvas = null;
- try {
- // Empty dirty rectangle means unspecified.
- if (mDirtyRect.isEmpty()) {
- mBounds.getBounds(mDirtyRect);
- }
- mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
- canvas = mSurface.lockCanvas(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
- }
- } catch (IllegalArgumentException iae) {
- /* ignore */
- } catch (Surface.OutOfResourcesException oore) {
- /* ignore */
- }
- if (canvas == null) {
- return;
- }
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Bounds: " + mBounds);
- }
- canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
- mPaint.setAlpha(mAlpha);
- Path path = mBounds.getBoundaryPath();
- canvas.drawPath(path, mPaint);
-
- mSurface.unlockCanvasAndPost(canvas);
-
if (mAlpha > 0) {
+ Canvas canvas = null;
+ try {
+ // Empty dirty rectangle means unspecified.
+ if (mDirtyRect.isEmpty()) {
+ mBounds.getBounds(mDirtyRect);
+ }
+ mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
+ canvas = mSurface.lockCanvas(mDirtyRect);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
+ }
+ } catch (IllegalArgumentException iae) {
+ /* ignore */
+ } catch (Surface.OutOfResourcesException oore) {
+ /* ignore */
+ }
+ if (canvas == null) {
+ return;
+ }
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "Bounds: " + mBounds);
+ }
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mPaint.setAlpha(mAlpha);
+ Path path = mBounds.getBoundaryPath();
+ canvas.drawPath(path, mPaint);
+
+ mSurface.unlockCanvasAndPost(canvas);
mSurfaceControl.show();
} else {
mSurfaceControl.hide();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b1b026e..09e43f8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6864,7 +6864,6 @@
}
}
- @Override
public void setDockedStackResizing(boolean resizing) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizing(resizing);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6af730f..6c2821d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2405,6 +2405,7 @@
@Override
public void binderDied() {
try {
+ boolean resetSplitScreenResizing = false;
synchronized(mService.mWindowMap) {
final WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
@@ -2424,13 +2425,23 @@
if (stack != null) {
stack.resetDockedStackToMiddle();
}
- mService.setDockedStackResizing(false);
+ resetSplitScreenResizing = true;
}
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
WindowState.this.removeIfPossible();
}
}
+ if (resetSplitScreenResizing) {
+ try {
+ // Note: this calls into ActivityManager, so we must *not* hold the window
+ // manager lock while calling this.
+ mService.mActivityManager.setSplitScreenResizing(false);
+ } catch (RemoteException e) {
+ // Local call, shouldn't return RemoteException.
+ throw e.rethrowAsRuntimeException();
+ }
+ }
} catch (IllegalArgumentException ex) {
// This will happen if the window has already been removed.
}
@@ -4706,16 +4717,38 @@
outPoint.offset(-mAttrs.surfaceInsets.left, -mAttrs.surfaceInsets.top);
}
+ boolean needsRelativeLayeringToIme() {
+ // We only use the relative layering mode in split screen, as part of elevating the IME
+ // and windows above it's target above the docked divider.
+ if (!inSplitScreenWindowingMode()) {
+ return false;
+ }
+
+ if (isChildWindow()) {
+ // If we are a child of the input method target we need this promotion.
+ if (getParentWindow().isInputMethodTarget()) {
+ return true;
+ }
+ } else if (mAppToken != null) {
+ // Likewise if we share a token with the Input method target and are ordered
+ // above it but not necessarily a child (e.g. a Dialog) then we also need
+ // this promotion.
+ final WindowState imeTarget = mService.mInputMethodTarget;
+ boolean inTokenWithAndAboveImeTarget = imeTarget != null && imeTarget != this
+ && imeTarget.mToken == mToken && imeTarget.compareTo(this) <= 0;
+ return inTokenWithAndAboveImeTarget;
+ }
+ return false;
+ }
+
@Override
void assignLayer(Transaction t, int layer) {
// See comment in assignRelativeLayerForImeTargetChild
- if (!isChildWindow()
- || (!getParentWindow().isInputMethodTarget())
- || !inSplitScreenWindowingMode()) {
- super.assignLayer(t, layer);
+ if (needsRelativeLayeringToIme()) {
+ getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
return;
}
- getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
+ super.assignLayer(t, layer);
}
@Override
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index fcfb19c..99b2fc6 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -841,30 +841,30 @@
/**
* Add an instruction to the end of the program to load 32 bits from the data memory into
- * {@code register}. The source address is computed by adding @{code offset} to the other
- * register.
+ * {@code register}. The source address is computed by adding the signed immediate
+ * @{code offset} to the other register.
* Requires APF v3 or greater.
*/
public ApfGenerator addLoadData(Register destinationRegister, int offset)
throws IllegalInstructionException {
requireApfVersion(3);
Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister);
- instruction.setUnsignedImm(offset);
+ instruction.setSignedImm(offset);
addInstruction(instruction);
return this;
}
/**
* Add an instruction to the end of the program to store 32 bits from {@code register} into the
- * data memory. The destination address is computed by adding @{code offset} to the other
- * register.
+ * data memory. The destination address is computed by adding the signed immediate
+ * @{code offset} to the other register.
* Requires APF v3 or greater.
*/
public ApfGenerator addStoreData(Register sourceRegister, int offset)
throws IllegalInstructionException {
requireApfVersion(3);
Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister);
- instruction.setUnsignedImm(offset);
+ instruction.setSignedImm(offset);
addInstruction(instruction);
return this;
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 181fceb..ef9ba78 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -24,7 +24,13 @@
.USER_SENTIMENT_POSITIVE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.content.Intent;
import android.os.Binder;
@@ -34,6 +40,7 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -52,6 +59,19 @@
private String[] mKeys = new String[] { "key", "key1", "key2", "key3"};
@Test
+ public void testGetActiveNotifications_notNull() throws Exception {
+ TestListenerService service = new TestListenerService();
+ INotificationManager noMan = service.getNoMan();
+ when(noMan.getActiveNotificationsFromListener(any(), any(), anyInt())).thenReturn(null);
+
+ assertNotNull(service.getActiveNotifications());
+ assertNotNull(service.getActiveNotifications(NotificationListenerService.TRIM_FULL));
+ assertNotNull(service.getActiveNotifications(new String[0]));
+ assertNotNull(service.getActiveNotifications(
+ new String[0], NotificationListenerService.TRIM_LIGHT));
+ }
+
+ @Test
public void testRanking() throws Exception {
TestListenerService service = new TestListenerService();
service.applyUpdateLocked(generateUpdate());
@@ -180,7 +200,12 @@
private final IBinder binder = new LocalBinder();
public TestListenerService() {
+ mWrapper = mock(NotificationListenerWrapper.class);
+ mNoMan = mock(INotificationManager.class);
+ }
+ INotificationManager getNoMan() {
+ return mNoMan;
}
@Override
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 082f310..f8a4132 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -160,7 +160,7 @@
throw new Exception(
"program: " + HexDump.toHexString(program) +
"\ndata memory: " + HexDump.toHexString(data) +
- "\nexpected: " + HexDump.toHexString(expected_data));
+ "\nexpected: " + HexDump.toHexString(expected_data));
}
}
@@ -625,18 +625,19 @@
@Test
public void testApfDataWrite() throws IllegalInstructionException, Exception {
byte[] packet = new byte[MIN_PKT_SIZE];
- byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
+ byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
byte[] expected_data = data.clone();
// No memory access instructions: should leave the data segment untouched.
ApfGenerator gen = new ApfGenerator(3);
assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
- // Expect value 0x87654321 to be stored starting from address 3 + 2, in big-endian order.
+ // Expect value 0x87654321 to be stored starting from address -11 from the end of the
+ // data buffer, in big-endian order.
gen = new ApfGenerator(3);
gen.addLoadImmediate(Register.R0, 0x87654321);
- gen.addLoadImmediate(Register.R1, 2);
- gen.addStoreData(Register.R0, 3);
+ gen.addLoadImmediate(Register.R1, -5);
+ gen.addStoreData(Register.R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16)
expected_data[5] = (byte)0x87;
expected_data[6] = (byte)0x65;
expected_data[7] = (byte)0x43;
@@ -646,16 +647,16 @@
@Test
public void testApfDataRead() throws IllegalInstructionException, Exception {
- // Program that DROPs if address 11 (7 + 3) contains 0x87654321.
+ // Program that DROPs if address 10 (-6) contains 0x87654321.
ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addLoadData(Register.R0, 7);
+ gen.addLoadImmediate(Register.R1, 10);
+ gen.addLoadData(Register.R0, -16); // 10 + -16 = -6 (offset +10 with data_len=16)
gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL);
byte[] program = gen.generate();
byte[] packet = new byte[MIN_PKT_SIZE];
// Content is incorrect (last byte does not match) -> PASS
- byte[] data = new byte[32];
+ byte[] data = new byte[16];
data[10] = (byte)0x87;
data[11] = (byte)0x65;
data[12] = (byte)0x43;
@@ -672,10 +673,10 @@
@Test
public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception {
ApfGenerator gen = new ApfGenerator(3);
- gen.addLoadImmediate(Register.R1, 3);
- gen.addLoadData(Register.R0, 7); // Load from address 7 + 3 = 10
+ gen.addLoadImmediate(Register.R1, -22);
+ gen.addLoadData(Register.R0, 0); // Load from address 32 -22 + 0 = 10
gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733
- gen.addStoreData(Register.R0, 11); // Write back to address 11 + 3 = 14
+ gen.addStoreData(Register.R0, 4); // Write back to address 32 -22 + 4 = 14
byte[] packet = new byte[MIN_PKT_SIZE];
byte[] data = new byte[32];
@@ -718,10 +719,17 @@
gen.addJump(gen.DROP_LABEL);
assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
- // ...but underflowing isn't allowed.
+ // ...and underflowing simply wraps around to the end of the buffer...
gen = new ApfGenerator(3);
gen.addLoadImmediate(Register.R0, 20);
gen.addLoadData(Register.R1, -30);
+ gen.addJump(gen.DROP_LABEL);
+ assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data);
+
+ // ...but doesn't allow accesses before the start of the buffer
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 20);
+ gen.addLoadData(Register.R1, -1000);
gen.addJump(gen.DROP_LABEL); // Not reached.
assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data);
}
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index 79444e3..1ea9e27 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -36,11 +36,20 @@
uint32_t program_len = env->GetArrayLength(program);
uint32_t packet_len = env->GetArrayLength(packet);
uint32_t data_len = data ? env->GetArrayLength(data) : 0;
- jint result = accept_packet(program_raw, program_len, packet_raw,
- packet_len, data_raw, data_len, filter_age);
+
+ // Merge program and data into a single buffer.
+ uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len);
+ memcpy(program_and_data, program_raw, program_len);
+ memcpy(program_and_data + program_len, data_raw, data_len);
+
+ jint result =
+ accept_packet(program_and_data, program_len, program_len + data_len,
+ packet_raw, packet_len, filter_age);
if (data) {
+ memcpy(data_raw, program_and_data + program_len, data_len);
env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */);
}
+ free(program_and_data);
env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT);
env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT);
return result;
@@ -109,8 +118,8 @@
jstring jpcap_filename, jbyteArray japf_program) {
ScopedUtfChars filter(env, jfilter);
ScopedUtfChars pcap_filename(env, jpcap_filename);
- const uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
- const uint32_t apf_program_len = env->GetArrayLength(japf_program);
+ uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
+ uint32_t apf_program_len = env->GetArrayLength(japf_program);
// Open pcap file for BPF filtering
ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
@@ -152,8 +161,8 @@
do {
apf_packet = pcap_next(apf_pcap.get(), &apf_header);
} while (apf_packet != NULL && !accept_packet(
- apf_program, apf_program_len, apf_packet, apf_header.len,
- nullptr, 0, 0));
+ apf_program, apf_program_len, 0 /* data_len */,
+ apf_packet, apf_header.len, 0 /* filter_age */));
// Make sure both filters matched the same packet.
if (apf_packet == NULL && bpf_packet == NULL)
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index df483b2..d7a3771 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -306,6 +306,7 @@
}
// A value that represents a primitive data type (float, int, boolean, etc.).
+// Refer to Res_value in ResourceTypes.h for info on types and formatting
message Primitive {
message NullType {
}
@@ -315,8 +316,8 @@
NullType null_value = 1;
EmptyType empty_value = 2;
float float_value = 3;
- float dimension_value = 4;
- float fraction_value = 5;
+ uint32 dimension_value = 13;
+ uint32 fraction_value = 14;
int32 int_decimal_value = 6;
uint32 int_hexadecimal_value = 7;
bool boolean_value = 8;
@@ -324,6 +325,8 @@
uint32 color_rgb8_value = 10;
uint32 color_argb4_value = 11;
uint32 color_rgb4_value = 12;
+ float dimension_value_deprecated = 4 [deprecated=true];
+ float fraction_value_deprecated = 5 [deprecated=true];
}
}
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index f1eb952..3b101b7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -780,13 +780,11 @@
} break;
case pb::Primitive::kDimensionValue: {
val.dataType = android::Res_value::TYPE_DIMENSION;
- float dimen_val = pb_prim.dimension_value();
- val.data = *(uint32_t*)&dimen_val;
+ val.data = pb_prim.dimension_value();
} break;
case pb::Primitive::kFractionValue: {
val.dataType = android::Res_value::TYPE_FRACTION;
- float fraction_val = pb_prim.fraction_value();
- val.data = *(uint32_t*)&fraction_val;
+ val.data = pb_prim.fraction_value();
} break;
case pb::Primitive::kIntDecimalValue: {
val.dataType = android::Res_value::TYPE_INT_DEC;
@@ -816,6 +814,16 @@
val.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
val.data = pb_prim.color_rgb4_value();
} break;
+ case pb::Primitive::kDimensionValueDeprecated: { // DEPRECATED
+ val.dataType = android::Res_value::TYPE_DIMENSION;
+ float dimen_val = pb_prim.dimension_value_deprecated();
+ val.data = *(uint32_t*)&dimen_val;
+ } break;
+ case pb::Primitive::kFractionValueDeprecated: { // DEPRECATED
+ val.dataType = android::Res_value::TYPE_FRACTION;
+ float fraction_val = pb_prim.fraction_value_deprecated();
+ val.data = *(uint32_t*)&fraction_val;
+ } break;
default: {
LOG(FATAL) << "Unexpected Primitive type: "
<< static_cast<uint32_t>(pb_prim.oneof_value_case());
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 2e56359..411cc29 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -452,10 +452,10 @@
pb_prim->set_float_value(*(float*)&val.data);
} break;
case android::Res_value::TYPE_DIMENSION: {
- pb_prim->set_dimension_value(*(float*)&val.data);
+ pb_prim->set_dimension_value(val.data);
} break;
case android::Res_value::TYPE_FRACTION: {
- pb_prim->set_fraction_value(*(float*)&val.data);
+ pb_prim->set_fraction_value(val.data);
} break;
case android::Res_value::TYPE_INT_DEC: {
pb_prim->set_int_decimal_value(static_cast<int32_t>(val.data));
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 6366a3d..21fdbd8 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -271,6 +271,7 @@
.AddValue("android:integer/hex_int_abcd", ResourceUtils::TryParseInt("0xABCD"))
.AddValue("android:dimen/dimen_1.39mm", ResourceUtils::TryParseFloat("1.39mm"))
.AddValue("android:fraction/fraction_27", ResourceUtils::TryParseFloat("27%"))
+ .AddValue("android:dimen/neg_2.3in", ResourceUtils::TryParseFloat("-2.3in"))
.AddValue("android:integer/null", ResourceUtils::MakeEmpty())
.Build();
@@ -353,6 +354,12 @@
EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_FRACTION));
EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("27%")->value.data));
+ bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:dimen/neg_2.3in",
+ ConfigDescription::DefaultConfig(), "");
+ ASSERT_THAT(bp, NotNull());
+ EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_DIMENSION));
+ EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseFloat("-2.3in")->value.data));
+
bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "android:integer/null",
ConfigDescription::DefaultConfig(), "");
ASSERT_THAT(bp, NotNull());