Merge "Fix lost window warning"
diff --git a/api/current.txt b/api/current.txt
index dabc084..96ecad6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -933,6 +933,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1710,11 +1711,13 @@
field public static final int icon = 16908294; // 0x1020006
field public static final int icon1 = 16908295; // 0x1020007
field public static final int icon2 = 16908296; // 0x1020008
+ field public static final int icon_frame = 16908350; // 0x102003e
field public static final int input = 16908297; // 0x1020009
field public static final int inputArea = 16908318; // 0x102001e
field public static final int inputExtractEditText = 16908325; // 0x1020025
field public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
+ field public static final int list_container = 16908351; // 0x102003f
field public static final int mask = 16908334; // 0x102002e
field public static final int message = 16908299; // 0x102000b
field public static final int navigationBarBackground = 16908336; // 0x1020030
@@ -1734,6 +1737,7 @@
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
field public static final int switchInputMethod = 16908324; // 0x1020024
+ field public static final int switch_widget = 16908352; // 0x1020040
field public static final int tabcontent = 16908305; // 0x1020011
field public static final int tabhost = 16908306; // 0x1020012
field public static final int tabs = 16908307; // 0x1020013
@@ -5707,6 +5711,7 @@
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
+ method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5759,6 +5764,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 19d9aa2..0605851 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1025,6 +1025,7 @@
field public static final int port = 16842793; // 0x1010029
field public static final int positiveButtonText = 16843253; // 0x10101f5
field public static final int preferenceCategoryStyle = 16842892; // 0x101008c
+ field public static final int preferenceFragmentStyle = 16844039; // 0x1010507
field public static final int preferenceInformationStyle = 16842893; // 0x101008d
field public static final int preferenceLayoutChild = 16842900; // 0x1010094
field public static final int preferenceScreenStyle = 16842891; // 0x101008b
@@ -1806,11 +1807,13 @@
field public static final int icon = 16908294; // 0x1020006
field public static final int icon1 = 16908295; // 0x1020007
field public static final int icon2 = 16908296; // 0x1020008
+ field public static final int icon_frame = 16908350; // 0x102003e
field public static final int input = 16908297; // 0x1020009
field public static final int inputArea = 16908318; // 0x102001e
field public static final int inputExtractEditText = 16908325; // 0x1020025
field public static final int keyboardView = 16908326; // 0x1020026
field public static final int list = 16908298; // 0x102000a
+ field public static final int list_container = 16908351; // 0x102003f
field public static final int mask = 16908334; // 0x102002e
field public static final int message = 16908299; // 0x102000b
field public static final int navigationBarBackground = 16908336; // 0x1020030
@@ -1830,6 +1833,7 @@
field public static final int stopSelectingText = 16908329; // 0x1020029
field public static final int summary = 16908304; // 0x1020010
field public static final int switchInputMethod = 16908324; // 0x1020024
+ field public static final int switch_widget = 16908352; // 0x1020040
field public static final int tabcontent = 16908305; // 0x1020011
field public static final int tabhost = 16908306; // 0x1020012
field public static final int tabs = 16908307; // 0x1020013
@@ -5833,6 +5837,7 @@
method public deprecated java.lang.String getDeviceInitializerApp();
method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public java.lang.String getDeviceOwner();
+ method public java.lang.String getDeviceOwnerLockScreenInfo();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
@@ -5891,6 +5896,7 @@
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setCertInstallerPackage(android.content.ComponentName, java.lang.String) throws java.lang.SecurityException;
method public void setCrossProfileCallerIdDisabled(android.content.ComponentName, boolean);
+ method public boolean setDeviceOwnerLockScreenInfo(android.content.ComponentName, java.lang.String);
method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public boolean setKeyguardDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4e61103..8bf8dcb 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2869,9 +2869,6 @@
*/
public boolean setProfileOwner(@NonNull ComponentName admin, @Deprecated String ownerName,
int userHandle) throws IllegalArgumentException {
- if (admin == null) {
- throw new NullPointerException("admin cannot be null");
- }
if (mService != null) {
try {
if (ownerName == null) {
@@ -2887,6 +2884,42 @@
}
/**
+ * Sets the device owner information to be shown on the lock screen.
+ *
+ * <p>If the device owner information is {@code null} or empty then the device owner info is
+ * cleared and the user owner info is shown on the lock screen if it is set.
+ *
+ * @param admin The name of the admin component to check.
+ * @param info Device owner information which will be displayed instead of the user
+ * owner info.
+ * @return Whether the device owner information has been set.
+ */
+ public boolean setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, String info) {
+ if (mService != null) {
+ try {
+ return mService.setDeviceOwnerLockScreenInfo(admin, info);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return The device owner information. If it is not set returns {@code null}.
+ */
+ public String getDeviceOwnerLockScreenInfo() {
+ if (mService != null) {
+ try {
+ return mService.getDeviceOwnerLockScreenInfo();
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
+ return null;
+ }
+
+ /**
* Sets the enabled state of the profile. A profile should be enabled only once it is ready to
* be used. Only the profile owner can call this.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 95a22ef..e7e1833 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -127,6 +127,9 @@
void clearProfileOwner(in ComponentName who);
boolean hasUserSetupCompleted();
+ boolean setDeviceOwnerLockScreenInfo(in ComponentName who, String deviceOwnerInfo);
+ String getDeviceOwnerLockScreenInfo();
+
boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
void uninstallCaCerts(in ComponentName admin, in String[] aliases);
void enforceCanManageCaCerts(in ComponentName admin);
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index 25ef8b5..6729347 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -20,6 +20,9 @@
import com.android.org.conscrypt.TrustManagerImpl;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
@@ -36,7 +39,11 @@
*/
public class X509TrustManagerExtensions {
- final TrustManagerImpl mDelegate;
+ private final TrustManagerImpl mDelegate;
+ // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used.
+ private final X509TrustManager mTrustManager;
+ private final Method mCheckServerTrusted;
+ private final Method mIsUserAddedCertificate;
/**
* Constructs a new X509TrustManagerExtensions wrapper.
@@ -47,10 +54,31 @@
public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException {
if (tm instanceof TrustManagerImpl) {
mDelegate = (TrustManagerImpl) tm;
- } else {
- mDelegate = null;
- throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() +
- " which is not a supported type of X509TrustManager");
+ mTrustManager = null;
+ mCheckServerTrusted = null;
+ mIsUserAddedCertificate = null;
+ return;
+ }
+ // Use duck typing if possible.
+ mDelegate = null;
+ mTrustManager = tm;
+ // Check that the hostname aware checkServerTrusted is present.
+ try {
+ mCheckServerTrusted = tm.getClass().getMethod("checkServerTrusted",
+ X509Certificate[].class,
+ String.class,
+ String.class);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException("Required method"
+ + " checkServerTrusted(X509Certificate[], String, String, String) missing");
+ }
+ // Check that isUserAddedCertificate is present.
+ try {
+ mIsUserAddedCertificate = tm.getClass().getMethod("isUserAddedCertificate",
+ X509Certificate.class);
+ } catch (NoSuchMethodException e) {
+ throw new IllegalArgumentException(
+ "Required method isUserAddedCertificate(X509Certificate) missing");
}
}
@@ -66,7 +94,24 @@
*/
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
String host) throws CertificateException {
- return mDelegate.checkServerTrusted(chain, authType, host);
+ if (mDelegate != null) {
+ return mDelegate.checkServerTrusted(chain, authType, host);
+ } else {
+ try {
+ return (List<X509Certificate>) mCheckServerTrusted.invoke(mTrustManager, chain,
+ authType, host);
+ } catch (IllegalAccessException e) {
+ throw new CertificateException("Failed to call checkServerTrusted", e);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof CertificateException) {
+ throw (CertificateException) e.getCause();
+ }
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ }
+ throw new CertificateException("checkServerTrusted failed", e.getCause());
+ }
+ }
}
/**
@@ -80,7 +125,21 @@
* otherwise.
*/
public boolean isUserAddedCertificate(X509Certificate cert) {
- return mDelegate.isUserAddedCertificate(cert);
+ if (mDelegate != null) {
+ return mDelegate.isUserAddedCertificate(cert);
+ } else {
+ try {
+ return (Boolean) mIsUserAddedCertificate.invoke(mTrustManager, cert);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Failed to call isUserAddedCertificate", e);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ } else {
+ throw new RuntimeException("isUserAddedCertificate failed", e.getCause());
+ }
+ }
+ }
}
/**
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index db04c71..3e496b6 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -23,14 +23,15 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnKeyListener;
+import android.view.ViewGroup;
import android.widget.ListView;
/**
@@ -179,6 +180,27 @@
}
@Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ TypedArray a = getActivity().obtainStyledAttributes(null,
+ com.android.internal.R.styleable.PreferenceFragment,
+ com.android.internal.R.attr.preferenceFragmentStyle,
+ 0);
+
+ ListView lv = (ListView) view.findViewById(android.R.id.list);
+ if (lv != null) {
+ Drawable divider =
+ a.getDrawable(com.android.internal.R.styleable.PreferenceFragment_divider);
+ if (divider != null) {
+ lv.setDivider(divider);
+ }
+ }
+
+ a.recycle();
+ }
+
+ @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 9c3cefc..aa8674e 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -122,7 +122,7 @@
protected void onBindView(View view) {
super.onBindView(view);
- View checkableView = view.findViewById(com.android.internal.R.id.switchWidget);
+ View checkableView = view.findViewById(com.android.internal.R.id.switch_widget);
if (checkableView != null && checkableView instanceof Checkable) {
if (checkableView instanceof Switch) {
final Switch switchView = (Switch) checkableView;
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index 90d30d6..2afbb99 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -60,7 +60,7 @@
private Margins mMinMargins;
private int mColorMode;
- private int mDuplexMode = DUPLEX_MODE_NONE;
+ private int mDuplexMode;
PrintAttributes() {
/* hide constructor */
@@ -403,7 +403,7 @@
mResolution = null;
mMinMargins = null;
mColorMode = 0;
- mDuplexMode = DUPLEX_MODE_NONE;
+ mDuplexMode = 0;
}
/**
@@ -1427,10 +1427,6 @@
/**
* Creates a new {@link PrintAttributes} instance.
- * <p>
- * If you do not specify a duplex mode, the default
- * {@link #DUPLEX_MODE_NONE} will be used.
- * </p>
*
* @return The new instance.
*/
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
index 503854e..8906f9b 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfig.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -41,7 +41,7 @@
private final List<CertificatesEntryRef> mCertificatesEntryRefs;
private Set<TrustAnchor> mAnchors;
private final Object mAnchorsLock = new Object();
- private X509TrustManager mTrustManager;
+ private NetworkSecurityTrustManager mTrustManager;
private final Object mTrustManagerLock = new Object();
private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
@@ -78,7 +78,7 @@
return mPins;
}
- public X509TrustManager getTrustManager() {
+ public NetworkSecurityTrustManager getTrustManager() {
synchronized(mTrustManagerLock) {
if (mTrustManager == null) {
mTrustManager = new NetworkSecurityTrustManager(this);
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
index e69082d..7f5b3ca 100644
--- a/core/java/android/security/net/config/NetworkSecurityTrustManager.java
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -71,9 +71,28 @@
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
- List<X509Certificate> trustedChain =
- mDelegate.checkServerTrusted(certs, authType, (String) null);
+ checkServerTrusted(certs, authType, null);
+ }
+
+ /**
+ * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
+ * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
+ * modify without modifying those callers.
+ */
+ public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
+ String host) throws CertificateException {
+ List<X509Certificate> trustedChain = mDelegate.checkServerTrusted(certs, authType, host);
checkPins(trustedChain);
+ return trustedChain;
+ }
+
+ /**
+ * Check if the provided certificate is a user added certificate authority.
+ * This is required by android.net.http.X509TrustManagerExtensions.
+ */
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ // TODO: Figure out the right way to handle this, and if it is still even used.
+ return false;
}
private void checkPins(List<X509Certificate> chain) throws CertificateException {
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index 1338b9f..b87bf1f 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -18,6 +18,7 @@
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.List;
import javax.net.ssl.X509TrustManager;
@@ -61,10 +62,24 @@
config.getTrustManager().checkServerTrusted(certs, authType);
}
- public void checkServerTrusted(X509Certificate[] certs, String authType, String hostname)
- throws CertificateException {
+ /**
+ * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
+ * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
+ * modify without modifying those callers.
+ */
+ public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
+ String hostname) throws CertificateException {
NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
- config.getTrustManager().checkServerTrusted(certs, authType);
+ return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
+ }
+
+ /**
+ * Check if the provided certificate is a user added certificate authority.
+ * This is required by android.net.http.X509TrustManagerExtensions.
+ */
+ public boolean isUserAddedCertificate(X509Certificate cert) {
+ // TODO: Figure out the right way to handle this, and if it is still even used.
+ return false;
}
@Override
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 66b05a2..461506b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12968,10 +12968,6 @@
mPrivateFlags |= PFLAG_DIRTY;
- // Release any resources in-case we don't end up drawing again
- // as anything cached is no longer valid
- resetDisplayList();
-
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index d80c6a3..625ed38 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1625,7 +1625,7 @@
if (mPendingConfiguration.seq != 0) {
if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
+ mPendingConfiguration);
- updateConfiguration(mPendingConfiguration, !mFirst);
+ updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
mPendingConfiguration.seq = 0;
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2fabe33..2983053 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -2705,11 +2705,22 @@
mIsShowingUp = false;
}
- private class SuggestionInfo {
+ private final class SuggestionInfo {
int suggestionStart, suggestionEnd; // range of actual suggestion within text
- SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
+
+ // the SuggestionSpan that this TextView represents
+ @Nullable
+ SuggestionSpan suggestionSpan;
+
int suggestionIndex; // the index of this suggestion inside suggestionSpan
- SpannableStringBuilder text = new SpannableStringBuilder();
+
+ @Nullable
+ final SpannableStringBuilder text = new SpannableStringBuilder();
+
+ void clear() {
+ suggestionSpan = null;
+ text.clear();
+ }
}
private class SuggestionAdapter extends BaseAdapter {
@@ -2863,9 +2874,11 @@
return Math.min(positionY, displayMetrics.heightPixels - height);
}
- @Override
- public void hide() {
- super.hide();
+ private void hideWithCleanUp() {
+ for (final SuggestionInfo info : mSuggestionInfos) {
+ info.clear();
+ }
+ hide();
}
private boolean updateSuggestions() {
@@ -3017,7 +3030,7 @@
}
mTextView.deleteText_internal(spanUnionStart, spanUnionEnd);
}
- hide();
+ hideWithCleanUp();
return;
}
@@ -3025,7 +3038,7 @@
final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan);
if (spanStart < 0 || spanEnd <= spanStart) {
// Span has been removed
- hide();
+ hideWithCleanUp();
return;
}
@@ -3100,7 +3113,7 @@
mTextView.setCursorPosition_internal(newCursorPosition, newCursorPosition);
}
- hide();
+ hideWithCleanUp();
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index c54a574..b5d994d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3975,6 +3975,15 @@
}
((Editable) mText).append(text, start, end);
+
+ if (mAutoLinkMask != 0) {
+ boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
+ // Do not change the movement method for text that support text selection as it
+ // would prevent an arbitrary cursor displacement.
+ if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
+ setMovementMethod(LinkMovementMethod.getInstance());
+ }
+ }
}
private void updateTextColors() {
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 60ef4a4..889c7b3 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -126,6 +126,8 @@
private static final String LOCK_SCREEN_OWNER_INFO_ENABLED =
Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED;
+ private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info";
+
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
// Maximum allowed number of repeated or ordered characters in a sequence before we'll
@@ -578,6 +580,29 @@
}
/**
+ * Sets the device owner information. If the information is {@code null} or empty then the
+ * device owner info is cleared.
+ *
+ * @param info Device owner information which will be displayed instead of the user
+ * owner info.
+ */
+ public void setDeviceOwnerInfo(String info) {
+ if (info != null && info.isEmpty()) {
+ info = null;
+ }
+
+ setString(LOCK_SCREEN_DEVICE_OWNER_INFO, info, UserHandle.USER_SYSTEM);
+ }
+
+ public String getDeviceOwnerInfo() {
+ return getString(LOCK_SCREEN_DEVICE_OWNER_INFO, UserHandle.USER_SYSTEM);
+ }
+
+ public boolean isDeviceOwnerInfoEnabled() {
+ return getDeviceOwnerInfo() != null;
+ }
+
+ /**
* Compute the password quality from the given password string.
*/
static public int computePasswordQuality(String password) {
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index f073c33..fc53a1a 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -24,18 +24,23 @@
android:background="@android:color/transparent"
android:layout_removeBorders="true">
- <ListView android:id="@android:id/list"
- style="?attr/preferenceFragmentListStyle"
+ <FrameLayout
+ android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="0px"
- android:layout_weight="1"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
- android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:cacheColorHint="@android:color/transparent"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
+ android:layout_weight="1">
+ <ListView android:id="@android:id/list"
+ style="?attr/preferenceFragmentListStyle"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="0dip"
+ android:paddingBottom="@dimen/preference_fragment_padding_bottom"
+ android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+ </FrameLayout>
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/preference_list_fragment_material.xml b/core/res/res/layout/preference_list_fragment_material.xml
index 62bdffd..e411c0e 100644
--- a/core/res/res/layout/preference_list_fragment_material.xml
+++ b/core/res/res/layout/preference_list_fragment_material.xml
@@ -24,18 +24,23 @@
android:background="@android:color/transparent"
android:layout_removeBorders="true">
- <ListView android:id="@android:id/list"
- style="?attr/preferenceFragmentListStyle"
+ <FrameLayout
+ android:id="@android:id/list_container"
android:layout_width="match_parent"
android:layout_height="0px"
- android:layout_weight="1"
- android:paddingTop="0dip"
- android:paddingBottom="@dimen/preference_fragment_padding_bottom"
- android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
- android:clipToPadding="false"
- android:drawSelectorOnTop="false"
- android:cacheColorHint="@android:color/transparent"
- android:scrollbarAlwaysDrawVerticalTrack="true" />
+ android:layout_weight="1">
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="0dip"
+ android:paddingBottom="@dimen/preference_fragment_padding_bottom"
+ style="?attr/preferenceFragmentListStyle"
+ android:scrollbarStyle="@integer/preference_fragment_scrollbarStyle"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="false"
+ android:cacheColorHint="@android:color/transparent"
+ android:scrollbarAlwaysDrawVerticalTrack="true" />
+ </FrameLayout>
<TextView android:id="@android:id/empty"
android:layout_width="match_parent"
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index 25e8aa6..80c572b 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -17,7 +17,7 @@
<!-- Layout used by SwitchPreference for the switch widget style. This is inflated
inside android.R.layout.preference. -->
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+android:id/switchWidget"
+ android:id="@+android:id/switch_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d7dd3ec..50cf302 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7714,6 +7714,7 @@
<declare-styleable name="PreferenceFragment">
<!-- The layout for the PreferenceFragment. This should rarely need to be changed. -->
<attr name="layout" />
+ <attr name="divider" />
</declare-styleable>
<!-- Base attributes available to PreferenceActivity. -->
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 695dafa..9d5e5ac 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -23,12 +23,14 @@
<item type="id" name="empty" />
<item type="id" name="hint" />
<item type="id" name="icon" />
+ <item type="id" name="icon_frame" />
<item type="id" name="icon_badge" />
<item type="id" name="icon1" />
<item type="id" name="icon2" />
<item type="id" name="input" />
<item type="id" name="left_icon" />
<item type="id" name="list" />
+ <item type="id" name="list_container" />
<item type="id" name="menu" />
<item type="id" name="message" />
<item type="id" name="primary" />
@@ -48,6 +50,7 @@
<item type="id" name="lock_screen" />
<item type="id" name="edit" />
<item type="id" name="widget_frame" />
+ <item type="id" name="switch_widget" />
<item type="id" name="button1" />
<item type="id" name="button2" />
<item type="id" name="button3" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 43a6acd..54e43c8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2681,6 +2681,7 @@
<public type="attr" name="windowBackgroundFallback" />
<public type="attr" name="forceDeviceEncrypted" />
<public type="attr" name="encryptionAware" />
+ <public type="attr" name="preferenceFragmentStyle" />
<public type="style" name="Theme.Material.DayNight" />
<public type="style" name="Theme.Material.DayNight.DarkActionBar" />
@@ -2701,5 +2702,8 @@
<public type="style" name="Theme.Material.DayNight.DialogWhenLarge.DarkActionBar" />
<public type="id" name="accessibilityActionSetProgress" />
+ <public type="id" name="icon_frame" />
+ <public type="id" name="list_container" />
+ <public type="id" name="switch_widget" />
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 58640eb..4b2a451 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -40,6 +40,7 @@
<item name="layout">@layout/preference_list_fragment_material</item>
<item name="paddingStart">@dimen/preference_fragment_padding_side_material</item>
<item name="paddingEnd">@dimen/preference_fragment_padding_side_material</item>
+ <item name="divider">?attr/listDivider</item>
</style>
<style name="PreferenceActivity.Material">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cda7faa..6820c25 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -162,7 +162,7 @@
<java-symbol type="id" name="submit_area" />
<java-symbol type="id" name="switch_new" />
<java-symbol type="id" name="switch_old" />
- <java-symbol type="id" name="switchWidget" />
+ <java-symbol type="id" name="switch_widget" />
<java-symbol type="id" name="text" />
<java-symbol type="id" name="time" />
<java-symbol type="id" name="time_current" />
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 87b3785..3ce45e9 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -61,11 +61,13 @@
final int expectedHighlightTextColor = tmpTp.getColor();
final float expectedHighlightTextSize = tmpTp.getTextSize();
- // Create and wait until SuggestionsPopupWindow is shown.
final EditText editText = (EditText) activity.findViewById(R.id.textview);
final Editor editor = editText.getEditorForTesting();
assertNotNull(editor);
- activity.runOnUiThread(new Runnable() {
+
+ // Request to show SuggestionsPopupWindow.
+ Runnable showSuggestionWindowRunner = new Runnable() {
+ @Override
public void run() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
ssb.append(sampleText);
@@ -78,8 +80,7 @@
Selection.setSelection(editText.getText(), singleWordSpanStart, singleWordSpanEnd);
editText.onTextContextMenuItem(TextView.ID_REPLACE);
}
- });
- getInstrumentation().waitForIdleSync();
+ };
// In this test, the SuggestionsPopupWindow looks like
// abc def ghi
@@ -91,7 +92,8 @@
// | DELETE |
// -----------------
// *XX* means that XX is highlighted.
- activity.runOnUiThread(new Runnable() {
+ Runnable popupVaridator = new Runnable() {
+ @Override
public void run() {
Editor.SuggestionsPopupWindow popupWindow =
editor.getSuggestionsPopupWindowForTesting();
@@ -155,6 +157,25 @@
assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
}
}
+ };
+
+ // Show the SuggestionWindow and verify the contents.
+ activity.runOnUiThread(showSuggestionWindowRunner);
+ getInstrumentation().waitForIdleSync();
+ activity.runOnUiThread(popupVaridator);
+
+ // Request to hide the SuggestionPopupWindow and wait until it is hidden.
+ activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ editText.setText("");
+ }
});
+ getInstrumentation().waitForIdleSync();
+
+ // Show and verify the contents again.
+ activity.runOnUiThread(showSuggestionWindowRunner);
+ getInstrumentation().waitForIdleSync();
+ activity.runOnUiThread(popupVaridator);
}
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 5cdd723..4acad67 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -9,6 +9,7 @@
font/Font.cpp \
renderstate/Blend.cpp \
renderstate/MeshState.cpp \
+ renderstate/OffscreenBufferPool.cpp \
renderstate/PixelBufferState.cpp \
renderstate/RenderState.cpp \
renderstate/Scissor.cpp \
@@ -216,6 +217,7 @@
unit_tests/LayerUpdateQueueTests.cpp \
unit_tests/LinearAllocatorTests.cpp \
unit_tests/PathParserTests.cpp \
+ unit_tests/OffscreenBufferPoolTests.cpp \
unit_tests/StringUtilsTests.cpp
ifeq (true, $(HWUI_NEW_OPS))
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 2fca5ea..1aa291f 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -19,104 +19,29 @@
#include "Caches.h"
#include "Glop.h"
#include "GlopBuilder.h"
+#include "renderstate/OffscreenBufferPool.h"
#include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
#include "utils/GLUtils.h"
+#include "VertexBuffer.h"
namespace android {
namespace uirenderer {
////////////////////////////////////////////////////////////////////////////////
-// OffscreenBuffer
-////////////////////////////////////////////////////////////////////////////////
-
-OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
- uint32_t textureWidth, uint32_t textureHeight,
- uint32_t viewportWidth, uint32_t viewportHeight)
- : renderState(renderState)
- , viewportWidth(viewportWidth)
- , viewportHeight(viewportHeight)
- , texture(caches) {
- texture.width = textureWidth;
- texture.height = textureHeight;
-
- caches.textureState().activateTexture(0);
- glGenTextures(1, &texture.id);
- caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
-
- texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
- // not setting filter on texture, since it's set when drawing, based on transform
-
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
-}
-
-void OffscreenBuffer::updateMeshFromRegion() {
- // avoid T-junctions as they cause artifacts in between the resultant
- // geometry when complex transforms occur.
- // TODO: generate the safeRegion only if necessary based on drawing transform
- Region safeRegion = Region::createTJunctionFreeRegion(region);
-
- size_t count;
- const android::Rect* rects = safeRegion.getArray(&count);
-
- const float texX = 1.0f / float(viewportWidth);
- const float texY = 1.0f / float(viewportHeight);
-
- FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
- TextureVertex* mesh = &meshVector[0];
- for (size_t i = 0; i < count; i++) {
- const android::Rect* r = &rects[i];
-
- const float u1 = r->left * texX;
- const float v1 = (viewportHeight - r->top) * texY;
- const float u2 = r->right * texX;
- const float v2 = (viewportHeight - r->bottom) * texY;
-
- TextureVertex::set(mesh++, r->left, r->top, u1, v1);
- TextureVertex::set(mesh++, r->right, r->top, u2, v1);
- TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
- TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
- }
- elementCount = count * 6;
- renderState.meshState().genOrUpdateMeshBuffer(&vbo,
- sizeof(TextureVertex) * count * 4,
- &meshVector[0],
- GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
-}
-
-OffscreenBuffer::~OffscreenBuffer() {
- texture.deleteTexture();
- renderState.meshState().deleteMeshBuffer(vbo);
- elementCount = 0;
- vbo = 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
// BakedOpRenderer
////////////////////////////////////////////////////////////////////////////////
-OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(RenderState& renderState,
- uint32_t width, uint32_t height) {
- // TODO: get from cache!
- return new OffscreenBuffer(renderState, Caches::getInstance(), width, height, width, height);
-}
-
-void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
- // TODO: return texture/offscreenbuffer to cache!
- delete offscreenBuffer;
-}
-
-OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
+OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t height) {
LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
- OffscreenBuffer* buffer = createOffscreenBuffer(mRenderState, width, height);
- startLayer(buffer);
+ OffscreenBuffer* buffer = mRenderState.layerPool().get(mRenderState, width, height);
+ startRepaintLayer(buffer);
return buffer;
}
-void BakedOpRenderer::startLayer(OffscreenBuffer* offscreenBuffer) {
+void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer) {
+ LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
+
mRenderTarget.offscreenBuffer = offscreenBuffer;
// create and bind framebuffer
@@ -261,6 +186,71 @@
renderer.renderGlop(state, glop);
}
+namespace VertexBufferRenderFlags {
+ enum {
+ Offset = 0x1,
+ ShadowInterp = 0x2,
+ };
+}
+
+static void renderVertexBuffer(BakedOpRenderer& renderer, const BakedOpState& state,
+ const VertexBuffer& vertexBuffer, float translateX, float translateY,
+ SkPaint& paint, int vertexBufferRenderFlags) {
+ if (CC_LIKELY(vertexBuffer.getVertexCount())) {
+ bool shadowInterp = vertexBufferRenderFlags & VertexBufferRenderFlags::ShadowInterp;
+ const int transformFlags = TransformFlags::OffsetByFudgeFactor;
+ Glop glop;
+ GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
+ .setRoundRectClipState(state.roundRectClipState)
+ .setMeshVertexBuffer(vertexBuffer, shadowInterp)
+ .setFillPaint(paint, state.alpha)
+ .setTransform(state.computedState.transform, transformFlags)
+ .setModelViewOffsetRect(translateX, translateY, vertexBuffer.getBounds())
+ .build();
+ renderer.renderGlop(state, glop);
+ }
+}
+
+static void renderShadow(BakedOpRenderer& renderer, const BakedOpState& state, float casterAlpha,
+ const VertexBuffer* ambientShadowVertexBuffer, const VertexBuffer* spotShadowVertexBuffer) {
+ SkPaint paint;
+ paint.setAntiAlias(true); // want to use AlphaVertex
+
+ // The caller has made sure casterAlpha > 0.
+ uint8_t ambientShadowAlpha = 128u; //TODO: mAmbientShadowAlpha;
+ if (CC_UNLIKELY(Properties::overrideAmbientShadowStrength >= 0)) {
+ ambientShadowAlpha = Properties::overrideAmbientShadowStrength;
+ }
+ if (ambientShadowVertexBuffer && ambientShadowAlpha > 0) {
+ paint.setAlpha((uint8_t)(casterAlpha * ambientShadowAlpha));
+ renderVertexBuffer(renderer, state, *ambientShadowVertexBuffer, 0, 0,
+ paint, VertexBufferRenderFlags::ShadowInterp);
+ }
+
+ uint8_t spotShadowAlpha = 128u; //TODO: mSpotShadowAlpha;
+ if (CC_UNLIKELY(Properties::overrideSpotShadowStrength >= 0)) {
+ spotShadowAlpha = Properties::overrideSpotShadowStrength;
+ }
+ if (spotShadowVertexBuffer && spotShadowAlpha > 0) {
+ paint.setAlpha((uint8_t)(casterAlpha * spotShadowAlpha));
+ renderVertexBuffer(renderer, state, *spotShadowVertexBuffer, 0, 0,
+ paint, VertexBufferRenderFlags::ShadowInterp);
+ }
+}
+
+void BakedOpDispatcher::onShadowOp(BakedOpRenderer& renderer, const ShadowOp& op, const BakedOpState& state) {
+ TessellationCache::vertexBuffer_pair_t buffers;
+ Vector3 lightCenter = { 300, 300, 300 }; // TODO!
+ float lightRadius = 150; // TODO!
+
+ renderer.caches().tessellationCache.getShadowBuffers(&state.computedState.transform,
+ op.localClipRect, op.casterAlpha >= 1.0f, op.casterPath,
+ &op.shadowMatrixXY, &op.shadowMatrixZ, lightCenter, lightRadius,
+ buffers);
+
+ renderShadow(renderer, state, op.casterAlpha, buffers.first, buffers.second);
+}
+
void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleRectsOp& op, const BakedOpState& state) {
Glop glop;
GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
@@ -291,7 +281,7 @@
renderer.renderGlop(state, glop);
if (op.destroy) {
- BakedOpRenderer::destroyOffscreenBuffer(buffer);
+ renderer.renderState().layerPool().putOrDelete(buffer);
}
}
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index aa1e67d..d6d9cb1 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -29,33 +29,6 @@
class RenderState;
/**
- * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
- * encompasses enough information to draw it back on screen (minus paint properties, which are held
- * by LayerOp).
- */
-class OffscreenBuffer {
-public:
- OffscreenBuffer(RenderState& renderState, Caches& caches,
- uint32_t textureWidth, uint32_t textureHeight,
- uint32_t viewportWidth, uint32_t viewportHeight);
- ~OffscreenBuffer();
-
- // must be called prior to rendering, to construct/update vertex buffer
- void updateMeshFromRegion();
-
- RenderState& renderState;
- uint32_t viewportWidth;
- uint32_t viewportHeight;
- Texture texture;
-
- // Portion of offscreen buffer that has been drawn to. Used to minimize drawing area when
- // drawing back to screen / parent FBO.
- Region region;
- GLsizei elementCount = 0;
- GLuint vbo = 0;
-};
-
-/**
* Main rendering manager for a collection of work - one frame + any contained FBOs.
*
* Manages frame and FBO lifecycle, binding the GL framebuffer as appropriate. This is the only
@@ -72,17 +45,13 @@
, mOpaque(opaque) {
}
- static OffscreenBuffer* createOffscreenBuffer(RenderState& renderState,
- uint32_t width, uint32_t height);
- static void destroyOffscreenBuffer(OffscreenBuffer*);
-
RenderState& renderState() { return mRenderState; }
Caches& caches() { return mCaches; }
void startFrame(uint32_t width, uint32_t height);
void endFrame();
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height);
- void startLayer(OffscreenBuffer* offscreenBuffer);
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height);
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer);
void endLayer();
Texture* getTexture(const SkBitmap* bitmap);
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index ddb8c84..9a40c3b 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -89,6 +89,21 @@
* and early return null in one place.
*/
}
+
+ /**
+ * Constructor for unbounded ops without transform/clip (namely shadows)
+ *
+ * Since the op doesn't have known bounds, we conservatively set the mapped bounds
+ * to the current clipRect, and clipSideFlags to Full.
+ */
+ ResolvedRenderState(const Snapshot& snapshot) {
+ transform = *snapshot.transform;
+ clipRect = snapshot.getRenderTargetClip();
+ clippedBounds = clipRect;
+ transform.mapRect(clippedBounds);
+ clipSideFlags = OpClipSideFlags::Full;
+ }
+
Matrix4 transform;
Rect clipRect;
int clipSideFlags = 0;
@@ -104,8 +119,7 @@
public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
const Snapshot& snapshot, const RecordedOp& recordedOp) {
- BakedOpState* bakedOp = new (allocator) BakedOpState(
- snapshot, recordedOp);
+ BakedOpState* bakedOp = new (allocator) BakedOpState(snapshot, recordedOp);
if (bakedOp->computedState.clippedBounds.isEmpty()) {
// bounds are empty, so op is rejected
allocator.rewindIfLastAlloc(bakedOp);
@@ -114,6 +128,14 @@
return bakedOp;
}
+ static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
+ const Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
+ if (snapshot.getRenderTargetClip().isEmpty()) return nullptr;
+
+ // clip isn't empty, so construct the op
+ return new (allocator) BakedOpState(snapshot, shadowOpPtr);
+ }
+
static void* operator new(size_t size, LinearAllocator& allocator) {
return allocator.alloc(size);
}
@@ -134,6 +156,13 @@
, roundRectClipState(snapshot.roundRectClipState)
, projectionPathMask(snapshot.projectionPathMask)
, op(&recordedOp) {}
+
+ BakedOpState(const Snapshot& snapshot, const ShadowOp* shadowOpPtr)
+ : computedState(snapshot)
+ , alpha(snapshot.alpha)
+ , roundRectClipState(snapshot.roundRectClipState)
+ , projectionPathMask(snapshot.projectionPathMask)
+ , op(shadowOpPtr) {}
};
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h
index fc08504..609103b 100644
--- a/libs/hwui/DisplayListCanvas.h
+++ b/libs/hwui/DisplayListCanvas.h
@@ -17,6 +17,14 @@
#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H
+#include "Canvas.h"
+#include "CanvasState.h"
+#include "DisplayList.h"
+#include "RenderNode.h"
+#include "ResourceCache.h"
+#include "SkiaCanvasProxy.h"
+#include "utils/Macros.h"
+
#include <SkDrawFilter.h>
#include <SkMatrix.h>
#include <SkPaint.h>
@@ -25,13 +33,6 @@
#include <SkTLazy.h>
#include <cutils/compiler.h>
-#include "Canvas.h"
-#include "CanvasState.h"
-#include "DisplayList.h"
-#include "SkiaCanvasProxy.h"
-#include "RenderNode.h"
-#include "ResourceCache.h"
-
namespace android {
namespace uirenderer {
@@ -66,7 +67,7 @@
virtual ~DisplayListCanvas();
void reset(int width, int height);
- __attribute__((warn_unused_result)) DisplayList* finishRecording();
+ WARN_UNUSED_RESULT DisplayList* finishRecording();
// ----------------------------------------------------------------------------
// HWUI Canvas state operations
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 39cadd1..b117754 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-#include <GLES2/gl2.h>
+#include "LayerCache.h"
+
+#include "Caches.h"
+#include "Properties.h"
#include <utils/Log.h>
-#include "Caches.h"
-#include "LayerCache.h"
-#include "Properties.h"
+#include <GLES2/gl2.h>
namespace android {
namespace uirenderer {
@@ -29,15 +30,9 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, nullptr) > 0) {
- INIT_LOGD(" Setting layer cache size to %sMB", property);
- setMaxSize(MB(atof(property)));
- } else {
- INIT_LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
- }
-}
+LayerCache::LayerCache()
+ : mSize(0)
+ , mMaxSize(Properties::layerPoolSize) {}
LayerCache::~LayerCache() {
clear();
diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp
index 68f80ea..80efaed 100644
--- a/libs/hwui/OpReorderer.cpp
+++ b/libs/hwui/OpReorderer.cpp
@@ -22,6 +22,7 @@
#include "utils/PaintUtils.h"
#include <SkCanvas.h>
+#include <SkPathOps.h>
#include <utils/Trace.h>
#include <utils/TypeHelpers.h>
@@ -312,7 +313,7 @@
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
- mLayerStack.push_back(0);
+ mLayerStack.push_back(0);
mCanvasState.initializeSaveStack(viewportWidth, viewportHeight,
clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
@@ -347,7 +348,6 @@
OpReorderer::OpReorderer(int viewportWidth, int viewportHeight, const DisplayList& displayList)
: mCanvasState(*this) {
ATRACE_NAME("prepare drawing commands");
-
mLayerReorderers.emplace_back(viewportWidth, viewportHeight);
mLayerStack.push_back(0);
@@ -462,8 +462,60 @@
}
void OpReorderer::deferShadow(const RenderNodeOp& casterNodeOp) {
- // TODO
+ auto& node = *casterNodeOp.renderNode;
+ auto& properties = node.properties();
+
+ if (properties.getAlpha() <= 0.0f
+ || properties.getOutline().getAlpha() <= 0.0f
+ || !properties.getOutline().getPath()
+ || properties.getScaleX() == 0
+ || properties.getScaleY() == 0) {
+ // no shadow to draw
+ return;
+ }
+
+ const SkPath* casterOutlinePath = properties.getOutline().getPath();
+ const SkPath* revealClipPath = properties.getRevealClip().getPath();
+ if (revealClipPath && revealClipPath->isEmpty()) return;
+
+ float casterAlpha = properties.getAlpha() * properties.getOutline().getAlpha();
+
+ // holds temporary SkPath to store the result of intersections
+ SkPath* frameAllocatedPath = nullptr;
+ const SkPath* casterPath = casterOutlinePath;
+
+ // intersect the shadow-casting path with the reveal, if present
+ if (revealClipPath) {
+ frameAllocatedPath = createFrameAllocatedPath();
+
+ Op(*casterPath, *revealClipPath, kIntersect_SkPathOp, frameAllocatedPath);
+ casterPath = frameAllocatedPath;
+ }
+
+ // intersect the shadow-casting path with the clipBounds, if present
+ if (properties.getClippingFlags() & CLIP_TO_CLIP_BOUNDS) {
+ if (!frameAllocatedPath) {
+ frameAllocatedPath = createFrameAllocatedPath();
+ }
+ Rect clipBounds;
+ properties.getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds);
+ SkPath clipBoundsPath;
+ clipBoundsPath.addRect(clipBounds.left, clipBounds.top,
+ clipBounds.right, clipBounds.bottom);
+
+ Op(*casterPath, clipBoundsPath, kIntersect_SkPathOp, frameAllocatedPath);
+ casterPath = frameAllocatedPath;
+ }
+
+ ShadowOp* shadowOp = new (mAllocator) ShadowOp(casterNodeOp, casterAlpha, casterPath,
+ mCanvasState.getLocalClipBounds());
+ BakedOpState* bakedOpState = BakedOpState::tryShadowOpConstruct(
+ mAllocator, *mCanvasState.currentSnapshot(), shadowOp);
+ if (CC_LIKELY(bakedOpState)) {
+ currentLayer().deferUnmergeableOp(mAllocator, bakedOpState, OpBatchType::Shadow);
+ }
}
+
/**
* Used to define a list of lambdas referencing private OpReorderer::onXXXXOp() methods.
*
@@ -593,5 +645,9 @@
LOG_ALWAYS_FATAL("unsupported");
}
+void OpReorderer::onShadowOp(const ShadowOp& op) {
+ LOG_ALWAYS_FATAL("unsupported");
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/OpReorderer.h b/libs/hwui/OpReorderer.h
index 936b6ed..2c30f0d 100644
--- a/libs/hwui/OpReorderer.h
+++ b/libs/hwui/OpReorderer.h
@@ -51,6 +51,7 @@
AlphaMaskTexture,
Text,
ColorText,
+ Shadow,
Count // must be last
};
@@ -152,11 +153,11 @@
LayerReorderer& layer = mLayerReorderers[i];
if (layer.renderNode) {
// cached HW layer - can't skip layer if empty
- renderer.startLayer(layer.offscreenBuffer);
+ renderer.startRepaintLayer(layer.offscreenBuffer);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
} else if (!layer.empty()) { // save layer - skip entire layer if empty
- layer.offscreenBuffer = renderer.createLayer(layer.width, layer.height);
+ layer.offscreenBuffer = renderer.startTemporaryLayer(layer.width, layer.height);
layer.replayBakedOpsImpl((void*)&renderer, receivers);
renderer.endLayer();
}
@@ -210,6 +211,10 @@
void replayBakedOpsImpl(void* arg, BakedOpDispatcher* receivers);
+ SkPath* createFrameAllocatedPath() {
+ mFrameAllocatedPaths.emplace_back(new SkPath);
+ return mFrameAllocatedPaths.back().get();
+ }
/**
* Declares all OpReorderer::onXXXXOp() methods for every RecordedOp type.
*
@@ -220,6 +225,8 @@
void on##Type(const Type& op);
MAP_OPS(INTERNAL_OP_HANDLER)
+ std::vector<std::unique_ptr<SkPath> > mFrameAllocatedPaths;
+
// List of every deferred layer's render state. Replayed in reverse order to render a frame.
std::vector<LayerReorderer> mLayerReorderers;
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index e818186..0669596 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -37,6 +37,7 @@
bool Properties::enablePartialUpdates = true;
float Properties::textGamma = DEFAULT_TEXT_GAMMA;
+int Properties::layerPoolSize = DEFAULT_LAYER_CACHE_SIZE;
DebugLevel Properties::debugLevel = kDebugDisabled;
OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default;
@@ -52,10 +53,19 @@
ProfileType Properties::sProfileType = ProfileType::None;
bool Properties::sDisableProfileBars = false;
+static int property_get_int(const char* key, int defaultValue) {
+ char buf[PROPERTY_VALUE_MAX] = {'\0',};
+
+ if (property_get(key, buf, "") > 0) {
+ return atoi(buf);
+ }
+ return defaultValue;
+}
+
static float property_get_float(const char* key, float defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {'\0',};
- if (property_get(PROPERTY_PROFILE, buf, "") > 0) {
+ if (property_get(key, buf, "") > 0) {
return atof(buf);
}
return defaultValue;
@@ -114,16 +124,14 @@
showDirtyRegions = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
- debugLevel = kDebugDisabled;
- if (property_get(PROPERTY_DEBUG, property, nullptr) > 0) {
- debugLevel = (DebugLevel) atoi(property);
- }
+ debugLevel = (DebugLevel) property_get_int(PROPERTY_DEBUG, kDebugDisabled);
skipEmptyFrames = property_get_bool(PROPERTY_SKIP_EMPTY_DAMAGE, true);
useBufferAge = property_get_bool(PROPERTY_USE_BUFFER_AGE, true);
enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true);
textGamma = property_get_float(PROPERTY_TEXT_GAMMA, DEFAULT_TEXT_GAMMA);
+ layerPoolSize = MB(property_get_float(PROPERTY_LAYER_CACHE_SIZE, DEFAULT_LAYER_CACHE_SIZE));
return (prevDebugLayersUpdates != debugLayersUpdates)
|| (prevDebugOverdraw != debugOverdraw)
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1293c78..1dde7e0 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -267,6 +267,8 @@
static float textGamma;
+ static int layerPoolSize;
+
static DebugLevel debugLevel;
static OverdrawColorSet overdrawColorSet;
static StencilClipDebug debugStencilClip;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 04af8e3..bb7a0a7c 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -43,6 +43,7 @@
OP_FN(BitmapOp) \
OP_FN(RectOp) \
OP_FN(RenderNodeOp) \
+ OP_FN(ShadowOp) \
OP_FN(SimpleRectsOp) \
OP_FN(BeginLayerOp) \
OP_FN(EndLayerOp) \
@@ -109,6 +110,31 @@
: SUPER(RectOp) {}
};
+/**
+ * Real-time, dynamic-lit shadow.
+ *
+ * Uses invalid/empty bounds and matrix since ShadowOp bounds aren't known at defer time,
+ * and are resolved dynamically, and transform isn't needed.
+ *
+ * State construction handles these properties specially.
+ */
+struct ShadowOp : RecordedOp {
+ ShadowOp(const RenderNodeOp& casterOp, float casterAlpha, const SkPath* casterPath, const Rect& clipRect)
+ : RecordedOp(RecordedOpId::ShadowOp, Rect(), Matrix4::identity(), clipRect, nullptr)
+ , shadowMatrixXY(casterOp.localMatrix)
+ , shadowMatrixZ(casterOp.localMatrix)
+ , casterAlpha(casterAlpha)
+ , casterPath(casterPath) {
+ const RenderNode& node = *casterOp.renderNode;
+ node.applyViewPropertyTransforms(shadowMatrixXY, false);
+ node.applyViewPropertyTransforms(shadowMatrixZ, true);
+ };
+ Matrix4 shadowMatrixXY;
+ Matrix4 shadowMatrixZ;
+ const float casterAlpha;
+ const SkPath* casterPath;
+};
+
struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?)
SimpleRectsOp(BASE_PARAMS, Vertex* vertices, size_t vertexCount)
: SUPER(SimpleRectsOp)
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index fc84c98..f26b0c8 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -20,11 +20,12 @@
#include "Canvas.h"
#include "CanvasState.h"
#include "DisplayList.h"
-#include "utils/LinearAllocator.h"
-#include "utils/NinePatch.h"
#include "ResourceCache.h"
#include "SkiaCanvasProxy.h"
#include "Snapshot.h"
+#include "utils/LinearAllocator.h"
+#include "utils/Macros.h"
+#include "utils/NinePatch.h"
#include <SkDrawFilter.h>
#include <SkPaint.h>
@@ -49,7 +50,7 @@
virtual ~RecordingCanvas();
void reset(int width, int height);
- __attribute__((warn_unused_result)) DisplayList* finishRecording();
+ WARN_UNUSED_RESULT DisplayList* finishRecording();
// ----------------------------------------------------------------------------
// MISC HWUI OPERATIONS - TODO: CATEGORIZE
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 15ca718..e177f9a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -248,22 +248,31 @@
}
}
-layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
+static layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
#if HWUI_NEW_OPS
- return BakedOpRenderer::createOffscreenBuffer(renderState, width, height);
+ return renderState.layerPool().get(renderState, width, height);
#else
return LayerRenderer::createRenderLayer(renderState, width, height);
#endif
}
-void destroyLayer(layer_t* layer) {
+static void destroyLayer(layer_t* layer) {
#if HWUI_NEW_OPS
- BakedOpRenderer::destroyOffscreenBuffer(layer);
+ RenderState& renderState = layer->renderState;
+ renderState.layerPool().putOrDelete(layer);
#else
LayerRenderer::destroyLayer(layer);
#endif
}
+static bool layerMatchesWidthAndHeight(layer_t* layer, int width, int height) {
+#if HWUI_NEW_OPS
+ return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
+#else
+ return layer->layer.getWidth() == width && layer->layer.getHeight() == height;
+#endif
+}
+
void RenderNode::pushLayerUpdate(TreeInfo& info) {
LayerType layerType = properties().effectiveLayerType();
// If we are not a layer OR we cannot be rendered (eg, view was detached)
@@ -278,17 +287,16 @@
bool transformUpdateNeeded = false;
if (!mLayer) {
- mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
- damageSelf(info);
- transformUpdateNeeded = true;
+ mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+ damageSelf(info);
+ transformUpdateNeeded = true;
+ } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
#if HWUI_NEW_OPS
- } else if (mLayer->viewportWidth != (uint32_t) getWidth()
- || mLayer->viewportHeight != (uint32_t)getHeight()) {
- // TODO: allow node's layer to grow larger
- if ((uint32_t)getWidth() > mLayer->texture.width
- || (uint32_t)getHeight() > mLayer->texture.height) {
+ RenderState& renderState = mLayer->renderState;
+ if (properties().fitsOnLayer()) {
+ mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
+ } else {
#else
- } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
#endif
destroyLayer(mLayer);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index ca7789e..0bd5b65 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -608,12 +608,16 @@
&& getOutline().getAlpha() != 0.0f;
}
- bool promotedToLayer() const {
+ bool fitsOnLayer() const {
const DeviceInfo* deviceInfo = DeviceInfo::get();
LOG_ALWAYS_FATAL_IF(!deviceInfo, "DeviceInfo uninitialized");
+ return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
+ && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+ }
+
+ bool promotedToLayer() const {
return mLayerProperties.mType == LayerType::None
- && mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
- && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
+ && fitsOnLayer()
&& (mComputedFields.mNeedLayerForFunctors
|| (!MathUtils::isZero(mPrimitiveFields.mAlpha)
&& mPrimitiveFields.mAlpha < 1
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp
new file mode 100644
index 0000000..6b44557
--- /dev/null
+++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#include "OffscreenBufferPool.h"
+
+#include "Caches.h"
+#include "Properties.h"
+#include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
+
+#include <utils/Log.h>
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace uirenderer {
+
+////////////////////////////////////////////////////////////////////////////////
+// OffscreenBuffer
+////////////////////////////////////////////////////////////////////////////////
+
+OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t viewportWidth, uint32_t viewportHeight)
+ : renderState(renderState)
+ , viewportWidth(viewportWidth)
+ , viewportHeight(viewportHeight)
+ , texture(caches) {
+ texture.width = computeIdealDimension(viewportWidth);
+ texture.height = computeIdealDimension(viewportHeight);
+ texture.blend = true;
+
+ caches.textureState().activateTexture(0);
+ glGenTextures(1, &texture.id);
+ caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id);
+
+ texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D);
+ // not setting filter on texture, since it's set when drawing, based on transform
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+}
+
+void OffscreenBuffer::updateMeshFromRegion() {
+ // avoid T-junctions as they cause artifacts in between the resultant
+ // geometry when complex transforms occur.
+ // TODO: generate the safeRegion only if necessary based on drawing transform
+ Region safeRegion = Region::createTJunctionFreeRegion(region);
+
+ size_t count;
+ const android::Rect* rects = safeRegion.getArray(&count);
+
+ const float texX = 1.0f / float(texture.width);
+ const float texY = 1.0f / float(texture.height);
+
+ FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
+ TextureVertex* mesh = &meshVector[0];
+ for (size_t i = 0; i < count; i++) {
+ const android::Rect* r = &rects[i];
+
+ const float u1 = r->left * texX;
+ const float v1 = (viewportHeight - r->top) * texY;
+ const float u2 = r->right * texX;
+ const float v2 = (viewportHeight - r->bottom) * texY;
+
+ TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+ TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+ TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+ TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+ }
+ elementCount = count * 6;
+ renderState.meshState().genOrUpdateMeshBuffer(&vbo,
+ sizeof(TextureVertex) * count * 4,
+ &meshVector[0],
+ GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
+}
+
+uint32_t OffscreenBuffer::computeIdealDimension(uint32_t dimension) {
+ return uint32_t(ceilf(dimension / float(LAYER_SIZE)) * LAYER_SIZE);
+}
+
+OffscreenBuffer::~OffscreenBuffer() {
+ texture.deleteTexture();
+ renderState.meshState().deleteMeshBuffer(vbo);
+ elementCount = 0;
+ vbo = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// OffscreenBufferPool
+///////////////////////////////////////////////////////////////////////////////
+
+OffscreenBufferPool::OffscreenBufferPool()
+ : mMaxSize(Properties::layerPoolSize) {
+}
+
+OffscreenBufferPool::~OffscreenBufferPool() {
+ clear(); // TODO: unique_ptr?
+}
+
+int OffscreenBufferPool::Entry::compare(const Entry& lhs, const Entry& rhs) {
+ int deltaInt = int(lhs.width) - int(rhs.width);
+ if (deltaInt != 0) return deltaInt;
+
+ return int(lhs.height) - int(rhs.height);
+}
+
+void OffscreenBufferPool::clear() {
+ for (auto entry : mPool) {
+ delete entry.layer;
+ }
+ mPool.clear();
+ mSize = 0;
+}
+
+OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState,
+ const uint32_t width, const uint32_t height) {
+ OffscreenBuffer* layer = nullptr;
+
+ Entry entry(width, height);
+ auto iter = mPool.find(entry);
+
+ if (iter != mPool.end()) {
+ entry = *iter;
+ mPool.erase(iter);
+
+ layer = entry.layer;
+ layer->viewportWidth = width;
+ layer->viewportHeight = height;
+ mSize -= layer->getSizeInBytes();
+ } else {
+ layer = new OffscreenBuffer(renderState, Caches::getInstance(), width, height);
+ }
+
+ return layer;
+}
+
+OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
+ const uint32_t width, const uint32_t height) {
+ RenderState& renderState = layer->renderState;
+ if (layer->texture.width == OffscreenBuffer::computeIdealDimension(width)
+ && layer->texture.height == OffscreenBuffer::computeIdealDimension(height)) {
+ // resize in place
+ layer->viewportWidth = width;
+ layer->viewportHeight = height;
+ return layer;
+ }
+ putOrDelete(layer);
+ return get(renderState, width, height);
+}
+
+void OffscreenBufferPool::dump() {
+ for (auto entry : mPool) {
+ ALOGD(" Layer size %dx%d", entry.width, entry.height);
+ }
+}
+
+void OffscreenBufferPool::putOrDelete(OffscreenBuffer* layer) {
+ const uint32_t size = layer->getSizeInBytes();
+ // Don't even try to cache a layer that's bigger than the cache
+ if (size < mMaxSize) {
+ // TODO: Use an LRU
+ while (mSize + size > mMaxSize) {
+ OffscreenBuffer* victim = mPool.begin()->layer;
+ mSize -= victim->getSizeInBytes();
+ delete victim;
+ mPool.erase(mPool.begin());
+ }
+
+ // clear region, since it's no longer valid
+ layer->region.clear();
+
+ Entry entry(layer);
+
+ mPool.insert(entry);
+ mSize += size;
+ } else {
+ delete layer;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h
new file mode 100644
index 0000000..f0fd82d
--- /dev/null
+++ b/libs/hwui/renderstate/OffscreenBufferPool.h
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
+#define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
+
+#include "Caches.h"
+#include "Texture.h"
+#include "utils/Macros.h"
+
+#include <ui/Region.h>
+
+#include <set>
+
+namespace android {
+namespace uirenderer {
+
+class RenderState;
+
+/**
+ * Lightweight alternative to Layer. Owns the persistent state of an offscreen render target, and
+ * encompasses enough information to draw it back on screen (minus paint properties, which are held
+ * by LayerOp).
+ */
+class OffscreenBuffer {
+public:
+ OffscreenBuffer(RenderState& renderState, Caches& caches,
+ uint32_t viewportWidth, uint32_t viewportHeight);
+ ~OffscreenBuffer();
+
+ // must be called prior to rendering, to construct/update vertex buffer
+ void updateMeshFromRegion();
+
+ static uint32_t computeIdealDimension(uint32_t dimension);
+
+ uint32_t getSizeInBytes() { return texture.width * texture.height * 4; }
+
+ RenderState& renderState;
+ uint32_t viewportWidth;
+ uint32_t viewportHeight;
+ Texture texture;
+
+ // Portion of layer that has been drawn to. Used to minimize drawing area when
+ // drawing back to screen / parent FBO.
+ Region region;
+ GLsizei elementCount = 0;
+ GLuint vbo = 0;
+};
+
+/**
+ * Pool of OffscreenBuffers allocated, but not currently in use.
+ */
+class OffscreenBufferPool {
+public:
+ OffscreenBufferPool();
+ ~OffscreenBufferPool();
+
+ WARN_UNUSED_RESULT OffscreenBuffer* get(RenderState& renderState,
+ const uint32_t width, const uint32_t height);
+
+ WARN_UNUSED_RESULT OffscreenBuffer* resize(OffscreenBuffer* layer,
+ const uint32_t width, const uint32_t height);
+
+ void putOrDelete(OffscreenBuffer* layer);
+
+ /**
+ * Clears the pool. This causes all layers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Returns the maximum size of the pool in bytes.
+ */
+ uint32_t getMaxSize() { return mMaxSize; }
+
+ /**
+ * Returns the current size of the pool in bytes.
+ */
+ uint32_t getSize() { return mSize; }
+
+ size_t getCount() { return mPool.size(); }
+
+ /**
+ * Prints out the content of the pool.
+ */
+ void dump();
+private:
+ struct Entry {
+ Entry() {}
+
+ Entry(const uint32_t layerWidth, const uint32_t layerHeight)
+ : width(OffscreenBuffer::computeIdealDimension(layerWidth))
+ , height(OffscreenBuffer::computeIdealDimension(layerHeight)) {}
+
+ Entry(OffscreenBuffer* layer)
+ : layer(layer)
+ , width(layer->texture.width)
+ , height(layer->texture.height) {
+ }
+
+ static int compare(const Entry& lhs, const Entry& rhs);
+
+ bool operator==(const Entry& other) const {
+ return compare(*this, other) == 0;
+ }
+
+ bool operator!=(const Entry& other) const {
+ return compare(*this, other) != 0;
+ }
+
+ bool operator<(const Entry& other) const {
+ return Entry::compare(*this, other) < 0;
+ }
+
+ OffscreenBuffer* layer = nullptr;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ }; // struct Entry
+
+ std::multiset<Entry> mPool;
+
+ uint32_t mSize = 0;
+ uint32_t mMaxSize;
+}; // class OffscreenBufferCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index 9637117..4fa8200 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -90,6 +90,8 @@
}
*/
+ mLayerPool.clear();
+
// TODO: reset all cached state in state objects
std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext);
mAssetAtlas.terminate();
@@ -106,6 +108,19 @@
mStencil = nullptr;
}
+void RenderState::flush(Caches::FlushMode mode) {
+ switch (mode) {
+ case Caches::FlushMode::Full:
+ // fall through
+ case Caches::FlushMode::Moderate:
+ // fall through
+ case Caches::FlushMode::Layers:
+ mLayerPool.clear();
+ break;
+ }
+ mCaches->flush(mode);
+}
+
void RenderState::setViewport(GLsizei width, GLsizei height) {
mViewportWidth = width;
mViewportHeight = height;
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 3cda170..dcd5ea6 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -21,6 +21,7 @@
#include "Glop.h"
#include "renderstate/Blend.h"
#include "renderstate/MeshState.h"
+#include "renderstate/OffscreenBufferPool.h"
#include "renderstate/PixelBufferState.h"
#include "renderstate/Scissor.h"
#include "renderstate/Stencil.h"
@@ -56,6 +57,8 @@
void onGLContextCreated();
void onGLContextDestroyed();
+ void flush(Caches::FlushMode flushMode);
+
void setViewport(GLsizei width, GLsizei height);
void getViewport(GLsizei* outWidth, GLsizei* outHeight);
@@ -97,6 +100,8 @@
Scissor& scissor() { return *mScissor; }
Stencil& stencil() { return *mStencil; }
+ OffscreenBufferPool& layerPool() { return mLayerPool; }
+
void dump();
private:
@@ -116,6 +121,8 @@
Scissor* mScissor = nullptr;
Stencil* mStencil = nullptr;
+ OffscreenBufferPool mLayerPool;
+
AssetAtlas mAssetAtlas;
std::set<Layer*> mActiveLayers;
std::set<renderthread::CanvasContext*> mRegisteredContexts;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 1b89960..f094b2d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -599,7 +599,7 @@
// Make sure to release all the textures we were owning as there won't
// be another draw
caches.textureCache.resetMarkInUse(this);
- caches.flush(Caches::FlushMode::Layers);
+ mRenderThread.renderState().flush(Caches::FlushMode::Layers);
}
}
@@ -609,10 +609,10 @@
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
- Caches::getInstance().flush(Caches::FlushMode::Full);
+ thread.renderState().flush(Caches::FlushMode::Full);
thread.eglManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- Caches::getInstance().flush(Caches::FlushMode::Moderate);
+ thread.renderState().flush(Caches::FlushMode::Moderate);
}
}
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
index 29d9803..81bf9ed 100644
--- a/libs/hwui/tests/TreeContentAnimation.cpp
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -408,7 +408,7 @@
public:
sp<RenderNode> card = TestUtils::createNode<TestCanvas>(0, 0, 200, 200, [] (TestCanvas& canvas) {
canvas.drawColor(0xFF0000FF, SkXfermode::kSrcOver_Mode);
- }, true);
+ }, TestUtils::getHwLayerSetupCallback());
void createContent(int width, int height, TestCanvas* canvas) override {
canvas->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); // background
canvas->drawRenderNode(card.get());
diff --git a/libs/hwui/unit_tests/BakedOpStateTests.cpp b/libs/hwui/unit_tests/BakedOpStateTests.cpp
index bc1b69f..4e00fb3 100644
--- a/libs/hwui/unit_tests/BakedOpStateTests.cpp
+++ b/libs/hwui/unit_tests/BakedOpStateTests.cpp
@@ -60,24 +60,21 @@
TEST(BakedOpState, constructAndReject) {
LinearAllocator allocator;
- Matrix4 identity;
- identity.loadIdentity();
-
Matrix4 translate100x0;
translate100x0.loadTranslate(100, 0, 0);
SkPaint paint;
{
RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, Rect(0, 0, 100, 200), &paint);
- auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, rejectOp);
EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
}
{
- RectOp successOp(Rect(30, 40, 100, 200), identity, Rect(0, 0, 100, 200), &paint);
- auto snapshot = TestUtils::makeSnapshot(identity, Rect(0, 0, 100, 200));
+ RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), Rect(0, 0, 100, 200), &paint);
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
BakedOpState* bakedOp = BakedOpState::tryConstruct(allocator, *snapshot, successOp);
EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
@@ -85,5 +82,24 @@
}
}
+TEST(BakedOpState, oplessConstructAndReject) {
+ LinearAllocator allocator;
+ {
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 0, 0)); // empty
+ BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+
+ EXPECT_EQ(bakedOp, nullptr); // rejected by clip, so not constructed
+ EXPECT_LE(allocator.usedSize(), 8u); // no significant allocation space used for rejected op
+ }
+ {
+ auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 200));
+ BakedOpState* bakedOp = BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
+
+ EXPECT_NE(bakedOp, nullptr); // NOT rejected by clip, so will be constructed
+ EXPECT_GT(allocator.usedSize(), 64u); // relatively large alloc for non-rejected op
+ EXPECT_EQ((ShadowOp*)0x1234, bakedOp->op);
+ }
+}
+
}
}
diff --git a/libs/hwui/unit_tests/OffscreenBufferPoolTests.cpp b/libs/hwui/unit_tests/OffscreenBufferPoolTests.cpp
new file mode 100644
index 0000000..ba92157
--- /dev/null
+++ b/libs/hwui/unit_tests/OffscreenBufferPoolTests.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <renderstate/OffscreenBufferPool.h>
+
+#include <unit_tests/TestUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(OffscreenBuffer, computeIdealDimension) {
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(1));
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(31));
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(33));
+ EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(64));
+ EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
+}
+
+TEST(OffscreenBuffer, construct) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBuffer layer(thread.renderState(), Caches::getInstance(), 49u, 149u);
+ EXPECT_EQ(49u, layer.viewportWidth);
+ EXPECT_EQ(149u, layer.viewportHeight);
+
+ EXPECT_EQ(64u, layer.texture.width);
+ EXPECT_EQ(192u, layer.texture.height);
+
+ EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
+ });
+}
+
+TEST(OffscreenBufferPool, construct) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+ EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
+ EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
+ EXPECT_EQ((uint32_t) Properties::layerPoolSize, pool.getMaxSize())
+ << "pool must read size from Properties";
+ });
+
+}
+
+TEST(OffscreenBufferPool, getPutClear) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(thread.renderState(), 100u, 200u);
+ EXPECT_EQ(100u, layer->viewportWidth);
+ EXPECT_EQ(200u, layer->viewportHeight);
+
+ ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
+
+ pool.putOrDelete(layer);
+ ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
+
+ auto layer2 = pool.get(thread.renderState(), 102u, 202u);
+ EXPECT_EQ(layer, layer2) << "layer should be recycled";
+ ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
+
+ pool.putOrDelete(layer);
+ EXPECT_EQ(1u, pool.getCount());
+ pool.clear();
+ EXPECT_EQ(0u, pool.getSize());
+ EXPECT_EQ(0u, pool.getCount());
+ });
+}
+
+TEST(OffscreenBufferPool, resize) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+
+ auto layer = pool.get(thread.renderState(), 64u, 64u);
+
+ // resize in place
+ ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+ EXPECT_EQ(60u, layer->viewportWidth);
+ EXPECT_EQ(55u, layer->viewportHeight);
+ EXPECT_EQ(64u, layer->texture.width);
+ EXPECT_EQ(64u, layer->texture.height);
+
+ // resized to use different object in pool
+ auto layer2 = pool.get(thread.renderState(), 128u, 128u);
+ pool.putOrDelete(layer2);
+ ASSERT_EQ(1u, pool.getCount());
+ ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+ EXPECT_EQ(120u, layer2->viewportWidth);
+ EXPECT_EQ(125u, layer2->viewportHeight);
+ EXPECT_EQ(128u, layer2->texture.width);
+ EXPECT_EQ(128u, layer2->texture.height);
+
+ // original allocation now only thing in pool
+ EXPECT_EQ(1u, pool.getCount());
+ EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
+ });
+}
+
+TEST(OffscreenBufferPool, putAndDestroy) {
+ TestUtils::runOnRenderThread([] (renderthread::RenderThread& thread) {
+ OffscreenBufferPool pool;
+ // layer too big to return to the pool
+ // Note: this relies on the fact that the pool won't reject based on max texture size
+ auto hugeLayer = pool.get(thread.renderState(), pool.getMaxSize() / 64, 64);
+ EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
+ pool.putOrDelete(hugeLayer);
+ EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
+ });
+}
diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp
index f67c24a..07080a2 100644
--- a/libs/hwui/unit_tests/OpReordererTests.cpp
+++ b/libs/hwui/unit_tests/OpReordererTests.cpp
@@ -38,17 +38,17 @@
* and allows Renderer vs Dispatching behavior to be merged.
*
* onXXXOp methods fail by default - tests should override ops they expect
- * startLayer fails by default - tests should override if expected
+ * startRepaintLayer fails by default - tests should override if expected
* startFrame/endFrame do nothing by default - tests should override to intercept
*/
class TestRendererBase {
public:
virtual ~TestRendererBase() {}
- virtual OffscreenBuffer* createLayer(uint32_t, uint32_t) {
+ virtual OffscreenBuffer* startTemporaryLayer(uint32_t, uint32_t) {
ADD_FAILURE() << "Layer creation not expected in this test";
return nullptr;
}
- virtual void startLayer(OffscreenBuffer*) {
+ virtual void startRepaintLayer(OffscreenBuffer*) {
ADD_FAILURE() << "Layer repaint not expected in this test";
}
virtual void endLayer() {
@@ -82,27 +82,27 @@
MAP_OPS(DISPATCHER_METHOD);
};
-
class FailRenderer : public TestRendererBase {};
-class SimpleTestRenderer : public TestRendererBase {
-public:
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(100u, width);
- EXPECT_EQ(200u, height);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- }
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(2, mIndex++);
- }
- void endFrame() override {
- EXPECT_EQ(3, mIndex++);
- }
-};
TEST(OpReorderer, simple) {
+ class SimpleTestRenderer : public TestRendererBase {
+ public:
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(100u, width);
+ EXPECT_EQ(200u, height);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(25, 25);
canvas.drawRect(0, 0, 100, 200, SkPaint());
@@ -115,7 +115,6 @@
EXPECT_EQ(4, renderer.getIndex()); // 2 ops + start + end
}
-
TEST(OpReorderer, simpleRejection) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -129,18 +128,18 @@
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-
-static int SIMPLE_BATCHING_LOOPS = 5;
-class SimpleBatchingTestRenderer : public TestRendererBase {
-public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
- }
-};
TEST(OpReorderer, simpleBatching) {
+ static int SIMPLE_BATCHING_LOOPS = 5;
+ class SimpleBatchingTestRenderer : public TestRendererBase {
+ public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS);
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(10, 10);
@@ -162,24 +161,25 @@
EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging)
}
-class RenderNodeTestRenderer : public TestRendererBase {
-public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- switch(mIndex++) {
- case 0:
- EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- break;
- case 1:
- EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- break;
- default:
- ADD_FAILURE();
- }
- }
-};
TEST(OpReorderer, renderNode) {
+ class RenderNodeTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ switch(mIndex++) {
+ case 0:
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clippedBounds);
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ break;
+ case 1:
+ EXPECT_EQ(Rect(50, 50, 150, 150), state.computedState.clippedBounds);
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ break;
+ default:
+ ADD_FAILURE();
+ }
+ }
+ };
+
sp<RenderNode> child = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
@@ -210,16 +210,17 @@
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-class ClippedTestRenderer : public TestRendererBase {
-public:
- void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
-};
TEST(OpReorderer, clipped) {
+ class ClippedTestRenderer : public TestRendererBase {
+ public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(10, 20, 30, 40), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+ };
+
sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200, [](RecordingCanvas& canvas) {
SkBitmap bitmap = TestUtils::createSkBitmap(200, 200);
canvas.drawBitmap(bitmap, 0, 0, nullptr);
@@ -236,36 +237,36 @@
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-
-class SaveLayerSimpleTestRenderer : public TestRendererBase {
-public:
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(180u, width);
- EXPECT_EQ(180u, height);
- return nullptr;
- }
- void endLayer() override {
- EXPECT_EQ(2, mIndex++);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
- EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
-
- Matrix4 expectedTransform;
- expectedTransform.loadTranslate(-10, -10, 0);
- EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(3, mIndex++);
- EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
- EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
- EXPECT_TRUE(state.computedState.transform.isIdentity());
- }
-};
TEST(OpReorderer, saveLayerSimple) {
+ class SaveLayerSimpleTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(180u, width);
+ EXPECT_EQ(180u, height);
+ return nullptr;
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 180, 180), state.computedState.clipRect);
+
+ Matrix4 expectedTransform;
+ expectedTransform.loadTranslate(-10, -10, 0);
+ EXPECT_MATRIX_APPROX_EQ(expectedTransform, state.computedState.transform);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(3, mIndex++);
+ EXPECT_EQ(Rect(10, 10, 190, 190), state.computedState.clippedBounds);
+ EXPECT_EQ(Rect(0, 0, 200, 200), state.computedState.clipRect);
+ EXPECT_TRUE(state.computedState.transform.isIdentity());
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 10, 190, 190, 128, SkCanvas::kClipToLayer_SaveFlag);
canvas.drawRect(10, 10, 190, 190, SkPaint());
@@ -279,57 +280,57 @@
EXPECT_EQ(4, renderer.getIndex());
}
-
-/* saveLayer1 {rect1, saveLayer2 { rect2 } } will play back as:
- * - createLayer2, rect2 endLayer2
- * - createLayer1, rect1, drawLayer2, endLayer1
- * - startFrame, layerOp1, endFrame
- */
-class SaveLayerNestedTestRenderer : public TestRendererBase {
-public:
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height) override {
- const int index = mIndex++;
- if (index == 0) {
- EXPECT_EQ(400u, width);
- EXPECT_EQ(400u, height);
- return (OffscreenBuffer*) 0x400;
- } else if (index == 3) {
- EXPECT_EQ(800u, width);
- EXPECT_EQ(800u, height);
- return (OffscreenBuffer*) 0x800;
- } else { ADD_FAILURE(); }
- return (OffscreenBuffer*) nullptr;
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 6);
- }
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(7, mIndex++);
- }
- void endFrame() override {
- EXPECT_EQ(9, mIndex++);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 1) {
- EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
- } else if (index == 4) {
- EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
- } else { ADD_FAILURE(); }
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- const int index = mIndex++;
- if (index == 5) {
- EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
- EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
- EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
- } else { ADD_FAILURE(); }
- }
-};
TEST(OpReorderer, saveLayerNested) {
+ /* saveLayer1 { rect1, saveLayer2 { rect2 } } will play back as:
+ * - startTemporaryLayer2, rect2 endLayer2
+ * - startTemporaryLayer1, rect1, drawLayer2, endLayer1
+ * - startFrame, layerOp1, endFrame
+ */
+ class SaveLayerNestedTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override {
+ const int index = mIndex++;
+ if (index == 0) {
+ EXPECT_EQ(400u, width);
+ EXPECT_EQ(400u, height);
+ return (OffscreenBuffer*) 0x400;
+ } else if (index == 3) {
+ EXPECT_EQ(800u, width);
+ EXPECT_EQ(800u, height);
+ return (OffscreenBuffer*) 0x800;
+ } else { ADD_FAILURE(); }
+ return (OffscreenBuffer*) nullptr;
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 6);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(7, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(9, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
+ if (index == 1) {
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner rect
+ } else if (index == 4) {
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer rect
+ } else { ADD_FAILURE(); }
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ const int index = mIndex++;
+ if (index == 5) {
+ EXPECT_EQ((OffscreenBuffer*)0x400, *op.layerHandle);
+ EXPECT_EQ(Rect(0, 0, 400, 400), op.unmappedBounds); // inner layer
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0x800, *op.layerHandle);
+ EXPECT_EQ(Rect(0, 0, 800, 800), op.unmappedBounds); // outer layer
+ } else { ADD_FAILURE(); }
+ }
+ };
+
auto dl = TestUtils::createDisplayList<RecordingCanvas>(800, 800, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 800, 800, 128, SkCanvas::kClipToLayer_SaveFlag);
{
@@ -369,42 +370,41 @@
reorderer.replayBakedOps<TestDispatcher>(renderer);
}
-class HwLayerSimpleTestRenderer : public TestRendererBase {
-public:
- void startLayer(OffscreenBuffer* offscreenBuffer) override {
- EXPECT_EQ(0, mIndex++);
- EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(1, mIndex++);
-
- EXPECT_TRUE(state.computedState.transform.isIdentity())
- << "Transform should be reset within layer";
-
- EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
- << "Damage rect should be used to clip layer content";
- }
- void endLayer() override {
- EXPECT_EQ(2, mIndex++);
- }
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(3, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- EXPECT_EQ(4, mIndex++);
- }
- void endFrame() override {
- EXPECT_EQ(5, mIndex++);
- }
-};
TEST(OpReorderer, hwLayerSimple) {
+ class HwLayerSimpleTestRenderer : public TestRendererBase {
+ public:
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer) override {
+ EXPECT_EQ(0, mIndex++);
+ EXPECT_EQ(offscreenBuffer, (OffscreenBuffer*) 0x0124);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+
+ EXPECT_TRUE(state.computedState.transform.isIdentity())
+ << "Transform should be reset within layer";
+
+ EXPECT_EQ(state.computedState.clipRect, Rect(25, 25, 75, 75))
+ << "Damage rect should be used to clip layer content";
+ }
+ void endLayer() override {
+ EXPECT_EQ(2, mIndex++);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(3, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(4, mIndex++);
+ }
+ void endFrame() override {
+ EXPECT_EQ(5, mIndex++);
+ }
+ };
+
sp<RenderNode> node = TestUtils::createNode<RecordingCanvas>(10, 10, 110, 110, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
- });
- node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }, TestUtils::getHwLayerSetupCallback());
OffscreenBuffer** bufferHandle = node->getLayerHandle();
*bufferHandle = (OffscreenBuffer*) 0x0124;
@@ -427,69 +427,67 @@
*bufferHandle = nullptr;
}
-
-/* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
- * - startLayer(child), rect(grey), endLayer
- * - createLayer, drawLayer(child), endLayer
- * - startLayer(parent), rect(white), drawLayer(saveLayer), endLayer
- * - startFrame, drawLayer(parent), endLayerb
- */
-class HwLayerComplexTestRenderer : public TestRendererBase {
-public:
- OffscreenBuffer* createLayer(uint32_t width, uint32_t height) {
- EXPECT_EQ(3, mIndex++); // savelayer first
- return (OffscreenBuffer*)0xabcd;
- }
- void startLayer(OffscreenBuffer* offscreenBuffer) override {
- int index = mIndex++;
- if (index == 0) {
- // starting inner layer
- EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
- } else if (index == 6) {
- // starting outer layer
- EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
- } else { ADD_FAILURE(); }
- }
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- if (index == 1) {
- // inner layer's rect (white)
- EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
- } else if (index == 7) {
- // outer layer's rect (grey)
- EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
- } else { ADD_FAILURE(); }
- }
- void endLayer() override {
- int index = mIndex++;
- EXPECT_TRUE(index == 2 || index == 5 || index == 9);
- }
- void startFrame(uint32_t width, uint32_t height) override {
- EXPECT_EQ(10, mIndex++);
- }
- void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
- int index = mIndex++;
- if (index == 4) {
- EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
- } else if (index == 8) {
- EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
- } else if (index == 11) {
- EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
- } else { ADD_FAILURE(); }
- }
- void endFrame() override {
- EXPECT_EQ(12, mIndex++);
- }
-};
TEST(OpReorderer, hwLayerComplex) {
+ /* parentLayer { greyRect, saveLayer { childLayer { whiteRect } } } will play back as:
+ * - startRepaintLayer(child), rect(grey), endLayer
+ * - startTemporaryLayer, drawLayer(child), endLayer
+ * - startRepaintLayer(parent), rect(white), drawLayer(saveLayer), endLayer
+ * - startFrame, drawLayer(parent), endLayerb
+ */
+ class HwLayerComplexTestRenderer : public TestRendererBase {
+ public:
+ OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) {
+ EXPECT_EQ(3, mIndex++); // savelayer first
+ return (OffscreenBuffer*)0xabcd;
+ }
+ void startRepaintLayer(OffscreenBuffer* offscreenBuffer) override {
+ int index = mIndex++;
+ if (index == 0) {
+ // starting inner layer
+ EXPECT_EQ((OffscreenBuffer*)0x4567, offscreenBuffer);
+ } else if (index == 6) {
+ // starting outer layer
+ EXPECT_EQ((OffscreenBuffer*)0x0123, offscreenBuffer);
+ } else { ADD_FAILURE(); }
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 1) {
+ // inner layer's rect (white)
+ EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
+ } else if (index == 7) {
+ // outer layer's rect (grey)
+ EXPECT_EQ(SK_ColorDKGRAY, op.paint->getColor());
+ } else { ADD_FAILURE(); }
+ }
+ void endLayer() override {
+ int index = mIndex++;
+ EXPECT_TRUE(index == 2 || index == 5 || index == 9);
+ }
+ void startFrame(uint32_t width, uint32_t height) override {
+ EXPECT_EQ(10, mIndex++);
+ }
+ void onLayerOp(const LayerOp& op, const BakedOpState& state) override {
+ int index = mIndex++;
+ if (index == 4) {
+ EXPECT_EQ((OffscreenBuffer*)0x4567, *op.layerHandle);
+ } else if (index == 8) {
+ EXPECT_EQ((OffscreenBuffer*)0xabcd, *op.layerHandle);
+ } else if (index == 11) {
+ EXPECT_EQ((OffscreenBuffer*)0x0123, *op.layerHandle);
+ } else { ADD_FAILURE(); }
+ }
+ void endFrame() override {
+ EXPECT_EQ(12, mIndex++);
+ }
+ };
+
auto child = TestUtils::createNode<RecordingCanvas>(50, 50, 150, 150,
[](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
- });
- child->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- child->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }, TestUtils::getHwLayerSetupCallback());
*(child->getLayerHandle()) = (OffscreenBuffer*) 0x4567;
RenderNode* childPtr = child.get();
@@ -502,9 +500,7 @@
canvas.saveLayerAlpha(50, 50, 150, 150, 128, SkCanvas::kClipToLayer_SaveFlag);
canvas.drawRenderNode(childPtr);
canvas.restore();
- });
- parent->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- parent->setPropertyFieldsDirty(RenderNode::GENERIC);
+ }, TestUtils::getHwLayerSetupCallback());
*(parent->getLayerHandle()) = (OffscreenBuffer*) 0x0123;
TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
@@ -527,14 +523,6 @@
*(parent->getLayerHandle()) = nullptr;
}
-
-class ZReorderTestRenderer : public TestRendererBase {
-public:
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
- EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
- }
-};
static void drawOrderedRect(RecordingCanvas* canvas, uint8_t expectedDrawOrder) {
SkPaint paint;
paint.setColor(SkColorSetARGB(256, 0, 0, expectedDrawOrder)); // order put in blue channel
@@ -550,6 +538,14 @@
canvas->drawRenderNode(node.get()); // canvas takes reference/sole ownership
}
TEST(OpReorderer, zReorder) {
+ class ZReorderTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ int expectedOrder = SkColorGetB(op.paint->getColor()); // extract order from blue channel
+ EXPECT_EQ(expectedOrder, mIndex++) << "An op was drawn out of order";
+ }
+ };
+
auto parent = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
[](RecordingCanvas& canvas) {
drawOrderedNode(&canvas, 0, 10.0f); // in reorder=false at this point, so played inorder
@@ -576,27 +572,64 @@
EXPECT_EQ(10, renderer.getIndex());
};
+TEST(OpReorderer, shadow) {
+ class ShadowTestRenderer : public TestRendererBase {
+ public:
+ void onShadowOp(const ShadowOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(0, mIndex++);
+ }
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(1, mIndex++);
+ }
+ };
-class PropertyTestRenderer : public TestRendererBase {
-public:
- PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
- : mCallback(callback) {}
- void onRectOp(const RectOp& op, const BakedOpState& state) override {
- EXPECT_EQ(mIndex++, 0);
- mCallback(op, state);
- }
- std::function<void(const RectOp&, const BakedOpState&)> mCallback;
-};
+ sp<RenderNode> caster = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100,
+ [](RecordingCanvas& canvas) {
+ SkPaint paint;
+ paint.setColor(SK_ColorWHITE);
+ canvas.drawRect(0, 0, 100, 100, paint);
+ }, [] (RenderProperties& properties) {
+ properties.setTranslationZ(5.0f);
+ properties.mutableOutline().setRoundRect(0, 0, 100, 100, 5, 1.0f);
+ return RenderNode::GENERIC | RenderNode::TRANSLATION_Z;
+ });
+ sp<RenderNode> parent = TestUtils::createNode<RecordingCanvas>(0, 0, 200, 200,
+ [&caster] (RecordingCanvas& canvas) {
+ canvas.insertReorderBarrier(true);
+ canvas.drawRenderNode(caster.get());
+ });
+
+ TestUtils::syncHierarchyPropertiesAndDisplayList(parent);
+
+ std::vector< sp<RenderNode> > nodes;
+ nodes.push_back(parent.get());
+
+ OpReorderer reorderer(sEmptyLayerUpdateQueue, SkRect::MakeWH(200, 200), 200, 200, nodes);
+
+ ShadowTestRenderer renderer;
+ reorderer.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
static void testProperty(
- std::function<int(RenderProperties&)> propSetupCallback,
+ TestUtils::PropSetupCallback propSetupCallback,
std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) {
+ class PropertyTestRenderer : public TestRendererBase {
+ public:
+ PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback)
+ : mCallback(callback) {}
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(mIndex++, 0);
+ mCallback(op, state);
+ }
+ std::function<void(const RectOp&, const BakedOpState&)> mCallback;
+ };
+
auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 100, 100, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
- });
- node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties()));
+ }, propSetupCallback);
TestUtils::syncHierarchyPropertiesAndDisplayList(node);
std::vector< sp<RenderNode> > nodes;
diff --git a/libs/hwui/unit_tests/PathParserTests.cpp b/libs/hwui/unit_tests/PathParserTests.cpp
index ef263a1..60ea219 100644
--- a/libs/hwui/unit_tests/PathParserTests.cpp
+++ b/libs/hwui/unit_tests/PathParserTests.cpp
@@ -30,7 +30,7 @@
const std::function<void(SkPath*)> skPathLamda;
};
-const static std::vector<TestData> testDataSet = {
+const static TestData sTestDataSet[] = {
// TestData with scientific notation -2e3 etc.
{
// Path
@@ -165,7 +165,7 @@
};
TEST(PathParser, parseStringForData) {
- for (TestData testData: testDataSet) {
+ for (TestData testData: sTestDataSet) {
// Test generated path data against the given data.
PathData pathData;
size_t length = strlen(testData.pathString);
@@ -177,7 +177,7 @@
}
TEST(PathParser, createSkPathFromPathData) {
- for (TestData testData: testDataSet) {
+ for (TestData testData: sTestDataSet) {
SkPath expectedPath;
testData.skPathLamda(&expectedPath);
SkPath actualPath;
@@ -187,7 +187,7 @@
}
TEST(PathParser, parseStringForSkPath) {
- for (TestData testData: testDataSet) {
+ for (TestData testData: sTestDataSet) {
size_t length = strlen(testData.pathString);
// Check the return value as well as the SkPath generated.
SkPath actualPath;
diff --git a/libs/hwui/unit_tests/TestUtils.h b/libs/hwui/unit_tests/TestUtils.h
index 28e0fd8..efa28ae 100644
--- a/libs/hwui/unit_tests/TestUtils.h
+++ b/libs/hwui/unit_tests/TestUtils.h
@@ -97,25 +97,38 @@
return std::unique_ptr<DisplayList>(canvas.finishRecording());
}
- static sp<RenderNode> createNode(int left, int top, int right, int bottom, bool onLayer = false) {
+ typedef std::function<int(RenderProperties&)> PropSetupCallback;
+
+ static PropSetupCallback getHwLayerSetupCallback() {
+ static PropSetupCallback sLayerSetupCallback = [] (RenderProperties& properties) {
+ properties.mutateLayerProperties().setType(LayerType::RenderLayer);
+ return RenderNode::GENERIC;
+ };
+ return sLayerSetupCallback;
+ }
+
+ static sp<RenderNode> createNode(int left, int top, int right, int bottom,
+ PropSetupCallback propSetupCallback = nullptr) {
+#if HWUI_NULL_GPU
// if RenderNodes are being sync'd/used, device info will be needed, since
// DeviceInfo::maxTextureSize() affects layer property
DeviceInfo::initialize();
+#endif
sp<RenderNode> node = new RenderNode();
node->mutateStagingProperties().setLeftTopRightBottom(left, top, right, bottom);
node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- if (onLayer) {
- node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer);
- node->setPropertyFieldsDirty(RenderNode::GENERIC);
+ if (propSetupCallback) {
+ node->setPropertyFieldsDirty(propSetupCallback(node->mutateStagingProperties()));
}
return node;
}
template<class CanvasType>
static sp<RenderNode> createNode(int left, int top, int right, int bottom,
- std::function<void(CanvasType& canvas)> canvasCallback, bool onLayer = false) {
- sp<RenderNode> node = createNode(left, top, right, bottom, onLayer);
+ std::function<void(CanvasType& canvas)> canvasCallback,
+ PropSetupCallback propSetupCallback = nullptr) {
+ sp<RenderNode> node = createNode(left, top, right, bottom, propSetupCallback);
auto&& props = node->stagingProperties(); // staging, since not sync'd yet
CanvasType canvas(props.getWidth(), props.getHeight());
diff --git a/libs/hwui/utils/Macros.h b/libs/hwui/utils/Macros.h
index 5ca9083..ccf2287 100644
--- a/libs/hwui/utils/Macros.h
+++ b/libs/hwui/utils/Macros.h
@@ -35,4 +35,7 @@
static_assert(std::is_standard_layout<Type>::value, \
#Type " must have standard layout")
+#define WARN_UNUSED_RESULT \
+ __attribute__((warn_unused_result))
+
#endif /* MACROS_H */
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index f95b0ae..9d1df26 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -18,7 +18,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -198,12 +197,18 @@
}
private String getOwnerInfo() {
- ContentResolver res = getContext().getContentResolver();
String info = null;
- final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
- KeyguardUpdateMonitor.getCurrentUser());
- if (ownerInfoEnabled) {
- info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+ if (mLockPatternUtils.isDeviceOwnerInfoEnabled()) {
+ // Use the device owner information set by device policy client via
+ // device policy manager.
+ info = mLockPatternUtils.getDeviceOwnerInfo();
+ } else {
+ // Use the current user owner information if enabled.
+ final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (ownerInfoEnabled) {
+ info = mLockPatternUtils.getOwnerInfo(KeyguardUpdateMonitor.getCurrentUser());
+ }
}
return info;
}
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 50237832..70abdf4 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -149,6 +149,9 @@
<!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
<string name="print_searching_for_printers">Searching for printers</string>
+ <!-- Title for the prompt shown as a placeholder if there are no print services. [CHAR LIMIT=50] -->
+ <string name="print_no_print_services">No print services enabled</string>
+
<!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
<string name="print_no_printers">No printers found</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 53e07e9..f409fd4 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -240,7 +240,9 @@
throw new IllegalArgumentException(PrintManager.EXTRA_PRINT_JOB
+ " cannot be null");
}
- mPrintJob.setAttributes(new PrintAttributes.Builder().build());
+ if (mPrintJob.getAttributes() == null) {
+ mPrintJob.setAttributes(new PrintAttributes.Builder().build());
+ }
final IBinder adapter = extras.getBinder(PrintManager.EXTRA_PRINT_DOCUMENT_ADAPTER);
if (adapter == null) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 3905bada..f4c15bd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -84,6 +84,11 @@
private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
+ /**
+ * If there are any enabled print services
+ */
+ private boolean mHasEnabledPrintServices;
+
private final ArrayList<PrintServiceInfo> mAddPrinterServices =
new ArrayList<>();
@@ -175,10 +180,6 @@
}
});
- if (mAddPrinterServices.isEmpty()) {
- menu.removeItem(R.id.action_add_printer);
- }
-
return true;
}
@@ -230,6 +231,7 @@
public void onResume() {
super.onResume();
updateServicesWithAddPrinterActivity();
+ updateEmptyView((DestinationAdapter)mListView.getAdapter());
invalidateOptionsMenu();
}
@@ -258,6 +260,7 @@
}
private void updateServicesWithAddPrinterActivity() {
+ mHasEnabledPrintServices = true;
mAddPrinterServices.clear();
// Get all enabled print services.
@@ -266,6 +269,7 @@
// No enabled print services - done.
if (enabledServices.isEmpty()) {
+ mHasEnabledPrintServices = false;
return;
}
@@ -324,7 +328,10 @@
}
TextView titleView = (TextView) findViewById(R.id.title);
View progressBar = findViewById(R.id.progress_bar);
- if (adapter.getUnfilteredCount() <= 0) {
+ if (!mHasEnabledPrintServices) {
+ titleView.setText(R.string.print_no_print_services);
+ progressBar.setVisibility(View.GONE);
+ } else if (adapter.getUnfilteredCount() <= 0) {
titleView.setText(R.string.print_searching_for_printers);
progressBar.setVisibility(View.VISIBLE);
} else {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index f5fc698..6102bef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -18,6 +18,7 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
import android.app.Activity;
+import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.widget.DrawerLayout;
import android.util.Pair;
@@ -54,6 +55,13 @@
return;
}
Toolbar toolbar = (Toolbar) findViewById(R.id.action_bar);
+ TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
+ if (theme.getBoolean(android.R.styleable.Theme_windowNoTitle, false)) {
+ toolbar.setVisibility(View.GONE);
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ mDrawerLayout = null;
+ return;
+ }
setActionBar(toolbar);
mDrawerAdapter = new SettingsDrawerAdapter(this);
ListView listView = (ListView) findViewById(R.id.left_drawer);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 58f7124..3ae8827 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -42,7 +42,7 @@
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
@@ -115,29 +115,33 @@
});
/**
- * A common Runnable to finish Recents either by calling finish() (with a custom animation) or
- * launching Home with some ActivityOptions. Generally we always launch home when we exit
- * Recents rather than just finishing the activity since we don't know what is behind Recents in
- * the task stack. The only case where we finish() directly is when we are cancelling the full
- * screen transition from the app.
+ * A common Runnable to finish Recents by launching Home with an animation depending on the
+ * last activity launch state. Generally we always launch home when we exit Recents rather than
+ * just finishing the activity since we don't know what is behind Recents in the task stack.
*/
class FinishRecentsRunnable implements Runnable {
Intent mLaunchIntent;
- ActivityOptions mLaunchOpts;
/**
- * Creates a finish runnable that starts the specified intent, using the given
- * ActivityOptions.
+ * Creates a finish runnable that starts the specified intent.
*/
- public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
+ public FinishRecentsRunnable(Intent launchIntent) {
mLaunchIntent = launchIntent;
- mLaunchOpts = opts;
}
@Override
public void run() {
try {
- startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
+ RecentsActivityLaunchState launchState =
+ Recents.getConfiguration().getLaunchState();
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
+ launchState.launchedFromSearchHome ?
+ R.anim.recents_to_search_launcher_enter :
+ R.anim.recents_to_launcher_enter,
+ launchState.launchedFromSearchHome ?
+ R.anim.recents_to_search_launcher_exit :
+ R.anim.recents_to_launcher_exit);
+ startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
} catch (Exception e) {
Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
}
@@ -191,18 +195,6 @@
mRecentsView.setTaskStack(stack);
}
- // Create the home intent runnable
- Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
- homeIntent.addCategory(Intent.CATEGORY_HOME);
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
- ActivityOptions.makeCustomAnimation(this,
- launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
- R.anim.recents_to_launcher_enter,
- launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
- R.anim.recents_to_launcher_exit));
-
// Mark the task that is the launch target
int launchTaskIndexInStack = 0;
if (launchState.launchedToTaskId != -1) {
@@ -361,6 +353,13 @@
mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
mScrimViews = new SystemBarScrimViews(this);
+ // Create the home intent runnable
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
+ homeIntent.addCategory(Intent.CATEGORY_HOME);
+ homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
+
// Bind the search app widget when we first start up
if (!Constants.DebugFlags.App.DisableSearchBar) {
mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
@@ -396,7 +395,7 @@
boolean wasLaunchedByAm = !launchState.launchedFromHome &&
!launchState.launchedFromAppWithThumbnail;
if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
- EventBus.getDefault().send(new EnterRecentsWindowAnimationStartedEvent());
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
}
if (!launchState.launchedHasConfigurationChanged) {
@@ -422,6 +421,12 @@
}
@Override
+ public void onEnterAnimationComplete() {
+ super.onEnterAnimationComplete();
+ EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
+ }
+
+ @Override
protected void onPause() {
super.onPause();
@@ -603,7 +608,7 @@
}
}
- public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
+ public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
// Try and start the enter animation (or restart it on configuration changed)
ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 4059543..6fe6909 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -40,7 +40,6 @@
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
@@ -70,7 +69,7 @@
* be called remotely from the system user.
*/
public class RecentsImpl extends IRecentsNonSystemUserCallbacks.Stub
- implements ActivityOptions.OnAnimationStartedListener, ActivityOptions.OnAnimationFinishedListener {
+ implements ActivityOptions.OnAnimationFinishedListener {
private final static String TAG = "RecentsImpl";
private final static boolean DEBUG = false;
@@ -140,7 +139,6 @@
TaskStackListenerImpl mTaskStackListener;
RecentsAppWidgetHost mAppWidgetHost;
boolean mBootCompleted;
- boolean mStartAnimationTriggered;
boolean mCanReuseTaskStackViews = true;
// Task launching
@@ -613,7 +611,7 @@
final Task toTask = new Task();
final TaskViewTransform toTransform = getThumbnailTransitionTransform(stack, stackView,
topTask.id, toTask);
- ForegroundThread.getHandler().post(new Runnable() {
+ ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
final Bitmap transitionBitmap = drawThumbnailTransitionBitmap(toTask, toTransform);
@@ -635,7 +633,7 @@
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_unknown_enter,
R.anim.recents_from_unknown_exit,
- mHandler, this);
+ mHandler, null);
}
/**
@@ -646,12 +644,12 @@
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_search_launcher_enter,
R.anim.recents_from_search_launcher_exit,
- mHandler, this);
+ mHandler, null);
}
return ActivityOptions.makeCustomAnimation(mContext,
R.anim.recents_from_launcher_enter,
R.anim.recents_from_launcher_exit,
- mHandler, this);
+ mHandler, null);
}
/**
@@ -677,7 +675,7 @@
AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
specs.toArray(specsArray);
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
- specsArray, mHandler, this, this);
+ specsArray, mHandler, null, this);
} else {
// Update the destination rect
Task toTask = new Task();
@@ -688,7 +686,7 @@
if (thumbnail != null) {
return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
- (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, this);
+ (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, null);
}
// If both the screenshot and thumbnail fails, then just fall back to the default transition
return getUnknownTransitionActivityOptions();
@@ -841,8 +839,6 @@
private void startRecentsActivity(ActivityManager.RunningTaskInfo topTask,
ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail,
TaskStackLayoutAlgorithm.VisibilityReport vr) {
- mStartAnimationTriggered = false;
-
// Update the configuration based on the launch options
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
@@ -870,16 +866,7 @@
mCanReuseTaskStackViews = true;
}
- /**** OnAnimationStartedListener Implementation ****/
-
- @Override
- public void onAnimationStarted() {
- // Notify recents to start the enter animation
- if (!mStartAnimationTriggered) {
- mStartAnimationTriggered = true;
- EventBus.getDefault().post(new EnterRecentsWindowAnimationStartedEvent());
- }
- }
+ /**** OnAnimationFinishedListener Implementation ****/
@Override
public void onAnimationFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
rename to packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
index f187178..b31f320 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationStartedEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/EnterRecentsWindowAnimationCompletedEvent.java
@@ -19,8 +19,10 @@
import com.android.systemui.recents.events.EventBus;
/**
- * This is sent when the window animation into Recents starts.
+ * This is sent when the window animation into Recents completes. We use this signal to know when
+ * we can start in-app animations so that they don't conflict with the window transition into
+ * Recents.
*/
-public class EnterRecentsWindowAnimationStartedEvent extends EventBus.Event {
+public class EnterRecentsWindowAnimationCompletedEvent extends EventBus.Event {
// Simple event
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index a28601b..85b8fcf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -270,7 +270,7 @@
int taskCount = tasks.size();
for (int i = taskCount - 1; i >= 0; i--) {
Task t = tasks.get(i);
- if (t.isFreeformTask()) {
+ if (t.isFreeformTask() || targetStackId == FREEFORM_WORKSPACE_STACK_ID) {
TaskView tv = stackView.getChildViewForTask(t);
if (tv == null) {
// TODO: Create a different animation task rect for this case (though it should
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index c4e2d8a..5a09ee4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -26,7 +26,7 @@
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
+import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
/** Manages the scrims for the various system bars. */
public class SystemBarScrimViews {
@@ -81,21 +81,11 @@
/**
* Starts animating the scrim views when entering Recents.
*/
- public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
- RecentsConfiguration config = Recents.getConfiguration();
- RecentsActivityLaunchState launchState = config.getLaunchState();
- int transitionEnterFromAppDelay = mContext.getResources().getInteger(
- R.integer.recents_enter_from_app_transition_duration);
- int transitionEnterFromHomeDelay = mContext.getResources().getInteger(
- R.integer.recents_enter_from_home_transition_duration);
-
+ public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
mStatusBarScrimView.animate()
.translationY(0)
- .setStartDelay(launchState.launchedFromHome ?
- transitionEnterFromHomeDelay :
- transitionEnterFromAppDelay)
.setDuration(mNavBarScrimEnterDuration)
.setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
@@ -110,9 +100,6 @@
mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
mNavBarScrimView.animate()
.translationY(0)
- .setStartDelay(launchState.launchedFromHome ?
- transitionEnterFromHomeDelay :
- transitionEnterFromAppDelay)
.setDuration(mNavBarScrimEnterDuration)
.setInterpolator(mQuintOutInterpolator)
.withStartAction(new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 9c8829f..a57ac9d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -1161,7 +1161,7 @@
TaskView frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
frontTv.onTaskBound(newFrontMostTask);
- frontTv.fadeInActionButton(0, getResources().getInteger(
+ frontTv.fadeInActionButton(getResources().getInteger(
R.integer.recents_task_enter_from_app_duration));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 4f4b91a..cb7465d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -313,10 +313,6 @@
RecentsActivityLaunchState launchState = config.getLaunchState();
Resources res = mContext.getResources();
final TaskViewTransform transform = ctx.currentTaskTransform;
- final int transitionEnterFromAppDelay = res.getInteger(
- R.integer.recents_enter_from_app_transition_duration);
- final int transitionEnterFromHomeDelay = res.getInteger(
- R.integer.recents_enter_from_home_transition_duration);
final int taskViewEnterFromAppDuration = res.getInteger(
R.integer.recents_task_enter_from_app_duration);
final int taskViewEnterFromHomeDuration = res.getInteger(
@@ -332,25 +328,22 @@
if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) {
// Animate the thumbnail alpha before the dim animation (to prevent updating the
// hardware layer)
- mThumbnailView.startEnterRecentsAnimation(transitionEnterFromAppDelay,
- new Runnable() {
- @Override
- public void run() {
- animateDimToProgress(0, taskViewEnterFromAppDuration,
- ctx.postAnimationTrigger.decrementOnAnimationEnd());
- }
- });
+ mThumbnailView.startEnterRecentsAnimation(new Runnable() {
+ @Override
+ public void run() {
+ animateDimToProgress(taskViewEnterFromAppDuration,
+ ctx.postAnimationTrigger.decrementOnAnimationEnd());
+ }
+ });
} else {
// Immediately start the dim animation
- animateDimToProgress(transitionEnterFromAppDelay,
- taskViewEnterFromAppDuration,
+ animateDimToProgress(taskViewEnterFromAppDuration,
ctx.postAnimationTrigger.decrementOnAnimationEnd());
}
ctx.postAnimationTrigger.increment();
// Animate the action button in
- fadeInActionButton(transitionEnterFromAppDelay,
- taskViewEnterFromAppDuration);
+ fadeInActionButton(taskViewEnterFromAppDuration);
} else {
// Animate the task up if it was occluding the launch target
if (ctx.currentTaskOccludesLaunchTarget) {
@@ -358,7 +351,6 @@
setAlpha(0f);
animate().alpha(1f)
.translationY(transform.translationY)
- .setStartDelay(transitionEnterFromAppDelay)
.setUpdateListener(null)
.setInterpolator(mFastOutSlowInInterpolator)
.setDuration(taskViewEnterFromHomeDuration)
@@ -377,8 +369,7 @@
} else if (launchState.launchedFromHome) {
// Animate the tasks up
int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1);
- int delay = transitionEnterFromHomeDelay +
- frontIndex * taskViewEnterFromHomeStaggerDelay;
+ int delay = frontIndex * taskViewEnterFromHomeStaggerDelay;
setScaleX(transform.scale);
setScaleY(transform.scale);
@@ -404,13 +395,12 @@
}
}
- public void fadeInActionButton(int delay, int duration) {
+ public void fadeInActionButton(int duration) {
// Hide the action button
mActionButtonView.setAlpha(0f);
// Animate the action button in
mActionButtonView.animate().alpha(1f)
- .setStartDelay(delay)
.setDuration(duration)
.setInterpolator(PhoneStatusBar.ALPHA_IN)
.start();
@@ -611,12 +601,11 @@
}
/** Animates the dim to the task progress. */
- void animateDimToProgress(int delay, int duration, Animator.AnimatorListener postAnimRunnable) {
+ void animateDimToProgress(int duration, Animator.AnimatorListener postAnimRunnable) {
// Animate the dim into view as well
int toDim = getDimFromTaskProgress();
if (toDim != getDim()) {
ObjectAnimator anim = ObjectAnimator.ofInt(TaskView.this, "dim", toDim);
- anim.setStartDelay(delay);
anim.setDuration(duration);
if (postAnimRunnable != null) {
anim.addListener(postAnimRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 690c297..bc50846 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -218,13 +218,13 @@
void onFocusChanged(boolean focused) {
if (focused) {
if (Float.compare(getAlpha(), 1f) != 0) {
- startFadeAnimation(1f, 0, 150, null);
+ startFadeAnimation(1f, 150, null);
}
} else {
float taskViewThumbnailAlpha = getResources().getFloat(
R.dimen.recents_task_view_thumbnail_alpha);
if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) {
- startFadeAnimation(taskViewThumbnailAlpha, 0, 150, null);
+ startFadeAnimation(taskViewThumbnailAlpha, 150, null);
}
}
}
@@ -244,10 +244,10 @@
}
/** Animates this task thumbnail as it enters Recents. */
- void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) {
+ void startEnterRecentsAnimation(Runnable postAnimRunnable) {
float taskViewThumbnailAlpha = getResources().getFloat(
R.dimen.recents_task_view_thumbnail_alpha);
- startFadeAnimation(taskViewThumbnailAlpha, delay,
+ startFadeAnimation(taskViewThumbnailAlpha,
getResources().getInteger(R.integer.recents_task_enter_from_app_duration),
postAnimRunnable);
}
@@ -256,14 +256,13 @@
void startLaunchTaskAnimation(Runnable postAnimRunnable) {
int taskViewExitToAppDuration = mContext.getResources().getInteger(
R.integer.recents_task_exit_to_app_duration);
- startFadeAnimation(1f, 0, taskViewExitToAppDuration, postAnimRunnable);
+ startFadeAnimation(1f, taskViewExitToAppDuration, postAnimRunnable);
}
/** Starts a new thumbnail alpha animation. */
- void startFadeAnimation(float finalAlpha, int delay, int duration, final Runnable postAnimRunnable) {
+ void startFadeAnimation(float finalAlpha, int duration, final Runnable postAnimRunnable) {
Utilities.cancelAnimationWithoutCallbacks(mThumbnailAlphaAnimator);
mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha);
- mThumbnailAlphaAnimator.setStartDelay(delay);
mThumbnailAlphaAnimator.setDuration(duration);
mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator);
mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener);
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index b4411cf..781d134 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -341,7 +341,7 @@
mTransformationAnimator.start();
}
- private void setMagnificationSpec(MagnificationSpec spec) {
+ public void setMagnificationSpec(MagnificationSpec spec) {
if (DEBUG_SET_MAGNIFICATION_SPEC) {
Slog.i(LOG_TAG, "Sending: " + spec);
}
@@ -351,6 +351,10 @@
mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
}
+ public MagnificationSpec getMagnificationSpec() {
+ return mSentMagnificationSpec;
+ }
+
private static class MagnificationSpecEvaluator implements TypeEvaluator<MagnificationSpec> {
private final MagnificationSpec mTempTransformationSpec = MagnificationSpec.obtain();
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index fd67b41..f329cff 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -95,8 +95,6 @@
private static final int MESSAGE_BLUETOOTH_STATE_CHANGE=60;
private static final int MESSAGE_TIMEOUT_BIND =100;
private static final int MESSAGE_TIMEOUT_UNBIND =101;
- private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
- private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
private static final int MESSAGE_USER_SWITCHED = 300;
private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
@@ -587,15 +585,6 @@
}
}
- /** @hide*/
- public void getNameAndAddress() {
- if (DBG) {
- Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
- " mBinding = " + mBinding);
- }
- Message msg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(msg);
- }
public boolean enableNoAutoConnect()
{
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
@@ -681,14 +670,13 @@
if (mUnbinding) return;
mUnbinding = true;
if (mBluetooth != null) {
- if (!mConnection.isGetNameAddressOnly()) {
- //Unregister callback object
- try {
- mBluetooth.unregisterCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to unregister BluetoothCallback",re);
- }
+ //Unregister callback object
+ try {
+ mBluetooth.unregisterCallback(mBluetoothCallback);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to unregister BluetoothCallback",re);
}
+
if (DBG) Log.d(TAG, "Sending unbind request.");
mBluetooth = null;
//Unbind
@@ -780,11 +768,6 @@
if (DBG) Log.d(TAG, "Auto-enabling Bluetooth.");
sendEnableMsg(mQuietEnableExternal);
}
- if (!isNameAndAddressSet()) {
- // Sync the Bluetooth name and address from the Bluetooth Adapter
- if (DBG) Log.d(TAG, "Retrieving Bluetooth Adapter name and address...");
- getNameAndAddress();
- }
}
/**
@@ -957,42 +940,38 @@
* Inform BluetoothAdapter instances that Adapter service is up
*/
private void sendBluetoothServiceUpCallback() {
- if (!mConnection.isGetNameAddressOnly()) {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
- try {
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
- }
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceUp callbacks");
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceUp() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceUp(mBluetooth);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
}
- } finally {
- mCallbacks.finishBroadcast();
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
}
/**
* Inform BluetoothAdapter instances that Adapter service is down
*/
private void sendBluetoothServiceDownCallback() {
- if (!mConnection.isGetNameAddressOnly()) {
- if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
- try {
- int n = mCallbacks.beginBroadcast();
- Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
- for (int i=0; i <n;i++) {
- try {
- mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
- }
+ if (DBG) Log.d(TAG,"Calling onBluetoothServiceDown callbacks");
+ try {
+ int n = mCallbacks.beginBroadcast();
+ Log.d(TAG,"Broadcasting onBluetoothServiceDown() to " + n + " receivers.");
+ for (int i=0; i <n;i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onBluetoothServiceDown();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
}
- } finally {
- mCallbacks.finishBroadcast();
}
+ } finally {
+ mCallbacks.finishBroadcast();
}
}
@@ -1052,17 +1031,6 @@
}
private class BluetoothServiceConnection implements ServiceConnection {
-
- private boolean mGetNameAddressOnly;
-
- public void setGetNameAddressOnly(boolean getOnly) {
- mGetNameAddressOnly = getOnly;
- }
-
- public boolean isGetNameAddressOnly() {
- return mGetNameAddressOnly;
- }
-
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
@@ -1108,104 +1076,6 @@
public void handleMessage(Message msg) {
if (DBG) Log.d (TAG, "Message: " + msg.what);
switch (msg.what) {
- case MESSAGE_GET_NAME_AND_ADDRESS: {
- if (DBG) Log.d(TAG,"MESSAGE_GET_NAME_AND_ADDRESS");
- synchronized(mConnection) {
- //Start bind request
- if ((mBluetooth == null) && (!mBinding)) {
- if (DBG) Log.d(TAG, "Binding to service to get name and address");
- mConnection.setGetNameAddressOnly(true);
- //Start bind timeout and bind
- Message timeoutMsg = mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
- mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
- Intent i = new Intent(IBluetooth.class.getName());
- if (!doBind(i, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.CURRENT)) {
- mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
- } else {
- mBinding = true;
- }
- }
- else {
- Message saveMsg= mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- saveMsg.arg1 = 0;
- if (mBluetooth != null) {
- mHandler.sendMessage(saveMsg);
- } else {
- // if enable is also called to bind the service
- // wait for MESSAGE_BLUETOOTH_SERVICE_CONNECTED
- mHandler.sendMessageDelayed(saveMsg, TIMEOUT_SAVE_MS);
- }
- }
- }
- break;
- }
- case MESSAGE_SAVE_NAME_AND_ADDRESS: {
- boolean unbind = false;
- if (DBG) Log.d(TAG,"MESSAGE_SAVE_NAME_AND_ADDRESS");
- synchronized(mConnection) {
- if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) {
- try {
- mBluetooth.enable();
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call enable()",e);
- }
- }
- }
- if (mBluetooth != null && !mConnection.isGetNameAddressOnly()) waitForOnOff(true, false);
- synchronized(mConnection) {
- if (mBluetooth != null) {
- String name = null;
- String address = null;
- try {
- name = mBluetooth.getName();
- address = mBluetooth.getAddress();
- } catch (RemoteException re) {
- Log.e(TAG,"",re);
- }
-
- if (name != null && address != null) {
- storeNameAndAddress(name,address);
- if (mConnection.isGetNameAddressOnly()) {
- unbind = true;
- }
- } else {
- if (msg.arg1 < MAX_SAVE_RETRIES) {
- Message retryMsg = mHandler.obtainMessage(MESSAGE_SAVE_NAME_AND_ADDRESS);
- retryMsg.arg1= 1+msg.arg1;
- if (DBG) Log.d(TAG,"Retrying name/address remote retrieval and save.....Retry count =" + retryMsg.arg1);
- mHandler.sendMessageDelayed(retryMsg, TIMEOUT_SAVE_MS);
- } else {
- Log.w(TAG,"Maximum name/address remote retrieval retry exceeded");
- if (mConnection.isGetNameAddressOnly()) {
- unbind = true;
- }
- }
- }
- if (!mEnable && !mConnection.isGetNameAddressOnly()) {
- try {
- mBluetooth.disable();
- } catch (RemoteException e) {
- Log.e(TAG,"Unable to call disable()",e);
- }
- }
- } else {
- // rebind service by Request GET NAME AND ADDRESS
- // if service is unbinded by disable or
- // MESSAGE_BLUETOOTH_SERVICE_CONNECTED is not received
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- }
- }
- if (!mEnable && mBluetooth != null && !mConnection.isGetNameAddressOnly()) {
- waitForOnOff(false, true);
- }
- if (unbind) {
- unbindAndFinish();
- }
- break;
- }
case MESSAGE_ENABLE:
if (DBG) {
Log.d(TAG, "MESSAGE_ENABLE: mBluetooth = " + mBluetooth);
@@ -1308,14 +1178,6 @@
Log.e(TAG,"Unable to call configHciSnoopLog", e);
}
- if (mConnection.isGetNameAddressOnly()) {
- //Request GET NAME AND ADDRESS
- Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
- mHandler.sendMessage(getMsg);
- if (!mEnable) return;
- }
-
- mConnection.setGetNameAddressOnly(false);
//Register callback object
try {
mBluetooth.registerCallback(mBluetoothCallback);
@@ -1412,25 +1274,23 @@
SERVICE_RESTART_TIME_MS);
}
- if (!mConnection.isGetNameAddressOnly()) {
- sendBluetoothServiceDownCallback();
+ sendBluetoothServiceDownCallback();
- // Send BT state broadcast to update
- // the BT icon correctly
- if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
- (mState == BluetoothAdapter.STATE_ON)) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
- BluetoothAdapter.STATE_TURNING_OFF);
- mState = BluetoothAdapter.STATE_TURNING_OFF;
- }
- if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
- bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
- BluetoothAdapter.STATE_OFF);
- }
-
- mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
- mState = BluetoothAdapter.STATE_OFF;
+ // Send BT state broadcast to update
+ // the BT icon correctly
+ if ((mState == BluetoothAdapter.STATE_TURNING_ON) ||
+ (mState == BluetoothAdapter.STATE_ON)) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
+ BluetoothAdapter.STATE_TURNING_OFF);
+ mState = BluetoothAdapter.STATE_TURNING_OFF;
}
+ if (mState == BluetoothAdapter.STATE_TURNING_OFF) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
+ BluetoothAdapter.STATE_OFF);
+ }
+
+ mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
+ mState = BluetoothAdapter.STATE_OFF;
break;
}
case MESSAGE_RESTART_BLUETOOTH_SERVICE:
@@ -1539,7 +1399,6 @@
//Start bind timeout and bind
Message timeoutMsg=mHandler.obtainMessage(MESSAGE_TIMEOUT_BIND);
mHandler.sendMessageDelayed(timeoutMsg,TIMEOUT_BIND_MS);
- mConnection.setGetNameAddressOnly(false);
Intent i = new Intent(IBluetooth.class.getName());
if (!doBind(i, mConnection,Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
UserHandle.CURRENT)) {
@@ -1548,21 +1407,6 @@
mBinding = true;
}
} else if (mBluetooth != null) {
- if (mConnection.isGetNameAddressOnly()) {
- // if GetNameAddressOnly is set, we can clear this flag,
- // so the service won't be unbind
- // after name and address are saved
- mConnection.setGetNameAddressOnly(false);
- //Register callback object
- try {
- mBluetooth.registerCallback(mBluetoothCallback);
- } catch (RemoteException re) {
- Log.e(TAG, "Unable to register BluetoothCallback",re);
- }
- //Inform BluetoothAdapter instances that service is up
- sendBluetoothServiceUpCallback();
- }
-
//Enable bluetooth
try {
if (!mQuietEnable) {
@@ -1594,9 +1438,7 @@
private void handleDisable() {
synchronized(mConnection) {
- // don't need to disable if GetNameAddressOnly is set,
- // service will be unbinded after Name and Address are saved
- if ((mBluetooth != null) && (!mConnection.isGetNameAddressOnly())) {
+ if (mBluetooth != null) {
if (DBG) Log.d(TAG,"Sending off request.");
try {
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
index 73e8c52..019d03d 100644
--- a/services/core/java/com/android/server/BluetoothService.java
+++ b/services/core/java/com/android/server/BluetoothService.java
@@ -31,14 +31,15 @@
@Override
public void onStart() {
- Log.d(TAG, "onStart: publishing BluetoothManagerService");
- publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
+ publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService);
+ } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+ Log.d(TAG, "onBootPhase: PHASE_ACTIVITY_MANAGER_READY");
mBluetoothManagerService.handleOnBootPhase();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0e5eadc..cde126a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -332,6 +332,11 @@
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+ // How long we will retain processes hosting content providers in the "last activity"
+ // state before allowing them to drop down to the regular cached LRU list. This is
+ // to avoid thrashing of provider processes under low memory situations.
+ static final int CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
+
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
@@ -9646,6 +9651,14 @@
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);
return true;
}
@@ -18400,6 +18413,18 @@
}
}
+ if (app.lastProviderTime > 0 && (app.lastProviderTime+CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+ adj = ProcessList.PREVIOUS_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.cached = false;
+ app.adjType = "provider";
+ }
+ if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ }
+ }
+
if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
// A client of one of our services or providers is in the top state. We
// *may* want to be in the top state, but not if we are already running in
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b77eec8..4bfe300 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -136,6 +136,7 @@
long curCpuTime; // How long proc has run CPU most recently
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
+ long lastProviderTime; // The last time someone else was using a provider in this process.
boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
boolean cached; // Is this a cached process?
@@ -317,6 +318,11 @@
pw.print(" foregroundActivities="); pw.print(foregroundActivities);
pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
}
+ if (lastProviderTime > 0) {
+ pw.print(prefix); pw.print("lastProviderTime=");
+ TimeUtils.formatDuration(lastProviderTime, now, pw);
+ pw.println();
+ }
if (hasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f54fd83..e264c43 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -291,16 +291,18 @@
final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
- // We need to use the visible frame on the window for any touch-related tests.
- // Can't use the task's bounds because the original task bounds might be adjusted
- // to fit the content frame. For example, the presence of the IME adjusting the
+ final WindowState win = task.getTopVisibleAppMainWindow();
+ if (win == null) {
+ continue;
+ }
+ // We need to use the task's dim bounds (which is derived from the visible
+ // bounds of its apps windows) for any touch-related tests. Can't use
+ // the task's original bounds because it might be adjusted to fit the
+ // content frame. For example, the presence of the IME adjusting the
// windows frames when the app window is the IME target.
- final WindowState win = task.getTopAppMainWindow();
- if (win != null) {
- win.getVisibleBounds(mTmpRect);
- if (mTmpRect.contains(x, y)) {
- return task.mTaskId;
- }
+ task.getDimBounds(mTmpRect);
+ if (mTmpRect.contains(x, y)) {
+ return task.mTaskId;
}
}
}
@@ -308,10 +310,10 @@
}
/**
- * Find the window whose outside touch area (for resizing) (x, y) falls within.
+ * Find the task whose outside touch area (for resizing) (x, y) falls within.
* Returns null if the touch doesn't fall into a resizing area.
*/
- WindowState findWindowForControlPoint(int x, int y) {
+ Task findTaskForControlPoint(int x, int y) {
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
@@ -325,24 +327,22 @@
return null;
}
- // We need to use the visible frame on the window for any touch-related
- // tests. Can't use the task's bounds because the original task bounds
- // might be adjusted to fit the content frame. (One example is when the
- // task is put to top-left quadrant, the actual visible frame would not
- // start at (0,0) after it's adjusted for the status bar.)
- final WindowState win = task.getTopAppMainWindow();
- if (win != null) {
- win.getVisibleBounds(mTmpRect);
- mTmpRect.inset(-delta, -delta);
- if (mTmpRect.contains(x, y)) {
- mTmpRect.inset(delta, delta);
- if (!mTmpRect.contains(x, y)) {
- return win;
- }
- // User touched inside the task. No need to look further,
- // focus transfer will be handled in ACTION_UP.
- return null;
+ // We need to use the task's dim bounds (which is derived from the visible
+ // bounds of its apps windows) for any touch-related tests. Can't use
+ // the task's original bounds because it might be adjusted to fit the
+ // content frame. One example is when the task is put to top-left quadrant,
+ // the actual visible area would not start at (0,0) after it's adjusted
+ // for the status bar.
+ task.getDimBounds(mTmpRect);
+ mTmpRect.inset(-delta, -delta);
+ if (mTmpRect.contains(x, y)) {
+ mTmpRect.inset(delta, delta);
+ if (!mTmpRect.contains(x, y)) {
+ return task;
}
+ // User touched inside the task. No need to look further,
+ // focus transfer will be handled in ACTION_UP.
+ return null;
}
}
}
@@ -351,12 +351,18 @@
void setTouchExcludeRegion(Task focusedTask) {
mTouchExcludeRegion.set(mBaseDisplayRect);
- WindowList windows = getWindowList();
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
- for (int i = windows.size() - 1; i >= 0; --i) {
- final WindowState win = windows.get(i);
- final Task task = win.getTask();
- if (win.isVisibleLw() && task != null) {
+ boolean addBackFocusedTask = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ TaskStack stack = mStacks.get(stackNdx);
+ final ArrayList<Task> tasks = stack.getTasks();
+ for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+ final Task task = tasks.get(taskNdx);
+ final WindowState win = task.getTopVisibleAppMainWindow();
+ if (win == null) {
+ continue;
+ }
+
/**
* Exclusion region is the region that TapDetector doesn't care about.
* Here we want to remove all non-focused tasks from the exclusion region.
@@ -368,13 +374,17 @@
*/
final boolean isFreeformed = task.inFreeformWorkspace();
if (task != focusedTask || isFreeformed) {
- mTmpRect.set(win.mVisibleFrame);
- mTmpRect.intersect(win.mVisibleInsets);
- /**
- * If the task is freeformed, enlarge the area to account for outside
- * touch area for resize.
- */
+ task.getDimBounds(mTmpRect);
if (isFreeformed) {
+ // If we're removing a freeform, focused app from the exclusion region,
+ // we need to add back its touchable frame later. Remember the touchable
+ // frame now.
+ if (task == focusedTask) {
+ addBackFocusedTask = true;
+ mTmpRect2.set(mTmpRect);
+ }
+ // If the task is freeformed, enlarge the area to account for outside
+ // touch area for resize.
mTmpRect.inset(-delta, -delta);
// Intersect with display content rect. If we have system decor (status bar/
// navigation bar), we want to exclude that from the tap detection.
@@ -385,17 +395,14 @@
}
mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
- /**
- * If we removed the focused task above, add it back and only leave its
- * outside touch area in the exclusion. TapDectector is not interested in
- * any touch inside the focused task itself.
- */
- if (task == focusedTask && isFreeformed) {
- mTmpRect.inset(delta, delta);
- mTouchExcludeRegion.op(mTmpRect, Region.Op.UNION);
- }
}
}
+ // If we removed the focused task above, add it back and only leave its
+ // outside touch area in the exclusion. TapDectector is not interested in
+ // any touch inside the focused task itself.
+ if (addBackFocusedTask) {
+ mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
+ }
if (mTapDetector != null) {
mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1b86488..46cd7cd 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -284,7 +284,12 @@
boolean getMaxVisibleBounds(Rect out) {
boolean foundTop = false;
for (int i = mAppTokens.size() - 1; i >= 0; i--) {
- final WindowState win = mAppTokens.get(i).findMainWindow();
+ final AppWindowToken token = mAppTokens.get(i);
+ // skip hidden (or about to hide) apps
+ if (token.mIsExiting || token.clientHidden || token.hiddenRequested) {
+ continue;
+ }
+ final WindowState win = token.findMainWindow();
if (win == null) {
continue;
}
@@ -413,14 +418,20 @@
return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
}
- WindowState getTopAppMainWindow() {
- final int tokensCount = mAppTokens.size();
- return tokensCount > 0 ? mAppTokens.get(tokensCount - 1).findMainWindow() : null;
+ WindowState getTopVisibleAppMainWindow() {
+ final AppWindowToken token = getTopVisibleAppToken();
+ return token != null ? token.findMainWindow() : null;
}
- AppWindowToken getTopAppWindowToken() {
- final int tokensCount = mAppTokens.size();
- return tokensCount > 0 ? mAppTokens.get(tokensCount - 1) : null;
+ AppWindowToken getTopVisibleAppToken() {
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ final AppWindowToken token = mAppTokens.get(i);
+ // skip hidden (or about to hide) apps
+ if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) {
+ return token;
+ }
+ }
+ return null;
}
@Override
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index dd47e7a..f5e4e3b 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -332,30 +332,34 @@
+ ", {" + startX + ", " + startY + "}");
}
mCtrlType = CTRL_NONE;
+ mTask = win.getTask();
+ mStartDragX = startX;
+ mStartDragY = startY;
+
+ // Use the dim bounds, not the original task bounds. The cursor
+ // movement should be calculated relative to the visible bounds.
+ // Also, use the dim bounds of the task which accounts for
+ // multiple app windows. Don't use any bounds from win itself as it
+ // may not be the same size as the task.
+ mTask.getDimBounds(mTmpRect);
+
if (resize) {
- final Rect visibleFrame = win.mVisibleFrame;
- if (startX < visibleFrame.left) {
+ if (startX < mTmpRect.left) {
mCtrlType |= CTRL_LEFT;
}
- if (startX > visibleFrame.right) {
+ if (startX > mTmpRect.right) {
mCtrlType |= CTRL_RIGHT;
}
- if (startY < visibleFrame.top) {
+ if (startY < mTmpRect.top) {
mCtrlType |= CTRL_TOP;
}
- if (startY > visibleFrame.bottom) {
+ if (startY > mTmpRect.bottom) {
mCtrlType |= CTRL_BOTTOM;
}
mResizing = true;
}
- mTask = win.getTask();
- mStartDragX = startX;
- mStartDragY = startY;
-
- // Use the visible bounds, not the original task bounds. The cursor
- // movement should be calculated relative to the visible bounds.
- mWindowOriginalBounds.set(win.mVisibleFrame);
+ mWindowOriginalBounds.set(mTmpRect);
}
private void endDragLocked() {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 1fe359e..f5b83bb 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -90,11 +90,11 @@
case MotionEvent.ACTION_HOVER_MOVE: {
final int x = (int) motionEvent.getX();
final int y = (int) motionEvent.getY();
- final WindowState window = mDisplayContent.findWindowForControlPoint(x, y);
- if (window == null) {
+ final Task task = mDisplayContent.findTaskForControlPoint(x, y);
+ if (task == null) {
break;
}
- window.getVisibleBounds(mTmpRect);
+ task.getDimBounds(mTmpRect);
if (!mTmpRect.isEmpty() && !mTmpRect.contains(x, y)) {
int iconShape = STYLE_DEFAULT;
if (x < mTmpRect.left) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f17698c..a22f821 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7069,15 +7069,16 @@
}
private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
- WindowState win = null;
+ Task task = null;
synchronized (mWindowMap) {
- win = displayContent.findWindowForControlPoint(startX, startY);
- if (win == null || !startPositioningLocked(win, true /*resize*/, startX, startY)) {
+ task = displayContent.findTaskForControlPoint(startX, startY);
+ if (task == null || !startPositioningLocked(
+ task.getTopVisibleAppMainWindow(), true /*resize*/, startX, startY)) {
return;
}
}
try {
- mActivityManager.setFocusedTask(win.getTask().mTaskId);
+ mActivityManager.setFocusedTask(task.mTaskId);
} catch(RemoteException e) {}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 93b40c0..673c21f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -729,8 +729,18 @@
mVisibleFrame.set(mContentFrame);
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
- mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
- mContentFrame.set(mFrame);
+ if (isVisibleLw()) {
+ // We don't adjust the dock divider frame for reasons other than performance. The
+ // real reason is that if it gets adjusted before it is shown for the first time,
+ // it would get size (0, 0). This causes a problem when we finally show the dock
+ // divider and try to draw to it. We do set the surface size at that moment to
+ // the correct size, but it's too late for the Surface Flinger to make it
+ // available for view rendering and as a result the renderer receives size 1, 1.
+ // This way we just keep the divider at the original size and Surface Flinger
+ // will return the correct value to the renderer.
+ mDisplayContent.getDockedDividerController().positionDockedStackedDivider(mFrame);
+ mContentFrame.set(mFrame);
+ }
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
Math.max(mContentFrame.top, mFrame.top),
@@ -1698,7 +1708,7 @@
Task task = getTask();
if (task == null || task.inHomeStack()
- || task.getTopAppWindowToken() != mAppToken) {
+ || task.getTopVisibleAppToken() != mAppToken) {
// Don't save surfaces for home stack apps. These usually resume and draw
// first frame very fast. Saving surfaces are mostly a waste of memory.
// Don't save if the window is not the topmost window.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1e32f60..6c2bd00 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4636,6 +4636,30 @@
}
}
+ @Override
+ public boolean setDeviceOwnerLockScreenInfo(ComponentName who, String info) {
+ Preconditions.checkNotNull(who, "ComponentName is null");
+ if (!mHasFeature) {
+ return false;
+ }
+
+ synchronized (this) {
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+ long token = mInjector.binderClearCallingIdentity();
+ try {
+ new LockPatternUtils(mContext).setDeviceOwnerInfo(info);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(token);
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public String getDeviceOwnerLockScreenInfo() {
+ return new LockPatternUtils(mContext).getDeviceOwnerInfo();
+ }
+
private void clearUserPoliciesLocked(UserHandle userHandle) {
int userId = userHandle.getIdentifier();
// Reset some of the user-specific policies
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 5db1bde..723e827 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -23,6 +23,7 @@
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.MockView;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil;
@@ -126,6 +127,9 @@
if (view == null) {
view = loadCustomView(name, attrs);
}
+ } catch (InflateException e) {
+ // Don't catch the InflateException below as that results in hiding the real cause.
+ throw e;
} catch (Exception e) {
// Wrap the real exception in a ClassNotFoundException, so that the calling method
// can deal with it.
@@ -154,23 +158,30 @@
}
ta.recycle();
}
- final Object lastContext = mConstructorArgs[0];
- mConstructorArgs[0] = context;
- // try to load the class from using the custom view loader
- try {
- view = loadCustomView(name, attrs);
- } catch (Exception e2) {
- // Wrap the real exception in an InflateException so that the calling
- // method can deal with it.
- InflateException exception = new InflateException();
- if (!e2.getClass().equals(ClassNotFoundException.class)) {
- exception.initCause(e2);
- } else {
- exception.initCause(e);
+ if (!(e.getCause() instanceof ClassNotFoundException)) {
+ // There is some unknown inflation exception in inflating a View that was found.
+ view = new MockView(context, attrs);
+ ((MockView) view).setText(name);
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null);
+ } else {
+ final Object lastContext = mConstructorArgs[0];
+ mConstructorArgs[0] = context;
+ // try to load the class from using the custom view loader
+ try {
+ view = loadCustomView(name, attrs);
+ } catch (Exception e2) {
+ // Wrap the real exception in an InflateException so that the calling
+ // method can deal with it.
+ InflateException exception = new InflateException();
+ if (!e2.getClass().equals(ClassNotFoundException.class)) {
+ exception.initCause(e2);
+ } else {
+ exception.initCause(e);
+ }
+ throw exception;
+ } finally {
+ mConstructorArgs[0] = lastContext;
}
- throw exception;
- } finally {
- mConstructorArgs[0] = lastContext;
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 44a9aad..d392f21 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -17,39 +17,90 @@
package com.android.layoutlib.bridge;
import android.content.Context;
-import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.TextView;
/**
* Base class for mocked views.
- *
- * TODO: implement onDraw and draw a rectangle in a random color with the name of the class
- * (or better the id of the view).
+ * <p/>
+ * FrameLayout with a single TextView. Doesn't allow adding any other views to itself.
*/
-public class MockView extends TextView {
+public class MockView extends FrameLayout {
+
+ private final TextView mView;
+
+ public MockView(Context context) {
+ this(context, null);
+ }
public MockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public MockView(Context context, AttributeSet attrs, int defStyle) {
- this(context, attrs, defStyle, 0);
+ public MockView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
}
public MockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
-
- setText(this.getClass().getSimpleName());
- setTextColor(0xFF000000);
+ mView = new TextView(context, attrs);
+ mView.setTextColor(0xFF000000);
setGravity(Gravity.CENTER);
+ setText(getClass().getSimpleName());
+ addView(mView);
+ setBackgroundColor(0xFF7F7F7F);
+ }
+
+ // Only allow adding one TextView.
+ @Override
+ public void addView(View child) {
+ if (child == mView) {
+ super.addView(child);
+ }
}
@Override
- public void onDraw(Canvas canvas) {
- canvas.drawARGB(0xFF, 0x7F, 0x7F, 0x7F);
+ public void addView(View child, int index) {
+ if (child == mView) {
+ super.addView(child, index);
+ }
+ }
- super.onDraw(canvas);
+ @Override
+ public void addView(View child, int width, int height) {
+ if (child == mView) {
+ super.addView(child, width, height);
+ }
+ }
+
+ @Override
+ public void addView(View child, ViewGroup.LayoutParams params) {
+ if (child == mView) {
+ super.addView(child, params);
+ }
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params) {
+ if (child == mView) {
+ super.addView(child, index, params);
+ }
+ }
+
+ // The following methods are called by the IDE via reflection, and should be considered part
+ // of the API.
+ // Historically, MockView used to be a textView and had these methods. Now, we simply delegate
+ // them to the contained textView.
+
+ public void setText(CharSequence text) {
+ mView.setText(text);
+ }
+
+ public void setGravity(int gravity) {
+ mView.setGravity(gravity);
}
}