summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt9
-rw-r--r--api/system-current.txt9
-rw-r--r--api/test-current.txt9
-rw-r--r--core/java/android/animation/PropertyValuesHolder.java27
-rw-r--r--core/java/android/app/Activity.java15
-rw-r--r--core/java/android/content/pm/PackageManager.java8
-rw-r--r--core/java/android/service/notification/NotificationAssistantService.java3
-rw-r--r--core/java/android/view/WindowManagerPolicy.java11
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java21
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java15
-rw-r--r--core/java/com/android/internal/policy/DividerSnapAlgorithm.java (renamed from packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java)32
-rw-r--r--core/java/com/android/internal/policy/DockedDividerUtils.java74
-rw-r--r--libs/hwui/Android.mk9
-rw-r--r--libs/hwui/BakedOpDispatcher.cpp4
-rw-r--r--libs/hwui/FrameReorderer.cpp (renamed from libs/hwui/OpReorderer.cpp)418
-rw-r--r--libs/hwui/FrameReorderer.h (renamed from libs/hwui/OpReorderer.h)130
-rw-r--r--libs/hwui/LayerReorderer.cpp367
-rw-r--r--libs/hwui/LayerReorderer.h135
-rw-r--r--libs/hwui/RenderNode.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp4
-rw-r--r--libs/hwui/tests/microbench/FrameReordererBench.cpp (renamed from libs/hwui/tests/microbench/OpReordererBench.cpp)28
-rw-r--r--libs/hwui/tests/unit/FrameReordererTests.cpp (renamed from libs/hwui/tests/unit/OpReordererTests.cpp)120
-rw-r--r--packages/DocumentsUI/res/menu/activity.xml10
-rw-r--r--packages/DocumentsUI/res/values/colors.xml2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java87
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java6
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java3
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java17
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java27
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java25
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java22
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java75
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java11
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java4
-rw-r--r--tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java24
40 files changed, 1090 insertions, 744 deletions
diff --git a/api/current.txt b/api/current.txt
index 693d9f4223a8..b0eb006e5b5a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9488,8 +9488,6 @@ package android.content.pm {
method public abstract int getComponentEnabledSetting(android.content.ComponentName);
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public abstract byte[] getEphemeralCookie();
- method public abstract int getEphemeralCookieMaxSizeBytes();
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9523,7 +9521,6 @@ package android.content.pm {
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract boolean hasSystemFeature(java.lang.String);
- method public abstract boolean isEphemeralApplication();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9541,7 +9538,6 @@ package android.content.pm {
method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public abstract boolean setEphemeralCookie(byte[]);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -33616,6 +33612,7 @@ package android.service.notification {
field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_TOPIC_BANNED = 14; // 0xe
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
@@ -36840,8 +36837,6 @@ package android.test.mock {
method public int getComponentEnabledSetting(android.content.ComponentName);
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public byte[] getEphemeralCookie();
- method public int getEphemeralCookieMaxSizeBytes();
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36874,7 +36869,6 @@ package android.test.mock {
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public boolean hasSystemFeature(java.lang.String);
- method public boolean isEphemeralApplication();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36892,7 +36886,6 @@ package android.test.mock {
method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public boolean setEphemeralCookie(byte[]);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void verifyPendingInstall(int, int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index d4a97b650fa2..bd7dc9742a5a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9790,8 +9790,6 @@ package android.content.pm {
method public abstract int getComponentEnabledSetting(android.content.ComponentName);
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public abstract byte[] getEphemeralCookie();
- method public abstract int getEphemeralCookieMaxSizeBytes();
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9827,7 +9825,6 @@ package android.content.pm {
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract boolean hasSystemFeature(java.lang.String);
- method public abstract boolean isEphemeralApplication();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9847,7 +9844,6 @@ package android.content.pm {
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public abstract boolean setEphemeralCookie(byte[]);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -35753,6 +35749,7 @@ package android.service.notification {
field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_TOPIC_BANNED = 14; // 0xe
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
@@ -39183,8 +39180,6 @@ package android.test.mock {
method public int getComponentEnabledSetting(android.content.ComponentName);
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public byte[] getEphemeralCookie();
- method public int getEphemeralCookieMaxSizeBytes();
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -39219,7 +39214,6 @@ package android.test.mock {
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public boolean hasSystemFeature(java.lang.String);
- method public boolean isEphemeralApplication();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -39239,7 +39233,6 @@ package android.test.mock {
method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public boolean setEphemeralCookie(byte[]);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
diff --git a/api/test-current.txt b/api/test-current.txt
index 2f24953ce7da..13d98f49b0e8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -9496,8 +9496,6 @@ package android.content.pm {
method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public abstract byte[] getEphemeralCookie();
- method public abstract int getEphemeralCookieMaxSizeBytes();
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public abstract java.lang.String getInstallerPackageName(java.lang.String);
@@ -9531,7 +9529,6 @@ package android.content.pm {
method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public abstract boolean hasSystemFeature(java.lang.String);
- method public abstract boolean isEphemeralApplication();
method public abstract boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public abstract boolean isSafeMode();
method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9549,7 +9546,6 @@ package android.content.pm {
method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public abstract boolean setEphemeralCookie(byte[]);
method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
method public abstract void verifyPendingInstall(int, int);
field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -33630,6 +33626,7 @@ package android.service.notification {
field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_TOPIC_BANNED = 14; // 0xe
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
@@ -36856,8 +36853,6 @@ package android.test.mock {
method public android.graphics.drawable.Drawable getDefaultActivityIcon();
method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
method public android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
- method public byte[] getEphemeralCookie();
- method public int getEphemeralCookieMaxSizeBytes();
method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
method public java.lang.String getInstallerPackageName(java.lang.String);
@@ -36890,7 +36885,6 @@ package android.test.mock {
method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
method public boolean hasSystemFeature(java.lang.String);
- method public boolean isEphemeralApplication();
method public boolean isPermissionRevokedByPolicy(java.lang.String, java.lang.String);
method public boolean isSafeMode();
method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -36908,7 +36902,6 @@ package android.test.mock {
method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
method public void setApplicationEnabledSetting(java.lang.String, int, int);
method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
- method public boolean setEphemeralCookie(byte[]);
method public void setInstallerPackageName(java.lang.String, java.lang.String);
method public void verifyPendingInstall(int, int);
}
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 8928e99cecd2..e993cca9e325 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -861,22 +861,23 @@ public class PropertyValuesHolder implements Cloneable {
if (mProperty != null) {
Object value = convertBack(mProperty.get(target));
kf.setValue(value);
- }
- try {
- if (mGetter == null) {
- Class targetClass = target.getClass();
- setupGetter(targetClass);
+ } else {
+ try {
if (mGetter == null) {
- // Already logged the error - just return to avoid NPE
- return;
+ Class targetClass = target.getClass();
+ setupGetter(targetClass);
+ if (mGetter == null) {
+ // Already logged the error - just return to avoid NPE
+ return;
+ }
}
+ Object value = convertBack(mGetter.invoke(target));
+ kf.setValue(value);
+ } catch (InvocationTargetException e) {
+ Log.e("PropertyValuesHolder", e.toString());
+ } catch (IllegalAccessException e) {
+ Log.e("PropertyValuesHolder", e.toString());
}
- Object value = convertBack(mGetter.invoke(target));
- kf.setValue(value);
- } catch (InvocationTargetException e) {
- Log.e("PropertyValuesHolder", e.toString());
- } catch (IllegalAccessException e) {
- Log.e("PropertyValuesHolder", e.toString());
}
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 64642ac5122d..34527c2623c6 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2843,16 +2843,15 @@ public class Activity extends ContextThemeWrapper
if (keyCode == KeyEvent.KEYCODE_MENU &&
mActionBar != null && mActionBar.onMenuKeyEvent(event)) {
return true;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
- // Capture the Alt-up and send focus to the ActionBar
+ } else if (event.isCtrlPressed() &&
+ event.getUnicodeChar(event.getMetaState() & ~KeyEvent.META_CTRL_MASK) == '<') {
+ // Capture the Control-< and send focus to the ActionBar
final int action = event.getAction();
if (action == KeyEvent.ACTION_DOWN) {
- if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
- final ActionBar actionBar = getActionBar();
- if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
- mEatKeyUpEvent = true;
- return true;
- }
+ final ActionBar actionBar = getActionBar();
+ if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) {
+ mEatKeyUpEvent = true;
+ return true;
}
} else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) {
mEatKeyUpEvent = false;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f41928ebe9d7..38abac7e741b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3258,6 +3258,8 @@ public abstract class PackageManager {
* @see #setEphemeralCookie(byte[])
* @see #getEphemeralCookie()
* @see #getEphemeralCookieMaxSizeBytes()
+ *
+ * @hide
*/
public abstract boolean isEphemeralApplication();
@@ -3270,6 +3272,8 @@ public abstract class PackageManager {
* @see #isEphemeralApplication()
* @see #setEphemeralCookie(byte[])
* @see #getEphemeralCookie()
+ *
+ * @hide
*/
public abstract int getEphemeralCookieMaxSizeBytes();
@@ -3286,6 +3290,8 @@ public abstract class PackageManager {
* @see #isEphemeralApplication()
* @see #setEphemeralCookie(byte[])
* @see #getEphemeralCookieMaxSizeBytes()
+ *
+ * @hide
*/
public abstract @NonNull byte[] getEphemeralCookie();
@@ -3304,6 +3310,8 @@ public abstract class PackageManager {
* @see #isEphemeralApplication()
* @see #getEphemeralCookieMaxSizeBytes()
* @see #getEphemeralCookie()
+ *
+ * @hide
*/
public abstract boolean setEphemeralCookie(@NonNull byte[] cookie);
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index aba82fad0bfa..035462d56fc5 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -95,6 +95,9 @@ public abstract class NotificationAssistantService extends NotificationListenerS
/** Notification was canceled because it was an invisible member of a group. */
public static final int REASON_GROUP_OPTIMIZATION = 13;
+ /** Notification was canceled by the user banning the topic. */
+ public static final int REASON_TOPIC_BANNED = 14;
+
public class Adjustment {
int mImportance;
CharSequence mExplanation;
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index ecec25852cbb..a78b56ab194c 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1328,4 +1328,15 @@ public interface WindowManagerPolicy {
* @param fadeoutDuration the duration of the exit animation, in milliseconds
*/
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
+
+ /**
+ * Calculates the stable insets without running a layout.
+ *
+ * @param displayRotation the current display rotation
+ * @param outInsets the insets to return
+ * @param displayWidth the current display width
+ * @param displayHeight the current display height
+ */
+ public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+ Rect outInsets);
}
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index ba5d6c2896ef..6e5e5915bb6f 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -201,10 +201,17 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * The default implementation performs the deletion around the current
- * selection position of the editable text.
- * @param beforeLength
- * @param afterLength
+ * The default implementation performs the deletion around the current selection position of the
+ * editable text.
+ *
+ * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+ * If this is greater than the number of existing characters between the cursor and
+ * the end of the text, then this method does not fail but deletes all the characters in
+ * that range.
*/
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
@@ -213,7 +220,7 @@ public class BaseInputConnection implements InputConnection {
if (content == null) return false;
beginBatchEdit();
-
+
int a = Selection.getSelectionStart(content);
int b = Selection.getSelectionEnd(content);
@@ -253,9 +260,9 @@ public class BaseInputConnection implements InputConnection {
content.delete(b, end);
}
-
+
endBatchEdit();
-
+
return true;
}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index ff992d39fc66..be7bc14f0515 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -335,12 +335,15 @@ public interface InputConnection {
* but be careful to wait until the batch edit is over if one is
* in progress.</p>
*
- * @param beforeLength The number of characters to be deleted before the
- * current cursor position.
- * @param afterLength The number of characters to be deleted after the
- * current cursor position.
- * @return true on success, false if the input connection is no longer
- * valid.
+ * @param beforeLength The number of characters before the cursor to be deleted, in code unit.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code unit.
+ * If this is greater than the number of existing characters between the cursor and
+ * the end of the text, then this method does not fail but deletes all the characters in
+ * that range.
+ * @return true on success, false if the input connection is no longer valid.
*/
public boolean deleteSurroundingText(int beforeLength, int afterLength);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
index e43d531c7a5c..e79f1b8a3133 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
@@ -14,18 +14,19 @@
* limitations under the License.
*/
-package com.android.systemui.stackdivider;
+package com.android.internal.policy;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
import java.util.ArrayList;
/**
* Calculates the snap targets and the snap position given a position and a velocity. All positions
* here are to be interpreted as the left/top edge of the divider rectangle.
+ *
+ * @hide
*/
public class DividerSnapAlgorithm {
@@ -44,8 +45,7 @@ public class DividerSnapAlgorithm {
*/
private static final int SNAP_ONLY_1_1 = 2;
- private final Context mContext;
- private final FlingAnimationUtils mFlingAnimationUtils;
+ private final float mMinFlingVelocityPxPerSecond;
private final int mDisplayWidth;
private final int mDisplayHeight;
private final int mDividerSize;
@@ -63,18 +63,17 @@ public class DividerSnapAlgorithm {
private final SnapTarget mDismissStartTarget;
private final SnapTarget mDismissEndTarget;
- public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils,
+ public DividerSnapAlgorithm(Resources res, float minFlingVelocityPxPerSecond,
int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision,
Rect insets) {
- mContext = ctx;
- mFlingAnimationUtils = flingAnimationUtils;
+ mMinFlingVelocityPxPerSecond = minFlingVelocityPxPerSecond;
mDividerSize = dividerSize;
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
mInsets.set(insets);
- mSnapMode = ctx.getResources().getInteger(
+ mSnapMode = res.getInteger(
com.android.internal.R.integer.config_dockedStackDividerSnapMode);
- mFixedRatio = ctx.getResources().getFraction(
+ mFixedRatio = res.getFraction(
com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
calculateTargets(isHorizontalDivision);
mFirstSplitTarget = mTargets.get(1);
@@ -84,7 +83,7 @@ public class DividerSnapAlgorithm {
}
public SnapTarget calculateSnapTarget(int position, float velocity) {
- if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+ if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) {
return snap(position);
}
if (position < mFirstSplitTarget.position && velocity < 0) {
@@ -100,6 +99,17 @@ public class DividerSnapAlgorithm {
}
}
+ public SnapTarget calculateNonDismissingSnapTarget(int position) {
+ SnapTarget target = snap(position);
+ if (target == mDismissStartTarget) {
+ return mFirstSplitTarget;
+ } else if (target == mDismissEndTarget) {
+ return mLastSplitTarget;
+ } else {
+ return target;
+ }
+ }
+
public float calculateDismissingFraction(int position) {
if (position < mFirstSplitTarget.position) {
return 1f - (float) position / mFirstSplitTarget.position;
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
new file mode 100644
index 000000000000..25a060e0a0a7
--- /dev/null
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 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.internal.policy;
+
+import android.graphics.Rect;
+import android.view.WindowManager;
+
+/**
+ * Utility functions for docked stack divider used by both window manager and System UI.
+ *
+ * @hide
+ */
+public class DockedDividerUtils {
+
+ public static void calculateBoundsForPosition(int position, int dockSide, Rect outRect,
+ int displayWidth, int displayHeight, int dividerSize) {
+ outRect.set(0, 0, displayWidth, displayHeight);
+ switch (dockSide) {
+ case WindowManager.DOCKED_LEFT:
+ outRect.right = position;
+ break;
+ case WindowManager.DOCKED_TOP:
+ outRect.bottom = position;
+ break;
+ case WindowManager.DOCKED_RIGHT:
+ outRect.left = position + dividerSize;
+ break;
+ case WindowManager.DOCKED_BOTTOM:
+ outRect.top = position + dividerSize;
+ break;
+ }
+ if (outRect.left > outRect.right) {
+ outRect.left = outRect.right;
+ }
+ if (outRect.top > outRect.bottom) {
+ outRect.top = outRect.bottom;
+ }
+ if (outRect.right < outRect.left) {
+ outRect.right = outRect.left;
+ }
+ if (outRect.bottom < outRect.top) {
+ outRect.bottom = outRect.top;
+ }
+ }
+
+ public static int calculatePositionForBounds(Rect bounds, int dockSide, int dividerSize) {
+ switch (dockSide) {
+ case WindowManager.DOCKED_LEFT:
+ return bounds.right;
+ case WindowManager.DOCKED_TOP:
+ return bounds.bottom;
+ case WindowManager.DOCKED_RIGHT:
+ return bounds.left - dividerSize;
+ case WindowManager.DOCKED_BOTTOM:
+ return bounds.top - dividerSize;
+ default:
+ return 0;
+ }
+ }
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 11056d4fac97..0932e8998ce2 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -109,7 +109,8 @@ ifeq (true, $(HWUI_NEW_OPS))
BakedOpDispatcher.cpp \
BakedOpRenderer.cpp \
BakedOpState.cpp \
- OpReorderer.cpp \
+ FrameReorderer.cpp \
+ LayerReorderer.cpp \
RecordingCanvas.cpp
hwui_cflags += -DHWUI_NEW_OPS
@@ -236,8 +237,8 @@ LOCAL_SRC_FILES += \
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
tests/unit/BakedOpStateTests.cpp \
- tests/unit/RecordingCanvasTests.cpp \
- tests/unit/OpReordererTests.cpp
+ tests/unit/FrameReordererTests.cpp \
+ tests/unit/RecordingCanvasTests.cpp
endif
include $(BUILD_NATIVE_TEST)
@@ -298,7 +299,7 @@ LOCAL_SRC_FILES += \
ifeq (true, $(HWUI_NEW_OPS))
LOCAL_SRC_FILES += \
- tests/microbench/OpReordererBench.cpp
+ tests/microbench/FrameReordererBench.cpp
endif
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp
index 195d235fc7c1..23aca899a485 100644
--- a/libs/hwui/BakedOpDispatcher.cpp
+++ b/libs/hwui/BakedOpDispatcher.cpp
@@ -338,8 +338,8 @@ static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& stat
static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state,
PathTexture& texture, const RecordedOp& op) {
Rect dest(texture.width, texture.height);
- dest.translate(texture.left + op.unmappedBounds.left - texture.offset,
- texture.top + op.unmappedBounds.top - texture.offset);
+ dest.translate(texture.left - texture.offset,
+ texture.top - texture.offset);
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
.setRoundRectClipState(state.roundRectClipState)
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/FrameReorderer.cpp
index cff4f3c47d60..4bfc0b413c56 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/FrameReorderer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "OpReorderer.h"
+#include "FrameReorderer.h"
#include "LayerUpdateQueue.h"
#include "RenderNode.h"
@@ -30,343 +30,7 @@
namespace android {
namespace uirenderer {
-class BatchBase {
-public:
- BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
- : mBatchId(batchId)
- , mMerging(merging) {
- mBounds = op->computedState.clippedBounds;
- mOps.push_back(op);
- }
-
- bool intersects(const Rect& rect) const {
- if (!rect.intersects(mBounds)) return false;
-
- for (const BakedOpState* op : mOps) {
- if (rect.intersects(op->computedState.clippedBounds)) {
- return true;
- }
- }
- return false;
- }
-
- batchid_t getBatchId() const { return mBatchId; }
- bool isMerging() const { return mMerging; }
-
- const std::vector<BakedOpState*>& getOps() const { return mOps; }
-
- void dump() const {
- ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
- this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
- }
-protected:
- batchid_t mBatchId;
- Rect mBounds;
- std::vector<BakedOpState*> mOps;
- bool mMerging;
-};
-
-class OpBatch : public BatchBase {
-public:
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
- OpBatch(batchid_t batchId, BakedOpState* op)
- : BatchBase(batchId, op, false) {
- }
-
- void batchOp(BakedOpState* op) {
- mBounds.unionWith(op->computedState.clippedBounds);
- mOps.push_back(op);
- }
-};
-
-class MergingOpBatch : public BatchBase {
-public:
- static void* operator new(size_t size, LinearAllocator& allocator) {
- return allocator.alloc(size);
- }
-
- MergingOpBatch(batchid_t batchId, BakedOpState* op)
- : BatchBase(batchId, op, true)
- , mClipSideFlags(op->computedState.clipSideFlags) {
- }
-
- /*
- * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
- * and clip side flags. Positive bounds delta means new bounds fit in old.
- */
- static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
- float boundsDelta) {
- bool currentClipExists = currentFlags & side;
- bool newClipExists = newFlags & side;
-
- // if current is clipped, we must be able to fit new bounds in current
- if (boundsDelta > 0 && currentClipExists) return false;
-
- // if new is clipped, we must be able to fit current bounds in new
- if (boundsDelta < 0 && newClipExists) return false;
-
- return true;
- }
-
- static bool paintIsDefault(const SkPaint& paint) {
- return paint.getAlpha() == 255
- && paint.getColorFilter() == nullptr
- && paint.getShader() == nullptr;
- }
-
- static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
- return a.getAlpha() == b.getAlpha()
- && a.getColorFilter() == b.getColorFilter()
- && a.getShader() == b.getShader();
- }
-
- /*
- * Checks if a (mergeable) op can be merged into this batch
- *
- * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
- * important to consider all paint attributes used in the draw calls in deciding both a) if an
- * op tries to merge at all, and b) if the op can merge with another set of ops
- *
- * False positives can lead to information from the paints of subsequent merged operations being
- * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
- */
- bool canMergeWith(BakedOpState* op) const {
- bool isTextBatch = getBatchId() == OpBatchType::Text
- || getBatchId() == OpBatchType::ColorText;
-
- // Overlapping other operations is only allowed for text without shadow. For other ops,
- // multiDraw isn't guaranteed to overdraw correctly
- if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
- if (intersects(op->computedState.clippedBounds)) return false;
- }
-
- const BakedOpState* lhs = op;
- const BakedOpState* rhs = mOps[0];
-
- if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
-
- // Identical round rect clip state means both ops will clip in the same way, or not at all.
- // As the state objects are const, we can compare their pointers to determine mergeability
- if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
- if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
-
- /* Clipping compatibility check
- *
- * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
- * clip for that side.
- */
- const int currentFlags = mClipSideFlags;
- const int newFlags = op->computedState.clipSideFlags;
- if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
- const Rect& opBounds = op->computedState.clippedBounds;
- float boundsDelta = mBounds.left - opBounds.left;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
- boundsDelta = mBounds.top - opBounds.top;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
-
- // right and bottom delta calculation reversed to account for direction
- boundsDelta = opBounds.right - mBounds.right;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
- boundsDelta = opBounds.bottom - mBounds.bottom;
- if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
- }
-
- const SkPaint* newPaint = op->op->paint;
- const SkPaint* oldPaint = mOps[0]->op->paint;
-
- if (newPaint == oldPaint) {
- // if paints are equal, then modifiers + paint attribs don't need to be compared
- return true;
- } else if (newPaint && !oldPaint) {
- return paintIsDefault(*newPaint);
- } else if (!newPaint && oldPaint) {
- return paintIsDefault(*oldPaint);
- }
- return paintsAreEquivalent(*newPaint, *oldPaint);
- }
-
- void mergeOp(BakedOpState* op) {
- mBounds.unionWith(op->computedState.clippedBounds);
- mOps.push_back(op);
-
- // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
- // check, and doesn't extend past a side of the clip that's in use by the merged batch.
- // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
- mClipSideFlags |= op->computedState.clipSideFlags;
- }
-
- int getClipSideFlags() const { return mClipSideFlags; }
- const Rect& getClipRect() const { return mBounds; }
-
-private:
- int mClipSideFlags;
-};
-
-OpReorderer::LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
- const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
- : width(width)
- , height(height)
- , repaintRect(repaintRect)
- , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
- , beginLayerOp(beginLayerOp)
- , renderNode(renderNode)
- , viewportClip(Rect(width, height)) {}
-
-// iterate back toward target to see if anything drawn since should overlap the new op
-// if no target, merging ops still iterate to find similar batch to insert after
-void OpReorderer::LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
- BatchBase** targetBatch, size_t* insertBatchIndex) const {
- for (int i = mBatches.size() - 1; i >= 0; i--) {
- BatchBase* overBatch = mBatches[i];
-
- if (overBatch == *targetBatch) break;
-
- // TODO: also consider shader shared between batch types
- if (batchId == overBatch->getBatchId()) {
- *insertBatchIndex = i + 1;
- if (!*targetBatch) break; // found insert position, quit
- }
-
- if (overBatch->intersects(clippedBounds)) {
- // NOTE: it may be possible to optimize for special cases where two operations
- // of the same batch/paint could swap order, such as with a non-mergeable
- // (clipped) and a mergeable text operation
- *targetBatch = nullptr;
- break;
- }
- }
-}
-
-void OpReorderer::LayerReorderer::deferLayerClear(const Rect& rect) {
- mClearRects.push_back(rect);
-}
-
-void OpReorderer::LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
- if (CC_UNLIKELY(!mClearRects.empty())) {
- const int vertCount = mClearRects.size() * 4;
- // put the verts in the frame allocator, since
- // 1) SimpleRectsOps needs verts, not rects
- // 2) even if mClearRects stored verts, std::vectors will move their contents
- Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
-
- Vertex* currentVert = verts;
- Rect bounds = mClearRects[0];
- for (auto&& rect : mClearRects) {
- bounds.unionWith(rect);
- Vertex::set(currentVert++, rect.left, rect.top);
- Vertex::set(currentVert++, rect.right, rect.top);
- Vertex::set(currentVert++, rect.left, rect.bottom);
- Vertex::set(currentVert++, rect.right, rect.bottom);
- }
- mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
-
- // One or more unclipped saveLayers have been enqueued, with deferred clears.
- // Flush all of these clears with a single draw
- SkPaint* paint = allocator.create<SkPaint>();
- paint->setXfermodeMode(SkXfermode::kClear_Mode);
- SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
- Matrix4::identity(), nullptr, paint,
- verts, vertCount);
- BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
- &viewportClip, bounds, *op);
-
-
- deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
- }
-}
-
-void OpReorderer::LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
- BakedOpState* op, batchid_t batchId) {
- if (batchId != OpBatchType::CopyToLayer) {
- // if first op after one or more unclipped saveLayers, flush the layer clears
- flushLayerClears(allocator);
- }
-
- OpBatch* targetBatch = mBatchLookup[batchId];
-
- size_t insertBatchIndex = mBatches.size();
- if (targetBatch) {
- locateInsertIndex(batchId, op->computedState.clippedBounds,
- (BatchBase**)(&targetBatch), &insertBatchIndex);
- }
-
- if (targetBatch) {
- targetBatch->batchOp(op);
- } else {
- // new non-merging batch
- targetBatch = new (allocator) OpBatch(batchId, op);
- mBatchLookup[batchId] = targetBatch;
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-}
-
-void OpReorderer::LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
- BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
- if (batchId != OpBatchType::CopyToLayer) {
- // if first op after one or more unclipped saveLayers, flush the layer clears
- flushLayerClears(allocator);
- }
- MergingOpBatch* targetBatch = nullptr;
-
- // Try to merge with any existing batch with same mergeId
- auto getResult = mMergingBatchLookup[batchId].find(mergeId);
- if (getResult != mMergingBatchLookup[batchId].end()) {
- targetBatch = getResult->second;
- if (!targetBatch->canMergeWith(op)) {
- targetBatch = nullptr;
- }
- }
-
- size_t insertBatchIndex = mBatches.size();
- locateInsertIndex(batchId, op->computedState.clippedBounds,
- (BatchBase**)(&targetBatch), &insertBatchIndex);
-
- if (targetBatch) {
- targetBatch->mergeOp(op);
- } else {
- // new merging batch
- targetBatch = new (allocator) MergingOpBatch(batchId, op);
- mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
-
- mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
- }
-}
-
-void OpReorderer::LayerReorderer::replayBakedOpsImpl(void* arg,
- BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
- ATRACE_NAME("flush drawing commands");
- for (const BatchBase* batch : mBatches) {
- size_t size = batch->getOps().size();
- if (size > 1 && batch->isMerging()) {
- int opId = batch->getOps()[0]->op->opId;
- const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
- MergedBakedOpList data = {
- batch->getOps().data(),
- size,
- mergingBatch->getClipSideFlags(),
- mergingBatch->getClipRect()
- };
- mergedReceivers[opId](arg, data);
- } else {
- for (const BakedOpState* op : batch->getOps()) {
- unmergedReceivers[op->op->opId](arg, *op);
- }
- }
- }
-}
-
-void OpReorderer::LayerReorderer::dump() const {
- ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
- this, width, height, offscreenBuffer, beginLayerOp, renderNode);
- for (const BatchBase* batch : mBatches) {
- batch->dump();
- }
-}
-
-OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+FrameReorderer::FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter)
: mCanvasState(*this) {
@@ -413,11 +77,11 @@ OpReorderer::OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
}
}
-void OpReorderer::onViewportInitialized() {}
+void FrameReorderer::onViewportInitialized() {}
-void OpReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
+void FrameReorderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}
-void OpReorderer::deferNodePropsAndOps(RenderNode& node) {
+void FrameReorderer::deferNodePropsAndOps(RenderNode& node) {
const RenderProperties& properties = node.properties();
const Outline& outline = properties.getOutline();
if (properties.getAlpha() <= 0
@@ -549,7 +213,7 @@ static size_t findNonNegativeIndex(const V& zTranslatedNodes) {
}
template <typename V>
-void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
+void FrameReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedNodes) {
const int size = zTranslatedNodes.size();
if (size == 0
|| (mode == ChildrenSelectMode::Negative&& zTranslatedNodes[0].key > 0.0f)
@@ -599,7 +263,7 @@ void OpReorderer::defer3dChildren(ChildrenSelectMode mode, const V& zTranslatedN
}
}
-void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
+void FrameReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
auto& node = *casterNodeOp.renderNode;
auto& properties = node.properties();
@@ -655,7 +319,7 @@ void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
}
}
-void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {
+void FrameReorderer::deferProjectedChildren(const RenderNode& renderNode) {
const SkPath* projectionReceiverOutline = renderNode.properties().getOutline().getPath();
int count = mCanvasState.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -688,15 +352,15 @@ void OpReorderer::deferProjectedChildren(const RenderNode& renderNode) {
}
/**
- * Used to define a list of lambdas referencing private OpReorderer::onXX::defer() methods.
+ * Used to define a list of lambdas referencing private FrameReorderer::onXX::defer() methods.
*
* This allows opIds embedded in the RecordedOps to be used for dispatching to these lambdas.
- * E.g. a BitmapOp op then would be dispatched to OpReorderer::onBitmapOp(const BitmapOp&)
+ * E.g. a BitmapOp op then would be dispatched to FrameReorderer::onBitmapOp(const BitmapOp&)
*/
#define OP_RECEIVER(Type) \
- [](OpReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
-void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
- typedef void (*OpDispatcher) (OpReorderer& reorderer, const RecordedOp& op);
+ [](FrameReorderer& reorderer, const RecordedOp& op) { reorderer.defer##Type(static_cast<const Type&>(op)); },
+void FrameReorderer::deferNodeOps(const RenderNode& renderNode) {
+ typedef void (*OpDispatcher) (FrameReorderer& reorderer, const RecordedOp& op);
static OpDispatcher receivers[] = BUILD_DEFERRABLE_OP_LUT(OP_RECEIVER);
// can't be null, since DL=null node rejection happens before deferNodePropsAndOps
@@ -720,7 +384,7 @@ void OpReorderer::deferNodeOps(const RenderNode& renderNode) {
}
}
-void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
+void FrameReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
if (op.renderNode->nothingToDraw()) return;
int count = mCanvasState.save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
@@ -735,7 +399,7 @@ void OpReorderer::deferRenderNodeOpImpl(const RenderNodeOp& op) {
mCanvasState.restoreToCount(count);
}
-void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
+void FrameReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
if (!op.skipInOrderDraw) {
deferRenderNodeOpImpl(op);
}
@@ -745,7 +409,7 @@ void OpReorderer::deferRenderNodeOp(const RenderNodeOp& op) {
* Defers an unmergeable, strokeable op, accounting correctly
* for paint's style on the bounds being computed.
*/
-void OpReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+void FrameReorderer::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
@@ -767,7 +431,7 @@ static batchid_t tessBatchId(const RecordedOp& op) {
: (paint.isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices);
}
-void OpReorderer::deferArcOp(const ArcOp& op) {
+void FrameReorderer::deferArcOp(const ArcOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
@@ -776,7 +440,7 @@ static bool hasMergeableClip(const BakedOpState& state) {
|| state.computedState.clipState->mode == ClipMode::Rectangle;
}
-void OpReorderer::deferBitmapOp(const BitmapOp& op) {
+void FrameReorderer::deferBitmapOp(const BitmapOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -796,19 +460,19 @@ void OpReorderer::deferBitmapOp(const BitmapOp& op) {
}
}
-void OpReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
+void FrameReorderer::deferBitmapMeshOp(const BitmapMeshOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void OpReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
+void FrameReorderer::deferBitmapRectOp(const BitmapRectOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Bitmap);
}
-void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
+void FrameReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
// allocate a temporary oval op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
float x = *(op.x);
@@ -823,22 +487,22 @@ void OpReorderer::deferCirclePropsOp(const CirclePropsOp& op) {
deferOvalOp(*resolvedOp);
}
-void OpReorderer::deferFunctorOp(const FunctorOp& op) {
+void FrameReorderer::deferFunctorOp(const FunctorOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Functor);
}
-void OpReorderer::deferLinesOp(const LinesOp& op) {
+void FrameReorderer::deferLinesOp(const LinesOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void OpReorderer::deferOvalOp(const OvalOp& op) {
+void FrameReorderer::deferOvalOp(const OvalOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::deferPatchOp(const PatchOp& op) {
+void FrameReorderer::deferPatchOp(const PatchOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -856,24 +520,24 @@ void OpReorderer::deferPatchOp(const PatchOp& op) {
}
}
-void OpReorderer::deferPathOp(const PathOp& op) {
+void FrameReorderer::deferPathOp(const PathOp& op) {
deferStrokeableOp(op, OpBatchType::Bitmap);
}
-void OpReorderer::deferPointsOp(const PointsOp& op) {
+void FrameReorderer::deferPointsOp(const PointsOp& op) {
batchid_t batch = op.paint->isAntiAlias() ? OpBatchType::AlphaVertices : OpBatchType::Vertices;
deferStrokeableOp(op, batch, BakedOpState::StrokeBehavior::Forced);
}
-void OpReorderer::deferRectOp(const RectOp& op) {
+void FrameReorderer::deferRectOp(const RectOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::deferRoundRectOp(const RoundRectOp& op) {
+void FrameReorderer::deferRoundRectOp(const RoundRectOp& op) {
deferStrokeableOp(op, tessBatchId(op));
}
-void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
+void FrameReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
// allocate a temporary round rect op (with mAllocator, so it persists until render), so the
// renderer doesn't have to handle the RoundRectPropsOp type, and so state baking is simple.
const RoundRectOp* resolvedOp = new (mAllocator) RoundRectOp(
@@ -884,7 +548,7 @@ void OpReorderer::deferRoundRectPropsOp(const RoundRectPropsOp& op) {
deferRoundRectOp(*resolvedOp);
}
-void OpReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
+void FrameReorderer::deferSimpleRectsOp(const SimpleRectsOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::Vertices);
@@ -895,7 +559,7 @@ static batchid_t textBatchId(const SkPaint& paint) {
return paint.getColor() == SK_ColorBLACK ? OpBatchType::Text : OpBatchType::ColorText;
}
-void OpReorderer::deferTextOp(const TextOp& op) {
+void FrameReorderer::deferTextOp(const TextOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
@@ -910,19 +574,19 @@ void OpReorderer::deferTextOp(const TextOp& op) {
}
}
-void OpReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
+void FrameReorderer::deferTextOnPathOp(const TextOnPathOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint)));
}
-void OpReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
+void FrameReorderer::deferTextureLayerOp(const TextureLayerOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
currentLayer().deferUnmergeableOp(mAllocator, bakedState, OpBatchType::TextureLayer);
}
-void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
+void FrameReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
float contentTranslateX, float contentTranslateY,
const Rect& repaintRect,
const Vector3& lightCenter,
@@ -941,7 +605,7 @@ void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight,
mLayerReorderers.emplace_back(layerWidth, layerHeight, repaintRect, beginLayerOp, renderNode);
}
-void OpReorderer::restoreForLayer() {
+void FrameReorderer::restoreForLayer() {
// restore canvas, and pop finished layer off of the stack
mCanvasState.restore();
mLayerStack.pop_back();
@@ -949,7 +613,7 @@ void OpReorderer::restoreForLayer() {
// TODO: defer time rejection (when bounds become empty) + tests
// Option - just skip layers with no bounds at playback + defer?
-void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
+void FrameReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
uint32_t layerWidth = (uint32_t) op.unmappedBounds.getWidth();
uint32_t layerHeight = (uint32_t) op.unmappedBounds.getHeight();
@@ -994,7 +658,7 @@ void OpReorderer::deferBeginLayerOp(const BeginLayerOp& op) {
&op, nullptr);
}
-void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
+void FrameReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
const BeginLayerOp& beginLayerOp = *currentLayer().beginLayerOp;
int finishedLayerIndex = mLayerStack.back();
@@ -1022,7 +686,7 @@ void OpReorderer::deferEndLayerOp(const EndLayerOp& /* ignored */) {
}
}
-void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
+void FrameReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
Matrix4 boundsTransform(*(mCanvasState.currentSnapshot()->transform));
boundsTransform.multiply(op.localMatrix);
@@ -1057,7 +721,7 @@ void OpReorderer::deferBeginUnclippedLayerOp(const BeginUnclippedLayerOp& op) {
currentLayer().activeUnclippedSaveLayers.push_back(bakedState);
}
-void OpReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
+void FrameReorderer::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignored */) {
LOG_ALWAYS_FATAL_IF(currentLayer().activeUnclippedSaveLayers.empty(), "no layer to end!");
BakedOpState* copyFromLayerOp = currentLayer().activeUnclippedSaveLayers.back();
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/FrameReorderer.h
index 8d9d90be057c..562e6a1a3d37 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/FrameReorderer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#ifndef ANDROID_HWUI_OP_REORDERER_H
-#define ANDROID_HWUI_OP_REORDERER_H
+#pragma once
#include "BakedOpState.h"
#include "CanvasState.h"
#include "DisplayList.h"
+#include "LayerReorderer.h"
#include "RecordedOp.h"
#include <vector>
@@ -31,114 +31,34 @@ namespace android {
namespace uirenderer {
class BakedOpState;
-class BatchBase;
class LayerUpdateQueue;
-class MergingOpBatch;
class OffscreenBuffer;
-class OpBatch;
class Rect;
-typedef int batchid_t;
-typedef const void* mergeid_t;
-
-namespace OpBatchType {
- enum {
- Bitmap,
- MergedPatch,
- AlphaVertices,
- Vertices,
- AlphaMaskTexture,
- Text,
- ColorText,
- Shadow,
- TextureLayer,
- Functor,
- CopyToLayer,
- CopyFromLayer,
-
- Count // must be last
- };
-}
-
-class OpReorderer : public CanvasStateClient {
- typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
- typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
-
- /**
- * Stores the deferred render operations and state used to compute ordering
- * for a single FBO/layer.
- */
- class LayerReorderer {
- public:
- // Create LayerReorderer for Fbo0
- LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
- : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
-
- // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
- // saveLayer, renderNode is present for a HW layer.
- LayerReorderer(uint32_t width, uint32_t height,
- const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
-
- // iterate back toward target to see if anything drawn since should overlap the new op
- // if no target, merging ops still iterate to find similar batch to insert after
- void locateInsertIndex(int batchId, const Rect& clippedBounds,
- BatchBase** targetBatch, size_t* insertBatchIndex) const;
-
- void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
-
- // insertion point of a new batch, will hopefully be immediately after similar batch
- // (generally, should be similar shader)
- void deferMergeableOp(LinearAllocator& allocator,
- BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
-
- void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
-
- void deferLayerClear(const Rect& dstRect);
-
- bool empty() const {
- return mBatches.empty();
- }
-
- void clear() {
- mBatches.clear();
- }
-
- void dump() const;
-
- const uint32_t width;
- const uint32_t height;
- const Rect repaintRect;
- OffscreenBuffer* offscreenBuffer;
- const BeginLayerOp* beginLayerOp;
- const RenderNode* renderNode;
- const ClipRect viewportClip;
-
- // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
- std::vector<BakedOpState*> activeUnclippedSaveLayers;
- private:
- void flushLayerClears(LinearAllocator& allocator);
-
- std::vector<BatchBase*> mBatches;
-
- /**
- * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
- * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
- * collide, which avoids the need to resolve mergeid collisions.
- */
- std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
-
- // Maps batch ids to the most recent *non-merging* batch of that id
- OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
-
- std::vector<Rect> mClearRects;
- };
-
+/**
+ * Traverses all of the drawing commands from the layers and RenderNodes passed into it, preparing
+ * them to be rendered.
+ *
+ * Resolves final drawing state for each operation (including clip, alpha and matrix), and then
+ * reorder and merge each op as it is resolved for drawing efficiency. Each layer of content (either
+ * from the LayerUpdateQueue, or temporary layers created by saveLayer operations in the
+ * draw stream) will create different reorder contexts, each in its own LayerReorderer.
+ *
+ * Then the prepared or 'baked' drawing commands can be issued by calling the templated
+ * replayBakedOps() function, which will dispatch them (including any created merged op collections)
+ * to a Dispatcher and Renderer. See BakedOpDispatcher for how these baked drawing operations are
+ * resolved into Glops and rendered via BakedOpRenderer.
+ *
+ * This class is also the authoritative source for traversing RenderNodes, both for standard op
+ * traversal within a DisplayList, and for out of order RenderNode traversal for Z and projection.
+ */
+class FrameReorderer : public CanvasStateClient {
public:
- OpReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
+ FrameReorderer(const LayerUpdateQueue& layers, const SkRect& clip,
uint32_t viewportWidth, uint32_t viewportHeight,
const std::vector< sp<RenderNode> >& nodes, const Vector3& lightCenter);
- virtual ~OpReorderer() {}
+ virtual ~FrameReorderer() {}
/**
* replayBakedOps() is templated based on what class will receive ops being replayed.
@@ -253,7 +173,7 @@ private:
BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
/**
- * Declares all OpReorderer::deferXXXXOp() methods for every RecordedOp type.
+ * Declares all FrameReorderer::deferXXXXOp() methods for every RecordedOp type.
*
* These private methods are called from within deferImpl to defer each individual op
* type differently.
@@ -287,5 +207,3 @@ private:
}; // namespace uirenderer
}; // namespace android
-
-#endif // ANDROID_HWUI_OP_REORDERER_H
diff --git a/libs/hwui/LayerReorderer.cpp b/libs/hwui/LayerReorderer.cpp
new file mode 100644
index 000000000000..9a17e9349e56
--- /dev/null
+++ b/libs/hwui/LayerReorderer.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include "LayerReorderer.h"
+
+#include "BakedOpState.h"
+#include "RenderNode.h"
+#include "utils/PaintUtils.h"
+#include "utils/TraceUtils.h"
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace uirenderer {
+
+class BatchBase {
+public:
+ BatchBase(batchid_t batchId, BakedOpState* op, bool merging)
+ : mBatchId(batchId)
+ , mMerging(merging) {
+ mBounds = op->computedState.clippedBounds;
+ mOps.push_back(op);
+ }
+
+ bool intersects(const Rect& rect) const {
+ if (!rect.intersects(mBounds)) return false;
+
+ for (const BakedOpState* op : mOps) {
+ if (rect.intersects(op->computedState.clippedBounds)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ batchid_t getBatchId() const { return mBatchId; }
+ bool isMerging() const { return mMerging; }
+
+ const std::vector<BakedOpState*>& getOps() const { return mOps; }
+
+ void dump() const {
+ ALOGD(" Batch %p, id %d, merging %d, count %d, bounds " RECT_STRING,
+ this, mBatchId, mMerging, mOps.size(), RECT_ARGS(mBounds));
+ }
+protected:
+ batchid_t mBatchId;
+ Rect mBounds;
+ std::vector<BakedOpState*> mOps;
+ bool mMerging;
+};
+
+class OpBatch : public BatchBase {
+public:
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ OpBatch(batchid_t batchId, BakedOpState* op)
+ : BatchBase(batchId, op, false) {
+ }
+
+ void batchOp(BakedOpState* op) {
+ mBounds.unionWith(op->computedState.clippedBounds);
+ mOps.push_back(op);
+ }
+};
+
+class MergingOpBatch : public BatchBase {
+public:
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ MergingOpBatch(batchid_t batchId, BakedOpState* op)
+ : BatchBase(batchId, op, true)
+ , mClipSideFlags(op->computedState.clipSideFlags) {
+ }
+
+ /*
+ * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
+ * and clip side flags. Positive bounds delta means new bounds fit in old.
+ */
+ static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
+ float boundsDelta) {
+ bool currentClipExists = currentFlags & side;
+ bool newClipExists = newFlags & side;
+
+ // if current is clipped, we must be able to fit new bounds in current
+ if (boundsDelta > 0 && currentClipExists) return false;
+
+ // if new is clipped, we must be able to fit current bounds in new
+ if (boundsDelta < 0 && newClipExists) return false;
+
+ return true;
+ }
+
+ static bool paintIsDefault(const SkPaint& paint) {
+ return paint.getAlpha() == 255
+ && paint.getColorFilter() == nullptr
+ && paint.getShader() == nullptr;
+ }
+
+ static bool paintsAreEquivalent(const SkPaint& a, const SkPaint& b) {
+ // Note: don't check color, since all currently mergeable ops can merge across colors
+ return a.getAlpha() == b.getAlpha()
+ && a.getColorFilter() == b.getColorFilter()
+ && a.getShader() == b.getShader();
+ }
+
+ /*
+ * Checks if a (mergeable) op can be merged into this batch
+ *
+ * If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
+ * important to consider all paint attributes used in the draw calls in deciding both a) if an
+ * op tries to merge at all, and b) if the op can merge with another set of ops
+ *
+ * False positives can lead to information from the paints of subsequent merged operations being
+ * dropped, so we make simplifying qualifications on the ops that can merge, per op type.
+ */
+ bool canMergeWith(BakedOpState* op) const {
+ bool isTextBatch = getBatchId() == OpBatchType::Text
+ || getBatchId() == OpBatchType::ColorText;
+
+ // Overlapping other operations is only allowed for text without shadow. For other ops,
+ // multiDraw isn't guaranteed to overdraw correctly
+ if (!isTextBatch || PaintUtils::hasTextShadow(op->op->paint)) {
+ if (intersects(op->computedState.clippedBounds)) return false;
+ }
+
+ const BakedOpState* lhs = op;
+ const BakedOpState* rhs = mOps[0];
+
+ if (!MathUtils::areEqual(lhs->alpha, rhs->alpha)) return false;
+
+ // Identical round rect clip state means both ops will clip in the same way, or not at all.
+ // As the state objects are const, we can compare their pointers to determine mergeability
+ if (lhs->roundRectClipState != rhs->roundRectClipState) return false;
+ if (lhs->projectionPathMask != rhs->projectionPathMask) return false;
+
+ /* Clipping compatibility check
+ *
+ * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
+ * clip for that side.
+ */
+ const int currentFlags = mClipSideFlags;
+ const int newFlags = op->computedState.clipSideFlags;
+ if (currentFlags != OpClipSideFlags::None || newFlags != OpClipSideFlags::None) {
+ const Rect& opBounds = op->computedState.clippedBounds;
+ float boundsDelta = mBounds.left - opBounds.left;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Left, boundsDelta)) return false;
+ boundsDelta = mBounds.top - opBounds.top;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Top, boundsDelta)) return false;
+
+ // right and bottom delta calculation reversed to account for direction
+ boundsDelta = opBounds.right - mBounds.right;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Right, boundsDelta)) return false;
+ boundsDelta = opBounds.bottom - mBounds.bottom;
+ if (!checkSide(currentFlags, newFlags, OpClipSideFlags::Bottom, boundsDelta)) return false;
+ }
+
+ const SkPaint* newPaint = op->op->paint;
+ const SkPaint* oldPaint = mOps[0]->op->paint;
+
+ if (newPaint == oldPaint) {
+ // if paints are equal, then modifiers + paint attribs don't need to be compared
+ return true;
+ } else if (newPaint && !oldPaint) {
+ return paintIsDefault(*newPaint);
+ } else if (!newPaint && oldPaint) {
+ return paintIsDefault(*oldPaint);
+ }
+ return paintsAreEquivalent(*newPaint, *oldPaint);
+ }
+
+ void mergeOp(BakedOpState* op) {
+ mBounds.unionWith(op->computedState.clippedBounds);
+ mOps.push_back(op);
+
+ // Because a new op must have passed canMergeWith(), we know it's passed the clipping compat
+ // check, and doesn't extend past a side of the clip that's in use by the merged batch.
+ // Therefore it's safe to simply always merge flags, and use the bounds as the clip rect.
+ mClipSideFlags |= op->computedState.clipSideFlags;
+ }
+
+ int getClipSideFlags() const { return mClipSideFlags; }
+ const Rect& getClipRect() const { return mBounds; }
+
+private:
+ int mClipSideFlags;
+};
+
+LayerReorderer::LayerReorderer(uint32_t width, uint32_t height,
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode)
+ : width(width)
+ , height(height)
+ , repaintRect(repaintRect)
+ , offscreenBuffer(renderNode ? renderNode->getLayer() : nullptr)
+ , beginLayerOp(beginLayerOp)
+ , renderNode(renderNode)
+ , viewportClip(Rect(width, height)) {}
+
+// iterate back toward target to see if anything drawn since should overlap the new op
+// if no target, merging ops still iterate to find similar batch to insert after
+void LayerReorderer::locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const {
+ for (int i = mBatches.size() - 1; i >= 0; i--) {
+ BatchBase* overBatch = mBatches[i];
+
+ if (overBatch == *targetBatch) break;
+
+ // TODO: also consider shader shared between batch types
+ if (batchId == overBatch->getBatchId()) {
+ *insertBatchIndex = i + 1;
+ if (!*targetBatch) break; // found insert position, quit
+ }
+
+ if (overBatch->intersects(clippedBounds)) {
+ // NOTE: it may be possible to optimize for special cases where two operations
+ // of the same batch/paint could swap order, such as with a non-mergeable
+ // (clipped) and a mergeable text operation
+ *targetBatch = nullptr;
+ break;
+ }
+ }
+}
+
+void LayerReorderer::deferLayerClear(const Rect& rect) {
+ mClearRects.push_back(rect);
+}
+
+void LayerReorderer::flushLayerClears(LinearAllocator& allocator) {
+ if (CC_UNLIKELY(!mClearRects.empty())) {
+ const int vertCount = mClearRects.size() * 4;
+ // put the verts in the frame allocator, since
+ // 1) SimpleRectsOps needs verts, not rects
+ // 2) even if mClearRects stored verts, std::vectors will move their contents
+ Vertex* const verts = (Vertex*) allocator.alloc(vertCount * sizeof(Vertex));
+
+ Vertex* currentVert = verts;
+ Rect bounds = mClearRects[0];
+ for (auto&& rect : mClearRects) {
+ bounds.unionWith(rect);
+ Vertex::set(currentVert++, rect.left, rect.top);
+ Vertex::set(currentVert++, rect.right, rect.top);
+ Vertex::set(currentVert++, rect.left, rect.bottom);
+ Vertex::set(currentVert++, rect.right, rect.bottom);
+ }
+ mClearRects.clear(); // discard rects before drawing so this method isn't reentrant
+
+ // One or more unclipped saveLayers have been enqueued, with deferred clears.
+ // Flush all of these clears with a single draw
+ SkPaint* paint = allocator.create<SkPaint>();
+ paint->setXfermodeMode(SkXfermode::kClear_Mode);
+ SimpleRectsOp* op = new (allocator) SimpleRectsOp(bounds,
+ Matrix4::identity(), nullptr, paint,
+ verts, vertCount);
+ BakedOpState* bakedState = BakedOpState::directConstruct(allocator,
+ &viewportClip, bounds, *op);
+
+
+ deferUnmergeableOp(allocator, bakedState, OpBatchType::Vertices);
+ }
+}
+
+void LayerReorderer::deferUnmergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId) {
+ if (batchId != OpBatchType::CopyToLayer) {
+ // if first op after one or more unclipped saveLayers, flush the layer clears
+ flushLayerClears(allocator);
+ }
+
+ OpBatch* targetBatch = mBatchLookup[batchId];
+
+ size_t insertBatchIndex = mBatches.size();
+ if (targetBatch) {
+ locateInsertIndex(batchId, op->computedState.clippedBounds,
+ (BatchBase**)(&targetBatch), &insertBatchIndex);
+ }
+
+ if (targetBatch) {
+ targetBatch->batchOp(op);
+ } else {
+ // new non-merging batch
+ targetBatch = new (allocator) OpBatch(batchId, op);
+ mBatchLookup[batchId] = targetBatch;
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+void LayerReorderer::deferMergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
+ if (batchId != OpBatchType::CopyToLayer) {
+ // if first op after one or more unclipped saveLayers, flush the layer clears
+ flushLayerClears(allocator);
+ }
+ MergingOpBatch* targetBatch = nullptr;
+
+ // Try to merge with any existing batch with same mergeId
+ auto getResult = mMergingBatchLookup[batchId].find(mergeId);
+ if (getResult != mMergingBatchLookup[batchId].end()) {
+ targetBatch = getResult->second;
+ if (!targetBatch->canMergeWith(op)) {
+ targetBatch = nullptr;
+ }
+ }
+
+ size_t insertBatchIndex = mBatches.size();
+ locateInsertIndex(batchId, op->computedState.clippedBounds,
+ (BatchBase**)(&targetBatch), &insertBatchIndex);
+
+ if (targetBatch) {
+ targetBatch->mergeOp(op);
+ } else {
+ // new merging batch
+ targetBatch = new (allocator) MergingOpBatch(batchId, op);
+ mMergingBatchLookup[batchId].insert(std::make_pair(mergeId, targetBatch));
+
+ mBatches.insert(mBatches.begin() + insertBatchIndex, targetBatch);
+ }
+}
+
+void LayerReorderer::replayBakedOpsImpl(void* arg,
+ BakedOpReceiver* unmergedReceivers, MergedOpReceiver* mergedReceivers) const {
+ ATRACE_NAME("flush drawing commands");
+ for (const BatchBase* batch : mBatches) {
+ size_t size = batch->getOps().size();
+ if (size > 1 && batch->isMerging()) {
+ int opId = batch->getOps()[0]->op->opId;
+ const MergingOpBatch* mergingBatch = static_cast<const MergingOpBatch*>(batch);
+ MergedBakedOpList data = {
+ batch->getOps().data(),
+ size,
+ mergingBatch->getClipSideFlags(),
+ mergingBatch->getClipRect()
+ };
+ mergedReceivers[opId](arg, data);
+ } else {
+ for (const BakedOpState* op : batch->getOps()) {
+ unmergedReceivers[op->op->opId](arg, *op);
+ }
+ }
+ }
+}
+
+void LayerReorderer::dump() const {
+ ALOGD("LayerReorderer %p, %ux%u buffer %p, blo %p, rn %p",
+ this, width, height, offscreenBuffer, beginLayerOp, renderNode);
+ for (const BatchBase* batch : mBatches) {
+ batch->dump();
+ }
+}
+
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/LayerReorderer.h b/libs/hwui/LayerReorderer.h
new file mode 100644
index 000000000000..83cda812035d
--- /dev/null
+++ b/libs/hwui/LayerReorderer.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include "ClipArea.h"
+#include "Rect.h"
+
+#include <vector>
+#include <unordered_map>
+
+struct SkRect;
+
+namespace android {
+namespace uirenderer {
+
+class BakedOpState;
+struct BeginLayerOp;
+class BatchBase;
+class LinearAllocator;
+struct MergedBakedOpList;
+class MergingOpBatch;
+class OffscreenBuffer;
+class OpBatch;
+class RenderNode;
+
+typedef int batchid_t;
+typedef const void* mergeid_t;
+
+namespace OpBatchType {
+ enum {
+ Bitmap,
+ MergedPatch,
+ AlphaVertices,
+ Vertices,
+ AlphaMaskTexture,
+ Text,
+ ColorText,
+ Shadow,
+ TextureLayer,
+ Functor,
+ CopyToLayer,
+ CopyFromLayer,
+
+ Count // must be last
+ };
+}
+
+typedef void (*BakedOpReceiver)(void*, const BakedOpState&);
+typedef void (*MergedOpReceiver)(void*, const MergedBakedOpList& opList);
+
+/**
+ * Stores the deferred render operations and state used to compute ordering
+ * for a single FBO/layer.
+ */
+class LayerReorderer {
+public:
+ // Create LayerReorderer for Fbo0
+ LayerReorderer(uint32_t width, uint32_t height, const Rect& repaintRect)
+ : LayerReorderer(width, height, repaintRect, nullptr, nullptr) {};
+
+ // Create LayerReorderer for an offscreen layer, where beginLayerOp is present for a
+ // saveLayer, renderNode is present for a HW layer.
+ LayerReorderer(uint32_t width, uint32_t height,
+ const Rect& repaintRect, const BeginLayerOp* beginLayerOp, RenderNode* renderNode);
+
+ // iterate back toward target to see if anything drawn since should overlap the new op
+ // if no target, merging ops still iterate to find similar batch to insert after
+ void locateInsertIndex(int batchId, const Rect& clippedBounds,
+ BatchBase** targetBatch, size_t* insertBatchIndex) const;
+
+ void deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId);
+
+ // insertion point of a new batch, will hopefully be immediately after similar batch
+ // (generally, should be similar shader)
+ void deferMergeableOp(LinearAllocator& allocator,
+ BakedOpState* op, batchid_t batchId, mergeid_t mergeId);
+
+ void replayBakedOpsImpl(void* arg, BakedOpReceiver* receivers, MergedOpReceiver*) const;
+
+ void deferLayerClear(const Rect& dstRect);
+
+ bool empty() const {
+ return mBatches.empty();
+ }
+
+ void clear() {
+ mBatches.clear();
+ }
+
+ void dump() const;
+
+ const uint32_t width;
+ const uint32_t height;
+ const Rect repaintRect;
+ OffscreenBuffer* offscreenBuffer;
+ const BeginLayerOp* beginLayerOp;
+ const RenderNode* renderNode;
+ const ClipRect viewportClip;
+
+ // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
+ std::vector<BakedOpState*> activeUnclippedSaveLayers;
+private:
+ void flushLayerClears(LinearAllocator& allocator);
+
+ std::vector<BatchBase*> mBatches;
+
+ /**
+ * Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
+ * MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
+ * collide, which avoids the need to resolve mergeid collisions.
+ */
+ std::unordered_map<mergeid_t, MergingOpBatch*> mMergingBatchLookup[OpBatchType::Count];
+
+ // Maps batch ids to the most recent *non-merging* batch of that id
+ OpBatch* mBatchLookup[OpBatchType::Count] = { nullptr };
+
+ std::vector<Rect> mClearRects;
+};
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index b6f50b111ab5..612cdfdc7185 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -87,7 +87,7 @@ class RenderNode;
*/
class RenderNode : public VirtualLightRefBase {
friend class TestUtils; // allow TestUtils to access syncDisplayList / syncProperties
-friend class OpReorderer;
+friend class FrameReorderer;
public:
enum DirtyPropertyMask {
GENERIC = 1 << 1,
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1af8f804e72a..fff8e0968ee6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -31,7 +31,7 @@
#include "utils/TimeUtils.h"
#if HWUI_NEW_OPS
-#include "OpReorderer.h"
+#include "FrameReorderer.h"
#endif
#include <cutils/properties.h>
@@ -338,7 +338,7 @@ void CanvasContext::draw() {
mEglManager.damageFrame(frame, dirty);
#if HWUI_NEW_OPS
- OpReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
+ FrameReorderer reorderer(mLayerUpdateQueue, dirty, frame.width(), frame.height(),
mRenderNodes, mLightCenter);
mLayerUpdateQueue.clear();
BakedOpRenderer renderer(Caches::getInstance(), mRenderThread.renderState(),
diff --git a/libs/hwui/tests/microbench/OpReordererBench.cpp b/libs/hwui/tests/microbench/FrameReordererBench.cpp
index 6bfe5a9a1028..b4c9a3626597 100644
--- a/libs/hwui/tests/microbench/OpReordererBench.cpp
+++ b/libs/hwui/tests/microbench/FrameReordererBench.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -19,8 +19,8 @@
#include "BakedOpState.h"
#include "BakedOpDispatcher.h"
#include "BakedOpRenderer.h"
+#include "FrameReorderer.h"
#include "LayerUpdateQueue.h"
-#include "OpReorderer.h"
#include "RecordedOp.h"
#include "RecordingCanvas.h"
#include "tests/common/TestContext.h"
@@ -61,20 +61,20 @@ static std::vector<sp<RenderNode>> createTestNodeList() {
return vec;
}
-BENCHMARK_NO_ARG(BM_OpReorderer_defer);
-void BM_OpReorderer_defer::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_defer);
+void BM_FrameBuilder_defer::Run(int iters) {
auto nodes = createTestNodeList();
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightCenter);
MicroBench::DoNotOptimize(&reorderer);
}
StopBenchmarkTiming();
}
-BENCHMARK_NO_ARG(BM_OpReorderer_deferAndRender);
-void BM_OpReorderer_deferAndRender::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_deferAndRender);
+void BM_FrameBuilder_deferAndRender::Run(int iters) {
TestUtils::runOnRenderThread([this, iters](RenderThread& thread) {
auto nodes = createTestNodeList();
BakedOpRenderer::LightInfo lightInfo = {50.0f, 128, 128 };
@@ -84,7 +84,7 @@ void BM_OpReorderer_deferAndRender::Run(int iters) {
StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
nodes, sLightCenter);
BakedOpRenderer renderer(caches, renderState, true, lightInfo);
@@ -117,7 +117,7 @@ static void benchDeferScene(testing::Benchmark& benchmark, int iters, const char
auto nodes = getSyncedSceneNodes(sceneName);
benchmark.StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightCenter);
MicroBench::DoNotOptimize(&reorderer);
@@ -136,7 +136,7 @@ static void benchDeferAndRenderScene(testing::Benchmark& benchmark,
benchmark.StartBenchmarkTiming();
for (int i = 0; i < iters; i++) {
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
nodes, sLightCenter);
@@ -148,13 +148,13 @@ static void benchDeferAndRenderScene(testing::Benchmark& benchmark,
});
}
-BENCHMARK_NO_ARG(BM_OpReorderer_listview_defer);
-void BM_OpReorderer_listview_defer::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_listview_defer);
+void BM_FrameBuilder_listview_defer::Run(int iters) {
benchDeferScene(*this, iters, "listview");
}
-BENCHMARK_NO_ARG(BM_OpReorderer_listview_deferAndRender);
-void BM_OpReorderer_listview_deferAndRender::Run(int iters) {
+BENCHMARK_NO_ARG(BM_FrameBuilder_listview_deferAndRender);
+void BM_FrameBuilder_listview_deferAndRender::Run(int iters) {
benchDeferAndRenderScene(*this, iters, "listview");
}
diff --git a/libs/hwui/tests/unit/OpReordererTests.cpp b/libs/hwui/tests/unit/FrameReordererTests.cpp
index 701e4460c35f..9d2eb98a011d 100644
--- a/libs/hwui/tests/unit/OpReordererTests.cpp
+++ b/libs/hwui/tests/unit/FrameReordererTests.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -18,8 +18,8 @@
#include <BakedOpState.h>
#include <DeferredLayerUpdater.h>
+#include <FrameReorderer.h>
#include <LayerUpdateQueue.h>
-#include <OpReorderer.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
#include <tests/common/TestUtils.h>
@@ -113,7 +113,7 @@ public:
class FailRenderer : public TestRendererBase {};
-TEST(OpReorderer, simple) {
+TEST(FrameReorderer, simple) {
class SimpleTestRenderer : public TestRendererBase {
public:
void startFrame(uint32_t width, uint32_t height, const Rect& repaintRect) override {
@@ -138,14 +138,14 @@ TEST(OpReorderer, simple) {
canvas.drawRect(0, 0, 100, 200, SkPaint());
canvas.drawBitmap(bitmap, 10, 10, nullptr);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
createSyncedNodeList(node), sLightCenter);
SimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
-TEST(OpReorderer, simpleStroke) {
+TEST(FrameReorderer, simpleStroke) {
class SimpleStrokeTestRenderer : public TestRendererBase {
public:
void onPointsOp(const PointsOp& op, const BakedOpState& state) override {
@@ -164,14 +164,14 @@ TEST(OpReorderer, simpleStroke) {
strokedPaint.setStrokeWidth(10);
canvas.drawPoint(50, 50, strokedPaint);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200,
createSyncedNodeList(node), sLightCenter);
SimpleStrokeTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
-TEST(OpReorderer, simpleRejection) {
+TEST(FrameReorderer, simpleRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -179,14 +179,14 @@ TEST(OpReorderer, simpleRejection) {
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
FailRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(OpReorderer, simpleBatching) {
+TEST(FrameReorderer, simpleBatching) {
const int LOOPS = 5;
class SimpleBatchingTestRenderer : public TestRendererBase {
public:
@@ -214,7 +214,7 @@ TEST(OpReorderer, simpleBatching) {
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SimpleBatchingTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -222,7 +222,7 @@ TEST(OpReorderer, simpleBatching) {
<< "Expect number of ops = 2 * loop count";
}
-TEST(OpReorderer, clippedMerging) {
+TEST(FrameReorderer, clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
void onMergedBitmapOps(const MergedBakedOpList& opList) override {
@@ -255,14 +255,14 @@ TEST(OpReorderer, clippedMerging) {
canvas.drawBitmap(bitmap, 40, 70, nullptr);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(node), sLightCenter);
ClippedMergingTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(OpReorderer, textMerging) {
+TEST(FrameReorderer, textMerging) {
class TextMergingTestRenderer : public TestRendererBase {
public:
void onMergedTextOps(const MergedBakedOpList& opList) override {
@@ -283,14 +283,14 @@ TEST(OpReorderer, textMerging) {
TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 0); // will be top clipped
TestUtils::drawTextToCanvas(&canvas, "Test string1", paint, 100, 100); // not clipped
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(400, 400), 400, 400,
createSyncedNodeList(node), sLightCenter);
TextMergingTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex()) << "Expect 2 ops";
}
-TEST(OpReorderer, textStrikethrough) {
+TEST(FrameReorderer, textStrikethrough) {
const int LOOPS = 5;
class TextStrikethroughTestRenderer : public TestRendererBase {
public:
@@ -314,7 +314,7 @@ TEST(OpReorderer, textStrikethrough) {
TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1));
}
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 2000), 200, 2000,
createSyncedNodeList(node), sLightCenter);
TextStrikethroughTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -322,7 +322,7 @@ TEST(OpReorderer, textStrikethrough) {
<< "Expect number of ops = 2 * loop count";
}
-RENDERTHREAD_TEST(OpReorderer, textureLayer) {
+RENDERTHREAD_TEST(FrameReorderer, textureLayer) {
class TextureLayerTestRenderer : public TestRendererBase {
public:
void onTextureLayerOp(const TextureLayerOp& op, const BakedOpState& state) override {
@@ -348,14 +348,14 @@ RENDERTHREAD_TEST(OpReorderer, textureLayer) {
canvas.drawLayer(layerUpdater.get());
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
TextureLayerTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex());
}
-TEST(OpReorderer, renderNode) {
+TEST(FrameReorderer, renderNode) {
class RenderNodeTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -393,13 +393,13 @@ TEST(OpReorderer, renderNode) {
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
RenderNodeTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(OpReorderer, clipped) {
+TEST(FrameReorderer, clipped) {
class ClippedTestRenderer : public TestRendererBase {
public:
void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
@@ -416,14 +416,14 @@ TEST(OpReorderer, clipped) {
canvas.drawBitmap(bitmap, 0, 0, nullptr);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue,
SkRect::MakeLTRB(10, 20, 30, 40), // clip to small area, should see in receiver
200, 200, createSyncedNodeList(node), sLightCenter);
ClippedTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(OpReorderer, saveLayer_simple) {
+TEST(FrameReorderer, saveLayer_simple) {
class SaveLayerSimpleTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -459,14 +459,14 @@ TEST(OpReorderer, saveLayer_simple) {
canvas.drawRect(10, 10, 190, 190, SkPaint());
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(OpReorderer, saveLayer_nested) {
+TEST(FrameReorderer, saveLayer_nested) {
/* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
* - startTemporaryLayer2, rect2 endLayer2
* - startTemporaryLayer1, rect1, drawLayer2, endLayer1
@@ -531,14 +531,14 @@ TEST(OpReorderer, saveLayer_nested) {
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(800, 800), 800, 800,
createSyncedNodeList(node), sLightCenter);
SaveLayerNestedTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
}
-TEST(OpReorderer, saveLayer_contentRejection) {
+TEST(FrameReorderer, saveLayer_contentRejection) {
auto node = TestUtils::createNode(0, 0, 200, 200,
[](RenderProperties& props, RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -551,7 +551,7 @@ TEST(OpReorderer, saveLayer_contentRejection) {
canvas.restore();
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
FailRenderer renderer;
@@ -559,7 +559,7 @@ TEST(OpReorderer, saveLayer_contentRejection) {
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-TEST(OpReorderer, saveLayerUnclipped_simple) {
+TEST(FrameReorderer, saveLayerUnclipped_simple) {
class SaveLayerUnclippedSimpleTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -594,14 +594,14 @@ TEST(OpReorderer, saveLayerUnclipped_simple) {
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(4, renderer.getIndex());
}
-TEST(OpReorderer, saveLayerUnclipped_mergedClears) {
+TEST(FrameReorderer, saveLayerUnclipped_mergedClears) {
class SaveLayerUnclippedMergedClearsTestRenderer : public TestRendererBase {
public:
void onCopyToLayerOp(const CopyToLayerOp& op, const BakedOpState& state) override {
@@ -648,7 +648,7 @@ TEST(OpReorderer, saveLayerUnclipped_mergedClears) {
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restoreToCount(restoreTo);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedMergedClearsTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -660,7 +660,7 @@ TEST(OpReorderer, saveLayerUnclipped_mergedClears) {
* - startTemporaryLayer, onCopyToLayer, onSimpleRects, onRect, onCopyFromLayer, endLayer
* - startFrame, onCopyToLayer, onSimpleRects, drawLayer, onCopyFromLayer, endframe
*/
-TEST(OpReorderer, saveLayerUnclipped_complex) {
+TEST(FrameReorderer, saveLayerUnclipped_complex) {
class SaveLayerUnclippedComplexTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
@@ -710,14 +710,14 @@ TEST(OpReorderer, saveLayerUnclipped_complex) {
canvas.restore();
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(600, 600), 600, 600,
createSyncedNodeList(node), sLightCenter);
SaveLayerUnclippedComplexTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(12, renderer.getIndex());
}
-RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {
+RENDERTHREAD_TEST(FrameReorderer, hwLayer_simple) {
class HwLayerSimpleTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -768,7 +768,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(25, 25, 75, 75));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedNodeList, sLightCenter);
HwLayerSimpleTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -778,7 +778,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_simple) {
*layerHandle = nullptr;
}
-RENDERTHREAD_TEST(OpReorderer, hwLayer_complex) {
+RENDERTHREAD_TEST(FrameReorderer, hwLayer_complex) {
/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
* - startRepaintLayer(child), rect(grey), endLayer
* - startTemporaryLayer, drawLayer(child), endLayer
@@ -869,7 +869,7 @@ RENDERTHREAD_TEST(OpReorderer, hwLayer_complex) {
layerUpdateQueue.enqueueLayerWithDamage(child.get(), Rect(100, 100));
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(200, 200));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedList, sLightCenter);
HwLayerComplexTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -894,7 +894,7 @@ static void drawOrderedNode(RecordingCanvas* canvas, uint8_t expectedDrawOrder,
node->setPropertyFieldsDirty(RenderNode::TRANSLATION_Z);
canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
}
-TEST(OpReorderer, zReorder) {
+TEST(FrameReorderer, zReorder) {
class ZReorderTestRenderer : public TestRendererBase {
public:
void onRectOp(const RectOp& op, const BakedOpState& state) override {
@@ -918,14 +918,14 @@ TEST(OpReorderer, zReorder) {
drawOrderedRect(&canvas, 8);
drawOrderedNode(&canvas, 9, -10.0f); // in reorder=false at this point, so played inorder
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(parent), sLightCenter);
ZReorderTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(10, renderer.getIndex());
};
-TEST(OpReorderer, projectionReorder) {
+TEST(FrameReorderer, projectionReorder) {
static const int scrollX = 5;
static const int scrollY = 10;
class ProjectionReorderTestRenderer : public TestRendererBase {
@@ -1001,7 +1001,7 @@ TEST(OpReorderer, projectionReorder) {
canvas.restore();
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 100, 100,
createSyncedNodeList(parent), sLightCenter);
ProjectionReorderTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1020,7 +1020,7 @@ static sp<RenderNode> createWhiteRectShadowCaster(float translationZ) {
});
}
-TEST(OpReorderer, shadow) {
+TEST(FrameReorderer, shadow) {
class ShadowTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1044,14 +1044,14 @@ TEST(OpReorderer, shadow) {
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0f).get());
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
ShadowTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(2, renderer.getIndex());
}
-TEST(OpReorderer, shadowSaveLayer) {
+TEST(FrameReorderer, shadowSaveLayer) {
class ShadowSaveLayerTestRenderer : public TestRendererBase {
public:
OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
@@ -1085,14 +1085,14 @@ TEST(OpReorderer, shadowSaveLayer) {
canvas.restoreToCount(count);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), (Vector3) { 100, 100, 100 });
ShadowSaveLayerTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(5, renderer.getIndex());
}
-RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
+RENDERTHREAD_TEST(FrameReorderer, shadowHwLayer) {
class ShadowHwLayerTestRenderer : public TestRendererBase {
public:
void startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) override {
@@ -1135,7 +1135,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
auto syncedList = createSyncedNodeList(parent);
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(parent.get(), Rect(100, 100));
- OpReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(layerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
syncedList, (Vector3) { 100, 100, 100 });
ShadowHwLayerTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1145,7 +1145,7 @@ RENDERTHREAD_TEST(OpReorderer, shadowHwLayer) {
*layerHandle = nullptr;
}
-TEST(OpReorderer, shadowLayering) {
+TEST(FrameReorderer, shadowLayering) {
class ShadowLayeringTestRenderer : public TestRendererBase {
public:
void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
@@ -1164,7 +1164,7 @@ TEST(OpReorderer, shadowLayering) {
canvas.drawRenderNode(createWhiteRectShadowCaster(5.0001f).get());
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200,
createSyncedNodeList(parent), sLightCenter);
ShadowLayeringTestRenderer renderer;
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1192,14 +1192,14 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac
canvas.drawRect(0, 0, 100, 100, paint);
});
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 100), 200, 200,
createSyncedNodeList(node), sLightCenter);
PropertyTestRenderer renderer(opValidateCallback);
reorderer.replayBakedOps<TestDispatcher>(renderer);
EXPECT_EQ(1, renderer.getIndex()) << "Should have seen one op";
}
-TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
+TEST(FrameReorderer, renderPropOverlappingRenderingAlpha) {
testProperty([](RenderProperties& properties) {
properties.setAlpha(0.5f);
properties.setHasOverlappingRendering(false);
@@ -1208,7 +1208,7 @@ TEST(OpReorderer, renderPropOverlappingRenderingAlpha) {
});
}
-TEST(OpReorderer, renderPropClipping) {
+TEST(FrameReorderer, renderPropClipping) {
testProperty([](RenderProperties& properties) {
properties.setClipToBounds(true);
properties.setClipBounds(Rect(10, 20, 300, 400));
@@ -1218,7 +1218,7 @@ TEST(OpReorderer, renderPropClipping) {
});
}
-TEST(OpReorderer, renderPropRevealClip) {
+TEST(FrameReorderer, renderPropRevealClip) {
testProperty([](RenderProperties& properties) {
properties.mutableRevealClip().set(true, 50, 50, 25);
}, [](const RectOp& op, const BakedOpState& state) {
@@ -1229,7 +1229,7 @@ TEST(OpReorderer, renderPropRevealClip) {
});
}
-TEST(OpReorderer, renderPropOutlineClip) {
+TEST(FrameReorderer, renderPropOutlineClip) {
testProperty([](RenderProperties& properties) {
properties.mutableOutline().setShouldClip(true);
properties.mutableOutline().setRoundRect(10, 20, 30, 40, 5.0f, 0.5f);
@@ -1241,7 +1241,7 @@ TEST(OpReorderer, renderPropOutlineClip) {
});
}
-TEST(OpReorderer, renderPropTransform) {
+TEST(FrameReorderer, renderPropTransform) {
testProperty([](RenderProperties& properties) {
properties.setLeftTopRightBottom(10, 10, 110, 110);
@@ -1334,7 +1334,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
});
auto nodes = createSyncedNodeList(node); // sync before querying height
- OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
+ FrameReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes, sLightCenter);
SaveLayerAlphaClipTestRenderer renderer(outObservedData);
reorderer.replayBakedOps<TestDispatcher>(renderer);
@@ -1342,7 +1342,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData,
ASSERT_EQ(4, renderer.getIndex()) << "Test must trigger saveLayer alpha behavior.";
}
-TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
+TEST(FrameReorderer, renderPropSaveLayerAlphaClipBig) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setTranslationX(10); // offset rendering content
@@ -1358,7 +1358,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaClipBig) {
<< "expect content to be translated as part of being clipped";
}
-TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
+TEST(FrameReorderer, renderPropSaveLayerAlphaRotate) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
// Translate and rotate the view so that the only visible part is the top left corner of
@@ -1377,7 +1377,7 @@ TEST(OpReorderer, renderPropSaveLayerAlphaRotate) {
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
}
-TEST(OpReorderer, renderPropSaveLayerAlphaScale) {
+TEST(FrameReorderer, renderPropSaveLayerAlphaScale) {
SaveLayerAlphaData observedData;
testSaveLayerAlphaClip(&observedData, [](RenderProperties& properties) {
properties.setPivotX(0);
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 7e0649be722d..a3cfde825f47 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -15,11 +15,19 @@
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
+<!-- showAsAction flag impacts the behavior of SearchView.
+ When set to collapseActionView, collapsing SearchView to icon is the
+ default behavior. It would fit UX, however after expanding SearchView is
+ shown on the left site of the toolbar (replacing title). Since no way to
+ prevent this behavior was found, the flag is set to always. SearchView is
+ always visible by default and it is being collapse manually by calling
+ setIconified() method
+-->
<item
android:id="@+id/menu_search"
android:title="@string/menu_search"
android:icon="@drawable/ic_menu_search"
- android:showAsAction="always|collapseActionView"
+ android:showAsAction="always"
android:actionViewClass="android.widget.SearchView"
android:imeOptions="actionSearch" />
<item
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 153c673caf56..c868d340e901 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -33,4 +33,6 @@
<color name="item_doc_background">#fffafafa</color>
<color name="item_doc_background_selected">#ffe0f2f1</color>
+ <color name="menu_search_background">#ff676f74</color>
+
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index 7f710fc58181..180a48eaca99 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -38,12 +38,14 @@ import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MenuItem.OnActionExpandListener;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnFocusChangeListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -218,6 +220,7 @@ public abstract class BaseActivity extends Activity {
case R.id.menu_advanced:
case R.id.menu_file_size:
case R.id.menu_new_window:
+ case R.id.menu_search:
break;
default:
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
@@ -318,6 +321,8 @@ public abstract class BaseActivity extends Activity {
* the (abstract) directoryChanged method will be called.
* @param anim
*/
+ // TODO: Refactor the usage of the method - now it is called not only when the directory
+ // changed, but also to refresh the content of the directory while searching
final void onCurrentDirectoryChanged(int anim) {
mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
onDirectoryChanged(anim);
@@ -328,7 +333,11 @@ public abstract class BaseActivity extends Activity {
}
updateActionBar();
- invalidateOptionsMenu();
+
+ // Prevents searchView from being recreated while searching
+ if (!mSearchManager.isSearching()) {
+ invalidateOptionsMenu();
+ }
}
final List<String> getExcludedAuthorities() {
@@ -720,7 +729,7 @@ public abstract class BaseActivity extends Activity {
* Facade over the various search parts in the menu.
*/
final class SearchManager implements
- SearchView.OnCloseListener, OnActionExpandListener, OnQueryTextListener,
+ SearchView.OnCloseListener, OnQueryTextListener, OnClickListener, OnFocusChangeListener,
DocumentsToolBar.OnActionViewCollapsedListener {
private boolean mSearchExpanded;
@@ -738,9 +747,10 @@ public abstract class BaseActivity extends Activity {
mView = (SearchView) mMenu.getActionView();
mActionBar.setOnActionViewCollapsedListener(this);
- mMenu.setOnActionExpandListener(this);
mView.setOnQueryTextListener(this);
mView.setOnCloseListener(this);
+ mView.setOnSearchClickListener(this);
+ mView.setOnQueryTextFocusChangeListener(this);
}
/**
@@ -793,19 +803,13 @@ public abstract class BaseActivity extends Activity {
* search currently.
*/
boolean cancelSearch() {
- boolean collapsed = false;
- boolean closed = false;
-
- if (mActionBar.hasExpandedActionView()) {
- mActionBar.collapseActionView();
- collapsed = true;
- }
-
if (isExpanded() || isSearching()) {
- onClose();
- closed = true;
+ // If the query string is not empty search view won't get iconified
+ mView.setQuery("", false);
+ mView.setIconified(true);
+ return true;
}
- return collapsed || closed;
+ return false;
}
boolean isSearching() {
@@ -816,6 +820,11 @@ public abstract class BaseActivity extends Activity {
return mSearchExpanded;
}
+ /**
+ * Clears the search.
+ * @return True if the default behavior of clearing/dismissing SearchView should be
+ * overridden. False otherwise.
+ */
@Override
public boolean onClose() {
mSearchExpanded = false;
@@ -824,33 +833,33 @@ public abstract class BaseActivity extends Activity {
return false;
}
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
+ mView.setBackgroundColor(
+ getResources().getColor(android.R.color.transparent, null));
+
+ // Refresh the directory if a search was done
+ if(mState.currentSearch != null) {
+ mState.currentSearch = null;
+ onCurrentDirectoryChanged(ANIM_NONE);
+ }
+
return false;
}
+ /**
+ * Sets mSearchExpanded.
+ * Called when search icon is clicked to start search.
+ * Used to detect when the view expanded instead of onMenuItemActionExpand, because
+ * SearchView has showAsAction set to always and onMenuItemAction* methods are not called.
+ */
@Override
- public boolean onMenuItemActionExpand(MenuItem item) {
+ public void onClick (View v) {
mSearchExpanded = true;
- updateActionBar();
- return true;
- }
-
- @Override
- public boolean onMenuItemActionCollapse(MenuItem item) {
- mSearchExpanded = false;
- if (mIgnoreNextCollapse) {
- mIgnoreNextCollapse = false;
- return true;
- }
- mState.currentSearch = null;
- onCurrentDirectoryChanged(ANIM_NONE);
- return true;
+ mView.setBackgroundColor(
+ getResources().getColor(R.color.menu_search_background, null));
}
@Override
public boolean onQueryTextSubmit(String query) {
- mSearchExpanded = true;
mState.currentSearch = query;
mView.clearFocus();
onCurrentDirectoryChanged(ANIM_NONE);
@@ -863,6 +872,18 @@ public abstract class BaseActivity extends Activity {
}
@Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if(!hasFocus) {
+ if(mState.currentSearch == null) {
+ mView.setIconified(true);
+ }
+ else if(TextUtils.isEmpty(mView.getQuery())) {
+ cancelSearch();
+ }
+ }
+ }
+
+ @Override
public void onActionViewCollapsed() {
updateActionBar();
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index ca8ef2e65bcb..223af896ae23 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -308,8 +308,10 @@ public class DocumentsActivity extends BaseActivity {
mSearchManager.showMenu(!picking);
// No display options in recent directories
- grid.setVisible(!(picking && recents));
- list.setVisible(!(picking && recents));
+ if (picking && recents) {
+ grid.setVisible(false);
+ list.setVisible(false);
+ }
fileSize.setVisible(fileSize.isVisible() && !picking);
settings.setVisible(false);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 84ab85e35dc0..898713f75e62 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -407,7 +407,6 @@ public class DirectoryFragment extends Fragment implements DocumentsAdapter.Envi
state.derivedMode = result.mode;
}
state.derivedSortOrder = result.sortOrder;
- ((BaseActivity) context).onStateChanged();
updateDisplayState();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 109cf4726101..824d10a3aa19 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -28,7 +28,6 @@ import android.graphics.Rect;
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
import android.util.AttributeSet;
-import android.util.MathUtils;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.MotionEvent;
@@ -39,7 +38,6 @@ import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
-import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
@@ -48,8 +46,10 @@ import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageButton;
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
import com.android.systemui.R;
-import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.statusbar.FlingAnimationUtils;
import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
@@ -167,7 +167,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
public boolean startDragging(boolean animate) {
mHandle.setTouching(true, animate);
mDockSide = mWindowManagerProxy.getDockSide();
- mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, mDisplayWidth,
+ mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
+ mFlingAnimationUtils.getMinVelocityPxPerSecond(), mDisplayWidth,
mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
if (mDockSide != WindowManager.DOCKED_INVALID) {
mWindowManagerProxy.setResizing(true);
@@ -362,36 +363,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
return mStartPosition + touchY - mStartY;
}
- public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
- outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- switch (dockSide) {
- case WindowManager.DOCKED_LEFT:
- outRect.right = position;
- break;
- case WindowManager.DOCKED_TOP:
- outRect.bottom = position;
- break;
- case WindowManager.DOCKED_RIGHT:
- outRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
- break;
- case WindowManager.DOCKED_BOTTOM:
- outRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
- break;
- }
- if (outRect.left > outRect.right) {
- outRect.left = outRect.right;
- }
- if (outRect.top > outRect.bottom) {
- outRect.top = outRect.bottom;
- }
- if (outRect.right < outRect.left) {
- outRect.right = outRect.left;
- }
- if (outRect.bottom < outRect.top) {
- outRect.bottom = outRect.top;
- }
- }
-
private int invertDockSide(int dockSide) {
switch (dockSide) {
case WindowManager.DOCKED_LEFT:
@@ -421,6 +392,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
containingRect.right, containingRect.bottom);
}
+ public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
+ DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
+ mDisplayHeight, mDividerSize);
+ }
+
public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
calculateBoundsForPosition(position, mDockSide, mDockedRect);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index fb2bc175b829..e5b4f4d0369d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -29,8 +29,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
-import com.android.systemui.stackdivider.DividerView;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import com.android.systemui.tuner.TunerService;
import static android.view.WindowManager.*;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f21eba1460ec..a4b13ed96e00 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -515,9 +515,9 @@ public final class ActivityManagerService extends ActivityManagerNative
private Installer mInstaller;
/** Run all ActivityStacks through this */
- ActivityStackSupervisor mStackSupervisor;
+ final ActivityStackSupervisor mStackSupervisor;
- ActivityStarter mActivityStarter;
+ final ActivityStarter mActivityStarter;
/** Task stack change listeners. */
private RemoteCallbackList<ITaskStackListener> mTaskStackListeners =
@@ -3643,11 +3643,9 @@ public final class ActivityManagerService extends ActivityManagerNative
return false;
}
Intent intent = getHomeIntent();
- ActivityInfo aInfo =
- resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
+ ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
- intent.setComponent(new ComponentName(
- aInfo.applicationInfo.packageName, aInfo.name));
+ intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index e123dbd3d84a..c44b4cfa7b26 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3368,9 +3368,9 @@ final class ActivityStack {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
destIntent.getComponent(), 0, srec.userId);
- int res = mService.mActivityStarter.startActivityLocked(srec.app.thread, destIntent,
- null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null, null,
- parent.appToken, null, 0, -1, parent.launchedFromUid,
+ int res = mService.mActivityStarter.startActivityLocked(srec.app.thread,
+ destIntent, null /*ephemeralIntent*/, null, aInfo, null /*rInfo*/, null,
+ null, parent.appToken, null, 0, -1, parent.launchedFromUid,
parent.launchedFromPackage, -1, parent.launchedFromUid, 0, null,
false, true, null, null, null);
foundParentInTask = res == ActivityManager.START_SUCCESS;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index f712613a2037..3ea11b6d3047 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -32,12 +32,12 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP;
import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -45,6 +45,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -64,7 +65,6 @@ import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.TAG_TASKS;
-import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
@@ -529,7 +529,10 @@ class ActivityStarter {
// switch... just dismiss the keyguard now, because we
// probably want to see whatever is behind it.
mSupervisor.notifyActivityDrawnForKeyguard();
+ } else {
+ launchRecentsAppIfNeeded(stack);
}
+
return err;
}
@@ -1006,6 +1009,16 @@ class ActivityStarter {
return START_SUCCESS;
}
+ private void launchRecentsAppIfNeeded(ActivityStack topStack) {
+ if (topStack.mStackId == HOME_STACK_ID && mTargetStack.mStackId == DOCKED_STACK_ID) {
+ // We launch an activity while being in home stack, which means either launcher or
+ // recents into docked stack. We don't want the launched activity to be alone in a
+ // docked stack, so we want to immediately launch recents too.
+ if (DEBUG_RECENTS) Slog.d(TAG, "Scheduling recents launch.");
+ mWindowManager.showRecentApps();
+ }
+ }
+
private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
boolean doResume, int startFlags, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e787eda171ea..018bf2d2cb27 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -28,6 +28,7 @@ import static android.service.notification.NotificationAssistantService.REASON_L
import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationAssistantService.REASON_TOPIC_BANNED;
import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
@@ -741,7 +742,7 @@ public class NotificationManagerService extends SystemService {
for (String pkgName : pkgList) {
if (cancelNotifications) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
- changeUserId, REASON_PACKAGE_CHANGED, null);
+ changeUserId, REASON_PACKAGE_CHANGED, null, null);
}
}
}
@@ -774,7 +775,7 @@ public class NotificationManagerService extends SystemService {
int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
if (userHandle >= 0) {
cancelAllNotificationsInt(MY_UID, MY_PID, null, 0, 0, true, userHandle,
- REASON_USER_STOPPED, null);
+ REASON_USER_STOPPED, null, null);
}
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
@@ -1051,7 +1052,7 @@ public class NotificationManagerService extends SystemService {
// Now, cancel any outstanding notifications that are part of a just-disabled app
if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true, UserHandle.getUserId(uid),
- REASON_PACKAGE_BANNED, null);
+ REASON_PACKAGE_BANNED, null, null);
}
}
@@ -1209,7 +1210,7 @@ public class NotificationManagerService extends SystemService {
// running foreground services.
cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId,
- REASON_APP_CANCEL_ALL, null);
+ REASON_APP_CANCEL_ALL, null, null);
}
@Override
@@ -1266,6 +1267,11 @@ public class NotificationManagerService extends SystemService {
public void setTopicImportance(String pkg, int uid, Notification.Topic topic,
int importance) {
enforceSystemOrSystemUI("Caller not system or systemui");
+ if (NotificationListenerService.Ranking.IMPORTANCE_NONE == importance) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, 0, 0, true,
+ UserHandle.getUserId(uid),
+ REASON_TOPIC_BANNED, topic, null);
+ }
mRankingHelper.setTopicImportance(pkg, uid, topic, importance);
savePolicyFile();
}
@@ -2284,8 +2290,9 @@ public class NotificationManagerService extends SystemService {
mRankingHelper.extractSignals(r);
savePolicyFile();
- // blocked apps
- if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
+ // blocked apps/topics
+ if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
+ || !noteNotificationOp(pkg, callingUid)) {
if (!isSystemNotification) {
Slog.e(TAG, "Suppressing notification from package " + pkg
+ " by user request.");
@@ -3067,11 +3074,11 @@ public class NotificationManagerService extends SystemService {
}
/**
- * Cancels all notifications from a given package that have all of the
+ * Cancels all notifications from a given package or topic that have all of the
* {@code mustHaveFlags}.
*/
boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
- int mustNotHaveFlags, boolean doit, int userId, int reason,
+ int mustNotHaveFlags, boolean doit, int userId, int reason, Notification.Topic topic,
ManagedServiceInfo listener) {
String listenerName = listener == null ? null : listener.component.toShortString();
EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
@@ -3099,6 +3106,10 @@ public class NotificationManagerService extends SystemService {
if (pkg != null && !r.sbn.getPackageName().equals(pkg)) {
continue;
}
+ if (topic != null
+ && !topic.getId().equals(r.getNotification().getTopic().getId())) {
+ continue;
+ }
if (canceledNotifications == null) {
canceledNotifications = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f1fd42c69101..ce4ecd39cd58 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -423,14 +423,16 @@ public class RankingHelper implements RankingConfig {
/**
* Sets the default importance for all new topics that appear in the future, and resets
- * the importance of all current topics.
+ * the importance of all current topics (unless the app is being blocked).
*/
@Override
public void setAppImportance(String pkgName, int uid, int importance) {
final Record r = getOrCreateRecord(pkgName, uid);
r.importance = importance;
- for (Topic t : r.topics.values()) {
- t.importance = importance;
+ if (Ranking.IMPORTANCE_NONE != importance) {
+ for (Topic t : r.topics.values()) {
+ t.importance = importance;
+ }
}
updateConfig();
}
@@ -483,7 +485,9 @@ public class RankingHelper implements RankingConfig {
pw.print(prefix);
pw.println("per-package config:");
}
+ pw.println("Records:");
dumpRecords(pw, prefix, filter, mRecords);
+ pw.println("Restored without uid:");
dumpRecords(pw, prefix, filter, mRestoredWithoutUids);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 870ae892d435..41627fd58f01 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -318,6 +318,8 @@ public class PackageManagerService extends IPackageManager.Stub {
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
+ private static final boolean DISABLE_EPHEMERAL_APPS = true;
+
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
@@ -4469,6 +4471,9 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean isEphemeralAllowed(
Intent intent, List<ResolveInfo> resolvedActivites, int userId) {
// Short circuit and return early if possible.
+ if (DISABLE_EPHEMERAL_APPS) {
+ return false;
+ }
final int callingUser = UserHandle.getCallingUserId();
if (callingUser != UserHandle.USER_SYSTEM) {
return false;
@@ -5803,6 +5808,10 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public ParceledListSlice<EphemeralApplicationInfo> getEphemeralApplications(int userId) {
+ if (DISABLE_EPHEMERAL_APPS) {
+ return null;
+ }
+
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
"getEphemeralApplications");
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
@@ -5821,6 +5830,10 @@ public class PackageManagerService extends IPackageManager.Stub {
public boolean isEphemeralApplication(String packageName, int userId) {
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
"isEphemeral");
+ if (DISABLE_EPHEMERAL_APPS) {
+ return false;
+ }
+
if (!isCallerSameApp(packageName)) {
return false;
}
@@ -5835,6 +5848,10 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public byte[] getEphemeralApplicationCookie(String packageName, int userId) {
+ if (DISABLE_EPHEMERAL_APPS) {
+ return null;
+ }
+
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
"getCookie");
if (!isCallerSameApp(packageName)) {
@@ -5848,6 +5865,10 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public boolean setEphemeralApplicationCookie(String packageName, byte[] cookie, int userId) {
+ if (DISABLE_EPHEMERAL_APPS) {
+ return true;
+ }
+
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
"setCookie");
if (!isCallerSameApp(packageName)) {
@@ -5861,6 +5882,10 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public Bitmap getEphemeralApplicationIcon(String packageName, int userId) {
+ if (DISABLE_EPHEMERAL_APPS) {
+ return null;
+ }
+
mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_EPHEMERAL_APPS,
"getEphemeralApplicationIcon");
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index f13d964e1e1b..de1c1ea091df 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3788,7 +3788,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// size. We need to do this directly, instead of relying on
// it to bubble up from the nav bar, because this needs to
// change atomically with screen rotations.
- mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
+ mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
if (mNavigationBarOnBottom) {
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
int top = displayHeight - overscanBottom
@@ -3859,6 +3859,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return false;
}
+ private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
+ return !mNavigationBarCanMove || displayWidth < displayHeight;
+ }
+
/** {@inheritDoc} */
@Override
public int getSystemDecorLayerLw() {
@@ -5931,6 +5935,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ @Override
+ public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
+ Rect outInsets) {
+ outInsets.setEmpty();
+ if (mStatusBar != null) {
+ outInsets.top = mStatusBarHeight;
+ }
+ if (mNavigationBar != null) {
+ if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
+ outInsets.bottom = getNavigationBarHeight(displayRotation, mUiMode);
+ } else {
+ outInsets.right = getNavigationBarWidth(displayRotation, mUiMode);
+ }
+ }
+ }
+
void sendCloseSystemWindows() {
PhoneWindow.sendCloseSystemWindows(mContext, null);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index fb778ec94f7f..e75780f6a095 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,6 +18,7 @@ package com.android.server.wm;
import android.app.ActivityManager.StackId;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Debug;
import android.util.EventLog;
@@ -26,6 +27,9 @@ import android.util.SparseArray;
import android.view.DisplayInfo;
import android.view.Surface;
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
+import com.android.internal.policy.DockedDividerUtils;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -245,19 +249,66 @@ public class TaskStack implements DimLayer.DimLayerUser {
setBounds(null);
} else {
mTmpRect2.set(mBounds);
- mDisplayContent.rotateBounds(
- mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
- if (setBounds(mTmpRect2)) {
- // Post message to inform activity manager of the bounds change simulating
- // a one-way call. We do this to prevent a deadlock between window manager
- // lock and activity manager lock been held.
- mService.mH.sendMessage(mService.mH.obtainMessage(
- RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mBounds));
+ final int newRotation = mDisplayContent.getDisplayInfo().rotation;
+ if (mRotation == newRotation) {
+ setBounds(mTmpRect2);
}
+
+ // If the rotation changes, we'll handle it in updateBoundsAfterRotation
}
}
}
+ /**
+ * Updates the bounds after rotating the screen. We can't handle it in
+ * {@link #updateDisplayInfo} because at that point the configuration might not be fully updated
+ * yet.
+ */
+ void updateBoundsAfterRotation() {
+ final int newRotation = getDisplayInfo().rotation;
+ mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
+ if (mStackId == DOCKED_STACK_ID) {
+ snapDockedStackAfterRotation(mTmpRect2);
+ }
+
+ // Post message to inform activity manager of the bounds change simulating
+ // a one-way call. We do this to prevent a deadlock between window manager
+ // lock and activity manager lock been held.
+ mService.mH.sendMessage(mService.mH.obtainMessage(
+ RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2));
+ }
+
+ /**
+ * Snaps the bounds after rotation to the closest snap target for the docked stack.
+ */
+ private void snapDockedStackAfterRotation(Rect outBounds) {
+
+ // Calculate the current position.
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ final int dividerSize = mService.getDefaultDisplayContentLocked()
+ .getDockedDividerController().getContentWidth();
+ final int dockSide = getDockSide(outBounds);
+ final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
+ dockSide, dividerSize);
+ final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
+ final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
+
+ // Snap the position to a target.
+ final int rotation = displayInfo.rotation;
+ final int orientation = mService.mCurConfiguration.orientation;
+ mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
+ final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
+ mService.mContext.getResources(),
+ 0 /* minFlingVelocityPxPerSecond */, displayWidth, displayHeight,
+ dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
+ final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
+
+ // Recalculate the bounds based on the position of the target.
+ DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
+ outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
+ dividerSize);
+ }
+
boolean isAnimating() {
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
@@ -682,6 +733,10 @@ public class TaskStack implements DimLayer.DimLayerUser {
* information which side of the screen was the dock anchored.
*/
int getDockSide() {
+ return getDockSide(mBounds);
+ }
+
+ int getDockSide(Rect bounds) {
if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
return DOCKED_INVALID;
}
@@ -692,14 +747,14 @@ public class TaskStack implements DimLayer.DimLayerUser {
final int orientation = mService.mCurConfiguration.orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
// Portrait mode, docked either at the top or the bottom.
- if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
+ if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) {
return DOCKED_TOP;
} else {
return DOCKED_BOTTOM;
}
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
// Landscape mode, docked either on the left or on the right.
- if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
+ if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) {
return DOCKED_LEFT;
} else {
return DOCKED_RIGHT;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 71cd66043331..685df25910d6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3527,6 +3527,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
public void setNewConfiguration(Configuration config) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setNewConfiguration()")) {
@@ -3540,10 +3541,20 @@ public class WindowManagerService extends IWindowManager.Stub
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
+ if (orientationChanged) {
+ updateTaskStackBoundsAfterRotation();
+ }
mWindowPlacerLocked.performSurfacePlacement();
}
}
+ private void updateTaskStackBoundsAfterRotation() {
+ for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
+ final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
+ stack.updateBoundsAfterRotation();
+ }
+ }
+
@Override
public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 01583f56ef73..81aa6c6eec3c 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -322,21 +322,25 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
+ /** @hide */
@Override
public byte[] getEphemeralCookie() {
return new byte[0];
}
+ /** @hide */
@Override
public boolean isEphemeralApplication() {
return false;
}
+ /** @hide */
@Override
public int getEphemeralCookieMaxSizeBytes() {
return 0;
}
+ /** @hide */
@Override
public boolean setEphemeralCookie(@NonNull byte[] cookie) {
return false;
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index fecfdf98d0e7..3a30230833ed 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -175,7 +175,7 @@ public class NotificationTestList extends TestActivity
.setTopic(new Notification.Topic("hello", "Hello"))
.build();
- mNM.notify(999, n);
+ mNM.notify(70, n);
}
},
@@ -194,7 +194,7 @@ public class NotificationTestList extends TestActivity
.setStyle(picture)
.build();
- mNM.notify(9999, n);
+ mNM.notify(71, n);
}
},
new Test("with topic Bananas") {
@@ -211,10 +211,28 @@ public class NotificationTestList extends TestActivity
.setTopic(new Notification.Topic("bananas", "Bananas"))
.build();
- mNM.notify(999, n);
+ mNM.notify(72, n);
}
},
+ new Test("with delete intent") {
+ public void run() {
+ Notification.BigTextStyle bigText = new Notification.BigTextStyle();
+ bigText.bigText("bananas are great\nso tasty\nyum\nyum\nyum\n");
+ Notification n = new Notification.Builder(NotificationTestList.this)
+ .setSmallIcon(R.drawable.icon1)
+ .setStyle(bigText)
+ .setWhen(mActivityCreateTime)
+ .setContentTitle("bananananana")
+ .setContentText("This is a banana!!!")
+ .setTopic(new Notification.Topic("bananas", "Bananas"))
+ .setDeleteIntent(makeIntent2())
+ .build();
+
+ mNM.notify(73, n);
+ }
+ },
+
new Test("Whens") {
public void run()
{