diff options
22 files changed, 457 insertions, 126 deletions
diff --git a/api/current.txt b/api/current.txt index 5dee15e6eed1..e5156e8328c4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2697,7 +2697,8 @@ package android.app { method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams); method public void closeContextMenu(); method public void closeOptionsMenu(); - method public void convertToOpaque(); + method public void convertFromTranslucent(); + method public void convertToTranslucent(android.app.Activity.TranslucentConversionListener); method public android.app.PendingIntent createPendingResult(int, android.content.Intent, int); method public final deprecated void dismissDialog(int); method public boolean dispatchGenericMotionEvent(android.view.MotionEvent); @@ -2883,6 +2884,10 @@ package android.app { field public static final int RESULT_OK = -1; // 0xffffffff } + public static abstract interface Activity.TranslucentConversionListener { + method public abstract void onTranslucentConversionComplete(boolean); + } + public deprecated class ActivityGroup extends android.app.Activity { ctor public ActivityGroup(); ctor public ActivityGroup(boolean); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d1efd0d18b26..fa746ba0706f 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -745,6 +745,7 @@ public class Activity extends ContextThemeWrapper // protected by synchronized (this) int mResultCode = RESULT_CANCELED; Intent mResultData = null; + private TranslucentConversionListener mTranslucentCallback; private boolean mTitleReady = false; @@ -1382,6 +1383,7 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); getApplication().dispatchActivityStopped(this); + mTranslucentCallback = null; mCalled = true; } @@ -4886,22 +4888,61 @@ public class Activity extends ContextThemeWrapper /** * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a * fullscreen opaque Activity. - * + * <p> * Call this whenever the background of a translucent Activity has changed to become opaque. - * Doing so will allow the previously visible Activity behind this one to be stopped. Stopped - * apps consume no CPU cycles and are eligible for removal when reclaiming memory. + * Doing so will allow the {@link android.view.Surface} of the Activity behind to be released. + * <p> + * This call has no effect on non-translucent activities or on activities with the + * {@link android.R.attr#windowIsFloating} attribute. * + * @see #convertToTranslucent(TranslucentConversionListener) + * @see TranslucentConversionListener + */ + public void convertFromTranslucent() { + try { + mTranslucentCallback = null; + ActivityManagerNative.getDefault().convertFromTranslucent(mToken); + } catch (RemoteException e) { + // pass + } + } + + /** + * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} back from + * opaque to translucent following a call to {@link #convertFromTranslucent()}. + * <p> + * Calling this allows the Activity behind this one to be seen again. Once all such Activities + * have been redrawn {@link TranslucentConversionListener#onTranslucentConversionComplete} will + * be called indicating that it is safe to make this activity translucent again. Until + * {@link TranslucentConversionListener#onTranslucentConversionComplete} is called the image + * behind the frontmost Activity will be indeterminate. + * <p> * This call has no effect on non-translucent activities or on activities with the * {@link android.R.attr#windowIsFloating} attribute. + * + * @param callback the method to call when all visible Activities behind this one have been + * drawn and it is safe to make this Activity translucent again. + * + * @see #convertFromTranslucent() + * @see TranslucentConversionListener */ - public void convertToOpaque() { + public void convertToTranslucent(TranslucentConversionListener callback) { try { - ActivityManagerNative.getDefault().convertToOpaque(mToken); + mTranslucentCallback = callback; + ActivityManagerNative.getDefault().convertToTranslucent(mToken); } catch (RemoteException e) { // pass } } + /** @hide */ + void onTranslucentConversionComplete(boolean drawComplete) { + if (mTranslucentCallback != null) { + mTranslucentCallback.onTranslucentConversionComplete(drawComplete); + mTranslucentCallback = null; + } + } + /** * Adjust the current immersive mode setting. * @@ -4947,6 +4988,7 @@ public class Activity extends ContextThemeWrapper * @return The new action mode, or <code>null</code> if the activity does not want to * provide special handling for this action mode. (It will be handled by the system.) */ + @Override public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) { initActionBar(); if (mActionBar != null) { @@ -4961,6 +5003,7 @@ public class Activity extends ContextThemeWrapper * * @param mode The new action mode. */ + @Override public void onActionModeStarted(ActionMode mode) { } @@ -4970,6 +5013,7 @@ public class Activity extends ContextThemeWrapper * * @param mode The action mode that just finished. */ + @Override public void onActionModeFinished(ActionMode mode) { } @@ -5373,4 +5417,26 @@ public class Activity extends ContextThemeWrapper } } } + + /** + * Interface for informing a translucent {@link Activity} once all visible activities below it + * have completed drawing. This is necessary only after an {@link Activity} has been made + * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn + * translucent again following a call to {@link + * Activity#convertToTranslucent(TranslucentConversionListener)}. + */ + public interface TranslucentConversionListener { + /** + * Callback made following {@link Activity#convertToTranslucent} once all visible Activities + * below the top one have been redrawn. Following this callback it is safe to make the top + * Activity translucent because the underlying Activity has been drawn. + * + * @param drawComplete True if the background Activity has drawn itself. False if a timeout + * occurred waiting for the Activity to complete drawing. + * + * @see Activity#convertFromTranslucent() + * @see Activity#convertToTranslucent(TranslucentConversionListener) + */ + public void onTranslucentConversionComplete(boolean drawComplete); + } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a23611ec6e62..acfcb408556c 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1499,10 +1499,18 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case CONVERT_TO_OPAQUE_TRANSACTION: { + case CONVERT_FROM_TRANSLUCENT_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); - convertToOpaque(token); + convertFromTranslucent(token); + reply.writeNoException(); + return true; + } + + case CONVERT_TO_TRANSLUCENT_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + convertToTranslucent(token); reply.writeNoException(); return true; } @@ -1957,6 +1965,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case NOTIFY_ACTIVITY_DRAWN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + notifyActivityDrawn(token); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -3840,13 +3855,25 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } - public void convertToOpaque(IBinder token) + public void convertFromTranslucent(IBinder token) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(CONVERT_FROM_TRANSLUCENT_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void convertToTranslucent(IBinder token) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); - mRemote.transact(CONVERT_TO_OPAQUE_TRANSACTION, data, reply, 0); + mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); @@ -4482,5 +4509,16 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void notifyActivityDrawn(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(NOTIFY_ACTIVITY_DRAWN_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index f1b5d4619578..b24aeb017109 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1215,6 +1215,9 @@ public final class ActivityThread { queueOrSendMessage(H.TRIM_MEMORY, null, level); } + public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) { + queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0); + } } private class H extends Handler { @@ -1262,6 +1265,7 @@ public final class ActivityThread { public static final int DUMP_PROVIDER = 141; public static final int UNSTABLE_PROVIDER_DIED = 142; public static final int REQUEST_ACTIVITY_EXTRAS = 143; + public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144; String codeToString(int code) { if (DEBUG_MESSAGES) { switch (code) { @@ -1309,6 +1313,7 @@ public final class ActivityThread { case DUMP_PROVIDER: return "DUMP_PROVIDER"; case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED"; case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS"; + case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE"; } } return Integer.toString(code); @@ -1523,6 +1528,9 @@ public final class ActivityThread { case REQUEST_ACTIVITY_EXTRAS: handleRequestActivityExtras((RequestActivityExtras)msg.obj); break; + case TRANSLUCENT_CONVERSION_COMPLETE: + handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1); + break; } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } @@ -2255,7 +2263,14 @@ public final class ActivityThread { } catch (RemoteException e) { } } - + + public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) { + ActivityClientRecord r = mActivities.get(token); + if (r != null) { + r.activity.onTranslucentConversionComplete(drawComplete); + } + } + private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>(); /** diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index e903447e44e5..cc495aae3593 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -603,6 +603,16 @@ public abstract class ApplicationThreadNative extends Binder reply.writeNoException(); return true; } + + case SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + IBinder token = data.readStrongBinder(); + boolean timeout = data.readInt() == 1; + scheduleTranslucentConversionComplete(token, timeout); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -1197,6 +1207,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + @Override public void unstableProviderDied(IBinder provider) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -1205,6 +1216,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + @Override public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType) throws RemoteException { Parcel data = Parcel.obtain(); @@ -1215,4 +1227,15 @@ class ApplicationThreadProxy implements IApplicationThread { mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); } + + @Override + public void scheduleTranslucentConversionComplete(IBinder token, boolean timeout) + throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeStrongBinder(token); + data.writeInt(timeout ? 1 : 0); + mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); + } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 3793c7338838..19858dc29674 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -258,7 +258,7 @@ public interface IActivityManager extends IInterface { StrictMode.ViolationInfo crashInfo) throws RemoteException; /* - * This will deliver the specified signal to all the persistent processes. Currently only + * This will deliver the specified signal to all the persistent processes. Currently only * SIGUSR1 is delivered. All others are ignored. */ public void signalPersistentProcesses(int signal) throws RemoteException; @@ -301,7 +301,9 @@ public interface IActivityManager extends IInterface { public void finishHeavyWeightApp() throws RemoteException; - public void convertToOpaque(IBinder token) throws RemoteException; + public void convertFromTranslucent(IBinder token) throws RemoteException; + public void convertToTranslucent(IBinder token) throws RemoteException; + public void notifyActivityDrawn(IBinder token) throws RemoteException; public void setImmersive(IBinder token, boolean immersive) throws RemoteException; public boolean isImmersive(IBinder token) throws RemoteException; @@ -494,7 +496,7 @@ public interface IActivityManager extends IInterface { thisTime = source.readLong(); totalTime = source.readLong(); } - }; + } String descriptor = "android.app.IActivityManager"; @@ -670,6 +672,8 @@ public interface IActivityManager extends IInterface { int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170; int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171; int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172; - int CONVERT_TO_OPAQUE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173; - int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174; + int CONVERT_FROM_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173; + int CONVERT_TO_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174; + int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175; + int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176; } diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index a009bd3b13c3..286566d9a40f 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -133,6 +133,8 @@ public interface IApplicationThread extends IInterface { void unstableProviderDied(IBinder provider) throws RemoteException; void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType) throws RemoteException; + void scheduleTranslucentConversionComplete(IBinder token, boolean timeout) + throws RemoteException; String descriptor = "android.app.IApplicationThread"; @@ -183,4 +185,5 @@ public interface IApplicationThread extends IInterface { int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45; int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46; int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47; + int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48; } diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java index 3c5b6fad622e..c2aa90beba76 100644 --- a/core/java/android/view/transition/Fade.java +++ b/core/java/android/view/transition/Fade.java @@ -96,12 +96,19 @@ public class Fade extends Visibility { protected Animator appear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility) { - View endView = (endValues != null) ? endValues.view : null; - if ((mFadingMode & IN) != IN) { + if ((mFadingMode & IN) != IN || endValues == null) { return null; } + final View endView = endValues.view; endView.setAlpha(0); - return runAnimation(endView, 0, 1, null); + final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Always end animation with full alpha, in case it's canceled mid-stream + endView.setAlpha(1); + } + }; + return runAnimation(endView, 0, 1, endListener); } @Override diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java index d0e61ea28c4b..313e33e601bd 100644 --- a/core/java/android/view/transition/TransitionGroup.java +++ b/core/java/android/view/transition/TransitionGroup.java @@ -104,7 +104,7 @@ public class TransitionGroup extends Transition { mTransitions.add(transitions[i]); transitions[i].mParent = this; if (mDuration >= 0) { - transitions[0].setDuration(mDuration); + transitions[i].setDuration(mDuration); } } } diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java index 464ae2f5e8dd..35b76dd2213f 100644 --- a/core/java/com/android/internal/view/menu/ListMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java @@ -242,17 +242,20 @@ public class ListMenuItemView extends RelativeLayout implements MenuView.ItemVie private void insertIconView() { mIconView = (ImageView) getInflater() - .inflate(com.android.internal.R.layout.list_menu_item_icon, this, true); + .inflate(com.android.internal.R.layout.list_menu_item_icon, this, false); + addView(mIconView); } private void insertRadioButton() { mRadioButton = (RadioButton) getInflater() - .inflate(com.android.internal.R.layout.list_menu_item_radio, this, true); + .inflate(com.android.internal.R.layout.list_menu_item_radio, this, false); + addView(mRadioButton); } private void insertCheckBox() { mCheckBox = (CheckBox) getInflater() - .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, true); + .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, false); + addView(mCheckBox); } private void alignTextToStartOf(View v) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 1057335243a0..65006e5c19c1 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1000,7 +1000,6 @@ public final class ActivityManagerService extends ActivityManagerNative static final int USER_SWITCH_TIMEOUT_MSG = 36; static final int IMMERSIVE_MODE_LOCK_MSG = 37; static final int PERSIST_URI_GRANTS = 38; - static final int SET_FOCUSED_STACK = 39; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1493,18 +1492,6 @@ public final class ActivityManagerService extends ActivityManagerNative writeGrantedUriPermissions(); break; } - case SET_FOCUSED_STACK: { - synchronized (ActivityManagerService.this) { - ActivityStack stack = mStackSupervisor.getStack(msg.arg1); - if (stack != null) { - ActivityRecord r = stack.topRunningActivityLocked(null); - if (r != null) { - setFocusedActivityLocked(r); - } - } - } - break; - } } } }; @@ -2081,7 +2068,26 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void setFocusedStack(int stackId) { if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId); - mHandler.obtainMessage(SET_FOCUSED_STACK, stackId, 0).sendToTarget(); + synchronized (ActivityManagerService.this) { + ActivityStack stack = mStackSupervisor.getStack(stackId); + if (stack != null) { + ActivityRecord r = stack.topRunningActivityLocked(null); + if (r != null) { + setFocusedActivityLocked(r); + } + } + } + } + + @Override + public void notifyActivityDrawn(IBinder token) { + if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token); + synchronized (this) { + ActivityRecord r= mStackSupervisor.isInAnyStackLocked(token); + if (r != null) { + r.task.stack.notifyActivityDrawnLocked(r); + } + } } final void applyUpdateLockStateLocked(ActivityRecord r) { @@ -8048,7 +8054,26 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override - public void convertToOpaque(IBinder token) { + public void convertFromTranslucent(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (this) { + final ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + if (r.changeWindowTranslucency(true)) { + mWindowManager.setAppFullscreen(token, true); + mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); + } + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void convertToTranslucent(IBinder token) { final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { @@ -8056,8 +8081,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (r == null) { return; } - if (r.convertToOpaque()) { - mWindowManager.setAppFullscreen(token); + if (r.changeWindowTranslucency(false)) { + r.task.stack.convertToTranslucent(r); + mWindowManager.setAppFullscreen(token, false); mStackSupervisor.ensureActivitiesVisibleLocked(null, 0); } } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 4f094076e379..2ae9ed6b75f5 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -17,6 +17,7 @@ package com.android.server.am; import android.os.Trace; +import com.android.internal.R.styleable; import com.android.internal.app.ResolverActivity; import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; @@ -505,19 +506,23 @@ final class ActivityRecord { } } - boolean convertToOpaque() { - if (fullscreen) { + boolean changeWindowTranslucency(boolean toOpaque) { + if (fullscreen == toOpaque) { return false; } - - AttributeCache.Entry ent = AttributeCache.instance().get(packageName, - realTheme, com.android.internal.R.styleable.Window); - if (ent != null && !ent.array.getBoolean( - com.android.internal.R.styleable.Window_windowIsFloating, false)) { - fullscreen = true; - ++task.numFullscreen; + AttributeCache.Entry ent = + AttributeCache.instance().get(packageName, realTheme, styleable.Window); + if (ent == null + || !ent.array.getBoolean(styleable.Window_windowIsTranslucent, false) + || ent.array.getBoolean(styleable.Window_windowIsFloating, false)) { + return false; } - return fullscreen; + + // Keep track of the number of fullscreen activities in this task. + task.numFullscreen += toOpaque ? +1 : -1; + + fullscreen = toOpaque; + return true; } void putInHistory() { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 98b3ce9cdb10..be03ee3a5ef8 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -118,6 +118,10 @@ final class ActivityStack { // is being started. static final boolean SHOW_APP_STARTING_PREVIEW = true; + // How long to wait for all background Activities to redraw following a call to + // convertToTranslucent(). + static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000; + enum ActivityState { INITIALIZING, RESUMED, @@ -184,6 +188,16 @@ final class ActivityStack { */ ActivityRecord mLastStartedActivity = null; + // The topmost Activity passed to convertToTranslucent(). When non-null it means we are + // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they + // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the + // Activity in mTranslucentActivityWaiting is notified via + // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last + // background activity being drawn then the same call will be made with a true value. + ActivityRecord mTranslucentActivityWaiting = null; + ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = + new ArrayList<ActivityRecord>(); + /** * Set when we know we are going to be calling updateConfiguration() * soon, so want to skip intermediate config checks. @@ -215,6 +229,7 @@ final class ActivityStack { static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3; static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4; static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5; + static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6; static class ScheduleDestroyArgs { final ProcessRecord mOwner; @@ -285,7 +300,12 @@ final class ActivityStack { synchronized (mService) { destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason); } - } + } break; + case TRANSLUCENT_TIMEOUT_MSG: { + synchronized (mService) { + notifyActivityDrawnLocked(null); + } + } break; } } } @@ -952,6 +972,16 @@ final class ActivityStack { TAG, "ensureActivitiesVisible behind " + top + " configChanges=0x" + Integer.toHexString(configChanges)); + if (mTranslucentActivityWaiting != top) { + mUndrawnActivitiesBelowTopTranslucent.clear(); + if (mTranslucentActivityWaiting != null) { + // Call the callback with a timeout indication. + notifyActivityDrawnLocked(null); + mTranslucentActivityWaiting = null; + } + mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG); + } + // If the top activity is not fullscreen, then we need to // make sure any activities under it are now visible. boolean aboveTop = true; @@ -1018,6 +1048,9 @@ final class ActivityStack { if (DEBUG_VISBILITY) Slog.v( TAG, "Making visible and scheduling visibility: " + r); try { + if (mTranslucentActivityWaiting != null) { + mUndrawnActivitiesBelowTopTranslucent.add(r); + } mWindowManager.setAppVisibility(r.appToken, true); r.sleeping = false; r.app.pendingUiClean = true; @@ -1091,6 +1124,42 @@ final class ActivityStack { return showHomeBehindStack; } + void convertToTranslucent(ActivityRecord r) { + mTranslucentActivityWaiting = r; + mUndrawnActivitiesBelowTopTranslucent.clear(); + mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT); + } + + /** + * Called as activities below the top translucent activity are redrawn. When the last one is + * redrawn notify the top activity by calling + * {@link Activity#onTranslucentConversionComplete}. + * + * @param r The most recent background activity to be drawn. Or, if r is null then a timeout + * occurred and the activity will be notified immediately. + */ + void notifyActivityDrawnLocked(ActivityRecord r) { + if ((r == null) + || (mUndrawnActivitiesBelowTopTranslucent.remove(r) && + mUndrawnActivitiesBelowTopTranslucent.isEmpty())) { + // The last undrawn activity below the top has just been drawn. If there is an + // opaque activity at the top, notify it that it can become translucent safely now. + final ActivityRecord waitingActivity = mTranslucentActivityWaiting; + mTranslucentActivityWaiting = null; + mUndrawnActivitiesBelowTopTranslucent.clear(); + mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG); + + if (waitingActivity != null && waitingActivity.app != null && + waitingActivity.app.thread != null) { + try { + waitingActivity.app.thread.scheduleTranslucentConversionComplete( + waitingActivity.appToken, r != null); + } catch (RemoteException e) { + } + } + } + } + /** * Ensure that the top activity in the stack is resumed. * diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java index 63f91ac435a2..d39798df8dd5 100644 --- a/services/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/java/com/android/server/am/ActivityStackSupervisor.java @@ -2475,8 +2475,7 @@ public final class ActivityStackSupervisor { public void handleMessage(Message msg) { switch (msg.what) { case IDLE_TIMEOUT_MSG: { - if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: Callers=" + - Debug.getCallers(4)); + if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj); if (mService.mDidDexOpt) { mService.mDidDexOpt = false; Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); @@ -2489,6 +2488,7 @@ public final class ActivityStackSupervisor { activityIdleInternal((ActivityRecord)msg.obj); } break; case IDLE_NOW_MSG: { + if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj); activityIdleInternal((ActivityRecord)msg.obj); } break; case RESUME_TOP_ACTIVITY_MSG: { diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java index 3480b195b3f7..93992c21f328 100644 --- a/services/java/com/android/server/pm/KeySetManager.java +++ b/services/java/com/android/server/pm/KeySetManager.java @@ -69,7 +69,7 @@ public class KeySetManager { mPackages = packages; } - /* + /** * Determine if a package is signed by the given KeySet. * * Returns false if the package was not signed by all the @@ -94,7 +94,7 @@ public class KeySetManager { } } - /* + /** * This informs the system that the given package has defined a KeySet * in its manifest that a) contains the given keys and b) is named * alias by that package. @@ -116,7 +116,7 @@ public class KeySetManager { } } - /* + /** * Similar to the above, this informs the system that the given package * was signed by the provided KeySet. */ @@ -153,10 +153,9 @@ public class KeySetManager { } } - /* - * Fetches the stable identifier associated with the given KeySet. - * - * Returns KEYSET_NOT_FOUND if the KeySet... wasn't found. + /** + * Fetches the stable identifier associated with the given KeySet. Returns + * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found. */ public long getIdByKeySet(KeySet ks) { synchronized (mLockObject) { @@ -174,10 +173,11 @@ public class KeySetManager { return KEYSET_NOT_FOUND; } - /* + /** * Fetches the KeySet corresponding to the given stable identifier. * - * Returns KEYSET_NOT_FOUND if the identifier doesn't identify a KeySet. + * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't + * identify a {@link KeySet}. */ public KeySet getKeySetById(long id) { synchronized (mLockObject) { @@ -185,7 +185,7 @@ public class KeySetManager { } } - /* + /** * Fetches the KeySet that a given package refers to by the provided alias. * * If the package isn't known to us, throws an IllegalArgumentException. @@ -205,10 +205,9 @@ public class KeySetManager { } } - /* - * Fetches all the known KeySets that signed the given package. - * - * If the package is unknown to us, throws an IllegalArgumentException. + /** + * Fetches all the known {@link KeySet KeySets} that signed the given + * package. Returns {@code null} if package is unknown. */ public Set<KeySet> getSigningKeySetsByPackageName(String packageName) { synchronized (mLockObject) { @@ -227,16 +226,16 @@ public class KeySetManager { } } - /* + /** * Creates a new KeySet corresponding to the given keys. * - * If the PublicKeys aren't known to the system, this adds them. Otherwise, - * they're deduped. + * If the {@link PublicKey PublicKeys} aren't known to the system, this + * adds them. Otherwise, they're deduped. * * If the KeySet isn't known to the system, this adds that and creates the * mapping to the PublicKeys. If it is known, then it's deduped. * - * Throws if the provided set is null. + * Throws if the provided set is {@code null}. */ private KeySet addKeySetLocked(Set<PublicKey> keys) { if (keys == null) { @@ -267,7 +266,7 @@ public class KeySetManager { return ks; } - /* + /** * Adds the given PublicKey to the system, deduping as it goes. */ private long addPublicKeyLocked(PublicKey key) { @@ -284,7 +283,7 @@ public class KeySetManager { return id; } - /* + /** * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs. * * Returns KEYSET_NOT_FOUND if there isn't one. @@ -299,7 +298,7 @@ public class KeySetManager { return KEYSET_NOT_FOUND; } - /* + /** * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND. */ private long getIdForPublicKeyLocked(PublicKey k) { @@ -314,7 +313,7 @@ public class KeySetManager { return PUBLIC_KEY_NOT_FOUND; } - /* + /** * Gets an unused stable identifier for a KeySet. */ private long getFreeKeySetIDLocked() { @@ -322,7 +321,7 @@ public class KeySetManager { return lastIssuedKeySetId; } - /* + /** * Same as above, but for public keys. */ private long getFreePublicKeyIdLocked() { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index ee2ef377a74c..50d267f7fc80 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -25,6 +25,7 @@ import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import android.app.AppOpsManager; import android.util.TimeUtils; import android.view.IWindowId; + import com.android.internal.app.IBatteryStats; import com.android.internal.policy.PolicyManager; import com.android.internal.policy.impl.PhoneWindowManager; @@ -4117,10 +4118,11 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void setAppFullscreen(IBinder token) { + public void setAppFullscreen(IBinder token, boolean toOpaque) { AppWindowToken atoken = findAppWindowToken(token); if (atoken != null) { - atoken.appFullscreen = true; + atoken.appFullscreen = toOpaque; + requestTraversal(); } } @@ -7020,6 +7022,7 @@ public class WindowManagerService extends IWindowManager.Stub public static final int CLIENT_FREEZE_TIMEOUT = 30; public static final int TAP_OUTSIDE_STACK = 31; + public static final int NOTIFY_ACTIVITY_DRAWN = 32; @Override public void handleMessage(Message msg) { @@ -7452,6 +7455,13 @@ public class WindowManagerService extends IWindowManager.Stub } } } + break; + case NOTIFY_ACTIVITY_DRAWN: + try { + mActivityManager.notifyActivityDrawn((IBinder) msg.obj); + } catch (RemoteException e) { + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG, "handleMessage: exit"); @@ -8759,6 +8769,7 @@ public class WindowManagerService extends IWindowManager.Stub + " interesting=" + numInteresting + " drawn=" + wtoken.numDrawnWindows); wtoken.allDrawn = true; + mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget(); } } } diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml index d7838eb65401..1a9288ce1993 100644 --- a/tests/CanvasCompare/res/layout/manual_layout.xml +++ b/tests/CanvasCompare/res/layout/manual_layout.xml @@ -64,7 +64,7 @@ </LinearLayout> </LinearLayout> - <com.android.test.hwuicompare.NearestImageView + <ImageView android:id="@+id/compare_image_view" android:layout_width="@dimen/layer_width_double" android:layout_height="@dimen/layer_height_double" diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java index 400dff026293..405ff65a34fd 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java @@ -82,6 +82,7 @@ public class ManualActivity extends CompareActivity { mCompareImageView.setImageBitmap(mCompareBitmap); break; } + mCompareImageView.getDrawable().setFilterBitmap(false); mCompareImageView.invalidate(); } diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java deleted file mode 100644 index 542b55addba5..000000000000 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2013 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.test.hwuicompare; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; -import android.util.AttributeSet; -import android.widget.ImageView; - -public class NearestImageView extends ImageView { - public NearestImageView(Context context) { - super(context); - } - - public NearestImageView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public NearestImageView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - final PaintFlagsDrawFilter mFilter = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0); - - @Override - public void onDraw(Canvas canvas) { - canvas.setDrawFilter(mFilter); - super.onDraw(canvas); - canvas.setDrawFilter(null); - } -}
\ No newline at end of file diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml index 5483f6490948..9a399d044af6 100644 --- a/tests/TransitionTests/AndroidManifest.xml +++ b/tests/TransitionTests/AndroidManifest.xml @@ -233,6 +233,13 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:label="CrossfadeImage" + android:name=".CrossfadeImage"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> diff --git a/tests/TransitionTests/res/layout/crossfade_image.xml b/tests/TransitionTests/res/layout/crossfade_image.xml new file mode 100644 index 000000000000..c46327a3e0a6 --- /dev/null +++ b/tests/TransitionTests/res/layout/crossfade_image.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/self_portrait_square_100" + android:onClick="sendMessage" + android:id="@+id/contact_picture"/> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/self_portrait_square_100" + android:onClick="sendMessage" + android:id="@+id/contact_picture1"/> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/self_portrait_square_100" + android:onClick="sendMessage" + android:id="@+id/contact_picture2"/> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java new file mode 100644 index 000000000000..28e055f704fe --- /dev/null +++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2013 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.transitiontests; + +import android.app.Activity; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.view.transition.Crossfade; +import android.view.transition.Move; +import android.view.transition.Scene; +import android.view.transition.Transition; +import android.view.transition.TransitionGroup; +import android.view.transition.TransitionManager; +import android.widget.ImageView; + +public class CrossfadeImage extends Activity { + ViewGroup mSceneRoot; + static int mCurrentScene; + Scene mScene1, mScene2; + TransitionManager mTransitionManager; + boolean mExpanded = false; + Transition mTransition; + ImageView mImageView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.crossfade_image); + + ViewGroup container = (ViewGroup) findViewById(R.id.container); + mSceneRoot = container; + + mImageView = (ImageView) findViewById(R.id.contact_picture); + mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + + Crossfade mCrossfade = new Crossfade(); + mCrossfade.setTargetIds(R.id.contact_picture); + + TransitionGroup group = new TransitionGroup(); + group.setDuration(1500); + group.addTransitions(mCrossfade, new Move()); + mTransition = group; + } + + public void sendMessage(View view) { + TransitionManager.beginDelayedTransition(mSceneRoot, mTransition); + if (mExpanded) { + mImageView.setImageResource(R.drawable.self_portrait_square_100); + } else { + mImageView.setImageResource(R.drawable.self_portrait_square_200); + } + mExpanded = !mExpanded; + } +} |