Merge "Revert "Update StaticLayout_Delegate to use new nLoadHyphenator"" into mnc-ub-dev
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 371c923..c075ed6 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -24,8 +24,6 @@
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.SurfaceTexture;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.OperationCanceledException;
@@ -45,6 +43,17 @@
import dalvik.system.CloseGuard;
import java.lang.ref.WeakReference;
+import java.util.ArrayDeque;
+import java.util.concurrent.Executor;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.android.internal.annotations.GuardedBy;
+
/** @hide */
public class ActivityView extends ViewGroup {
@@ -53,9 +62,64 @@
private static final int MSG_SET_SURFACE = 1;
- DisplayMetrics mMetrics = new DisplayMetrics();
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final int MINIMUM_POOL_SIZE = 1;
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE = 1;
+
+ private static final ThreadFactory sThreadFactory = new ThreadFactory() {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(Runnable r) {
+ return new Thread(r, "ActivityView #" + mCount.getAndIncrement());
+ }
+ };
+
+ private static final BlockingQueue<Runnable> sPoolWorkQueue =
+ new LinkedBlockingQueue<Runnable>(128);
+
+ /**
+ * An {@link Executor} that can be used to execute tasks in parallel.
+ */
+ private static final Executor sExecutor = new ThreadPoolExecutor(MINIMUM_POOL_SIZE,
+ MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
+
+
+ private static class SerialExecutor implements Executor {
+ private final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
+ private Runnable mActive;
+
+ public synchronized void execute(final Runnable r) {
+ mTasks.offer(new Runnable() {
+ public void run() {
+ try {
+ r.run();
+ } finally {
+ scheduleNext();
+ }
+ }
+ });
+ if (mActive == null) {
+ scheduleNext();
+ }
+ }
+
+ protected synchronized void scheduleNext() {
+ if ((mActive = mTasks.poll()) != null) {
+ sExecutor.execute(mActive);
+ }
+ }
+ }
+
+ private final SerialExecutor mExecutor = new SerialExecutor();
+
+ private final int mDensityDpi;
private final TextureView mTextureView;
+
+ @GuardedBy("mActivityContainerLock")
private ActivityContainerWrapper mActivityContainer;
+ private Object mActivityContainerLock = new Object();
+
private Activity mActivity;
private int mWidth;
private int mHeight;
@@ -63,8 +127,6 @@
private int mLastVisibility;
private ActivityViewCallback mActivityViewCallback;
- private HandlerThread mThread = new HandlerThread("ActivityViewThread");
- private Handler mHandler;
public ActivityView(Context context) {
this(context, null);
@@ -97,28 +159,14 @@
+ e);
}
- mThread.start();
- mHandler = new Handler(mThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- if (msg.what == MSG_SET_SURFACE) {
- try {
- mActivityContainer.setSurface((Surface) msg.obj, msg.arg1, msg.arg2,
- mMetrics.densityDpi);
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
- }
- }
- };
mTextureView = new TextureView(context);
mTextureView.setSurfaceTextureListener(new ActivityViewSurfaceTextureListener());
addView(mTextureView);
WindowManager wm = (WindowManager)mActivity.getSystemService(Context.WINDOW_SERVICE);
- wm.getDefaultDisplay().getMetrics(mMetrics);
+ DisplayMetrics metrics = new DisplayMetrics();
+ wm.getDefaultDisplay().getMetrics(metrics);
+ mDensityDpi = metrics.densityDpi;
mLastVisibility = getVisibility();
@@ -131,15 +179,13 @@
}
@Override
- protected void onVisibilityChanged(View changedView, int visibility) {
+ protected void onVisibilityChanged(View changedView, final int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (mSurface != null && (visibility == View.GONE || mLastVisibility == View.GONE)) {
- Message msg = Message.obtain(mHandler, MSG_SET_SURFACE);
- msg.obj = (visibility == View.GONE) ? null : mSurface;
- msg.arg1 = mWidth;
- msg.arg2 = mHeight;
- mHandler.sendMessage(msg);
+ if (DEBUG) Log.v(TAG, "visibility changed; enqueing runnable");
+ final Surface surface = (visibility == View.GONE) ? null : mSurface;
+ setSurfaceAsync(surface, mWidth, mHeight, mDensityDpi, false);
}
mLastVisibility = visibility;
}
@@ -230,8 +276,10 @@
Log.e(TAG, "Duplicate call to release");
return;
}
- mActivityContainer.release();
- mActivityContainer = null;
+ synchronized (mActivityContainerLock) {
+ mActivityContainer.release();
+ mActivityContainer = null;
+ }
if (mSurface != null) {
mSurface.release();
@@ -239,25 +287,39 @@
}
mTextureView.setSurfaceTextureListener(null);
-
- mThread.quit();
}
- private void attachToSurfaceWhenReady() {
- final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
- if (surfaceTexture == null || mSurface != null) {
- // Either not ready to attach, or already attached.
- return;
- }
-
- mSurface = new Surface(surfaceTexture);
- try {
- mActivityContainer.setSurface(mSurface, mWidth, mHeight, mMetrics.densityDpi);
- } catch (RemoteException e) {
- mSurface.release();
- mSurface = null;
- throw new RuntimeException("ActivityView: Unable to create ActivityContainer. " + e);
- }
+ private void setSurfaceAsync(final Surface surface, final int width, final int height,
+ final int densityDpi, final boolean callback) {
+ mExecutor.execute(new Runnable() {
+ public void run() {
+ try {
+ synchronized (mActivityContainerLock) {
+ if (mActivityContainer != null) {
+ mActivityContainer.setSurface(surface, width, height, densityDpi);
+ }
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(
+ "ActivityView: Unable to set surface of ActivityContainer. ",
+ e);
+ }
+ if (callback) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ if (mActivityViewCallback != null) {
+ if (surface != null) {
+ mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
+ } else {
+ mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
+ }
+ }
+ }
+ });
+ }
+ }
+ });
}
/**
@@ -308,10 +370,8 @@
+ height);
mWidth = width;
mHeight = height;
- attachToSurfaceWhenReady();
- if (mActivityViewCallback != null) {
- mActivityViewCallback.onSurfaceAvailable(ActivityView.this);
- }
+ mSurface = new Surface(surfaceTexture);
+ setSurfaceAsync(mSurface, mWidth, mHeight, mDensityDpi, true);
}
@Override
@@ -331,15 +391,7 @@
if (DEBUG) Log.d(TAG, "onSurfaceTextureDestroyed");
mSurface.release();
mSurface = null;
- try {
- mActivityContainer.setSurface(null, mWidth, mHeight, mMetrics.densityDpi);
- } catch (RemoteException e) {
- throw new RuntimeException(
- "ActivityView: Unable to set surface of ActivityContainer. " + e);
- }
- if (mActivityViewCallback != null) {
- mActivityViewCallback.onSurfaceDestroyed(ActivityView.this);
- }
+ setSurfaceAsync(null, mWidth, mHeight, mDensityDpi, true);
return true;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 21060e4..4acfa29 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3966,6 +3966,9 @@
return this;
}
+ /** @hide */
+ public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10);
+
/**
* @hide
*/
@@ -3974,7 +3977,7 @@
super.purgeResources();
if (mPicture != null &&
mPicture.isMutable() &&
- mPicture.getAllocationByteCount() >= (128 * (1 << 10))) {
+ mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) {
mPicture = mPicture.createAshmemBitmap();
}
if (mBigLargeIcon != null) {
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 25d9aa9..09a15de 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -685,6 +685,48 @@
}
/**
+ * Sets whether audio routing is allowed. When set to {@code false}, the AG will not route any
+ * audio to the HF unless explicitly told to.
+ * This method should be used in cases where the SCO channel is shared between multiple profiles
+ * and must be delegated by a source knowledgeable
+ * Note: This is an internal function and shouldn't be exposed
+ *
+ * @param allowed {@code true} if the profile can reroute audio, {@code false} otherwise.
+ *
+ * @hide
+ */
+ public void setAudioRouteAllowed(boolean allowed) {
+ if (VDBG) log("setAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ mService.setAudioRouteAllowed(allowed);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Returns whether audio routing is allowed. see {@link #setAudioRouteAllowed(boolean)}.
+ * Note: This is an internal function and shouldn't be exposed
+ *
+ * @hide
+ */
+ public boolean getAudioRouteAllowed() {
+ if (VDBG) log("getAudioRouteAllowed");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getAudioRouteAllowed();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
* Check if Bluetooth SCO audio is connected.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 0e23fad..0bb4088 100755
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -50,6 +50,8 @@
boolean isAudioOn();
boolean connectAudio();
boolean disconnectAudio();
+ void setAudioRouteAllowed(boolean allowed);
+ boolean getAudioRouteAllowed();
boolean startScoUsingVirtualVoiceCall(in BluetoothDevice device);
boolean stopScoUsingVirtualVoiceCall(in BluetoothDevice device);
void phoneStateChanged(int numActive, int numHeld, int callState, String number, int type);
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 1fc69c0..73bb426 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -3182,8 +3182,8 @@
}
/**
- * Sets GPS processing method. It will store up to 32 characters
- * in JPEG EXIF header.
+ * Sets GPS processing method. The method will be stored in a UTF-8 string up to 31 bytes
+ * long, in the JPEG EXIF header.
*
* @param processing_method The processing method to get this location.
*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 35a1d96..f61892e 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -472,13 +472,13 @@
* <li>The maximum available resolution for RAW_SENSOR streams
* will match either the value in
* {@link CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE android.sensor.info.pixelArraySize} or
- * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}.</li>
+ * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}.</li>
* <li>All DNG-related optional metadata entries are provided
* by the camera device.</li>
* </ul>
*
- * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#SENSOR_INFO_PIXEL_ARRAY_SIZE
+ * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 3f566eb..67835a0 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -556,6 +556,10 @@
* Set a capture request field to a value. The field definitions can be
* found in {@link CaptureRequest}.
*
+ * <p>Setting a field to {@code null} will remove that field from the capture request.
+ * Unless the field is optional, removing it will likely produce an error from the camera
+ * device when the request is submitted.</p>
+ *
* @param key The metadata field to write.
* @param value The value to set the field to, which must be of a matching
* type to the key.
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 6cbdeaa..f5fe086 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1006,6 +1006,7 @@
// is disposed.
int dupFd = dup(blob.fd());
if (dupFd < 0) {
+ ALOGE("Error allocating dup fd. Error:%d", errno);
blob.release();
SkSafeUnref(ctable);
doThrowRE(env, "Could not allocate dup blob fd.");
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index c1ccef0..21e3057 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -562,10 +562,11 @@
<tr>
<td rowspan="6">Camera</td>
<td><code>android.hardware.camera</code></td>
- <td>The application uses the device's camera. If the device supports
- multiple cameras, the application uses the camera that facing
- away from the screen.</td>
- <td></td>
+ <td>The application uses the device's back-facing (main) camera.</td>
+ <td>Devices with only a front-facing camera do not list this feature, so the
+ <code>android.hardware.camera.any</code> feature should be
+ used instead if a camera facing any direction is acceptable for the
+ application.</td>
</tr>
<tr>
<td><code>android.hardware.camera.autofocus</code></td>
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index 44d7530..d800acb 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -377,6 +377,9 @@
return loadDrawable(context);
}
+ /** @hide */
+ public static final int MIN_ASHMEM_ICON_SIZE = 128 * (1 << 10);
+
/**
* Puts the memory used by this instance into Ashmem memory, if possible.
* @hide
@@ -384,7 +387,7 @@
public void convertToAshmem() {
if (mType == TYPE_BITMAP &&
getBitmap().isMutable() &&
- getBitmap().getAllocationByteCount() >= (128 * (1 << 10))) {
+ getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) {
setBitmap(getBitmap().createAshmemBitmap());
}
}
diff --git a/libs/hwui/Android.common.mk b/libs/hwui/Android.common.mk
index c80f2cf..38e8be9 100644
--- a/libs/hwui/Android.common.mk
+++ b/libs/hwui/Android.common.mk
@@ -1,9 +1,6 @@
# getConfig in external/skia/include/core/SkBitmap.h is deprecated.
# Allow Gnu extension: in-class initializer of static 'const float' member.
# DeferredLayerUpdater.h: private field 'mRenderThread' is not used.
-
-LOCAL_CLANG := true
-
LOCAL_CLANG_CFLAGS += \
-Wno-deprecated-declarations \
-Wno-gnu-static-float-init \
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 0951fc1..642ec25 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -666,7 +666,9 @@
void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
if (properties().getAlpha() <= 0.0f
|| properties().getOutline().getAlpha() <= 0.0f
- || !properties().getOutline().getPath()) {
+ || !properties().getOutline().getPath()
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
// no shadow to draw
return;
}
@@ -856,7 +858,10 @@
const bool useViewProperties = (!mLayer || drawLayer);
if (useViewProperties) {
const Outline& outline = properties().getOutline();
- if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ if (properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
this, getName());
return;
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 09d1258..6c8665b 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -77,9 +77,14 @@
}
#if DEBUG_SHADOW
- ALOGD("light center %f %f %f",
- adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+ ALOGD("light center %f %f %f %d",
+ adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
#endif
+ if (isnan(adjustedLightCenter.x)
+ || isnan(adjustedLightCenter.y)
+ || isnan(adjustedLightCenter.z)) {
+ return;
+ }
// light position (because it's in local space) needs to compensate for receiver transform
// TODO: should apply to light orientation, not just position
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index b8c9804..7a2b9af 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -1052,7 +1052,7 @@
*/
void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) {
for (int i = 0; i < polyLength; i++) {
- ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
+ ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 006f6e9..0d326ec 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -19,7 +19,10 @@
import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE;
import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE_ALL;
import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
+import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
import static com.android.documentsui.BaseActivity.State.MODE_GRID;
import static com.android.documentsui.BaseActivity.State.MODE_LIST;
import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
@@ -578,11 +581,23 @@
// Directories and footer items cannot be checked
boolean valid = false;
+ final State state = getDisplayState(DirectoryFragment.this);
final Cursor cursor = mAdapter.getItem(position);
if (cursor != null) {
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
- valid = isDocumentEnabled(docMimeType, docFlags);
+ switch (state.action) {
+ case ACTION_OPEN:
+ case ACTION_CREATE:
+ case ACTION_GET_CONTENT:
+ case ACTION_OPEN_TREE:
+ valid = isDocumentEnabled(docMimeType, docFlags)
+ && !Document.MIME_TYPE_DIR.equals(docMimeType);
+ break;
+ default:
+ valid = isDocumentEnabled(docMimeType, docFlags);
+ break;
+ }
}
if (!valid) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e4b1ed8..d994841 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -808,7 +808,12 @@
// The pairing dialog now warns of phone-book access for paired devices.
// No separate prompt is displayed after pairing.
if (getPhonebookPermissionChoice() == CachedBluetoothDevice.ACCESS_UNKNOWN) {
- setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
+ if (mDevice.getBluetoothClass().getDeviceClass()
+ == BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE) {
+ setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED);
+ } else {
+ setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED);
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index f1550a0..3917bab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -34,6 +34,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.util.MutableBoolean;
import android.view.Display;
import android.view.LayoutInflater;
@@ -280,6 +281,12 @@
@ProxyFromPrimaryToCurrentUser
@Override
public void showRecents(boolean triggeredFromAltTab, View statusBarView) {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (mSystemServicesProxy.isForegroundUserOwner()) {
showRecentsInternal(triggeredFromAltTab);
} else {
@@ -304,6 +311,12 @@
@ProxyFromPrimaryToCurrentUser
@Override
public void hideRecents(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (mSystemServicesProxy.isForegroundUserOwner()) {
hideRecentsInternal(triggeredFromAltTab, triggeredFromHomeKey);
} else {
@@ -330,6 +343,12 @@
@ProxyFromPrimaryToCurrentUser
@Override
public void toggleRecents(Display display, int layoutDirection, View statusBarView) {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (mSystemServicesProxy.isForegroundUserOwner()) {
toggleRecentsInternal();
} else {
@@ -353,6 +372,12 @@
@ProxyFromPrimaryToCurrentUser
@Override
public void preloadRecents() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
if (mSystemServicesProxy.isForegroundUserOwner()) {
preloadRecentsInternal();
} else {
@@ -469,6 +494,12 @@
@Override
public void showNextAffiliatedTask() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
// Keep track of when the affiliated task is triggered
MetricsLogger.count(mContext, "overview_affiliated_task_next", 1);
showRelativeAffiliatedTask(true);
@@ -476,6 +507,12 @@
@Override
public void showPrevAffiliatedTask() {
+ // Ensure the device has been provisioned before allowing the user to interact with
+ // recents
+ if (!isDeviceProvisioned()) {
+ return;
+ }
+
// Keep track of when the affiliated task is triggered
MetricsLogger.count(mContext, "overview_affiliated_task_prev", 1);
showRelativeAffiliatedTask(false);
@@ -888,6 +925,14 @@
}
/**
+ * @return whether this device is provisioned.
+ */
+ private boolean isDeviceProvisioned() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) != 0;
+ }
+
+ /**
* Returns the preloaded load plan and invalidates it.
*/
public static RecentsTaskLoadPlan consumeInstanceLoadPlan() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4e49312..c1d091b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15986,13 +15986,18 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
final int moveId = mNextMoveId.getAndIncrement();
- try {
- movePackageInternal(packageName, volumeUuid, moveId);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to move " + packageName, e);
- mMoveCallbacks.notifyStatusChanged(moveId,
- PackageManager.MOVE_FAILED_INTERNAL_ERROR);
- }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ movePackageInternal(packageName, volumeUuid, moveId);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to move " + packageName, e);
+ mMoveCallbacks.notifyStatusChanged(moveId,
+ PackageManager.MOVE_FAILED_INTERNAL_ERROR);
+ }
+ }
+ });
return moveId;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ace2505..0cbf614 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5265,11 +5265,9 @@
}
private boolean shouldDispatchInputWhenNonInteractive() {
- if (mDisplay == null || mDisplay.getState() == Display.STATE_OFF) {
- return false;
- }
- // Send events to keyguard while the screen is on and it's showing.
- if (isKeyguardShowingAndNotOccluded()) {
+ // Send events to keyguard while the screen is on.
+ if (isKeyguardShowingAndNotOccluded() && mDisplay != null
+ && mDisplay.getState() != Display.STATE_OFF) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4db0b1e..7774057 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -116,6 +116,7 @@
display.getDisplayInfo(mDisplayInfo);
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
+ initializeDisplayBaseInfo();
}
int getDisplayId() {
@@ -176,6 +177,21 @@
}
}
+ void initializeDisplayBaseInfo() {
+ synchronized(mDisplaySizeLock) {
+ // Bootstrap the default logical display from the display manager.
+ final DisplayInfo newDisplayInfo =
+ mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+ if (newDisplayInfo != null) {
+ mDisplayInfo.copyFrom(newDisplayInfo);
+ }
+ mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
+ mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
+ mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
+ mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+ }
+ }
+
void getLogicalDisplayRect(Rect out) {
// Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
final int orientation = mDisplayInfo.rotation;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d4e51b2..c40947b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7670,22 +7670,7 @@
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
- synchronized(displayContent.mDisplaySizeLock) {
- // Bootstrap the default logical display from the display manager.
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
- if (newDisplayInfo != null) {
- displayInfo.copyFrom(newDisplayInfo);
- }
- displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
- displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
- displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
- displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
- displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
- displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
- displayContent.mBaseDisplayRect.set(0, 0,
- displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);
- }
+ displayContent.initializeDisplayBaseInfo();
}
}
}
diff --git a/tools/layoutlib/.idea/encodings.xml b/tools/layoutlib/.idea/encodings.xml
index e206d70..f758959 100644
--- a/tools/layoutlib/.idea/encodings.xml
+++ b/tools/layoutlib/.idea/encodings.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
- <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
-</project>
-
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
+ <file url="PROJECT" charset="UTF-8" />
+ </component>
+</project>
\ No newline at end of file
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);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index b2dc29a..f2d214c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -409,7 +409,7 @@
pushParser(blockParser);
return Pair.of(
mBridgeInflater.inflate(blockParser, parent, attachToRoot),
- true);
+ Boolean.TRUE);
} finally {
popParser();
}
@@ -447,7 +447,7 @@
pushParser(blockParser);
return Pair.of(
mBridgeInflater.inflate(blockParser, parent, attachToRoot),
- false);
+ Boolean.FALSE);
} finally {
popParser();
}
@@ -470,7 +470,7 @@
resource.getName()), null);
}
- return Pair.of(null, false);
+ return Pair.of(null, Boolean.FALSE);
}
@SuppressWarnings("deprecation")
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
index 868c6d3..cdcf0ea 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/AppCompatActionBar.java
@@ -16,10 +16,13 @@
package com.android.layoutlib.bridge.bars;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.SessionParams;
import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.ResourceType;
@@ -45,6 +48,8 @@
private Object mWindowDecorActionBar;
private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
+ // This is used on v23.1.1 and later.
+ private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar";
private Class<?> mWindowActionBarClass;
/**
@@ -70,14 +75,25 @@
try {
Class[] constructorParams = {View.class};
Object[] constructorArgs = {getDecorContent()};
- mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS,
- constructorParams, constructorArgs);
+ LayoutlibCallback callback = params.getLayoutlibCallback();
+ // Check if the old action bar class is present.
+ String actionBarClass = WINDOW_ACTION_BAR_CLASS;
+ try {
+ callback.findClass(actionBarClass);
+ } catch (ClassNotFoundException expected) {
+ // Failed to find the old class, use the newer one.
+ actionBarClass = WINDOW_ACTION_BAR_CLASS_NEW;
+ }
+
+ mWindowDecorActionBar = callback.loadView(actionBarClass,
+ constructorParams, constructorArgs);
mWindowActionBarClass = mWindowDecorActionBar == null ? null :
mWindowDecorActionBar.getClass();
setupActionBar();
} catch (Exception e) {
- e.printStackTrace();
+ Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
+ "Failed to load AppCompat ActionBar with unknown error.", e);
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 567002e..a6e5fb8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,7 +33,6 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -117,11 +116,11 @@
density = iconLoader.getDensity();
String path = iconLoader.getPath();
// look for a cached bitmap
- Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/);
+ Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
if (bitmap == null) {
try {
bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
- Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/);
+ Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
} catch (IOException e) {
return;
}
@@ -228,18 +227,16 @@
* Find the background color for this bar from the theme attributes. Only relevant to StatusBar
* and NavigationBar.
* <p/>
- * Returns null if not found.
+ * Returns 0 if not found.
*
* @param colorAttrName the attribute name for the background color
* @param translucentAttrName the attribute name for the translucency property of the bar.
*
* @throws NumberFormatException if color resolved to an invalid string.
*/
- @Nullable
- protected Integer getBarColor(@NonNull String colorAttrName,
- @NonNull String translucentAttrName) {
+ protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
- return null;
+ return 0;
}
RenderResources renderResources = getContext().getRenderResources();
// First check if the bar is translucent.
@@ -254,11 +251,10 @@
if (transparent) {
return getColor(renderResources, colorAttrName);
}
- return null;
+ return 0;
}
- @Nullable
- private static Integer getColor(RenderResources renderResources, String attr) {
+ private static int getColor(RenderResources renderResources, String attr) {
// From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
ResourceValue resource = renderResources.findItemInTheme(attr, true);
// Form @color/bar to the #AARRGGBB
@@ -279,7 +275,7 @@
}
}
}
- return null;
+ return 0;
}
private ResourceValue getResourceValue(String reference) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index d50ce23..9c89bfe2 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -65,8 +65,8 @@
super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
"navigation_bar.xml", simulatedPlatformVersion);
- Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(color == null ? 0xFF000000 : color);
+ int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(color == 0 ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 95a5a58..2dc7c65 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -71,9 +71,8 @@
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(
- color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
+ int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
index 9588035..80d7c68 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -21,6 +21,7 @@
import android.graphics.BlendComposite;
import android.graphics.BlendComposite.BlendingMode;
+import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter_Delegate;
import android.graphics.PorterDuffXfermode_Delegate;
@@ -34,6 +35,8 @@
*/
public final class PorterDuffUtility {
+ private static final int MODES_COUNT = Mode.values().length;
+
// Make the class non-instantiable.
private PorterDuffUtility() {
}
@@ -43,12 +46,11 @@
* {@link Mode#SRC_OVER} for invalid modes.
*/
public static Mode getPorterDuffMode(int porterDuffMode) {
- Mode[] values = Mode.values();
- if (porterDuffMode >= 0 && porterDuffMode < values.length) {
- return values[porterDuffMode];
+ if (porterDuffMode >= 0 && porterDuffMode < MODES_COUNT) {
+ return PorterDuff.intToMode(porterDuffMode);
}
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/);
+ String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null);
assert false;
return Mode.SRC_OVER;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 2a4f583..0ffa357 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -421,7 +421,8 @@
gc.setComposite(AlphaComposite.Src);
gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
+ gc.fillRect(0, 0,
+ mMeasuredScreenWidth, mMeasuredScreenHeight);
// done
gc.dispose();
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index ae4a57d..7ef7566 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -17,6 +17,7 @@
package com.android.tools.layoutlib.create;
import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -40,6 +41,7 @@
private final String mClassName;
private final Set<String> mDelegateMethods;
private final Log mLog;
+ private boolean mIsStaticInnerClass;
/**
* Creates a new {@link DelegateClassAdapter} that can transform some methods
@@ -62,16 +64,30 @@
mLog = log;
mClassName = className;
mDelegateMethods = delegateMethods;
+ // If this is an inner class, by default, we assume it's static. If it's not we will detect
+ // by looking at the fields (see visitField)
+ mIsStaticInnerClass = className.contains("$");
}
//----------------------------------
// Methods from the ClassAdapter
@Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ if (mIsStaticInnerClass && "this$0".equals(name)) {
+ // Having a "this$0" field, proves that this class is not a static inner class.
+ mIsStaticInnerClass = false;
+ }
+
+ return super.visitField(access, name, desc, signature, value);
+ }
+
+ @Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
- boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
+ boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;
boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
@@ -96,7 +112,8 @@
MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);
DelegateMethodAdapter a = new DelegateMethodAdapter(
- mLog, null, mwDelegate, mClassName, name, desc, isStatic);
+ mLog, null, mwDelegate, mClassName, name, desc, isStaticMethod,
+ mIsStaticInnerClass);
// A native has no code to visit, so we need to generate it directly.
a.generateDelegateCode();
@@ -120,6 +137,7 @@
desc, signature, exceptions);
return new DelegateMethodAdapter(
- mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
+ mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStaticMethod,
+ mIsStaticInnerClass);
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index 12690db..cca9e57 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -85,6 +85,8 @@
private String mDesc;
/** True if the original method is static. */
private final boolean mIsStatic;
+ /** True if the method is contained in a static inner class */
+ private final boolean mIsStaticInnerClass;
/** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
private final String mClassName;
/** The method name. */
@@ -120,7 +122,8 @@
String className,
String methodName,
String desc,
- boolean isStatic) {
+ boolean isStatic,
+ boolean isStaticClass) {
super(Opcodes.ASM4);
mLog = log;
mOrgWriter = mvOriginal;
@@ -129,6 +132,7 @@
mMethodName = methodName;
mDesc = desc;
mIsStatic = isStatic;
+ mIsStaticInnerClass = isStaticClass;
}
/**
@@ -206,7 +210,7 @@
// by the 'this' of any outer class, if any.
if (!mIsStatic) {
- if (outerType != null) {
+ if (outerType != null && !mIsStaticInnerClass) {
// The first-level inner class has a package-protected member called 'this$0'
// that points to the outer class.
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index 648cea43..e37a09b 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -27,6 +27,7 @@
import com.android.tools.layoutlib.create.dataclass.ClassWithNative;
import com.android.tools.layoutlib.create.dataclass.OuterClass;
import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
import org.junit.Before;
import org.junit.Test;
@@ -56,6 +57,8 @@
private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName();
private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" +
InnerClass.class.getSimpleName();
+ private static final String STATIC_INNER_CLASS_NAME =
+ OuterClass.class.getCanonicalName() + "$" + StaticInnerClass.class.getSimpleName();
@Before
public void setUp() throws Exception {
@@ -294,6 +297,61 @@
}
}
+ @Test
+ public void testDelegateStaticInner() throws Throwable {
+ // We'll delegate the "get" method of both the inner and outer class.
+ HashSet<String> delegateMethods = new HashSet<String>();
+ delegateMethods.add("get");
+
+ // Generate the delegate for the outer class.
+ ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
+ String outerClassName = OUTER_CLASS_NAME.replace('.', '/');
+ DelegateClassAdapter cvOuter = new DelegateClassAdapter(
+ mLog, cwOuter, outerClassName, delegateMethods);
+ ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
+ cr.accept(cvOuter, 0 /* flags */);
+
+ // Generate the delegate for the static inner class.
+ ClassWriter cwInner = new ClassWriter(0 /*flags*/);
+ String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/');
+ DelegateClassAdapter cvInner = new DelegateClassAdapter(
+ mLog, cwInner, innerClassName, delegateMethods);
+ cr = new ClassReader(STATIC_INNER_CLASS_NAME);
+ cr.accept(cvInner, 0 /* flags */);
+
+ // Load the generated classes in a different class loader and try them
+ ClassLoader2 cl2 = null;
+ try {
+ cl2 = new ClassLoader2() {
+ @Override
+ public void testModifiedInstance() throws Exception {
+
+ // Check the outer class
+ Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME);
+ Object o2 = outerClazz2.newInstance();
+ assertNotNull(o2);
+
+ // Check the inner class. Since it's not a static inner class, we need
+ // to use the hidden constructor that takes the outer class as first parameter.
+ Class<?> innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME);
+ Constructor<?> innerCons = innerClazz2.getConstructor();
+ Object i2 = innerCons.newInstance();
+ assertNotNull(i2);
+
+ // The original StaticInner.get returns 100+10+20,
+ // but the delegate makes it return 6+10+20
+ assertEquals(6+10+20, callGet(i2, 10, 20));
+ assertEquals(100+10+20, callGet_Original(i2, 10, 20));
+ }
+ };
+ cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
+ cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray());
+ cl2.testModifiedInstance();
+ } catch (Throwable t) {
+ throw dumpGeneratedClass(t, cl2);
+ }
+ }
+
//-------
/**
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
index f083e76..6dfb816 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java
@@ -45,6 +45,16 @@
}
}
+ public static class StaticInnerClass {
+ public StaticInnerClass() {
+ }
+
+ // StaticInnerClass.get returns 100 + a + b
+ public int get(int a, long b) {
+ return 100 + a + (int) b;
+ }
+ }
+
@SuppressWarnings("unused")
private String privateMethod() {
return "outerPrivateMethod";
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
new file mode 100644
index 0000000..a29439e
--- /dev/null
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_StaticInnerClass_Delegate.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.layoutlib.create.dataclass;
+
+import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
+import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;
+
+/**
+ * Used by {@link DelegateClassAdapterTest}.
+ */
+public class OuterClass_StaticInnerClass_Delegate {
+ // The delegate override of Inner.get return 6 + a + b
+ public static int get(StaticInnerClass inner, int a, long b) {
+ return 6 + a + (int) b;
+ }
+}