diff options
Diffstat (limited to 'core/java/android')
24 files changed, 706 insertions, 130 deletions
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index f9c2c8b..fafa948 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -578,9 +578,9 @@ public class ActivityOptions { mResultData = null; mResultCode = 0; mExitCoordinatorIndex = 0; + mAnimationType = otherOptions.mAnimationType; switch (otherOptions.mAnimationType) { case ANIM_CUSTOM: - mAnimationType = otherOptions.mAnimationType; mCustomEnterResId = otherOptions.mCustomEnterResId; mCustomExitResId = otherOptions.mCustomExitResId; mThumbnail = null; @@ -593,7 +593,6 @@ public class ActivityOptions { mAnimationStartedListener = otherOptions.mAnimationStartedListener; break; case ANIM_SCALE_UP: - mAnimationType = otherOptions.mAnimationType; mStartX = otherOptions.mStartX; mStartY = otherOptions.mStartY; mStartWidth = otherOptions.mStartWidth; @@ -608,7 +607,6 @@ public class ActivityOptions { break; case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: - mAnimationType = otherOptions.mAnimationType; mThumbnail = otherOptions.mThumbnail; mStartX = otherOptions.mStartX; mStartY = otherOptions.mStartY; @@ -621,7 +619,6 @@ public class ActivityOptions { mAnimationStartedListener = otherOptions.mAnimationStartedListener; break; case ANIM_SCENE_TRANSITION: - mAnimationType = otherOptions.mAnimationType; mTransitionReceiver = otherOptions.mTransitionReceiver; mSharedElementNames = otherOptions.mSharedElementNames; mIsReturning = otherOptions.mIsReturning; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index a480219..b7e64a2 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -191,7 +191,9 @@ public class AppOpsManager { /** @hide */ public static final int OP_MUTE_MICROPHONE = 44; /** @hide */ - public static final int _NUM_OP = 45; + public static final int OP_TOAST_WINDOW = 45; + /** @hide */ + public static final int _NUM_OP = 46; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = @@ -259,7 +261,8 @@ public class AppOpsManager { OP_COARSE_LOCATION, OP_COARSE_LOCATION, OP_GET_USAGE_STATS, - OP_MUTE_MICROPHONE + OP_MUTE_MICROPHONE, + OP_TOAST_WINDOW, }; /** @@ -312,6 +315,7 @@ public class AppOpsManager { OPSTR_MONITOR_HIGH_POWER_LOCATION, null, null, + null, }; /** @@ -364,6 +368,7 @@ public class AppOpsManager { "MONITOR_HIGH_POWER_LOCATION", "GET_USAGE_STATS", "OP_MUTE_MICROPHONE", + "TOAST_WINDOW", }; /** @@ -416,6 +421,7 @@ public class AppOpsManager { null, // no permission for high power location monitoring android.Manifest.permission.PACKAGE_USAGE_STATS, null, // no permission for muting/unmuting microphone + null, // no permission for displaying toasts }; /** @@ -448,7 +454,7 @@ public class AppOpsManager { null, //READ_ICC_SMS null, //WRITE_ICC_SMS null, //WRITE_SETTINGS - null, //SYSTEM_ALERT_WINDOW + UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW null, //ACCESS_NOTIFICATIONS null, //CAMERA null, //RECORD_AUDIO @@ -469,6 +475,60 @@ public class AppOpsManager { null, //MONITOR_HIGH_POWER_LOCATION null, //GET_USAGE_STATS UserManager.DISALLOW_UNMUTE_MICROPHONE, // MUTE_MICROPHONE + UserManager.DISALLOW_CREATE_WINDOWS, // TOAST_WINDOW + }; + + /** + * This specifies whether each option should allow the system + * (and system ui) to bypass the user restriction when active. + */ + private static boolean[] sOpAllowSystemRestrictionBypass = new boolean[] { + false, //COARSE_LOCATION + false, //FINE_LOCATION + false, //GPS + false, //VIBRATE + false, //READ_CONTACTS + false, //WRITE_CONTACTS + false, //READ_CALL_LOG + false, //WRITE_CALL_LOG + false, //READ_CALENDAR + false, //WRITE_CALENDAR + false, //WIFI_SCAN + false, //POST_NOTIFICATION + false, //NEIGHBORING_CELLS + false, //CALL_PHONE + false, //READ_SMS + false, //WRITE_SMS + false, //RECEIVE_SMS + false, //RECEIVE_EMERGECY_SMS + false, //RECEIVE_MMS + false, //RECEIVE_WAP_PUSH + false, //SEND_SMS + false, //READ_ICC_SMS + false, //WRITE_ICC_SMS + false, //WRITE_SETTINGS + true, //SYSTEM_ALERT_WINDOW + false, //ACCESS_NOTIFICATIONS + false, //CAMERA + false, //RECORD_AUDIO + false, //PLAY_AUDIO + false, //READ_CLIPBOARD + false, //WRITE_CLIPBOARD + false, //TAKE_MEDIA_BUTTONS + false, //TAKE_AUDIO_FOCUS + false, //AUDIO_MASTER_VOLUME + false, //AUDIO_VOICE_VOLUME + false, //AUDIO_RING_VOLUME + false, //AUDIO_MEDIA_VOLUME + false, //AUDIO_ALARM_VOLUME + false, //AUDIO_NOTIFICATION_VOLUME + false, //AUDIO_BLUETOOTH_VOLUME + false, //WAKE_LOCK + false, //MONITOR_LOCATION + false, //MONITOR_HIGH_POWER_LOCATION + false, //GET_USAGE_STATS + false, // MUTE_MICROPHONE + true, // TOAST_WINDOW }; /** @@ -520,6 +580,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, }; /** @@ -575,6 +636,7 @@ public class AppOpsManager { false, false, false, + false, }; private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); @@ -608,6 +670,10 @@ public class AppOpsManager { throw new IllegalStateException("sOpRestrictions length " + sOpRestrictions.length + " should be " + _NUM_OP); } + if (sOpAllowSystemRestrictionBypass.length != _NUM_OP) { + throw new IllegalStateException("sOpAllowSYstemRestrictionsBypass length " + + sOpRestrictions.length + " should be " + _NUM_OP); + } for (int i=0; i<_NUM_OP; i++) { if (sOpToString[i] != null) { sOpStrToOp.put(sOpToString[i], i); @@ -649,6 +715,15 @@ public class AppOpsManager { } /** + * Retrieve whether the op allows the system (and system ui) to + * bypass the user restriction. + * @hide + */ + public static boolean opAllowSystemBypassRestriction(int op) { + return sOpAllowSystemRestrictionBypass[op]; + } + + /** * Retrieve the default mode for the operation. * @hide */ diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index a8f6539..5e18d0f 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -57,6 +57,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { private boolean mIsReadyForTransition; private Bundle mSharedElementsBundle; private boolean mWasOpaque; + private boolean mAreViewsReady; public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver, ArrayList<String> sharedElementNames, boolean isReturning) { @@ -81,18 +82,11 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } public void viewInstancesReady(ArrayList<String> accepted, ArrayList<View> localViews) { - if (mIsReadyForTransition) { - return; - } - viewsReady(mapSharedElements(accepted, localViews)); + triggerViewsReady(mapSharedElements(accepted, localViews)); } public void namedViewsReady(ArrayList<String> accepted, ArrayList<String> localNames) { - if (mIsReadyForTransition) { - return; - } - - viewsReady(mapNamedElements(accepted, localNames)); + triggerViewsReady(mapNamedElements(accepted, localNames)); } @Override @@ -118,6 +112,27 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { } } + private void triggerViewsReady(final ArrayMap<String, View> sharedElements) { + if (mAreViewsReady) { + return; + } + mAreViewsReady = true; + // Ensure the views have been laid out before capturing the views -- we need the epicenter. + if (sharedElements.isEmpty() || !sharedElements.valueAt(0).isLayoutRequested()) { + viewsReady(sharedElements); + } else { + sharedElements.valueAt(0).getViewTreeObserver() + .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + sharedElements.valueAt(0).getViewTreeObserver().removeOnPreDrawListener(this); + viewsReady(sharedElements); + return true; + } + }); + } + } + private ArrayMap<String, View> mapNamedElements(ArrayList<String> accepted, ArrayList<String> localNames) { ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java index ba34184..93cba0d 100644 --- a/core/java/android/app/ExitTransitionCoordinator.java +++ b/core/java/android/app/ExitTransitionCoordinator.java @@ -123,6 +123,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator { startSharedElementExit(); } }); + } else { + sharedElementTransitionComplete(); } } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index d18647a..df51ff5 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2586,6 +2586,8 @@ public class DevicePolicyManager { * @param packages The list of packages allowed to enter lock task mode * * @see Activity#startLockTask() + * @see DeviceAdminReceiver#onLockTaskModeChanged(Context, Intent, boolean, String) + * @see UserManager#DISALLOW_CREATE_WINDOWS */ public void setLockTaskPackages(String[] packages) throws SecurityException { if (mService != null) { diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index a8b324a..05e179d 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -2208,15 +2208,15 @@ public class ConnectivityManager { if (need == null) throw new IllegalArgumentException("null NetworkCapabilities"); try { incCallbackHandlerRefCount(); - if (action == LISTEN) { - networkCallback.networkRequest = mService.listenForNetwork(need, - new Messenger(sCallbackHandler), new Binder()); - } else { - networkCallback.networkRequest = mService.requestNetwork(need, - new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); - } - if (networkCallback.networkRequest != null) { - synchronized(sNetworkCallback) { + synchronized(sNetworkCallback) { + if (action == LISTEN) { + networkCallback.networkRequest = mService.listenForNetwork(need, + new Messenger(sCallbackHandler), new Binder()); + } else { + networkCallback.networkRequest = mService.requestNetwork(need, + new Messenger(sCallbackHandler), timeoutSec, new Binder(), legacyType); + } + if (networkCallback.networkRequest != null) { sNetworkCallback.put(networkCallback.networkRequest, networkCallback); } } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b980e81..a54320f 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -27,6 +27,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.provider.Settings; import android.util.Log; +import android.view.WindowManager.LayoutParams; import com.android.internal.R; @@ -278,6 +279,24 @@ public class UserManager { */ public static final String DISALLOW_TELEPHONY = "no_telephony"; + /** + * Key for user restrictions. Specifies that windows besides app windows should not be + * created. This will block the creation of the following types of windows. + * <li>{@link LayoutParams#TYPE_TOAST}</li> + * <li>{@link LayoutParams#TYPE_PHONE}</li> + * <li>{@link LayoutParams#TYPE_PRIORITY_PHONE}</li> + * <li>{@link LayoutParams#TYPE_SYSTEM_ALERT}</li> + * <li>{@link LayoutParams#TYPE_SYSTEM_ERROR}</li> + * <li>{@link LayoutParams#TYPE_SYSTEM_OVERLAY}</li> + * + * <p>The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows"; + /** @hide */ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3; /** @hide */ diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 03ce4e0..f3c26c8 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -154,6 +154,7 @@ public abstract class WallpaperService extends Service { final Rect mWinFrame = new Rect(); final Rect mOverscanInsets = new Rect(); final Rect mContentInsets = new Rect(); + final Rect mStableInsets = new Rect(); final Configuration mConfiguration = new Configuration(); final WindowManager.LayoutParams mLayout @@ -253,7 +254,8 @@ public abstract class WallpaperService extends Service { final BaseIWindow mWindow = new BaseIWindow() { @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, - Rect visibleInsets, boolean reportDraw, Configuration newConfig) { + Rect visibleInsets, Rect stableInsets, boolean reportDraw, + Configuration newConfig) { Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED, reportDraw ? 1 : 0); mCaller.sendMessage(msg); @@ -628,7 +630,7 @@ public abstract class WallpaperService extends Service { final int relayoutResult = mSession.relayout( mWindow, mWindow.mSeq, mLayout, mWidth, mHeight, View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets, - mVisibleInsets, mConfiguration, mSurfaceHolder.mSurface); + mVisibleInsets, mStableInsets, mConfiguration, mSurfaceHolder.mSurface); if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface + ", frame=" + mWinFrame); diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl index 4d322df..694f25a 100644 --- a/core/java/android/speech/tts/ITextToSpeechService.aidl +++ b/core/java/android/speech/tts/ITextToSpeechService.aidl @@ -36,8 +36,10 @@ interface ITextToSpeechService { * @param text The text to synthesize. * @param queueMode Determines what to do to requests already in the queue. * @param param Request parameters. + * @param utteranceId Unique identifier of synthesized utterance. */ - int speak(in IBinder callingInstance, in String text, in int queueMode, in Bundle params); + int speak(in IBinder callingInstance, in CharSequence text, in int queueMode, in Bundle params, + String utteranceId); /** * Tells the engine to synthesize some speech and write it to a file. @@ -47,10 +49,11 @@ interface ITextToSpeechService { * @param text The text to synthesize. * @param fileDescriptor The file descriptor to write the synthesized audio to. Has to be writable. + * @param utteranceId Unique identifier of synthesized utterance. * @param param Request parameters. */ - int synthesizeToFileDescriptor(in IBinder callingInstance, in String text, - in ParcelFileDescriptor fileDescriptor, in Bundle params); + int synthesizeToFileDescriptor(in IBinder callingInstance, in CharSequence text, + in ParcelFileDescriptor fileDescriptor, in Bundle params, String utteranceId); /** * Plays an existing audio resource. @@ -59,9 +62,11 @@ interface ITextToSpeechService { * TextToSpeech object. * @param audioUri URI for the audio resource (a file or android.resource URI) * @param queueMode Determines what to do to requests already in the queue. + * @param utteranceId Unique identifier of synthesized utterance. * @param param Request parameters. */ - int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params); + int playAudio(in IBinder callingInstance, in Uri audioUri, in int queueMode, in Bundle params, + String utteranceId); /** * Plays silence. diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java index 12a026b..eaacc06 100644 --- a/core/java/android/speech/tts/SynthesisRequest.java +++ b/core/java/android/speech/tts/SynthesisRequest.java @@ -34,7 +34,7 @@ import android.os.Bundle; * and {@link TextToSpeech#synthesizeToFile}. */ public final class SynthesisRequest { - private final String mText; + private final CharSequence mText; private final Bundle mParams; private String mLanguage; private String mCountry; @@ -49,10 +49,25 @@ public final class SynthesisRequest { mParams = new Bundle(params); } + public SynthesisRequest(CharSequence text, Bundle params) { + mText = text; + // Makes a copy of params. + mParams = new Bundle(params); + } + /** * Gets the text which should be synthesized. + * @deprecated As of API level 20, replaced by {@link #getCharSequenceText}. */ + @Deprecated public String getText() { + return mText.toString(); + } + + /** + * Gets the text which should be synthesized. + */ + public CharSequence getCharSequenceText() { return mText; } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 0d2b69b..e1c1767 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -593,7 +593,7 @@ public class TextToSpeech { // too. private final boolean mUseFallback; private final Map<String, Uri> mEarcons; - private final Map<String, Uri> mUtterances; + private final Map<CharSequence, Uri> mUtterances; private final Bundle mParams = new Bundle(); private final TtsEngines mEnginesHelper; private final String mPackageName; @@ -644,7 +644,7 @@ public class TextToSpeech { mUseFallback = useFallback; mEarcons = new HashMap<String, Uri>(); - mUtterances = new HashMap<String, Uri>(); + mUtterances = new HashMap<CharSequence, Uri>(); mUtteranceProgressListener = null; mEnginesHelper = new TtsEngines(mContext); @@ -825,6 +825,40 @@ public class TextToSpeech { } /** + * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text + * and a sound resource in a package. After a call to this method, subsequent calls to + * {@link #speak(String, int, HashMap)} will play the specified sound resource + * if it is available, or synthesize the text it is missing. + * + * @param text + * The string of text. Example: <code>"south_south_east"</code> + * + * @param packagename + * Pass the packagename of the application that contains the + * resource. If the resource is in your own application (this is + * the most common case), then put the packagename of your + * application here.<br/> + * Example: <b>"com.google.marvin.compass"</b><br/> + * The packagename can be found in the AndroidManifest.xml of + * your application. + * <p> + * <code><manifest xmlns:android="..." + * package="<b>com.google.marvin.compass</b>"></code> + * </p> + * + * @param resourceId + * Example: <code>R.raw.south_south_east</code> + * + * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + */ + public int addSpeech(CharSequence text, String packagename, int resourceId) { + synchronized (mStartLock) { + mUtterances.put(text, makeResourceUri(packagename, resourceId)); + return SUCCESS; + } + } + + /** * Adds a mapping between a string of text and a sound file. Using this, it * is possible to add custom pronounciations for a string of text. * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)} @@ -846,6 +880,28 @@ public class TextToSpeech { } } + /** + * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file. + * Using this, it is possible to add custom pronounciations for a string of text. + * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)} + * will play the specified sound resource if it is available, or synthesize the text it is + * missing. + * + * @param text + * The string of text. Example: <code>"south_south_east"</code> + * @param filename + * The full path to the sound file (for example: + * "/sdcard/mysounds/hello.wav") + * + * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + */ + public int addSpeech(CharSequence text, String filename) { + synchronized (mStartLock) { + mUtterances.put(text, Uri.parse(filename)); + return SUCCESS; + } + } + /** * Adds a mapping between a string of text and a sound resource in a @@ -910,7 +966,8 @@ public class TextToSpeech { } /** - * Speaks the string using the specified queuing strategy and speech parameters. + * Speaks the text using the specified queuing strategy and speech parameters, the text may + * be spanned with TtsSpans. * This method is asynchronous, i.e. the method just adds the request to the queue of TTS * requests and then returns. The synthesis might not have finished (or even started!) at the * time when this method returns. In order to reliably detect errors during synthesis, @@ -924,32 +981,69 @@ public class TextToSpeech { * @param params Parameters for the request. Can be null. * Supported parameter names: * {@link Engine#KEY_PARAM_STREAM}, - * {@link Engine#KEY_PARAM_UTTERANCE_ID}, * {@link Engine#KEY_PARAM_VOLUME}, * {@link Engine#KEY_PARAM_PAN}. * Engine specific parameters may be passed in but the parameter keys * must be prefixed by the name of the engine they are intended for. For example * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. + * @param utteranceId An unique identifier for this request. * * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. */ - public int speak(final String text, final int queueMode, final HashMap<String, String> params) { + public int speak(final CharSequence text, + final int queueMode, + final HashMap<String, String> params, + final String utteranceId) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { Uri utteranceUri = mUtterances.get(text); if (utteranceUri != null) { return service.playAudio(getCallerIdentity(), utteranceUri, queueMode, - getParams(params)); + getParams(params), utteranceId); } else { - return service.speak(getCallerIdentity(), text, queueMode, getParams(params)); + return service.speak(getCallerIdentity(), text, queueMode, getParams(params), + utteranceId); } } }, ERROR, "speak"); } /** + * Speaks the string using the specified queuing strategy and speech parameters. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. + * + * @param text The string of text to be spoken. No longer than + * {@link #getMaxSpeechInputLength()} characters. + * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. + * @param params Parameters for the request. Can be null. + * Supported parameter names: + * {@link Engine#KEY_PARAM_STREAM}, + * {@link Engine#KEY_PARAM_UTTERANCE_ID}, + * {@link Engine#KEY_PARAM_VOLUME}, + * {@link Engine#KEY_PARAM_PAN}. + * Engine specific parameters may be passed in but the parameter keys + * must be prefixed by the name of the engine they are intended for. For example + * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the + * engine named "com.svox.pico" if it is being used. + * + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation. + * @deprecated As of API level 20, replaced by + * {@link #speak(CharSequence, int, HashMap, String)}. + */ + @Deprecated + public int speak(final String text, final int queueMode, final HashMap<String, String> params) { + return speak(text, queueMode, params, + params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + } + + /** * Plays the earcon using the specified queueing mode and parameters. * The earcon must already have been added with {@link #addEarcon(String, String)} or * {@link #addEarcon(String, String, int)}. @@ -965,7 +1059,6 @@ public class TextToSpeech { * @param params Parameters for the request. Can be null. * Supported parameter names: * {@link Engine#KEY_PARAM_STREAM}, - * {@link Engine#KEY_PARAM_UTTERANCE_ID}. * Engine specific parameters may be passed in but the parameter keys * must be prefixed by the name of the engine they are intended for. For example * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the @@ -974,7 +1067,7 @@ public class TextToSpeech { * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. */ public int playEarcon(final String earcon, final int queueMode, - final HashMap<String, String> params) { + final HashMap<String, String> params, final String utteranceId) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { @@ -983,12 +1076,45 @@ public class TextToSpeech { return ERROR; } return service.playAudio(getCallerIdentity(), earconUri, queueMode, - getParams(params)); + getParams(params), utteranceId); } }, ERROR, "playEarcon"); } /** + * Plays the earcon using the specified queueing mode and parameters. + * The earcon must already have been added with {@link #addEarcon(String, String)} or + * {@link #addEarcon(String, String, int)}. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. + * + * @param earcon The earcon that should be played + * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. + * @param params Parameters for the request. Can be null. + * Supported parameter names: + * {@link Engine#KEY_PARAM_STREAM}, + * {@link Engine#KEY_PARAM_UTTERANCE_ID}. + * Engine specific parameters may be passed in but the parameter keys + * must be prefixed by the name of the engine they are intended for. For example + * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the + * engine named "com.svox.pico" if it is being used. + * + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation. + * @deprecated As of API level 20, replaced by + * {@link #playEarcon(String, int, HashMap, String)}. + */ + @Deprecated + public int playEarcon(final String earcon, final int queueMode, + final HashMap<String, String> params) { + return playEarcon(earcon, queueMode, params, + params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + } + + /** * Plays silence for the specified amount of time using the specified * queue mode. * This method is asynchronous, i.e. the method just adds the request to the queue of TTS @@ -1001,27 +1127,57 @@ public class TextToSpeech { * @param durationInMs The duration of the silence. * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. * @param params Parameters for the request. Can be null. - * Supported parameter names: - * {@link Engine#KEY_PARAM_UTTERANCE_ID}. * Engine specific parameters may be passed in but the parameter keys * must be prefixed by the name of the engine they are intended for. For example * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. + * @param utteranceId An unique identifier for this request. * * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation. */ public int playSilence(final long durationInMs, final int queueMode, - final HashMap<String, String> params) { + final HashMap<String, String> params, final String utteranceId) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { - return service.playSilence(getCallerIdentity(), durationInMs, queueMode, - params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + return service.playSilence(getCallerIdentity(), durationInMs, + queueMode, utteranceId); } }, ERROR, "playSilence"); } /** + * Plays silence for the specified amount of time using the specified + * queue mode. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. + * + * @param durationInMs The duration of the silence. + * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}. + * @param params Parameters for the request. Can be null. + * Supported parameter names: + * {@link Engine#KEY_PARAM_UTTERANCE_ID}. + * Engine specific parameters may be passed in but the parameter keys + * must be prefixed by the name of the engine they are intended for. For example + * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the + * engine named "com.svox.pico" if it is being used. + * + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation. + * @deprecated As of API level 20, replaced by + * {@link #playSilence(long, int, HashMap, String)}. + */ + @Deprecated + public int playSilence(final long durationInMs, final int queueMode, + final HashMap<String, String> params) { + return playSilence(durationInMs, queueMode, params, + params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + } + + /** * Queries the engine for the set of features it supports for a given locale. * Features can either be framework defined, e.g. * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_SYNTHESIS} or engine specific. @@ -1294,25 +1450,22 @@ public class TextToSpeech { * requests and then returns. The synthesis might not have finished (or even started!) at the * time when this method returns. In order to reliably detect errors during synthesis, * we recommend setting an utterance progress listener (see - * {@link #setOnUtteranceProgressListener}) and using the - * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. + * {@link #setOnUtteranceProgressListener}). * * @param text The text that should be synthesized. No longer than * {@link #getMaxSpeechInputLength()} characters. * @param params Parameters for the request. Can be null. - * Supported parameter names: - * {@link Engine#KEY_PARAM_UTTERANCE_ID}. * Engine specific parameters may be passed in but the parameter keys * must be prefixed by the name of the engine they are intended for. For example * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. * @param filename Absolute file filename to write the generated audio data to.It should be * something like "/sdcard/myappsounds/mysound.wav". - * + * @param utteranceId An unique identifier for this request. * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. */ - public int synthesizeToFile(final String text, final HashMap<String, String> params, - final String filename) { + public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params, + final String filename, final String utteranceId) { return runAction(new Action<Integer>() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { @@ -1329,7 +1482,7 @@ public class TextToSpeech { ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE); returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text, - fileDescriptor, getParams(params)); + fileDescriptor, getParams(params), utteranceId); fileDescriptor.close(); return returnValue; } catch (FileNotFoundException e) { @@ -1343,6 +1496,36 @@ public class TextToSpeech { }, ERROR, "synthesizeToFile"); } + /** + * Synthesizes the given text to a file using the specified parameters. + * This method is asynchronous, i.e. the method just adds the request to the queue of TTS + * requests and then returns. The synthesis might not have finished (or even started!) at the + * time when this method returns. In order to reliably detect errors during synthesis, + * we recommend setting an utterance progress listener (see + * {@link #setOnUtteranceProgressListener}) and using the + * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter. + * + * @param text The text that should be synthesized. No longer than + * {@link #getMaxSpeechInputLength()} characters. + * @param params Parameters for the request. Can be null. + * Supported parameter names: + * {@link Engine#KEY_PARAM_UTTERANCE_ID}. + * Engine specific parameters may be passed in but the parameter keys + * must be prefixed by the name of the engine they are intended for. For example + * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the + * engine named "com.svox.pico" if it is being used. + * @param filename Absolute file filename to write the generated audio data to.It should be + * something like "/sdcard/myappsounds/mysound.wav". + * + * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation. + * @deprecated As of API level 20, replaced by + * {@link #synthesizeToFile(CharSequence, HashMap, String, String)}. + */ + public int synthesizeToFile(final String text, final HashMap<String, String> params, + final String filename) { + return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + } + private Bundle getParams(HashMap<String, String> params) { if (params != null && !params.isEmpty()) { Bundle bundle = new Bundle(mParams); diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 5a3c5f7..017be93 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -637,11 +637,13 @@ public abstract class TextToSpeechService extends Service { */ private abstract class SpeechItemV1 extends UtteranceSpeechItem { protected final Bundle mParams; + protected final String mUtteranceId; SpeechItemV1(Object callerIdentity, int callerUid, int callerPid, - Bundle params) { + Bundle params, String utteranceId) { super(callerIdentity, callerUid, callerPid); mParams = params; + mUtteranceId = utteranceId; } boolean hasLanguage() { @@ -658,7 +660,7 @@ public abstract class TextToSpeechService extends Service { @Override public String getUtteranceId() { - return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null); + return mUtteranceId; } AudioOutputParams getAudioParams() { @@ -668,7 +670,7 @@ public abstract class TextToSpeechService extends Service { class SynthesisSpeechItemV1 extends SpeechItemV1 { // Never null. - private final String mText; + private final CharSequence mText; private final SynthesisRequest mSynthesisRequest; private final String[] mDefaultLocale; // Non null after synthesis has started, and all accesses @@ -678,8 +680,8 @@ public abstract class TextToSpeechService extends Service { private final int mCallerUid; public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid, - Bundle params, String text) { - super(callerIdentity, callerUid, callerPid, params); + Bundle params, String utteranceId, CharSequence text) { + super(callerIdentity, callerUid, callerPid, params, utteranceId); mText = text; mCallerUid = callerUid; mSynthesisRequest = new SynthesisRequest(mText, mParams); @@ -689,7 +691,7 @@ public abstract class TextToSpeechService extends Service { mPackageName); } - public String getText() { + public CharSequence getText() { return mText; } @@ -774,8 +776,9 @@ public abstract class TextToSpeechService extends Service { private final FileOutputStream mFileOutputStream; public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid, - int callerPid, Bundle params, String text, FileOutputStream fileOutputStream) { - super(callerIdentity, callerUid, callerPid, params, text); + int callerPid, Bundle params, String utteranceId, CharSequence text, + FileOutputStream fileOutputStream) { + super(callerIdentity, callerUid, callerPid, params, utteranceId, text); mFileOutputStream = fileOutputStream; } @@ -801,8 +804,8 @@ public abstract class TextToSpeechService extends Service { private final AudioPlaybackQueueItem mItem; public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid, - Bundle params, Uri uri) { - super(callerIdentity, callerUid, callerPid, params); + Bundle params, String utteranceId, Uri uri) { + super(callerIdentity, callerUid, callerPid, params, utteranceId); mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(), TextToSpeechService.this, uri, getAudioParams()); } @@ -909,19 +912,20 @@ public abstract class TextToSpeechService extends Service { // they can be used as message objects (which are tested for equality using ==). private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() { @Override - public int speak(IBinder caller, String text, int queueMode, Bundle params) { + public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params, + String utteranceId) { if (!checkNonNull(caller, text, params)) { return TextToSpeech.ERROR; } SpeechItem item = new SynthesisSpeechItemV1(caller, - Binder.getCallingUid(), Binder.getCallingPid(), params, text); + Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text); return mSynthHandler.enqueueSpeechItem(queueMode, item); } @Override - public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor - fileDescriptor, Bundle params) { + public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor + fileDescriptor, Bundle params, String utteranceId) { if (!checkNonNull(caller, text, fileDescriptor, params)) { return TextToSpeech.ERROR; } @@ -933,19 +937,20 @@ public abstract class TextToSpeechService extends Service { fileDescriptor.detachFd()); SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller, - Binder.getCallingUid(), Binder.getCallingPid(), params, text, + Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text, new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor)); return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item); } @Override - public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params) { + public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params, + String utteranceId) { if (!checkNonNull(caller, audioUri, params)) { return TextToSpeech.ERROR; } SpeechItem item = new AudioSpeechItemV1(caller, - Binder.getCallingUid(), Binder.getCallingPid(), params, audioUri); + Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri); return mSynthHandler.enqueueSpeechItem(queueMode, item); } diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 1066430..f41afcf 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -431,7 +431,7 @@ public final class Choreographer { /** * Gets the time when the current frame started. * <p> - * This method provides the time in nanoseconds when the frame started being rendered. + * This method provides the time in milliseconds when the frame started being rendered. * The frame time provides a stable time base for synchronizing animations * and drawing. It should be used instead of {@link SystemClock#uptimeMillis()} * or {@link System#nanoTime()} for animations and drawing in the UI. Using the frame diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 3670eed..3e7aae0 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -46,7 +46,8 @@ oneway interface IWindow { void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor); void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets, - in Rect visibleInsets, boolean reportDraw, in Configuration newConfig); + in Rect visibleInsets, in Rect stableInsets, boolean reportDraw, + in Configuration newConfig); void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index fa5bd88..0f3f182 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -89,7 +89,7 @@ interface IWindowSession { int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, int flags, out Rect outFrame, out Rect outOverscanInsets, - out Rect outContentInsets, out Rect outVisibleInsets, + out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Configuration outConfig, out Surface outSurface); /** diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 1363a5c..4b53c8e 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -18,6 +18,7 @@ package android.view; import android.animation.Animator; import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.CanvasProperty; import android.graphics.Paint; @@ -34,7 +35,7 @@ import java.util.ArrayList; /** * @hide */ -public final class RenderNodeAnimator extends Animator { +public class RenderNodeAnimator extends Animator { // Keep in sync with enum RenderProperty in Animator.h public static final int TRANSLATION_X = 0; public static final int TRANSLATION_Y = 1; @@ -83,16 +84,23 @@ public final class RenderNodeAnimator extends Animator { private RenderNode mTarget; private View mViewTarget; + private int mRenderProperty = -1; + private float mFinalValue; private TimeInterpolator mInterpolator; private boolean mStarted = false; private boolean mFinished = false; + private long mUnscaledDuration = 300; + private long mUnscaledStartDelay = 0; + public static int mapViewPropertyToRenderProperty(int viewProperty) { return sViewPropertyAnimatorMap.get(viewProperty); } public RenderNodeAnimator(int property, float finalValue) { + mRenderProperty = property; + mFinalValue = finalValue; init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this), property, finalValue)); } @@ -156,7 +164,16 @@ public final class RenderNodeAnimator extends Animator { mStarted = true; applyInterpolator(); - mTarget.addAnimator(this); + nStart(mNativePtr.get()); + + // Alpha is a special snowflake that has the canonical value stored + // in mTransformationInfo instead of in RenderNode, so we need to update + // it with the final value here. + if (mRenderProperty == RenderNodeAnimator.ALPHA) { + // Don't need null check because ViewPropertyAnimator's + // ctor calls ensureTransformationInfo() + mViewTarget.mTransformationInfo.mAlpha = mFinalValue; + } final ArrayList<AnimatorListener> listeners = getListeners(); final int numListeners = listeners == null ? 0 : listeners.size(); @@ -201,6 +218,7 @@ public final class RenderNodeAnimator extends Animator { public void setTarget(View view) { mViewTarget = view; mTarget = view.mRenderNode; + mTarget.addAnimator(this); } public void setTarget(Canvas canvas) { @@ -213,12 +231,12 @@ public final class RenderNodeAnimator extends Animator { } public void setTarget(RenderNode node) { + if (mTarget != null) { + throw new IllegalStateException("Target already set!"); + } mViewTarget = null; mTarget = node; - } - - public RenderNode getTarget() { - return mTarget; + mTarget.addAnimator(this); } public void setStartValue(float startValue) { @@ -232,12 +250,13 @@ public final class RenderNodeAnimator extends Animator { if (startDelay < 0) { throw new IllegalArgumentException("startDelay must be positive; " + startDelay); } - nSetStartDelay(mNativePtr.get(), startDelay); + mUnscaledStartDelay = startDelay; + nSetStartDelay(mNativePtr.get(), (long) (startDelay * ValueAnimator.getDurationScale())); } @Override public long getStartDelay() { - return nGetStartDelay(mNativePtr.get()); + return mUnscaledStartDelay; } @Override @@ -246,13 +265,14 @@ public final class RenderNodeAnimator extends Animator { if (duration < 0) { throw new IllegalArgumentException("duration must be positive; " + duration); } - nSetDuration(mNativePtr.get(), duration); + mUnscaledDuration = duration; + nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale())); return this; } @Override public long getDuration() { - return nGetDuration(mNativePtr.get()); + return mUnscaledDuration; } @Override @@ -307,5 +327,6 @@ public final class RenderNodeAnimator extends Animator { private static native long nGetStartDelay(long nativePtr); private static native void nSetInterpolator(long animPtr, long interpolatorPtr); + private static native void nStart(long animPtr); private static native void nCancel(long animPtr); } diff --git a/core/java/android/view/RenderNodeAnimatorCompat.java b/core/java/android/view/RenderNodeAnimatorCompat.java new file mode 100644 index 0000000..151277a --- /dev/null +++ b/core/java/android/view/RenderNodeAnimatorCompat.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.animation.ValueAnimator; + +import java.util.ArrayList; + +/** + * This class provides compatibility for things like start listeners & + * start delays for use by ViewPropertyAnimator and ObjectAnimator + * @hide + */ +public class RenderNodeAnimatorCompat extends RenderNodeAnimator { + + private long mUnscaledStartDelay = 0; + private long mStartDelay = 0; + private long mStartTime; + private boolean mCanceled; + + public RenderNodeAnimatorCompat(int property, float finalValue) { + super(property, finalValue); + } + + @Override + public void setStartDelay(long startDelay) { + mUnscaledStartDelay = startDelay; + mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay); + } + + @Override + public long getStartDelay() { + return mUnscaledStartDelay; + } + + @Override + public void start() { + if (mStartDelay <= 0) { + doStart(); + } else { + getHelper().addDelayedAnimation(this); + } + } + + private void doStart() { + if (!mCanceled) { + super.start(); + } + } + + @Override + public void cancel() { + mCanceled = true; + super.cancel(); + } + + /** + * @return true if the animator was started, false if still delayed + */ + private boolean processDelayed(long frameTimeMs) { + if (mCanceled) return true; + + if (mStartTime == 0) { + mStartTime = frameTimeMs; + } else if ((frameTimeMs - mStartTime) >= mStartDelay) { + doStart(); + return true; + } + return false; + } + + private static AnimationHelper getHelper() { + AnimationHelper helper = sAnimationHelper.get(); + if (helper == null) { + helper = new AnimationHelper(); + sAnimationHelper.set(helper); + } + return helper; + } + + private static ThreadLocal<AnimationHelper> sAnimationHelper = + new ThreadLocal<AnimationHelper>(); + + private static class AnimationHelper implements Runnable { + + private ArrayList<RenderNodeAnimatorCompat> mDelayedAnims = new ArrayList<RenderNodeAnimatorCompat>(); + private final Choreographer mChoreographer; + private boolean mCallbackScheduled; + + public AnimationHelper() { + mChoreographer = Choreographer.getInstance(); + } + + public void addDelayedAnimation(RenderNodeAnimatorCompat animator) { + mDelayedAnims.add(animator); + scheduleCallback(); + } + + private void scheduleCallback() { + if (!mCallbackScheduled) { + mCallbackScheduled = true; + mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); + } + } + + @Override + public void run() { + long frameTimeMs = mChoreographer.getFrameTime(); + mCallbackScheduled = false; + + int end = 0; + for (int i = 0; i < mDelayedAnims.size(); i++) { + RenderNodeAnimatorCompat animator = mDelayedAnims.get(i); + if (!animator.processDelayed(frameTimeMs)) { + if (end != i) { + mDelayedAnims.set(end, animator); + } + end++; + } + } + while (mDelayedAnims.size() > end) { + mDelayedAnims.remove(mDelayedAnims.size() - 1); + } + + if (mDelayedAnims.size() > 0) { + scheduleCallback(); + } + } + } +} diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 4a2cc1a..a2a4540 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -105,6 +105,7 @@ public class SurfaceView extends View { final Rect mWinFrame = new Rect(); final Rect mOverscanInsets = new Rect(); final Rect mContentInsets = new Rect(); + final Rect mStableInsets = new Rect(); final Configuration mConfiguration = new Configuration(); static final int KEEP_SCREEN_ON_MSG = 1; @@ -518,7 +519,7 @@ public class SurfaceView extends View { visible ? VISIBLE : GONE, WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY, mWinFrame, mOverscanInsets, mContentInsets, - mVisibleInsets, mConfiguration, mNewSurface); + mVisibleInsets, mStableInsets, mConfiguration, mNewSurface); if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) { mReportDrawNeeded = true; } @@ -653,7 +654,8 @@ public class SurfaceView extends View { @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, - Rect visibleInsets, boolean reportDraw, Configuration newConfig) { + Rect visibleInsets, Rect stableInsets, boolean reportDraw, + Configuration newConfig) { SurfaceView surfaceView = mSurfaceView.get(); if (surfaceView != null) { if (DEBUG) Log.v( diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 33b6d6c..3b2e1d1 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -19805,6 +19805,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Rect mVisibleInsets = new Rect(); /** + * For windows that are full-screen but using insets to layout inside + * of the screen decorations, these are the current insets for the + * stable system windows. + */ + final Rect mStableInsets = new Rect(); + + /** * The internal insets given by this window. This value is * supplied by the client (through * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java index 31b360c..20f5182 100644 --- a/core/java/android/view/ViewPropertyAnimatorRT.java +++ b/core/java/android/view/ViewPropertyAnimatorRT.java @@ -81,21 +81,14 @@ class ViewPropertyAnimatorRT { int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant); final float finalValue = holder.mFromValue + holder.mDeltaValue; - RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue); + RenderNodeAnimator animator = new RenderNodeAnimatorCompat(property, finalValue); animator.setStartDelay(startDelay); animator.setDuration(duration); animator.setInterpolator(interpolator); animator.setTarget(mView); animator.start(); - // Alpha is a special snowflake that has the canonical value stored - // in mTransformationInfo instead of in RenderNode, so we need to update - // it with the final value here. - if (property == RenderNodeAnimator.ALPHA) { - // Don't need null check because ViewPropertyAnimator's - // ctor calls ensureTransformationInfo() - parent.mView.mTransformationInfo.mAlpha = finalValue; - } + mAnimators[property] = animator; } parent.mPendingAnimations.clear(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3219330..5def940 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -21,7 +21,6 @@ import android.animation.LayoutTransition; import android.app.ActivityManagerNative; import android.content.ClipDescription; import android.content.ComponentCallbacks; -import android.content.ComponentCallbacks2; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; @@ -205,7 +204,7 @@ public final class ViewRootImpl implements ViewParent, /** Set to true while in performTraversals for detecting when die(true) is called from internal * callbacks such as onMeasure, onPreDraw, onDraw and deferring doDie() until later. */ boolean mIsInTraversal; - boolean mFitSystemWindowsRequested; + boolean mApplyInsetsRequested; boolean mLayoutRequested; boolean mFirst; boolean mReportNextDraw; @@ -257,11 +256,13 @@ public final class ViewRootImpl implements ViewParent, final Rect mPendingOverscanInsets = new Rect(); final Rect mPendingVisibleInsets = new Rect(); + final Rect mPendingStableInsets = new Rect(); final Rect mPendingContentInsets = new Rect(); final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets = new ViewTreeObserver.InternalInsetsInfo(); - final Rect mFitSystemWindowsInsets = new Rect(); + final Rect mDispatchContentInsets = new Rect(); + final Rect mDispatchStableInsets = new Rect(); final Configuration mLastConfiguration = new Configuration(); final Configuration mPendingConfiguration = new Configuration(); @@ -532,6 +533,7 @@ public final class ViewRootImpl implements ViewParent, } mPendingOverscanInsets.set(0, 0, 0, 0); mPendingContentInsets.set(mAttachInfo.mContentInsets); + mPendingStableInsets.set(mAttachInfo.mStableInsets); mPendingVisibleInsets.set(0, 0, 0, 0); if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow); if (res < WindowManagerGlobal.ADD_OKAY) { @@ -816,7 +818,7 @@ public final class ViewRootImpl implements ViewParent, @Override public void requestFitSystemWindows() { checkThread(); - mFitSystemWindowsRequested = true; + mApplyInsetsRequested = true; scheduleTraversals(); } @@ -1161,7 +1163,8 @@ public final class ViewRootImpl implements ViewParent, } void dispatchApplyInsets(View host) { - mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets); + mDispatchContentInsets.set(mAttachInfo.mContentInsets); + mDispatchStableInsets.set(mAttachInfo.mStableInsets); boolean isRound = false; if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0 && mDisplay.getDisplayId() == 0) { @@ -1171,7 +1174,8 @@ public final class ViewRootImpl implements ViewParent, com.android.internal.R.bool.config_windowIsRound); } host.dispatchApplyWindowInsets(new WindowInsets( - mFitSystemWindowsInsets, isRound)); + mDispatchContentInsets, null /* windowDecorInsets */, + mDispatchStableInsets, isRound)); } private void performTraversals() { @@ -1310,6 +1314,9 @@ public final class ViewRootImpl implements ViewParent, if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) { insetsChanged = true; } + if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) { + insetsChanged = true; + } if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) { mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets); if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: " @@ -1383,8 +1390,8 @@ public final class ViewRootImpl implements ViewParent, & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0; } - if (mFitSystemWindowsRequested) { - mFitSystemWindowsRequested = false; + if (mApplyInsetsRequested) { + mApplyInsetsRequested = false; mLastOverscanRequested = mAttachInfo.mOverscanRequested; dispatchApplyInsets(host); if (mLayoutRequested) { @@ -1469,6 +1476,7 @@ public final class ViewRootImpl implements ViewParent, + " overscan=" + mPendingOverscanInsets.toShortString() + " content=" + mPendingContentInsets.toShortString() + " visible=" + mPendingVisibleInsets.toShortString() + + " visible=" + mPendingStableInsets.toShortString() + " surface=" + mSurface); if (mPendingConfiguration.seq != 0) { @@ -1484,6 +1492,8 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mContentInsets); final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals( mAttachInfo.mVisibleInsets); + final boolean stableInsetsChanged = !mPendingStableInsets.equals( + mAttachInfo.mStableInsets); if (contentInsetsChanged) { if (mWidth > 0 && mHeight > 0 && lp != null && ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility) @@ -1558,12 +1568,19 @@ public final class ViewRootImpl implements ViewParent, // Need to relayout with content insets. contentInsetsChanged = true; } + if (stableInsetsChanged) { + mAttachInfo.mStableInsets.set(mPendingStableInsets); + if (DEBUG_LAYOUT) Log.v(TAG, "Decor insets changing to: " + + mAttachInfo.mStableInsets); + // Need to relayout with content insets. + contentInsetsChanged = true; + } if (contentInsetsChanged || mLastSystemUiVisibility != - mAttachInfo.mSystemUiVisibility || mFitSystemWindowsRequested + mAttachInfo.mSystemUiVisibility || mApplyInsetsRequested || mLastOverscanRequested != mAttachInfo.mOverscanRequested) { mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility; mLastOverscanRequested = mAttachInfo.mOverscanRequested; - mFitSystemWindowsRequested = false; + mApplyInsetsRequested = false; dispatchApplyInsets(host); } if (visibleInsetsChanged) { @@ -3065,6 +3082,7 @@ public final class ViewRootImpl implements ViewParent, if (mWinFrame.equals(args.arg1) && mPendingOverscanInsets.equals(args.arg5) && mPendingContentInsets.equals(args.arg2) + && mPendingStableInsets.equals(args.arg6) && mPendingVisibleInsets.equals(args.arg3) && args.arg4 == null) { break; @@ -3082,6 +3100,7 @@ public final class ViewRootImpl implements ViewParent, mWinFrame.set((Rect) args.arg1); mPendingOverscanInsets.set((Rect) args.arg5); mPendingContentInsets.set((Rect) args.arg2); + mPendingStableInsets.set((Rect) args.arg6); mPendingVisibleInsets.set((Rect) args.arg3); args.recycle(); @@ -5167,7 +5186,7 @@ public final class ViewRootImpl implements ViewParent, (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets, - mPendingConfiguration, mSurface); + mPendingStableInsets, mPendingConfiguration, mSurface); //Log.d(TAG, "<<<<<< BACK FROM relayout"); if (restore) { params.restore(); @@ -5178,6 +5197,7 @@ public final class ViewRootImpl implements ViewParent, mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets); mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets); mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets); + mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets); } return relayoutResult; } @@ -5446,7 +5466,7 @@ public final class ViewRootImpl implements ViewParent, } public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets, - Rect visibleInsets, boolean reportDraw, Configuration newConfig) { + Rect visibleInsets, Rect stableInsets, boolean reportDraw, Configuration newConfig) { if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": frame=" + frame.toShortString() + " contentInsets=" + contentInsets.toShortString() + " visibleInsets=" + visibleInsets.toShortString() @@ -5465,6 +5485,7 @@ public final class ViewRootImpl implements ViewParent, args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets; args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig; args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets; + args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets; msg.obj = args; mHandler.sendMessage(msg); } @@ -6292,11 +6313,12 @@ public final class ViewRootImpl implements ViewParent, @Override public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, - Rect visibleInsets, boolean reportDraw, Configuration newConfig) { + Rect visibleInsets, Rect stableInsets, boolean reportDraw, + Configuration newConfig) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { viewAncestor.dispatchResized(frame, overscanInsets, contentInsets, - visibleInsets, reportDraw, newConfig); + visibleInsets, stableInsets, reportDraw, newConfig); } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0a44d23..aa71ed8 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -130,7 +130,20 @@ public abstract class Window { public static final int PROGRESS_SECONDARY_START = 20000; /** Highest possible value for the secondary progress */ public static final int PROGRESS_SECONDARY_END = 30000; - + + /** + * The transitionName for the status bar background View when a custom background is used. + * @see android.view.Window#setStatusBarColor(int) + */ + public static final String STATUS_BAR_BACKGROUND_TRANSITION_NAME = "android:status:background"; + + /** + * The transitionName for the navigation bar background View when a custom background is used. + * @see android.view.Window#setNavigationBarColor(int) + */ + public static final String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = + "android:navigation:background"; + /** The default features enabled */ @SuppressWarnings({"PointlessBitwiseExpression"}) protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) | @@ -1554,6 +1567,9 @@ public abstract class Window { * If {@param color} is not opaque, consider setting * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}. + * <p> + * The transitionName for the view background will be "android:status:background". + * </p> */ public abstract void setStatusBarColor(int color); @@ -1573,6 +1589,9 @@ public abstract class Window { * If {@param color} is not opaque, consider setting * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}. + * <p> + * The transitionName for the view background will be "android:navigation:background". + * </p> */ public abstract void setNavigationBarColor(int color); diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 1d2f1bf..1832cc3 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -30,13 +30,16 @@ import android.graphics.Rect; * @see View#onApplyWindowInsets(WindowInsets) */ public final class WindowInsets { + private Rect mSystemWindowInsets; private Rect mWindowDecorInsets; + private Rect mStableInsets; private Rect mTempRect; private boolean mIsRound; private boolean mSystemWindowInsetsConsumed = false; private boolean mWindowDecorInsetsConsumed = false; + private boolean mStableInsetsConsumed = false; private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0); @@ -49,29 +52,21 @@ public final class WindowInsets { public static final WindowInsets CONSUMED; static { - CONSUMED = new WindowInsets(EMPTY_RECT, EMPTY_RECT); - CONSUMED.mSystemWindowInsetsConsumed = true; - CONSUMED.mWindowDecorInsetsConsumed = true; - } - - /** @hide */ - public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets) { - this(systemWindowInsets, windowDecorInsets, false); - } - - /** @hide */ - public WindowInsets(Rect systemWindowInsets, boolean isRound) { - this(systemWindowInsets, null, isRound); + CONSUMED = new WindowInsets(null, null, null, false); } /** @hide */ - public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, boolean isRound) { + public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets, + boolean isRound) { mSystemWindowInsetsConsumed = systemWindowInsets == null; mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets; mWindowDecorInsetsConsumed = windowDecorInsets == null; mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets; + mStableInsetsConsumed = stableInsets == null; + mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : stableInsets; + mIsRound = isRound; } @@ -83,14 +78,16 @@ public final class WindowInsets { public WindowInsets(WindowInsets src) { mSystemWindowInsets = src.mSystemWindowInsets; mWindowDecorInsets = src.mWindowDecorInsets; + mStableInsets = src.mStableInsets; mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed; mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed; + mStableInsetsConsumed = src.mStableInsetsConsumed; mIsRound = src.mIsRound; } /** @hide */ public WindowInsets(Rect systemWindowInsets) { - this(systemWindowInsets, null); + this(systemWindowInsets, null, null, false); } /** @@ -272,7 +269,7 @@ public final class WindowInsets { * @hide Pending API */ public boolean isConsumed() { - return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed; + return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed; } /** @@ -381,9 +378,57 @@ public final class WindowInsets { return result; } + /** + * @hide + */ + public int getStableInsetTop() { + return mStableInsets.top; + } + + /** + * @hide + */ + public int getStableInsetLeft() { + return mStableInsets.left; + } + + /** + * @hide + */ + public int getStableInsetRight() { + return mStableInsets.right; + } + + /** + * @hide + */ + public int getStableInsetBottom() { + return mStableInsets.bottom; + } + + /** + * @hide + */ + public boolean hasStableInsets() { + return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0 + || mStableInsets.bottom != 0; + } + + /** + * @hide + */ + public WindowInsets consumeStableInsets() { + final WindowInsets result = new WindowInsets(this); + result.mStableInsets = EMPTY_RECT; + result.mStableInsetsConsumed = true; + return result; + } + @Override public String toString() { - return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets + " windowDecorInsets=" + - mWindowDecorInsets + (isRound() ? "round}" : "}"); + return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets + + " windowDecorInsets=" + mWindowDecorInsets + + " stableInsets=" + mStableInsets + + (isRound() ? " round}" : "}"); } } diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 024600d..ee542a1 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -149,9 +149,11 @@ public interface WindowManagerPolicy { * are visible. * @param decorFrame The decor frame specified by policy specific to this window, * to use for proper cropping during animation. + * @param stableFrame The frame around which stable system decoration is positioned. */ public void computeFrameLw(Rect parentFrame, Rect displayFrame, - Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame); + Rect overlayFrame, Rect contentFrame, Rect visibleFrame, Rect decorFrame, + Rect stableFrame); /** * Retrieve the current frame of the window that has been assigned by |
