diff options
82 files changed, 3329 insertions, 1994 deletions
diff --git a/api/current.txt b/api/current.txt index 7cea54676325..c4675ed23c95 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1368,6 +1368,7 @@ package android { field public static final int windowContentTransitions = 16843794; // 0x1010412 field public static final int windowDisablePreview = 16843298; // 0x1010222 field public static final int windowDrawsSystemBarBackgrounds = 16843858; // 0x1010452 + field public static final int windowElevation = 16843922; // 0x1010492 field public static final int windowEnableSplitTouch = 16843543; // 0x1010317 field public static final int windowEnterAnimation = 16842932; // 0x10100b4 field public static final int windowEnterTransition = 16843833; // 0x1010439 @@ -3309,6 +3310,7 @@ package android.app { method public android.view.LayoutInflater getLayoutInflater(); method public android.app.LoaderManager getLoaderManager(); method public java.lang.String getLocalClassName(); + method public final android.media.session.MediaController getMediaController(); method public android.view.MenuInflater getMenuInflater(); method public final android.app.Activity getParent(); method public android.content.Intent getParentActivityIntent(); @@ -3432,6 +3434,7 @@ package android.app { method public void setFinishOnTouchOutside(boolean); method public void setImmersive(boolean); method public void setIntent(android.content.Intent); + method public final void setMediaController(android.media.session.MediaController); method public boolean setMediaPlaying(boolean); method public final void setProgress(int); method public final void setProgressBarIndeterminate(boolean); @@ -3510,6 +3513,7 @@ package android.app { method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException; method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException; method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException; + method public boolean isInLockTaskMode(); method public boolean isLowRamDevice(); method public static boolean isRunningInTestHarness(); method public static boolean isUserAMonkey(); @@ -7058,6 +7062,7 @@ package android.content { method public abstract java.io.File getFileStreamPath(java.lang.String); method public abstract java.io.File getFilesDir(); method public abstract android.os.Looper getMainLooper(); + method public abstract java.io.File getNoBackupFilesDir(); method public abstract java.io.File getObbDir(); method public abstract java.io.File[] getObbDirs(); method public abstract java.lang.String getPackageCodePath(); @@ -7227,6 +7232,7 @@ package android.content { method public java.io.File getFileStreamPath(java.lang.String); method public java.io.File getFilesDir(); method public android.os.Looper getMainLooper(); + method public java.io.File getNoBackupFilesDir(); method public java.io.File getObbDir(); method public java.io.File[] getObbDirs(); method public java.lang.String getPackageCodePath(); @@ -8367,6 +8373,9 @@ package android.content.pm { field public java.lang.String targetPackage; } + public class KeySet { + } + public class LabeledIntent extends android.content.Intent { ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int); ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int); @@ -8504,6 +8513,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public abstract java.lang.String getInstallerPackageName(java.lang.String); method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String); method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String); method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); method public abstract java.lang.String getNameForUid(int); @@ -8522,12 +8532,15 @@ package android.content.pm { method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.KeySet getSigningKeySet(java.lang.String); method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); method public abstract java.lang.String[] getSystemSharedLibraryNames(); method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo); method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo); method public abstract boolean hasSystemFeature(java.lang.String); method public abstract boolean isSafeMode(); + method public abstract boolean isSignedBy(java.lang.String, android.content.pm.KeySet); + method public abstract boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet); method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int); method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int); method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int); @@ -16217,19 +16230,14 @@ package android.media.session { } public final class PlaybackState implements android.os.Parcelable { - ctor public PlaybackState(); - ctor public PlaybackState(android.media.session.PlaybackState); method public int describeContents(); method public long getActions(); method public long getBufferPosition(); method public java.lang.CharSequence getErrorMessage(); - method public float getPlaybackRate(); + method public long getLastPositionUpdateTime(); + method public float getPlaybackSpeed(); method public long getPosition(); method public int getState(); - method public void setActions(long); - method public void setBufferPosition(long); - method public void setErrorMessage(java.lang.CharSequence); - method public void setState(int, long, float); method public void writeToParcel(android.os.Parcel, int); field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L field public static final long ACTION_PAUSE = 2L; // 0x2L @@ -16256,6 +16264,17 @@ package android.media.session { field public static final int STATE_STOPPED = 1; // 0x1 } + public static final class PlaybackState.Builder { + ctor public PlaybackState.Builder(); + ctor public PlaybackState.Builder(android.media.session.PlaybackState); + method public android.media.session.PlaybackState build(); + method public android.media.session.PlaybackState.Builder setActions(long); + method public android.media.session.PlaybackState.Builder setBufferPosition(long); + method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence); + method public android.media.session.PlaybackState.Builder setState(int, long, float, long); + method public android.media.session.PlaybackState.Builder setState(int, long, float); + } + } package android.media.tv { @@ -27835,6 +27854,62 @@ package android.system { package android.telecomm { + public final class Call { + method public void addListener(android.telecomm.Call.Listener); + method public void answer(); + method public void conference(); + method public void disconnect(); + method public android.telecomm.RemoteCallVideoProvider getCallVideoProvider(); + method public java.util.List<java.lang.String> getCannedTextResponses(); + method public java.util.List<android.telecomm.Call> getChildren(); + method public android.telecomm.Call.Details getDetails(); + method public android.telecomm.Call getParent(); + method public java.lang.String getRemainingPostDialSequence(); + method public int getState(); + method public void hold(); + method public void phoneAccountClicked(); + method public void playDtmfTone(char); + method public void postDialContinue(boolean); + method public void reject(boolean, java.lang.String); + method public void removeListener(android.telecomm.Call.Listener); + method public void splitFromConference(); + method public void stopDtmfTone(); + method public void swapWithBackgroundCall(); + method public void unhold(); + field public static final int STATE_ACTIVE = 4; // 0x4 + field public static final int STATE_DIALING = 1; // 0x1 + field public static final int STATE_DISCONNECTED = 7; // 0x7 + field public static final int STATE_HOLDING = 3; // 0x3 + field public static final int STATE_NEW = 0; // 0x0 + field public static final int STATE_RINGING = 2; // 0x2 + } + + public static class Call.Details { + method public android.telecomm.PhoneAccount getAccount(); + method public java.lang.String getCallerDisplayName(); + method public int getCallerDisplayNamePresentation(); + method public int getCapabilities(); + method public long getConnectTimeMillis(); + method public int getDisconnectCauseCode(); + method public java.lang.String getDisconnectCauseMsg(); + method public android.telecomm.GatewayInfo getGatewayInfo(); + method public android.net.Uri getHandle(); + method public int getHandlePresentation(); + } + + public static abstract class Call.Listener { + ctor public Call.Listener(); + method public void onCallDestroyed(android.telecomm.Call); + method public void onCallVideoProviderChanged(android.telecomm.Call, android.telecomm.RemoteCallVideoProvider); + method public void onCannedTextResponsesLoaded(android.telecomm.Call, java.util.List<java.lang.String>); + method public void onChildrenChanged(android.telecomm.Call, java.util.List<android.telecomm.Call>); + method public void onDetailsChanged(android.telecomm.Call, android.telecomm.Call.Details); + method public void onParentChanged(android.telecomm.Call, android.telecomm.Call); + method public void onPostDial(android.telecomm.Call, java.lang.String); + method public void onPostDialWait(android.telecomm.Call, java.lang.String); + method public void onStateChanged(android.telecomm.Call, int); + } + public final class CallAudioState implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -27921,8 +27996,6 @@ package android.telecomm { enum_constant public static final android.telecomm.CallState DISCONNECTED; enum_constant public static final android.telecomm.CallState NEW; enum_constant public static final android.telecomm.CallState ON_HOLD; - enum_constant public static final android.telecomm.CallState POST_DIAL; - enum_constant public static final android.telecomm.CallState POST_DIAL_WAIT; enum_constant public static final android.telecomm.CallState RINGING; } @@ -28097,17 +28170,29 @@ package android.telecomm { field public static final android.os.Parcelable.Creator CREATOR; } - public abstract class InCallService extends android.app.Service { + public abstract class InCallService { ctor protected InCallService(); - method protected abstract void addCall(android.telecomm.InCallCall); - method protected abstract void bringToForeground(boolean); - method protected final android.telecomm.InCallAdapter getAdapter(); - method protected void onAdapterAttached(android.telecomm.InCallAdapter); - method protected abstract void onAudioStateChanged(android.telecomm.CallAudioState); - method public final android.os.IBinder onBind(android.content.Intent); - method protected abstract void setPostDial(java.lang.String, java.lang.String); - method protected abstract void setPostDialWait(java.lang.String, java.lang.String); - method protected abstract void updateCall(android.telecomm.InCallCall); + method public final android.os.IBinder getBinder(); + method public android.telecomm.Phone getPhone(); + method public void onPhoneCreated(android.telecomm.Phone); + method public void onPhoneDestroyed(android.telecomm.Phone); + } + + public final class Phone { + method public final void addListener(android.telecomm.Phone.Listener); + method public final android.telecomm.CallAudioState getAudioState(); + method public final java.util.List<android.telecomm.Call> getCalls(); + method public final void removeListener(android.telecomm.Phone.Listener); + method public final void setAudioRoute(int); + method public final void setMuted(boolean); + } + + public static abstract class Phone.Listener { + ctor public Phone.Listener(); + method public void onAudioStateChanged(android.telecomm.Phone, android.telecomm.CallAudioState); + method public void onBringToForeground(android.telecomm.Phone, boolean); + method public void onCallAdded(android.telecomm.Phone, android.telecomm.Call); + method public void onCallRemoved(android.telecomm.Phone, android.telecomm.Call); } public class PhoneAccount implements android.os.Parcelable { @@ -28145,18 +28230,17 @@ package android.telecomm { method public void updatePeerDimensions(int, int) throws android.os.RemoteException; } - public class RemoteCallVideoProvider implements android.os.IBinder.DeathRecipient { - method public void binderDied(); - method public void requestCallDataUsage() throws android.os.RemoteException; - method public void requestCameraCapabilities() throws android.os.RemoteException; - method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile) throws android.os.RemoteException; - method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile) throws android.os.RemoteException; - method public void setCallVideoClient(android.telecomm.CallVideoClient) throws android.os.RemoteException; + public class RemoteCallVideoProvider { + method public void requestCallDataUsage(); + method public void requestCameraCapabilities(); + method public void sendSessionModifyRequest(android.telecomm.VideoCallProfile); + method public void sendSessionModifyResponse(android.telecomm.VideoCallProfile); + method public void setCallVideoClient(android.telecomm.CallVideoClient); method public void setCamera(java.lang.String) throws android.os.RemoteException; - method public void setDeviceOrientation(int) throws android.os.RemoteException; - method public void setDisplaySurface(android.view.Surface) throws android.os.RemoteException; - method public void setPauseImage(java.lang.String) throws android.os.RemoteException; - method public void setPreviewSurface(android.view.Surface) throws android.os.RemoteException; + method public void setDeviceOrientation(int); + method public void setDisplaySurface(android.view.Surface); + method public void setPauseImage(java.lang.String); + method public void setPreviewSurface(android.view.Surface); method public void setZoom(float) throws android.os.RemoteException; } @@ -28225,6 +28309,7 @@ package android.telecomm { ctor public TelecommConstants(); field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER; field public static final java.lang.String ACTION_CONNECTION_SERVICE; + field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.intent.action.CONNECTION_SERVICE_CONFIGURE"; field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL"; field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ',' field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';' @@ -29234,6 +29319,7 @@ package android.test.mock { method public java.io.File getFileStreamPath(java.lang.String); method public java.io.File getFilesDir(); method public android.os.Looper getMainLooper(); + method public java.io.File getNoBackupFilesDir(); method public java.io.File getObbDir(); method public java.io.File[] getObbDirs(); method public java.lang.String getPackageCodePath(); @@ -34284,6 +34370,7 @@ package android.view { method protected final int getForcedWindowFlags(); method public abstract android.view.LayoutInflater getLayoutInflater(); method protected final int getLocalFeatures(); + method public android.media.session.MediaController getMediaController(); method public abstract int getNavigationBarColor(); method public android.transition.Transition getSharedElementEnterTransition(); method public android.transition.Transition getSharedElementExitTransition(); @@ -34340,6 +34427,7 @@ package android.view { method public void setLayout(int, int); method public void setLocalFocus(boolean, boolean); method public void setLogo(int); + method public void setMediaController(android.media.session.MediaController); method public abstract void setNavigationBarColor(int); method public void setSharedElementEnterTransition(android.transition.Transition); method public void setSharedElementExitTransition(android.transition.Transition); diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 3d0eec431bb1..3a2ca304afc3 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -50,6 +50,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; +import android.text.TextUtils; import android.util.Log; import com.android.internal.content.PackageHelper; @@ -923,33 +924,13 @@ public final class Pm { return; } } else if (opt.equals("--abi")) { - abi = nextOptionData(); - if (abi == null) { - System.err.println("Error: must supply argument for --abi"); - return; - } + abi = checkAbiArgument(nextOptionData()); } else { System.err.println("Error: Unknown option: " + opt); return; } } - if (abi != null) { - final String[] supportedAbis = Build.SUPPORTED_ABIS; - boolean matched = false; - for (String supportedAbi : supportedAbis) { - if (supportedAbi.equals(abi)) { - matched = true; - break; - } - } - - if (!matched) { - System.err.println("Error: abi " + abi + " not supported on this device."); - return; - } - } - final Uri verificationURI; final Uri originatingURI; final Uri referrerURI; @@ -1044,6 +1025,8 @@ public final class Pm { } else if (opt.equals("-S")) { params.deltaSize = Long.parseLong(nextOptionData()); params.progressMax = (int) params.deltaSize; + } else if (opt.equals("--abi")) { + params.abiOverride = checkAbiArgument(nextOptionData()); } else { throw new IllegalArgumentException("Unknown option " + opt); } @@ -1684,6 +1667,21 @@ public final class Pm { } } + private static String checkAbiArgument(String abi) { + if (TextUtils.isEmpty(abi)) { + throw new IllegalArgumentException("Missing ABI argument"); + } + + final String[] supportedAbis = Build.SUPPORTED_ABIS; + for (String supportedAbi : supportedAbis) { + if (supportedAbi.equals(abi)) { + return abi; + } + } + + throw new IllegalArgumentException("ABI " + abi + " not supported on this device"); + } + private String nextOption() { if (mNextArg >= mArgs.length) { return null; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d20a5dcf06d6..cac646d1c32a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -23,6 +23,7 @@ import android.transition.TransitionManager; import android.util.ArrayMap; import android.util.SuperNotCalledException; import android.widget.Toolbar; + import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.WindowDecorActionBar; import com.android.internal.app.ToolbarActionBar; @@ -51,6 +52,8 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.media.AudioManager; +import android.media.session.MediaController; +import android.media.session.MediaSession; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -109,14 +112,14 @@ import java.util.HashMap; * or embedded inside of another activity (using {@link ActivityGroup}). * * There are two methods almost all subclasses of Activity will implement: - * + * * <ul> * <li> {@link #onCreate} is where you initialize your activity. Most * importantly, here you will usually call {@link #setContentView(int)} * with a layout resource defining your UI, and using {@link #findViewById} * to retrieve the widgets in that UI that you need to interact with * programmatically. - * + * * <li> {@link #onPause} is where you deal with the user leaving your * activity. Most importantly, any changes made by the user should at this * point be committed (usually to the @@ -127,7 +130,7 @@ import java.util.HashMap; * activity classes must have a corresponding * {@link android.R.styleable#AndroidManifestActivity <activity>} * declaration in their package's <code>AndroidManifest.xml</code>.</p> - * + * * <p>Topics covered here: * <ol> * <li><a href="#Fragments">Fragments</a> @@ -170,14 +173,14 @@ import java.util.HashMap; * and becomes the running activity -- the previous activity always remains * below it in the stack, and will not come to the foreground again until * the new activity exits.</p> - * + * * <p>An activity has essentially four states:</p> * <ul> * <li> If an activity in the foreground of the screen (at the top of * the stack), * it is <em>active</em> or <em>running</em>. </li> * <li>If an activity has lost focus but is still visible (that is, a new non-full-sized - * or transparent activity has focus on top of your activity), it + * or transparent activity has focus on top of your activity), it * is <em>paused</em>. A paused activity is completely alive (it * maintains all state and member information and remains attached to * the window manager), but can be killed by the system in extreme @@ -197,13 +200,13 @@ import java.util.HashMap; * The square rectangles represent callback methods you can implement to * perform operations when the Activity moves between states. The colored * ovals are major states the Activity can be in.</p> - * + * * <p><img src="../../../images/activity_lifecycle.png" * alt="State diagram for an Android Activity Lifecycle." border="0" /></p> - * + * * <p>There are three key loops you may be interested in monitoring within your * activity: - * + * * <ul> * <li>The <b>entire lifetime</b> of an activity happens between the first call * to {@link android.app.Activity#onCreate} through to a single final call @@ -212,7 +215,7 @@ import java.util.HashMap; * onDestroy(). For example, if it has a thread running in the background * to download data from the network, it may create that thread in onCreate() * and then stop the thread in onDestroy(). - * + * * <li>The <b>visible lifetime</b> of an activity happens between a call to * {@link android.app.Activity#onStart} until a corresponding call to * {@link android.app.Activity#onStop}. During this time the user can see the @@ -224,7 +227,7 @@ import java.util.HashMap; * longer sees what you are displaying. The onStart() and onStop() methods * can be called multiple times, as the activity becomes visible and hidden * to the user. - * + * * <li>The <b>foreground lifetime</b> of an activity happens between a call to * {@link android.app.Activity#onResume} until a corresponding call to * {@link android.app.Activity#onPause}. During this time the activity is @@ -234,7 +237,7 @@ import java.util.HashMap; * intent is delivered -- so the code in these methods should be fairly * lightweight. * </ul> - * + * * <p>The entire lifecycle of an activity is defined by the following * Activity methods. All of these are hooks that you can override * to do appropriate work when the activity changes state. All @@ -250,7 +253,7 @@ import java.util.HashMap; * protected void onCreate(Bundle savedInstanceState); * * protected void onStart(); - * + * * protected void onRestart(); * * protected void onResume(); @@ -366,7 +369,7 @@ import java.util.HashMap; * {@link #onSaveInstanceState(Bundle)} is called before placing the activity * in such a background state, allowing you to save away any dynamic instance * state in your activity into the given Bundle, to be later received in - * {@link #onCreate} if the activity needs to be re-created. + * {@link #onCreate} if the activity needs to be re-created. * See the <a href="#ProcessLifecycle">Process Lifecycle</a> * section for more information on how the lifecycle of a process is tied * to the activities it is hosting. Note that it is important to save @@ -390,14 +393,14 @@ import java.util.HashMap; * * <a name="ConfigurationChanges"></a> * <h3>Configuration Changes</h3> - * + * * <p>If the configuration of the device (as defined by the * {@link Configuration Resources.Configuration} class) changes, * then anything displaying a user interface will need to update to match that * configuration. Because Activity is the primary mechanism for interacting * with the user, it includes special support for handling configuration * changes.</p> - * + * * <p>Unless you specify otherwise, a configuration change (such as a change * in screen orientation, language, input devices, etc) will cause your * current activity to be <em>destroyed</em>, going through the normal activity @@ -407,7 +410,7 @@ import java.util.HashMap; * called in that instance then a new instance of the activity will be * created, with whatever savedInstanceState the previous instance had generated * from {@link #onSaveInstanceState}.</p> - * + * * <p>This is done because any application resource, * including layout files, can change based on any configuration value. Thus * the only safe way to handle a configuration change is to re-retrieve all @@ -415,7 +418,7 @@ import java.util.HashMap; * must already know how to save their state and re-create themselves from * that state, this is a convenient way to have an activity restart itself * with a new configuration.</p> - * + * * <p>In some special cases, you may want to bypass restarting of your * activity based on one or more types of configuration changes. This is * done with the {@link android.R.attr#configChanges android:configChanges} @@ -425,7 +428,7 @@ import java.util.HashMap; * a configuration change involves any that you do not handle, however, the * activity will still be restarted and {@link #onConfigurationChanged} * will not be called.</p> - * + * * <a name="StartingActivities"></a> * <h3>Starting Activities and Getting Results</h3> * @@ -440,10 +443,10 @@ import java.util.HashMap; * ends. For example, you may start an activity that lets the user pick * a person in a list of contacts; when it ends, it returns the person * that was selected. To do this, you call the - * {@link android.app.Activity#startActivityForResult(Intent, int)} - * version with a second integer parameter identifying the call. The result + * {@link android.app.Activity#startActivityForResult(Intent, int)} + * version with a second integer parameter identifying the call. The result * will come back through your {@link android.app.Activity#onActivityResult} - * method.</p> + * method.</p> * * <p>When an activity exits, it can call * {@link android.app.Activity#setResult(int)} @@ -570,17 +573,17 @@ import java.util.HashMap; * * protected void onPause() { * super.onPause(); - * + * * SharedPreferences.Editor ed = mPrefs.edit(); * ed.putInt("view_mode", mCurViewMode); * ed.commit(); * } * } * </pre> - * + * * <a name="Permissions"></a> * <h3>Permissions</h3> - * + * * <p>The ability to start a particular Activity can be enforced when it is * declared in its * manifest's {@link android.R.styleable#AndroidManifestActivity <activity>} @@ -601,10 +604,10 @@ import java.util.HashMap; * * <p>See the <a href="{@docRoot}guide/topics/security/security.html">Security and Permissions</a> * document for more information on permissions and security in general. - * + * * <a name="ProcessLifecycle"></a> * <h3>Process Lifecycle</h3> - * + * * <p>The Android system attempts to keep application process around for as * long as possible, but eventually will need to remove old processes when * memory runs low. As described in <a href="#ActivityLifecycle">Activity @@ -614,7 +617,7 @@ import java.util.HashMap; * listed here in order of importance. The system will kill less important * processes (the last ones) before it resorts to killing more important * processes (the first ones). - * + * * <ol> * <li> <p>The <b>foreground activity</b> (the activity at the top of the screen * that the user is currently interacting with) is considered the most important. @@ -642,7 +645,7 @@ import java.util.HashMap; * context of an activity BroadcastReceiver or Service to ensure that the system * knows it needs to keep your process around. * </ol> - * + * * <p>Sometimes an Activity may need to do a long-running operation that exists * independently of the activity lifecycle itself. An example may be a camera * application that allows you to upload a picture to a web site. The upload @@ -720,7 +723,7 @@ public class Activity extends ContextThemeWrapper VoiceInteractor voiceInteractor; } /* package */ NonConfigurationInstances mLastNonConfigurationInstances; - + private Window mWindow; private WindowManager mWindowManager; @@ -764,7 +767,7 @@ public class Activity extends ContextThemeWrapper private final ArrayList<ManagedCursor> mManagedCursors = new ArrayList<ManagedCursor>(); - // protected by synchronized (this) + // protected by synchronized (this) int mResultCode = RESULT_CANCELED; Intent mResultData = null; @@ -775,7 +778,7 @@ public class Activity extends ContextThemeWrapper private int mDefaultKeyMode = DEFAULT_KEYS_DISABLE; private SpannableStringBuilder mDefaultKeySsb = null; - + protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused}; @SuppressWarnings("unused") @@ -793,16 +796,16 @@ public class Activity extends ContextThemeWrapper return mIntent; } - /** - * Change the intent returned by {@link #getIntent}. This holds a - * reference to the given intent; it does not copy it. Often used in - * conjunction with {@link #onNewIntent}. - * - * @param newIntent The new Intent object to return from getIntent - * + /** + * Change the intent returned by {@link #getIntent}. This holds a + * reference to the given intent; it does not copy it. Often used in + * conjunction with {@link #onNewIntent}. + * + * @param newIntent The new Intent object to return from getIntent + * * @see #getIntent * @see #onNewIntent - */ + */ public void setIntent(Intent newIntent) { mIntent = newIntent; } @@ -816,7 +819,7 @@ public class Activity extends ContextThemeWrapper public final boolean isChild() { return mParent != null; } - + /** Return the parent activity if this view is an embedded child. */ public final Activity getParent() { return mParent; @@ -831,7 +834,7 @@ public class Activity extends ContextThemeWrapper * Retrieve the current {@link android.view.Window} for the activity. * This can be used to directly access parts of the Window API that * are not available through Activity/Screen. - * + * * @return Window The current window, or null if the activity is not * visual. */ @@ -850,7 +853,7 @@ public class Activity extends ContextThemeWrapper mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true); return mLoaderManager; } - + LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) { if (mAllLoaderManagers == null) { mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>(); @@ -866,13 +869,13 @@ public class Activity extends ContextThemeWrapper } return lm; } - + /** * Calls {@link android.view.Window#getCurrentFocus} on the * Window of this Activity to return the currently focused view. - * + * * @return View The current View with focus or null. - * + * * @see #getWindow * @see android.view.Window#getCurrentFocus */ @@ -888,20 +891,20 @@ public class Activity extends ContextThemeWrapper * with widgets in the UI, calling * {@link #managedQuery(android.net.Uri , String[], String, String[], String)} to retrieve * cursors for data being displayed, etc. - * + * * <p>You can call {@link #finish} from within this function, in * which case onDestroy() will be immediately called without any of the rest * of the activity lifecycle ({@link #onStart}, {@link #onResume}, * {@link #onPause}, etc) executing. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @param savedInstanceState If the activity is being re-initialized after * previously being shut down then this Bundle contains the data it most * recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b> - * + * * @see #onStart * @see #onSaveInstanceState * @see #onRestoreInstanceState @@ -996,12 +999,12 @@ public class Activity extends ContextThemeWrapper * decide whether to use your default implementation. The default * implementation of this method performs a restore of any view state that * had previously been frozen by {@link #onSaveInstanceState}. - * + * * <p>This method is called between {@link #onStart} and * {@link #onPostCreate}. - * + * * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}. - * + * * @see #onCreate * @see #onPostCreate * @see #onResume @@ -1098,11 +1101,11 @@ public class Activity extends ContextThemeWrapper * and {@link #onRestoreInstanceState} have been called). Applications will * generally not implement this method; it is intended for system * classes to do final initialization after application code has run. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @param savedInstanceState If the activity is being re-initialized after * previously being shut down then this Bundle contains the data it most * recently supplied in {@link #onSaveInstanceState}. <b><i>Note: Otherwise it is null.</i></b> @@ -1133,14 +1136,14 @@ public class Activity extends ContextThemeWrapper } /** - * Called after {@link #onCreate} — or after {@link #onRestart} when - * the activity had been stopped, but is now again being displayed to the + * Called after {@link #onCreate} — or after {@link #onRestart} when + * the activity had been stopped, but is now again being displayed to the * user. It will be followed by {@link #onResume}. * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onCreate * @see #onStop * @see #onResume @@ -1148,7 +1151,7 @@ public class Activity extends ContextThemeWrapper protected void onStart() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this); mCalled = true; - + if (!mLoadersStarted) { mLoadersStarted = true; if (mLoaderManager != null) { @@ -1173,11 +1176,11 @@ public class Activity extends ContextThemeWrapper * this is usually the place * where the cursor should be requeried (because you had deactivated it in * {@link #onStop}. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onStop * @see #onStart * @see #onResume @@ -1200,7 +1203,7 @@ public class Activity extends ContextThemeWrapper * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onRestoreInstanceState * @see #onRestart * @see #onPostResume @@ -1218,11 +1221,11 @@ public class Activity extends ContextThemeWrapper * been called). Applications will generally not implement this method; * it is intended for system classes to do final setup after application * resume code has run. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onResume */ protected void onPostResume() { @@ -1256,19 +1259,19 @@ public class Activity extends ContextThemeWrapper * activity is re-launched while at the top of the activity stack instead * of a new instance of the activity being started, onNewIntent() will be * called on the existing instance with the Intent that was used to - * re-launch it. - * - * <p>An activity will always be paused before receiving a new intent, so - * you can count on {@link #onResume} being called after this method. - * - * <p>Note that {@link #getIntent} still returns the original Intent. You - * can use {@link #setIntent} to update it to this new Intent. - * - * @param intent The new intent that was started for the activity. - * + * re-launch it. + * + * <p>An activity will always be paused before receiving a new intent, so + * you can count on {@link #onResume} being called after this method. + * + * <p>Note that {@link #getIntent} still returns the original Intent. You + * can use {@link #setIntent} to update it to this new Intent. + * + * @param intent The new intent that was started for the activity. + * * @see #getIntent - * @see #setIntent - * @see #onResume + * @see #setIntent + * @see #onResume */ protected void onNewIntent(Intent intent) { } @@ -1342,9 +1345,9 @@ public class Activity extends ContextThemeWrapper * * <p>If called, this method will occur before {@link #onStop}. There are * no guarantees about whether it will occur before or after {@link #onPause}. - * + * * @param outState Bundle in which to place your saved state. - * + * * @see #onCreate * @see #onRestoreInstanceState * @see #onPause @@ -1429,23 +1432,23 @@ public class Activity extends ContextThemeWrapper * noticeable amount of CPU in order to make the switch to the next activity * as fast as possible, or to close resources that are exclusive access * such as the camera. - * + * * <p>In situations where the system needs more memory it may kill paused * processes to reclaim resources. Because of this, you should be sure * that all of your state is saved by the time you return from * this function. In general {@link #onSaveInstanceState} is used to save * per-instance state in the activity and this method is used to store * global persistent data (in content providers, files, etc.) - * + * * <p>After receiving this call you will usually receive a following call * to {@link #onStop} (after the next activity has been resumed and * displayed), however in some cases there will be a direct call back to * {@link #onResume} without going through the stopped state. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onResume * @see #onSaveInstanceState * @see #onStop @@ -1464,32 +1467,32 @@ public class Activity extends ContextThemeWrapper * brought to the foreground, {@link #onUserLeaveHint} will not be called on * the activity being interrupted. In cases when it is invoked, this method * is called right before the activity's {@link #onPause} callback. - * + * * <p>This callback and {@link #onUserInteraction} are intended to help * activities manage status bar notifications intelligently; specifically, * for helping activities determine the proper time to cancel a notfication. - * + * * @see #onUserInteraction() */ protected void onUserLeaveHint() { } - + /** * Generate a new thumbnail for this activity. This method is called before * pausing the activity, and should draw into <var>outBitmap</var> the * imagery for the desired thumbnail in the dimensions of that bitmap. It * can use the given <var>canvas</var>, which is configured to draw into the * bitmap, for rendering if desired. - * + * * <p>The default implementation returns fails and does not draw a thumbnail; * this will result in the platform creating its own thumbnail if needed. - * + * * @param outBitmap The bitmap to contain the thumbnail. * @param canvas Can be used to render into the bitmap. - * + * * @return Return true if you have drawn into the bitmap; otherwise after * you return it will be filled with a default thumbnail. - * + * * @see #onCreateDescription * @see #onSaveInstanceState * @see #onPause @@ -1502,15 +1505,15 @@ public class Activity extends ContextThemeWrapper * Generate a new description for this activity. This method is called * before pausing the activity and can, if desired, return some textual * description of its current state to be displayed to the user. - * + * * <p>The default implementation returns null, which will cause you to * inherit the description from the previous activity. If all activities * return null, generally the label of the top activity will be used as the * description. - * + * * @return A description of what the user is doing. It should be short and * sweet (only a few words). - * + * * @see #onCreateThumbnail * @see #onSaveInstanceState * @see #onPause @@ -1538,15 +1541,15 @@ public class Activity extends ContextThemeWrapper * Called when you are no longer visible to the user. You will next * receive either {@link #onRestart}, {@link #onDestroy}, or nothing, * depending on later user activity. - * + * * <p>Note that this method may never be called, in low memory situations * where the system does not have enough memory to keep your activity's * process running after its {@link #onPause} method is called. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onRestart * @see #onResume * @see #onSaveInstanceState @@ -1567,7 +1570,7 @@ public class Activity extends ContextThemeWrapper * {@link #finish} on it, or because the system is temporarily destroying * this instance of the activity to save space. You can distinguish * between these two scenarios with the {@link #isFinishing} method. - * + * * <p><em>Note: do not count on this method being called as a place for * saving data! For example, if an activity is editing data in a content * provider, those edits should be committed in either {@link #onPause} or @@ -1579,11 +1582,11 @@ public class Activity extends ContextThemeWrapper * calling this method (or any others) in it, so it should not be used to * do things that are intended to remain around after the process goes * away. - * + * * <p><em>Derived classes must call through to the super class's * implementation of this method. If they do not, an exception will be * thrown.</em></p> - * + * * @see #onPause * @see #onStop * @see #finish @@ -1657,11 +1660,11 @@ public class Activity extends ContextThemeWrapper * by that attribute, then instead of reporting it the system will stop * and restart the activity (to have it launched with the new * configuration). - * + * * <p>At the time that this function has been called, your Resources * object will have been updated to return resource values matching the * new configuration. - * + * * @param newConfig The new device configuration. */ public void onConfigurationChanged(Configuration newConfig) { @@ -1681,7 +1684,7 @@ public class Activity extends ContextThemeWrapper mActionBar.onConfigurationChanged(newConfig); } } - + /** * If this activity is being destroyed because it can not handle a * configuration parameter being changed (and thus its @@ -1691,7 +1694,7 @@ public class Activity extends ContextThemeWrapper * destroyed. Note that there is no guarantee that these will be * accurate (other changes could have happened at any time), so you should * only use this as an optimization hint. - * + * * @return Returns a bit field of the configuration parameters that are * changing, as defined by the {@link android.content.res.Configuration} * class. @@ -1699,21 +1702,21 @@ public class Activity extends ContextThemeWrapper public int getChangingConfigurations() { return mConfigChangeFlags; } - + /** * Retrieve the non-configuration instance data that was previously * returned by {@link #onRetainNonConfigurationInstance()}. This will * be available from the initial {@link #onCreate} and * {@link #onStart} calls to the new instance, allowing you to extract * any useful dynamic state from the previous instance. - * + * * <p>Note that the data you retrieve here should <em>only</em> be used * as an optimization for handling configuration changes. You should always * be able to handle getting a null pointer back, and an activity must * still be able to restore itself to its previous state (through the * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this * function returns null. - * + * * @return Returns the object previously returned by * {@link #onRetainNonConfigurationInstance()}. * @@ -1727,7 +1730,7 @@ public class Activity extends ContextThemeWrapper return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.activity : null; } - + /** * Called by the system, as part of destroying an * activity due to a configuration change, when it is known that a new @@ -1736,7 +1739,7 @@ public class Activity extends ContextThemeWrapper * itself, which can later be retrieved by calling * {@link #getLastNonConfigurationInstance()} in the new activity * instance. - * + * * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB} * or later, consider instead using a {@link Fragment} with * {@link Fragment#setRetainInstance(boolean) @@ -1756,14 +1759,14 @@ public class Activity extends ContextThemeWrapper * the {@link #getLastNonConfigurationInstance()} method of the following * activity instance as described there. * </ul> - * + * * <p>These guarantees are designed so that an activity can use this API * to propagate extensive state from the old to new activity instance, from * loaded bitmaps, to network connections, to evenly actively running * threads. Note that you should <em>not</em> propagate any data that * may change based on the configuration, including any data loaded from * resources such as strings, layouts, or drawables. - * + * * <p>The guarantee of no message handling during the switch to the next * activity simplifies use with active objects. For example if your retained * state is an {@link android.os.AsyncTask} you are guaranteed that its @@ -1783,21 +1786,21 @@ public class Activity extends ContextThemeWrapper public Object onRetainNonConfigurationInstance() { return null; } - + /** * Retrieve the non-configuration instance data that was previously * returned by {@link #onRetainNonConfigurationChildInstances()}. This will * be available from the initial {@link #onCreate} and * {@link #onStart} calls to the new instance, allowing you to extract * any useful dynamic state from the previous instance. - * + * * <p>Note that the data you retrieve here should <em>only</em> be used * as an optimization for handling configuration changes. You should always * be able to handle getting a null pointer back, and an activity must * still be able to restore itself to its previous state (through the * normal {@link #onSaveInstanceState(Bundle)} mechanism) even if this * function returns null. - * + * * @return Returns the object previously returned by * {@link #onRetainNonConfigurationChildInstances()} */ @@ -1806,7 +1809,7 @@ public class Activity extends ContextThemeWrapper return mLastNonConfigurationInstances != null ? mLastNonConfigurationInstances.children : null; } - + /** * This method is similar to {@link #onRetainNonConfigurationInstance()} except that * it should return either a mapping from child activity id strings to arbitrary objects, @@ -1818,7 +1821,7 @@ public class Activity extends ContextThemeWrapper HashMap<String,Object> onRetainNonConfigurationChildInstances() { return null; } - + NonConfigurationInstances retainNonConfigurationInstances() { Object activity = onRetainNonConfigurationInstance(); HashMap<String, Object> children = onRetainNonConfigurationChildInstances(); @@ -1846,7 +1849,7 @@ public class Activity extends ContextThemeWrapper && mVoiceInteractor == null) { return null; } - + NonConfigurationInstances nci = new NonConfigurationInstances(); nci.activity = activity; nci.children = children; @@ -1886,7 +1889,7 @@ public class Activity extends ContextThemeWrapper } } } - + /** * Called when a Fragment is being attached to this activity, immediately * after the call to its {@link Fragment#onAttach Fragment.onAttach()} @@ -1894,14 +1897,14 @@ public class Activity extends ContextThemeWrapper */ public void onAttachFragment(Fragment fragment) { } - + /** * Wrapper around * {@link ContentResolver#query(android.net.Uri , String[], String, String[], String)} * that gives the resulting {@link Cursor} to call * {@link #startManagingCursor} so that the activity will manage its * lifecycle for you. - * + * * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB} * or later, consider instead using {@link LoaderManager} instead, available * via {@link #getLoaderManager()}.</em> @@ -1911,14 +1914,14 @@ public class Activity extends ContextThemeWrapper * you call {@link #stopManagingCursor} on a cursor from a managed query, the system <em>will * not</em> automatically close the cursor and, in that case, you must call * {@link Cursor#close()}.</p> - * + * * @param uri The URI of the content provider to query. * @param projection List of columns to return. * @param selection SQL WHERE clause. * @param sortOrder SQL ORDER BY clause. - * + * * @return The Cursor that was returned by query(). - * + * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) * @see #startManagingCursor * @hide @@ -1941,7 +1944,7 @@ public class Activity extends ContextThemeWrapper * that gives the resulting {@link Cursor} to call * {@link #startManagingCursor} so that the activity will manage its * lifecycle for you. - * + * * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB} * or later, consider instead using {@link LoaderManager} instead, available * via {@link #getLoaderManager()}.</em> @@ -1951,15 +1954,15 @@ public class Activity extends ContextThemeWrapper * you call {@link #stopManagingCursor} on a cursor from a managed query, the system <em>will * not</em> automatically close the cursor and, in that case, you must call * {@link Cursor#close()}.</p> - * + * * @param uri The URI of the content provider to query. * @param projection List of columns to return. * @param selection SQL WHERE clause. * @param selectionArgs The arguments to selection, if any ?s are pesent * @param sortOrder SQL ORDER BY clause. - * + * * @return The Cursor that was returned by query(). - * + * * @see ContentResolver#query(android.net.Uri , String[], String, String[], String) * @see #startManagingCursor * @@ -1982,7 +1985,7 @@ public class Activity extends ContextThemeWrapper * {@link Cursor#deactivate} on the given Cursor, and when it is later restarted * it will call {@link Cursor#requery} for you. When the activity is * destroyed, all managed Cursors will be closed automatically. - * + * * <em>If you are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB} * or later, consider instead using {@link LoaderManager} instead, available * via {@link #getLoaderManager()}.</em> @@ -1992,9 +1995,9 @@ public class Activity extends ContextThemeWrapper * However, if you call {@link #stopManagingCursor} on a cursor from a managed query, the system * <em>will not</em> automatically close the cursor and, in that case, you must call * {@link Cursor#close()}.</p> - * + * * @param c The Cursor to be managed. - * + * * @see #managedQuery(android.net.Uri , String[], String, String[], String) * @see #stopManagingCursor * @@ -2013,13 +2016,13 @@ public class Activity extends ContextThemeWrapper * Given a Cursor that was previously given to * {@link #startManagingCursor}, stop the activity's management of that * cursor. - * + * * <p><strong>Warning:</strong> After calling this method on a cursor from a managed query, - * the system <em>will not</em> automatically close the cursor and you must call + * the system <em>will not</em> automatically close the cursor and you must call * {@link Cursor#close()}.</p> - * + * * @param c The Cursor that was being managed. - * + * * @see #startManagingCursor * * @deprecated Use the new {@link android.content.CursorLoader} class with @@ -2058,7 +2061,7 @@ public class Activity extends ContextThemeWrapper public View findViewById(int id) { return getWindow().findViewById(id); } - + /** * Retrieve a reference to this activity's ActionBar. * @@ -2094,7 +2097,7 @@ public class Activity extends ContextThemeWrapper mActionBar = new ToolbarActionBar(toolbar, getTitle(), this); mActionBar.invalidateOptionsMenu(); } - + /** * Creates a new ActionBar, locates the inflated ActionBarView, * initializes the ActionBar with the view, and sets mActionBar. @@ -2116,13 +2119,13 @@ public class Activity extends ContextThemeWrapper mWindow.setDefaultIcon(mActivityInfo.getIconResource()); mWindow.setDefaultLogo(mActivityInfo.getLogoResource()); } - + /** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. - * + * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ @@ -2140,7 +2143,7 @@ public class Activity extends ContextThemeWrapper * your own layout parameters, invoke * {@link #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)} * instead. - * + * * @param view The desired content to display. * * @see #setContentView(int) @@ -2155,7 +2158,7 @@ public class Activity extends ContextThemeWrapper * Set the activity content to an explicit view. This view is placed * directly into the activity's view hierarchy. It can itself be a complex * view hierarchy. - * + * * @param view The desired content to display. * @param params Layout parameters for the view. * @@ -2170,7 +2173,7 @@ public class Activity extends ContextThemeWrapper /** * Add an additional content view to the activity. Added after any existing * ones in the activity -- existing views are NOT removed. - * + * * @param view The desired content to display. * @param params Layout parameters for the view. */ @@ -2235,23 +2238,23 @@ public class Activity extends ContextThemeWrapper /** * Use with {@link #setDefaultKeyMode} to turn off default handling of * keys. - * + * * @see #setDefaultKeyMode */ static public final int DEFAULT_KEYS_DISABLE = 0; /** * Use with {@link #setDefaultKeyMode} to launch the dialer during default * key handling. - * + * * @see #setDefaultKeyMode */ static public final int DEFAULT_KEYS_DIALER = 1; /** * Use with {@link #setDefaultKeyMode} to execute a menu shortcut in * default key handling. - * + * * <p>That is, the user does not need to hold down the menu key to execute menu shortcuts. - * + * * @see #setDefaultKeyMode */ static public final int DEFAULT_KEYS_SHORTCUT = 2; @@ -2259,9 +2262,9 @@ public class Activity extends ContextThemeWrapper * Use with {@link #setDefaultKeyMode} to specify that unhandled keystrokes * will start an application-defined search. (If the application or activity does not * actually define a search, the the keys will be ignored.) - * + * * <p>See {@link android.app.SearchManager android.app.SearchManager} for more details. - * + * * @see #setDefaultKeyMode */ static public final int DEFAULT_KEYS_SEARCH_LOCAL = 3; @@ -2270,9 +2273,9 @@ public class Activity extends ContextThemeWrapper * Use with {@link #setDefaultKeyMode} to specify that unhandled keystrokes * will start a global search (typically web search, but some platforms may define alternate * methods for global search) - * + * * <p>See {@link android.app.SearchManager android.app.SearchManager} for more details. - * + * * @see #setDefaultKeyMode */ static public final int DEFAULT_KEYS_SEARCH_GLOBAL = 4; @@ -2284,16 +2287,16 @@ public class Activity extends ContextThemeWrapper * floor. Other modes allow you to launch the dialer * ({@link #DEFAULT_KEYS_DIALER}), execute a shortcut in your options * menu without requiring the menu key be held down - * ({@link #DEFAULT_KEYS_SHORTCUT}), or launch a search ({@link #DEFAULT_KEYS_SEARCH_LOCAL} + * ({@link #DEFAULT_KEYS_SHORTCUT}), or launch a search ({@link #DEFAULT_KEYS_SEARCH_LOCAL} * and {@link #DEFAULT_KEYS_SEARCH_GLOBAL}). - * + * * <p>Note that the mode selected here does not impact the default * handling of system keys, such as the "back" and "menu" keys, and your * activity and its views always get a first chance to receive and handle * all application keys. - * + * * @param mode The desired default key mode constant. - * + * * @see #DEFAULT_KEYS_DISABLE * @see #DEFAULT_KEYS_DIALER * @see #DEFAULT_KEYS_SHORTCUT @@ -2303,7 +2306,7 @@ public class Activity extends ContextThemeWrapper */ public final void setDefaultKeyMode(@DefaultKeyMode int mode) { mDefaultKeyMode = mode; - + // Some modes use a SpannableStringBuilder to track & dispatch input events // This list must remain in sync with the switch in onKeyDown() switch (mode) { @@ -2324,10 +2327,10 @@ public class Activity extends ContextThemeWrapper /** * Called when a key was pressed down and not handled by any of the views - * inside of the activity. So, for example, key presses while the cursor + * inside of the activity. So, for example, key presses while the cursor * is inside a TextView will not trigger the event (unless it is a navigation * to another object) because TextView handles its own key presses. - * + * * <p>If the focused view didn't want this event, this method is called. * * <p>The default implementation takes care of {@link KeyEvent#KEYCODE_BACK} @@ -2338,12 +2341,12 @@ public class Activity extends ContextThemeWrapper * will be performed; for earlier applications, it will perform the * action immediately in on-down, as those versions of the platform * behaved. - * + * * <p>Other additional default key handling may be performed * if configured with {@link #setDefaultKeyMode}. - * + * * @return Return <code>true</code> to prevent this event from being propagated - * further, or <code>false</code> to indicate that you have not handled + * further, or <code>false</code> to indicate that you have not handled * this event and it should continue to be propagated. * @see #onKeyUp * @see android.view.KeyEvent @@ -2358,11 +2361,11 @@ public class Activity extends ContextThemeWrapper } return true; } - + if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) { return false; } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) { - if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, + if (getWindow().performPanelShortcut(Window.FEATURE_OPTIONS_PANEL, keyCode, event, Menu.FLAG_ALWAYS_PERFORM_CLOSE)) { return true; } @@ -2382,12 +2385,12 @@ public class Activity extends ContextThemeWrapper final String str = mDefaultKeySsb.toString(); clearSpannable = true; - + switch (mDefaultKeyMode) { case DEFAULT_KEYS_DIALER: Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + str)); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + startActivity(intent); break; case DEFAULT_KEYS_SEARCH_LOCAL: startSearch(str, false, null, false); @@ -2418,16 +2421,16 @@ public class Activity extends ContextThemeWrapper /** * Called when a key was released and not handled by any of the views - * inside of the activity. So, for example, key presses while the cursor + * inside of the activity. So, for example, key presses while the cursor * is inside a TextView will not trigger the event (unless it is a navigation * to another object) because TextView handles its own key presses. - * + * * <p>The default implementation handles KEYCODE_BACK to stop the activity * and go back. - * + * * @return Return <code>true</code> to prevent this event from being propagated - * further, or <code>false</code> to indicate that you have not handled - * this event and it should continue to be propagated. + * further, or <code>false</code> to indicate that you have not handled + * this event and it should continue to be propagated. * @see #onKeyDown * @see KeyEvent */ @@ -2451,7 +2454,7 @@ public class Activity extends ContextThemeWrapper public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { return false; } - + /** * Called when the activity has detected the user's press of the back * key. The default implementation simply finishes the current activity, @@ -2485,9 +2488,9 @@ public class Activity extends ContextThemeWrapper * Called when a touch screen event was not handled by any of the views * under it. This is most useful to process touch events that happen * outside of your window bounds, where there is no view to receive it. - * + * * @param event The touch screen event being processed. - * + * * @return Return true if you have consumed the event, false if you haven't. * The default implementation always returns false. */ @@ -2496,10 +2499,10 @@ public class Activity extends ContextThemeWrapper finish(); return true; } - + return false; } - + /** * Called when the trackball was moved and not handled by any of the * views inside of the activity. So, for example, if the trackball moves @@ -2508,9 +2511,9 @@ public class Activity extends ContextThemeWrapper * here happens <em>before</em> trackball movements are converted to * DPAD key events, which then get sent back to the view hierarchy, and * will be processed at the point for things like focus navigation. - * + * * @param event The trackball event being processed. - * + * * @return Return true if you have consumed the event, false if you haven't. * The default implementation always returns false. */ @@ -2554,21 +2557,21 @@ public class Activity extends ContextThemeWrapper * This callback and {@link #onUserLeaveHint} are intended to help * activities manage status bar notifications intelligently; specifically, * for helping activities determine the proper time to cancel a notfication. - * + * * <p>All calls to your activity's {@link #onUserLeaveHint} callback will * be accompanied by calls to {@link #onUserInteraction}. This * ensures that your activity will be told of relevant user activity such * as pulling down the notification pane and touching an item there. - * + * * <p>Note that this callback will be invoked for the touch down action * that begins a touch gesture, but may not be invoked for the touch-moved * and touch-up actions that follow. - * + * * @see #onUserLeaveHint() */ public void onUserInteraction() { } - + public void onWindowAttributesChanged(WindowManager.LayoutParams params) { // Update window manager if: we have a view, that view is // attached to its parent (which will be a RootView), and @@ -2589,14 +2592,14 @@ public class Activity extends ContextThemeWrapper * focus. This is the best indicator of whether this activity is visible * to the user. The default implementation clears the key tracking * state, so should always be called. - * + * * <p>Note that this provides information about global focus state, which * is managed independently of activity lifecycles. As such, while focus * changes will generally have some relation to lifecycle changes (an * activity that is stopped will not generally get window focus), you * should not rely on any particular order between the callbacks here and * those in the other lifecycle methods such as {@link #onResume}. - * + * * <p>As a general rule, however, a resumed activity will have window * focus... unless it has displayed other dialogs or popups that take * input focus, in which case the activity itself will not have focus @@ -2606,14 +2609,14 @@ public class Activity extends ContextThemeWrapper * pausing the foreground activity. * * @param hasFocus Whether the window of this activity has focus. - * + * * @see #hasWindowFocus() * @see #onResume * @see View#onWindowFocusChanged(boolean) */ public void onWindowFocusChanged(boolean hasFocus) { } - + /** * Called when the main window associated with the activity has been * attached to the window manager. @@ -2623,7 +2626,7 @@ public class Activity extends ContextThemeWrapper */ public void onAttachedToWindow() { } - + /** * Called when the main window associated with the activity has been * detached from the window manager. @@ -2633,13 +2636,13 @@ public class Activity extends ContextThemeWrapper */ public void onDetachedFromWindow() { } - + /** * Returns true if this activity's <em>main</em> window currently has window focus. * Note that this is not the same as the view itself having focus. - * + * * @return True if this activity's main window currently has window focus. - * + * * @see #onWindowAttributesChanged(android.view.WindowManager.LayoutParams) */ public boolean hasWindowFocus() { @@ -2661,14 +2664,14 @@ public class Activity extends ContextThemeWrapper public void onWindowDismissed() { finish(); } - + /** - * Called to process key events. You can override this to intercept all - * key events before they are dispatched to the window. Be sure to call + * Called to process key events. You can override this to intercept all + * key events before they are dispatched to the window. Be sure to call * this implementation for key events that should be handled normally. - * + * * @param event The key event. - * + * * @return boolean Return true if this event was consumed. */ public boolean dispatchKeyEvent(KeyEvent event) { @@ -2713,9 +2716,9 @@ public class Activity extends ContextThemeWrapper * intercept all touch screen events before they are dispatched to the * window. Be sure to call this implementation for touch screen events * that should be handled normally. - * + * * @param ev The touch screen event. - * + * * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { @@ -2727,15 +2730,15 @@ public class Activity extends ContextThemeWrapper } return onTouchEvent(ev); } - + /** * Called to process trackball events. You can override this to * intercept all trackball events before they are dispatched to the * window. Be sure to call this implementation for trackball events * that should be handled normally. - * + * * @param ev The trackball event. - * + * * @return boolean Return true if this event was consumed. */ public boolean dispatchTrackballEvent(MotionEvent ev) { @@ -2830,7 +2833,7 @@ public class Activity extends ContextThemeWrapper /** * {@inheritDoc} - * + * * @return The default implementation returns true. */ public boolean onMenuOpened(int featureId, Menu menu) { @@ -2880,7 +2883,7 @@ public class Activity extends ContextThemeWrapper } } return false; - + case Window.FEATURE_CONTEXT_MENU: if(titleCondensed != null) { EventLog.writeEvent(50000, 1, titleCondensed.toString()); @@ -2894,7 +2897,7 @@ public class Activity extends ContextThemeWrapper return false; } } - + /** * Default implementation of * {@link android.view.Window.Callback#onPanelClosed(int, Menu)} for @@ -2910,7 +2913,7 @@ public class Activity extends ContextThemeWrapper mFragments.dispatchOptionsMenuClosed(menu); onOptionsMenuClosed(menu); break; - + case Window.FEATURE_CONTEXT_MENU: onContextMenuClosed(menu); break; @@ -2932,32 +2935,32 @@ public class Activity extends ContextThemeWrapper mWindow.invalidatePanelMenu(Window.FEATURE_OPTIONS_PANEL); } } - + /** * Initialize the contents of the Activity's standard options menu. You * should place your menu items in to <var>menu</var>. - * + * * <p>This is only called once, the first time the options menu is * displayed. To update the menu every time it is displayed, see * {@link #onPrepareOptionsMenu}. - * + * * <p>The default implementation populates the menu with standard system - * menu items. These are placed in the {@link Menu#CATEGORY_SYSTEM} group so that - * they will be correctly ordered with application-defined menu items. - * Deriving classes should always call through to the base implementation. - * + * menu items. These are placed in the {@link Menu#CATEGORY_SYSTEM} group so that + * they will be correctly ordered with application-defined menu items. + * Deriving classes should always call through to the base implementation. + * * <p>You can safely hold on to <var>menu</var> (and any items created * from it), making modifications to it as desired, until the next * time onCreateOptionsMenu() is called. - * + * * <p>When you add items to the menu, you can implement the Activity's * {@link #onOptionsItemSelected} method to handle them there. - * + * * @param menu The options menu in which you place your items. - * + * * @return You must return true for the menu to be displayed; * if you return false it will not be shown. - * + * * @see #onPrepareOptionsMenu * @see #onOptionsItemSelected */ @@ -2973,17 +2976,17 @@ public class Activity extends ContextThemeWrapper * called right before the menu is shown, every time it is shown. You can * use this method to efficiently enable/disable items or otherwise * dynamically modify the contents. - * + * * <p>The default implementation updates the system menu items based on the * activity's state. Deriving classes should always call through to the * base class implementation. - * + * * @param menu The options menu as last shown or first initialized by * onCreateOptionsMenu(). - * + * * @return You must return true for the menu to be displayed; * if you return false it will not be shown. - * + * * @see #onCreateOptionsMenu */ public boolean onPrepareOptionsMenu(Menu menu) { @@ -3000,15 +3003,15 @@ public class Activity extends ContextThemeWrapper * its Handler as appropriate). You can use this method for any items * for which you would like to do processing without those other * facilities. - * + * * <p>Derived classes should call through to the base class for it to * perform the default menu handling.</p> - * + * * @param item The menu item that was selected. - * + * * @return boolean Return false to allow normal menu processing to * proceed, true to consume it here. - * + * * @see #onCreateOptionsMenu */ public boolean onOptionsItemSelected(MenuItem item) { @@ -3125,7 +3128,7 @@ public class Activity extends ContextThemeWrapper /** * This hook is called whenever the options menu is being closed (either by the user canceling * the menu with the back/menu button, or when an item is selected). - * + * * @param menu The options menu as last shown or first initialized by * onCreateOptionsMenu(). */ @@ -3134,7 +3137,7 @@ public class Activity extends ContextThemeWrapper mParent.onOptionsMenuClosed(menu); } } - + /** * Programmatically opens the options menu. If the options menu is already * open, this method does nothing. @@ -3144,7 +3147,7 @@ public class Activity extends ContextThemeWrapper mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, null); } } - + /** * Progammatically closes the options menu. If the options menu is already * closed, this method does nothing. @@ -3175,43 +3178,43 @@ public class Activity extends ContextThemeWrapper * {@link OnCreateContextMenuListener} on the view to this activity, so * {@link #onCreateContextMenu(ContextMenu, View, ContextMenuInfo)} will be * called when it is time to show the context menu. - * + * * @see #unregisterForContextMenu(View) * @param view The view that should show a context menu. */ public void registerForContextMenu(View view) { view.setOnCreateContextMenuListener(this); } - + /** * Prevents a context menu to be shown for the given view. This method will remove the * {@link OnCreateContextMenuListener} on the view. - * + * * @see #registerForContextMenu(View) * @param view The view that should stop showing a context menu. */ public void unregisterForContextMenu(View view) { view.setOnCreateContextMenuListener(null); } - + /** * Programmatically opens the context menu for a particular {@code view}. * The {@code view} should have been added via * {@link #registerForContextMenu(View)}. - * + * * @param view The view to show the context menu for. */ public void openContextMenu(View view) { view.showContextMenu(); } - + /** * Programmatically closes the most recently opened context menu, if showing. */ public void closeContextMenu() { mWindow.closePanel(Window.FEATURE_CONTEXT_MENU); } - + /** * This hook is called whenever an item in a context menu is selected. The * default implementation simply returns false to have the normal processing @@ -3224,7 +3227,7 @@ public class Activity extends ContextThemeWrapper * <p> * Derived classes should call through to the base class for it to perform * the default menu handling. - * + * * @param item The context menu item that was selected. * @return boolean Return false to allow normal context menu processing to * proceed, true to consume it here. @@ -3240,7 +3243,7 @@ public class Activity extends ContextThemeWrapper * This hook is called whenever the context menu is being closed (either by * the user canceling the menu with the back/menu button, or when an item is * selected). - * + * * @param menu The context menu that is being closed. */ public void onContextMenuClosed(Menu menu) { @@ -3309,14 +3312,14 @@ public class Activity extends ContextThemeWrapper * Provides an opportunity to prepare a managed dialog before it is being * shown. The default implementation calls through to * {@link #onPrepareDialog(int, Dialog)} for compatibility. - * + * * <p> * Override this if you need to update a managed dialog based on the state * of the application each time it is shown. For example, a time picker * dialog might want to be updated with the current time. You should call * through to the superclass's implementation. The default implementation * will set this Activity as the owner activity on the Dialog. - * + * * @param id The id of the managed dialog. * @param dialog The dialog. * @param args The dialog arguments provided to {@link #showDialog(int, Bundle)}. @@ -3367,7 +3370,7 @@ public class Activity extends ContextThemeWrapper * If you need to rebuild the dialog, call {@link #removeDialog(int)} first. * @return Returns true if the Dialog was created; false is returned if * it is not created because {@link #onCreateDialog(int, Bundle)} returns false. - * + * * @see Dialog * @see #onCreateDialog(int, Bundle) * @see #onPrepareDialog(int, Dialog, Bundle) @@ -3393,7 +3396,7 @@ public class Activity extends ContextThemeWrapper } mManagedDialogs.put(id, md); } - + md.mArgs = args; onPrepareDialog(id, md.mDialog, args); md.mDialog.show(); @@ -3422,7 +3425,7 @@ public class Activity extends ContextThemeWrapper if (mManagedDialogs == null) { throw missingDialog(id); } - + final ManagedDialog md = mManagedDialogs.get(id); if (md == null) { throw missingDialog(id); @@ -3449,7 +3452,7 @@ public class Activity extends ContextThemeWrapper * <p>As of {@link android.os.Build.VERSION_CODES#GINGERBREAD}, this function * will not throw an exception if you try to remove an ID that does not * currently have an associated dialog.</p> - * + * * @param id The id of the managed dialog. * * @see #onCreateDialog(int, Bundle) @@ -3474,37 +3477,37 @@ public class Activity extends ContextThemeWrapper /** * This hook is called when the user signals the desire to start a search. - * + * * <p>You can use this function as a simple way to launch the search UI, in response to a - * menu item, search button, or other widgets within your activity. Unless overidden, + * menu item, search button, or other widgets within your activity. Unless overidden, * calling this function is the same as calling * {@link #startSearch startSearch(null, false, null, false)}, which launches * search for the current activity as specified in its manifest, see {@link SearchManager}. - * + * * <p>You can override this function to force global search, e.g. in response to a dedicated * search key, or to block search entirely (by simply returning false). - * + * * @return Returns {@code true} if search launched, and {@code false} if activity blocks it. * The default implementation always returns {@code true}. - * + * * @see android.app.SearchManager */ public boolean onSearchRequested() { - startSearch(null, false, null, false); + startSearch(null, false, null, false); return true; } - + /** * This hook is called to launch the search UI. - * - * <p>It is typically called from onSearchRequested(), either directly from - * Activity.onSearchRequested() or from an overridden version in any given + * + * <p>It is typically called from onSearchRequested(), either directly from + * Activity.onSearchRequested() or from an overridden version in any given * Activity. If your goal is simply to activate search, it is preferred to call * onSearchRequested(), which may have been overridden elsewhere in your Activity. If your goal * is to inject specific data such as context data, it is preferred to <i>override</i> * onSearchRequested(), so that any callers to it will benefit from the override. - * - * @param initialQuery Any non-null non-empty string will be inserted as + * + * @param initialQuery Any non-null non-empty string will be inserted as * pre-entered text in the search query box. * @param selectInitialQuery If true, the initial query will be preselected, which means that * any further typing will replace it. This is useful for cases where an entire pre-formed @@ -3512,15 +3515,15 @@ public class Activity extends ContextThemeWrapper * inserted query. This is useful when the inserted query is text that the user entered, * and the user would expect to be able to keep typing. <i>This parameter is only meaningful * if initialQuery is a non-empty string.</i> - * @param appSearchData An application can insert application-specific - * context here, in order to improve quality or specificity of its own + * @param appSearchData An application can insert application-specific + * context here, in order to improve quality or specificity of its own * searches. This data will be returned with SEARCH intent(s). Null if * no extra data is required. * @param globalSearch If false, this will only launch the search that has been specifically - * defined by the application (which is usually defined as a local search). If no default + * defined by the application (which is usually defined as a local search). If no default * search is defined in the current application or activity, global search will be launched. * If true, this will always launch a platform-global (e.g. web-based) search instead. - * + * * @see android.app.SearchManager * @see #onSearchRequested */ @@ -3528,7 +3531,7 @@ public class Activity extends ContextThemeWrapper @Nullable Bundle appSearchData, boolean globalSearch) { ensureSearchManager(); mSearchManager.startSearch(initialQuery, selectInitialQuery, getComponentName(), - appSearchData, globalSearch); + appSearchData, globalSearch); } /** @@ -3550,7 +3553,7 @@ public class Activity extends ContextThemeWrapper * Request that key events come to this activity. Use this if your * activity has no views with focus, but the activity still wants * a chance to process key events. - * + * * @see android.view.Window#takeKeyEvents */ public void takeKeyEvents(boolean get) { @@ -3560,12 +3563,12 @@ public class Activity extends ContextThemeWrapper /** * Enable extended window features. This is a convenience for calling * {@link android.view.Window#requestFeature getWindow().requestFeature()}. - * + * * @param featureId The desired feature as defined in * {@link android.view.Window}. * @return Returns true if the requested feature is supported and now * enabled. - * + * * @see android.view.Window#requestFeature */ public final boolean requestWindowFeature(int featureId) { @@ -3677,7 +3680,7 @@ public class Activity extends ContextThemeWrapper * Launch an activity for which you would like a result when it finished. * When this activity exits, your * onActivityResult() method will be called with the given requestCode. - * Using a negative requestCode is the same as calling + * Using a negative requestCode is the same as calling * {@link #startActivity} (the activity is not launched as a sub-activity). * * <p>Note that this method should only be used with Intent protocols @@ -3687,7 +3690,7 @@ public class Activity extends ContextThemeWrapper * are launching uses the singleTask launch mode, it will not run in your * task and thus you will immediately receive a cancel result. * - * <p>As a special case, if you call startActivityForResult() with a requestCode + * <p>As a special case, if you call startActivityForResult() with a requestCode * >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your * activity, then your window will not be displayed until a result is * returned back from the started activity. This is to avoid visible @@ -3845,7 +3848,7 @@ public class Activity extends ContextThemeWrapper * here; otherwise, its associated action will be executed (such as * sending a broadcast) as if you had called * {@link IntentSender#sendIntent IntentSender.sendIntent} on it. - * + * * @param intent The IntentSender to launch. * @param requestCode If >= 0, this code will be returned in * onActivityResult() when the activity exits. @@ -3936,19 +3939,19 @@ public class Activity extends ContextThemeWrapper * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not * required; if not specified, the new activity will be added to the * task of the caller. - * + * * <p>This method throws {@link android.content.ActivityNotFoundException} * if there was no Activity found to run the given Intent. - * - * @param intent The intent to start. + * + * @param intent The intent to start. * @param options Additional options for how the Activity should be started. * See {@link android.content.Context#startActivity(Intent, Bundle) * Context.startActivity(Intent, Bundle)} for more details. - * + * * @throws android.content.ActivityNotFoundException * * @see {@link #startActivity(Intent)} - * @see #startActivityForResult + * @see #startActivityForResult */ @Override public void startActivity(Intent intent, @Nullable Bundle options) { @@ -4008,7 +4011,7 @@ public class Activity extends ContextThemeWrapper /** * Same as calling {@link #startIntentSender(IntentSender, Intent, int, int, int, Bundle)} * with no options. - * + * * @param intent The IntentSender to launch. * @param fillInIntent If non-null, this will be provided as the * intent parameter to {@link IntentSender#sendIntent}. @@ -4081,19 +4084,19 @@ public class Activity extends ContextThemeWrapper /** * A special variation to launch an activity only if a new activity * instance is needed to handle the given Intent. In other words, this is - * just like {@link #startActivityForResult(Intent, int)} except: if you are + * just like {@link #startActivityForResult(Intent, int)} except: if you are * using the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag, or - * singleTask or singleTop + * singleTask or singleTop * {@link android.R.styleable#AndroidManifestActivity_launchMode launchMode}, - * and the activity - * that handles <var>intent</var> is the same as your currently running - * activity, then a new instance is not needed. In this case, instead of - * the normal behavior of calling {@link #onNewIntent} this function will - * return and you can handle the Intent yourself. - * + * and the activity + * that handles <var>intent</var> is the same as your currently running + * activity, then a new instance is not needed. In this case, instead of + * the normal behavior of calling {@link #onNewIntent} this function will + * return and you can handle the Intent yourself. + * * <p>This function can only be called from a top-level activity; if it is * called from a child activity, a runtime exception will be thrown. - * + * * @param intent The intent to start. * @param requestCode If >= 0, this code will be returned in * onActivityResult() when the activity exits, as described in @@ -4101,10 +4104,10 @@ public class Activity extends ContextThemeWrapper * @param options Additional options for how the Activity should be started. * See {@link android.content.Context#startActivity(Intent, Bundle) * Context.startActivity(Intent, Bundle)} for more details. - * + * * @return If a new activity was launched then true is returned; otherwise * false is returned and you must handle the Intent yourself. - * + * * @see #startActivity * @see #startActivityForResult */ @@ -4167,7 +4170,7 @@ public class Activity extends ContextThemeWrapper * other activity components. You can use this to hand the Intent off * to the next Activity that can handle it. You typically call this in * {@link #onCreate} with the Intent returned by {@link #getIntent}. - * + * * @param intent The intent to dispatch to the next activity. For * correct behavior, this must be the same as the Intent that started * your own activity; the only changes you can make are to the extras @@ -4175,7 +4178,7 @@ public class Activity extends ContextThemeWrapper * @param options Additional options for how the Activity should be started. * See {@link android.content.Context#startActivity(Intent, Bundle) * Context.startActivity(Intent, Bundle)} for more details. - * + * * @return Returns a boolean indicating whether there was another Activity * to start: true if there was a next activity to start, false if there * wasn't. In general, if true is returned you will then want to call @@ -4217,23 +4220,23 @@ public class Activity extends ContextThemeWrapper } /** - * This is called when a child activity of this one calls its + * This is called when a child activity of this one calls its * {@link #startActivity} or {@link #startActivityForResult} method. - * + * * <p>This method throws {@link android.content.ActivityNotFoundException} * if there was no Activity found to run the given Intent. - * + * * @param child The activity making the call. * @param intent The intent to start. * @param requestCode Reply request code. < 0 if reply is not requested. * @param options Additional options for how the Activity should be started. * See {@link android.content.Context#startActivity(Intent, Bundle) * Context.startActivity(Intent, Bundle)} for more details. - * + * * @throws android.content.ActivityNotFoundException - * - * @see #startActivity - * @see #startActivityForResult + * + * @see #startActivity + * @see #startActivityForResult */ public void startActivityFromChild(@NonNull Activity child, Intent intent, int requestCode, @Nullable Bundle options) { @@ -4267,24 +4270,24 @@ public class Activity extends ContextThemeWrapper } /** - * This is called when a Fragment in this activity calls its + * This is called when a Fragment in this activity calls its * {@link Fragment#startActivity} or {@link Fragment#startActivityForResult} * method. - * + * * <p>This method throws {@link android.content.ActivityNotFoundException} * if there was no Activity found to run the given Intent. - * + * * @param fragment The fragment making the call. * @param intent The intent to start. - * @param requestCode Reply request code. < 0 if reply is not requested. + * @param requestCode Reply request code. < 0 if reply is not requested. * @param options Additional options for how the Activity should be started. * See {@link android.content.Context#startActivity(Intent, Bundle) * Context.startActivity(Intent, Bundle)} for more details. - * + * * @throws android.content.ActivityNotFoundException - * - * @see Fragment#startActivity - * @see Fragment#startActivityForResult + * + * @see Fragment#startActivity + * @see Fragment#startActivityForResult */ public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) { @@ -4352,14 +4355,14 @@ public class Activity extends ContextThemeWrapper } catch (RemoteException e) { } } - + /** * Call this to set the result that your activity will return to its * caller. - * + * * @param resultCode The result code to propagate back to the originating * activity, often RESULT_CANCELED or RESULT_OK - * + * * @see #RESULT_CANCELED * @see #RESULT_OK * @see #RESULT_FIRST_USER @@ -4388,7 +4391,7 @@ public class Activity extends ContextThemeWrapper * @param resultCode The result code to propagate back to the originating * activity, often RESULT_CANCELED or RESULT_OK * @param data The data to propagate back to the originating activity. - * + * * @see #RESULT_CANCELED * @see #RESULT_OK * @see #RESULT_FIRST_USER @@ -4406,10 +4409,10 @@ public class Activity extends ContextThemeWrapper * the data in {@link #setResult setResult()} will be sent to. You can * use this information to validate that the recipient is allowed to * receive the data. - * + * * <p class="note">Note: if the calling activity is not expecting a result (that is it - * did not use the {@link #startActivityForResult} - * form that includes a request code), then the calling package will be + * did not use the {@link #startActivityForResult} + * form that includes a request code), then the calling package will be * null.</p> * * <p class="note">Note: prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, @@ -4417,7 +4420,7 @@ public class Activity extends ContextThemeWrapper * package was no longer running, it would return null instead of the proper package * name. You can use {@link #getCallingActivity()} and retrieve the package name * from that instead.</p> - * + * * @return The package of the activity that will receive your * reply, or null if none. */ @@ -4435,12 +4438,12 @@ public class Activity extends ContextThemeWrapper * who the data in {@link #setResult setResult()} will be sent to. You * can use this information to validate that the recipient is allowed to * receive the data. - * + * * <p class="note">Note: if the calling activity is not expecting a result (that is it - * did not use the {@link #startActivityForResult} - * form that includes a request code), then the calling package will be - * null. - * + * did not use the {@link #startActivityForResult} + * form that includes a request code), then the calling package will be + * null. + * * @return The ComponentName of the activity that will receive your * reply, or null if none. */ @@ -4459,7 +4462,7 @@ public class Activity extends ContextThemeWrapper * UI itself, but can't just finish prior to onResume() because it needs * to wait for a service binding or such. Setting this to false allows * you to prevent your UI from being shown during that time. - * + * * <p>The default value for this is taken from the * {@link android.R.attr#windowNoDisplay} attribute of the activity's theme. */ @@ -4472,7 +4475,7 @@ public class Activity extends ContextThemeWrapper } } } - + void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); @@ -4481,16 +4484,16 @@ public class Activity extends ContextThemeWrapper } mDecor.setVisibility(View.VISIBLE); } - + /** * Check to see whether this activity is in the process of finishing, * either because you called {@link #finish} on it or someone else * has requested that it finished. This is often used in * {@link #onPause} to determine whether the activity is simply pausing or * completely finishing. - * + * * @return If the activity is finishing, returns true; else returns false. - * + * * @see #finish */ public boolean isFinishing() { @@ -4510,7 +4513,7 @@ public class Activity extends ContextThemeWrapper * recreated with a new configuration. This is often used in * {@link #onStop} to determine whether the state needs to be cleaned up or will be passed * on to the next instance of the activity via {@link #onRetainNonConfigurationInstance()}. - * + * * @return If the activity is being torn down in order to be recreated with a new configuration, * returns true; else returns false. */ @@ -4603,12 +4606,12 @@ public class Activity extends ContextThemeWrapper } /** - * This is called when a child activity of this one calls its + * This is called when a child activity of this one calls its * {@link #finish} method. The default implementation simply calls * finish() on this activity (the parent), finishing the entire group. - * + * * @param child The activity making the call. - * + * * @see #finish */ public void finishFromChild(Activity child) { @@ -4631,7 +4634,7 @@ public class Activity extends ContextThemeWrapper /** * Force finish another activity that you had previously started with * {@link #startActivityForResult}. - * + * * @param requestCode The request code of the activity that you had * given to startActivityForResult(). If there are multiple * activities started with this request code, they @@ -4653,7 +4656,7 @@ public class Activity extends ContextThemeWrapper /** * This is called when a child activity of this one calls its * finishActivity(). - * + * * @param child The activity making the call. * @param requestCode Request code that had been used to start the * activity. @@ -4681,10 +4684,10 @@ public class Activity extends ContextThemeWrapper * data from it. The <var>resultCode</var> will be * {@link #RESULT_CANCELED} if the activity explicitly returned that, * didn't return any result, or crashed during its operation. - * + * * <p>You will receive this call immediately before onResume() when your * activity is re-starting. - * + * * @param requestCode The integer request code originally supplied to * startActivityForResult(), allowing you to identify who this * result came from. @@ -4692,7 +4695,7 @@ public class Activity extends ContextThemeWrapper * through its setResult(). * @param data An Intent, which can return result data to the caller * (various data can be attached to Intent "extras"). - * + * * @see #startActivityForResult * @see #createPendingResult * @see #setResult(int) @@ -4722,12 +4725,12 @@ public class Activity extends ContextThemeWrapper } /** - * Create a new PendingIntent object which you can hand to others - * for them to use to send result data back to your - * {@link #onActivityResult} callback. The created object will be either - * one-shot (becoming invalid after a result is sent back) or multiple - * (allowing any number of results to be sent through it). - * + * Create a new PendingIntent object which you can hand to others + * for them to use to send result data back to your + * {@link #onActivityResult} callback. The created object will be either + * one-shot (becoming invalid after a result is sent back) or multiple + * (allowing any number of results to be sent through it). + * * @param requestCode Private request code for the sender that will be * associated with the result data when it is returned. The sender can not * modify this value, allowing you to identify incoming results. @@ -4740,12 +4743,12 @@ public class Activity extends ContextThemeWrapper * or any of the flags as supported by * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts * of the intent that can be supplied when the actual send happens. - * + * * @return Returns an existing or new PendingIntent matching the given * parameters. May return null only if * {@link PendingIntent#FLAG_NO_CREATE PendingIntent.FLAG_NO_CREATE} has been * supplied. - * + * * @see PendingIntent */ public PendingIntent createPendingResult(int requestCode, @NonNull Intent data, @@ -4772,7 +4775,7 @@ public class Activity extends ContextThemeWrapper * orientation, the screen will immediately be changed (possibly causing * the activity to be restarted). Otherwise, this will be used the next * time the activity is visible. - * + * * @param requestedOrientation An orientation constant as used in * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}. */ @@ -4788,13 +4791,13 @@ public class Activity extends ContextThemeWrapper mParent.setRequestedOrientation(requestedOrientation); } } - + /** * Return the current requested orientation of the activity. This will * either be the orientation requested in its component's manifest, or * the last requested orientation given to * {@link #setRequestedOrientation(int)}. - * + * * @return Returns an orientation constant as used in * {@link ActivityInfo#screenOrientation ActivityInfo.screenOrientation}. */ @@ -4812,11 +4815,11 @@ public class Activity extends ContextThemeWrapper } return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; } - + /** * Return the identifier of the task this activity is in. This identifier * will remain the same for the lifetime of the activity. - * + * * @return Task identifier, an opaque integer. */ public int getTaskId() { @@ -4831,7 +4834,7 @@ public class Activity extends ContextThemeWrapper /** * Return whether this activity is the root of a task. The root is the * first activity in a task. - * + * * @return True if this is the root activity, else false. */ public boolean isTaskRoot() { @@ -4846,11 +4849,11 @@ public class Activity extends ContextThemeWrapper /** * Move the task containing this activity to the back of the activity * stack. The activity's order within the task is unchanged. - * + * * @param nonRoot If false then this only works if the activity is the root * of a task; if true it will work for any activity in * a task. - * + * * @return If the task was moved (or it was already at the * back) true is returned, else false. */ @@ -4867,7 +4870,7 @@ public class Activity extends ContextThemeWrapper /** * Returns class name for this activity with the package prefix removed. * This is the default name used to read and write settings. - * + * * @return The local class name. */ @NonNull @@ -4881,10 +4884,10 @@ public class Activity extends ContextThemeWrapper } return cls.substring(packageLen+1); } - + /** * Returns complete component name of this activity. - * + * * @return Returns the complete component name for this activity */ public ComponentName getComponentName() @@ -4897,9 +4900,9 @@ public class Activity extends ContextThemeWrapper * that are private to this activity. This simply calls the underlying * {@link #getSharedPreferences(String, int)} method by passing in this activity's * class name as the preferences name. - * - * @param mode Operating mode. Use {@link #MODE_PRIVATE} for the default - * operation, {@link #MODE_WORLD_READABLE} and + * + * @param mode Operating mode. Use {@link #MODE_PRIVATE} for the default + * operation, {@link #MODE_WORLD_READABLE} and * {@link #MODE_WORLD_WRITEABLE} to control permissions. * * @return Returns the single SharedPreferences instance that can be used @@ -4908,12 +4911,12 @@ public class Activity extends ContextThemeWrapper public SharedPreferences getPreferences(int mode) { return getSharedPreferences(getLocalClassName(), mode); } - + private void ensureSearchManager() { if (mSearchManager != null) { return; } - + mSearchManager = new SearchManager(this, null); } @@ -5031,7 +5034,7 @@ public class Activity extends ContextThemeWrapper * <p> * In order for the progress bar to be shown, the feature must be requested * via {@link #requestWindowFeature(int)}. - * + * * @param visible Whether to show the progress bars in the title. */ public final void setProgressBarVisibility(boolean visible) { @@ -5051,14 +5054,14 @@ public class Activity extends ContextThemeWrapper getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF); } - + /** * Sets whether the horizontal progress bar in the title should be indeterminate (the circular * is always indeterminate). * <p> * In order for the progress bar to be shown, the feature must be requested * via {@link #requestWindowFeature(int)}. - * + * * @param indeterminate Whether the horizontal progress bar should be indeterminate. */ public final void setProgressBarIndeterminate(boolean indeterminate) { @@ -5066,13 +5069,13 @@ public class Activity extends ContextThemeWrapper indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF); } - + /** * Sets the progress for the progress bars in the title. * <p> * In order for the progress bar to be shown, the feature must be requested * via {@link #requestWindowFeature(int)}. - * + * * @param progress The progress for the progress bar. Valid ranges are from * 0 to 10000 (both inclusive). If 10000 is given, the progress * bar will be completely filled and will fade out. @@ -5080,7 +5083,7 @@ public class Activity extends ContextThemeWrapper public final void setProgress(int progress) { getWindow().setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START); } - + /** * Sets the secondary progress for the progress bar in the title. This * progress is drawn between the primary progress (set via @@ -5090,7 +5093,7 @@ public class Activity extends ContextThemeWrapper * <p> * In order for the progress bar to be shown, the feature must be requested * via {@link #requestWindowFeature(int)}. - * + * * @param secondaryProgress The secondary progress for the progress bar. Valid ranges are from * 0 to 10000 (both inclusive). */ @@ -5104,16 +5107,16 @@ public class Activity extends ContextThemeWrapper * volume controls. * <p> * The suggested audio stream will be tied to the window of this Activity. - * If the Activity is switched, the stream set here is no longer the - * suggested stream. The client does not need to save and restore the old - * suggested stream value in onPause and onResume. - * + * Volume requests which are received while the Activity is in the + * foreground will affect this stream. + * <p> + * It is not guaranteed that the hardware volume controls will always change + * this stream's volume (for example, if a call is in progress, its stream's + * volume may be changed instead). To reset back to the default, use + * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}. + * * @param streamType The type of the audio stream whose volume should be - * changed by the hardware volume controls. It is not guaranteed that - * the hardware volume controls will always change this stream's - * volume (for example, if a call is in progress, its stream's volume - * may be changed instead). To reset back to the default, use - * {@link AudioManager#USE_DEFAULT_STREAM_TYPE}. + * changed by the hardware volume controls. */ public final void setVolumeControlStream(int streamType) { getWindow().setVolumeControlStream(streamType); @@ -5122,7 +5125,7 @@ public class Activity extends ContextThemeWrapper /** * Gets the suggested audio stream whose volume should be changed by the * hardware volume controls. - * + * * @return The suggested audio stream type whose volume should be changed by * the hardware volume controls. * @see #setVolumeControlStream(int) @@ -5130,7 +5133,40 @@ public class Activity extends ContextThemeWrapper public final int getVolumeControlStream() { return getWindow().getVolumeControlStream(); } - + + /** + * Sets a {@link MediaController} to send media keys and volume changes to. + * <p> + * The controller will be tied to the window of this Activity. Media key and + * volume events which are received while the Activity is in the foreground + * will be forwarded to the controller and used to invoke transport controls + * or adjust the volume. This may be used instead of or in addition to + * {@link #setVolumeControlStream} to affect a specific session instead of a + * specific stream. + * <p> + * It is not guaranteed that the hardware volume controls will always change + * this session's volume (for example, if a call is in progress, its + * stream's volume may be changed instead). To reset back to the default use + * null as the controller. + * + * @param controller The controller for the session which should receive + * media keys and volume changes. + */ + public final void setMediaController(MediaController controller) { + getWindow().setMediaController(controller); + } + + /** + * Gets the controller which should be receiving media key and volume events + * while this activity is in the foreground. + * + * @return The controller which should receive events. + * @see #setMediaController(android.media.session.MediaController) + */ + public final MediaController getMediaController() { + return getWindow().getMediaController(); + } + /** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is @@ -5176,7 +5212,7 @@ public class Activity extends ContextThemeWrapper if (!"fragment".equals(name)) { return onCreateView(name, context, attrs); } - + return mFragments.onCreateView(parent, name, context, attrs); } @@ -5737,7 +5773,7 @@ public class Activity extends ContextThemeWrapper } // ------------------ Internal API ------------------ - + final void setParent(Activity parent) { mParent = parent; } @@ -5751,7 +5787,7 @@ public class Activity extends ContextThemeWrapper attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); - + mWindow = PolicyManager.makeNewWindow(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); @@ -5846,7 +5882,7 @@ public class Activity extends ContextThemeWrapper } mActivityTransitionState.enterReady(this); } - + final void performRestart() { mFragments.noteStateNotSaved(); @@ -5885,14 +5921,14 @@ public class Activity extends ContextThemeWrapper performStart(); } } - + final void performResume() { performRestart(); - + mFragments.execPendingActions(); - + mLastNonConfigurationInstances = null; - + mCalled = false; // mResumed is set by the instrumentation mInstrumentation.callActivityOnResume(this); @@ -5904,10 +5940,10 @@ public class Activity extends ContextThemeWrapper // Now really resume, and install the current status bar and menu. mCalled = false; - + mFragments.dispatchResume(); mFragments.execPendingActions(); - + onPostResume(); if (!mCalled) { throw new SuperNotCalledException( @@ -5930,12 +5966,12 @@ public class Activity extends ContextThemeWrapper } mResumed = false; } - + final void performUserLeaving() { onUserInteraction(); onUserLeaveHint(); } - + final void performStop() { mDoReportFullyDrawn = false; if (mLoadersStarted) { @@ -5948,7 +5984,7 @@ public class Activity extends ContextThemeWrapper } } } - + if (!mStopped) { if (mWindow != null) { mWindow.closeAllPanels(); @@ -5957,9 +5993,9 @@ public class Activity extends ContextThemeWrapper if (mToken != null && mParent == null) { WindowManagerGlobal.getInstance().setStoppedState(mToken, true); } - + mFragments.dispatchStop(); - + mCalled = false; mInstrumentation.callActivityOnStop(this); if (!mCalled) { @@ -5967,7 +6003,7 @@ public class Activity extends ContextThemeWrapper "Activity " + mComponent.toShortString() + " did not call through to super.onStop()"); } - + synchronized (mManagedCursors) { final int N = mManagedCursors.size(); for (int i=0; i<N; i++) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e1d0b86f5979..c8cab6f11b03 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2456,7 +2456,10 @@ public class ActivityManager { } /** - * @hide + * Return whether currently in lock task mode. When in this mode + * no new tasks can be created or switched to. + * + * @see Activity#startLockTask() */ public boolean isInLockTaskMode() { try { diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 15be9b18289b..e07421983f1b 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -403,20 +403,18 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { view.layout(left, top, right, bottom); } - protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState( + protected ArrayList<SharedElementOriginalState> setSharedElementState( Bundle sharedElementState, final ArrayList<View> snapshots) { - ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState = - new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>(); + ArrayList<SharedElementOriginalState> originalImageState = + new ArrayList<SharedElementOriginalState>(); if (sharedElementState != null) { int[] tempLoc = new int[2]; for (int i = 0; i < mSharedElementNames.size(); i++) { View sharedElement = mSharedElements.get(i); String name = mSharedElementNames.get(i); - Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement, + SharedElementOriginalState originalState = getOldSharedElementState(sharedElement, name, sharedElementState); - if (originalState != null) { - originalImageState.put((ImageView) sharedElement, originalState); - } + originalImageState.add(originalState); View parent = (View) sharedElement.getParent(); parent.getLocationOnScreen(tempLoc); setSharedElementState(sharedElement, name, sharedElementState, tempLoc); @@ -438,29 +436,34 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return originalImageState; } - private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name, + private static SharedElementOriginalState getOldSharedElementState(View view, String name, Bundle transitionArgs) { + + SharedElementOriginalState state = new SharedElementOriginalState(); + state.mLeft = view.getLeft(); + state.mTop = view.getTop(); + state.mRight = view.getRight(); + state.mBottom = view.getBottom(); + state.mMeasuredWidth = view.getMeasuredWidth(); + state.mMeasuredHeight = view.getMeasuredHeight(); if (!(view instanceof ImageView)) { - return null; + return state; } Bundle bundle = transitionArgs.getBundle(name); if (bundle == null) { - return null; + return state; } int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1); if (scaleTypeInt < 0) { - return null; + return state; } ImageView imageView = (ImageView) view; - ImageView.ScaleType originalScaleType = imageView.getScaleType(); - - Matrix originalMatrix = null; - if (originalScaleType == ImageView.ScaleType.MATRIX) { - originalMatrix = new Matrix(imageView.getImageMatrix()); + state.mScaleType = imageView.getScaleType(); + if (state.mScaleType == ImageView.ScaleType.MATRIX) { + state.mMatrix = new Matrix(imageView.getImageMatrix()); } - - return Pair.create(originalScaleType, originalMatrix); + return state; } protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) { @@ -489,13 +492,26 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { return snapshots; } - protected static void setOriginalImageViewState( - ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) { + protected static void setOriginalSharedElementState(ArrayList<View> sharedElements, + ArrayList<SharedElementOriginalState> originalState) { for (int i = 0; i < originalState.size(); i++) { - ImageView imageView = originalState.keyAt(i); - Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i); - imageView.setScaleType(state.first); - imageView.setImageMatrix(state.second); + View view = sharedElements.get(i); + SharedElementOriginalState state = originalState.get(i); + if (view instanceof ImageView && state.mScaleType != null) { + ImageView imageView = (ImageView) view; + imageView.setScaleType(state.mScaleType); + if (state.mScaleType == ImageView.ScaleType.MATRIX) { + imageView.setImageMatrix(state.mMatrix); + } + } + // origignal widthspec might be AT_MOST, but it should work for most + // cases. + int widthSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredWidth, + View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredHeight, + View.MeasureSpec.EXACTLY); + view.measure(widthSpec, heightSpec); + view.layout(state.mLeft, state.mTop, state.mRight, state.mBottom); } } @@ -622,4 +638,15 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { } } + static class SharedElementOriginalState { + int mLeft; + int mTop; + int mRight; + int mBottom; + int mMeasuredWidth; + int mMeasuredHeight; + ImageView.ScaleType mScaleType; + Matrix mMatrix; + } + } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 2935b8e64d98..47305599ba13 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -33,6 +33,7 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; @@ -52,6 +53,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -59,6 +61,7 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.Log; import android.view.Display; +import com.android.internal.util.Preconditions; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -1447,6 +1450,62 @@ final class ApplicationPackageManager extends PackageManager { return false; } + @Override + public KeySet getKeySetByAlias(String packageName, String alias) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(alias); + IBinder keySetToken; + try { + keySetToken = mPM.getKeySetByAlias(packageName, alias); + } catch (RemoteException e) { + return null; + } + if (keySetToken == null) { + return null; + } + return new KeySet(keySetToken); + } + + @Override + public KeySet getSigningKeySet(String packageName) { + Preconditions.checkNotNull(packageName); + IBinder keySetToken; + try { + keySetToken = mPM.getSigningKeySet(packageName); + } catch (RemoteException e) { + return null; + } + if (keySetToken == null) { + return null; + } + return new KeySet(keySetToken); + } + + + @Override + public boolean isSignedBy(String packageName, KeySet ks) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(ks); + IBinder keySetToken = ks.getToken(); + try { + return mPM.isPackageSignedByKeySet(packageName, keySetToken); + } catch (RemoteException e) { + return false; + } + } + + @Override + public boolean isSignedByExactly(String packageName, KeySet ks) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(ks); + IBinder keySetToken = ks.getToken(); + try { + return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken); + } catch (RemoteException e) { + return false; + } + } + /** * @hide */ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 395c1b9b1286..b9de22028627 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -247,6 +247,8 @@ class ContextImpl extends Context { @GuardedBy("mSync") private File mFilesDir; @GuardedBy("mSync") + private File mNoBackupFilesDir; + @GuardedBy("mSync") private File mCacheDir; @GuardedBy("mSync") @@ -961,27 +963,42 @@ class ContextImpl extends Context { return f.delete(); } + // Common-path handling of app data dir creation + private static File createFilesDirLocked(File file) { + if (!file.exists()) { + if (!file.mkdirs()) { + if (file.exists()) { + // spurious failure; probably racing with another process for this app + return file; + } + Log.w(TAG, "Unable to create files subdir " + file.getPath()); + return null; + } + FileUtils.setPermissions( + file.getPath(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + -1, -1); + } + return file; + } + @Override public File getFilesDir() { synchronized (mSync) { if (mFilesDir == null) { mFilesDir = new File(getDataDirFile(), "files"); } - if (!mFilesDir.exists()) { - if(!mFilesDir.mkdirs()) { - if (mFilesDir.exists()) { - // spurious failure; probably racing with another process for this app - return mFilesDir; - } - Log.w(TAG, "Unable to create files directory " + mFilesDir.getPath()); - return null; - } - FileUtils.setPermissions( - mFilesDir.getPath(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, - -1, -1); + return createFilesDirLocked(mFilesDir); + } + } + + @Override + public File getNoBackupFilesDir() { + synchronized (mSync) { + if (mNoBackupFilesDir == null) { + mNoBackupFilesDir = new File(getDataDirFile(), "no_backup"); } - return mFilesDir; + return createFilesDirLocked(mNoBackupFilesDir); } } @@ -1033,22 +1050,8 @@ class ContextImpl extends Context { if (mCacheDir == null) { mCacheDir = new File(getDataDirFile(), "cache"); } - if (!mCacheDir.exists()) { - if(!mCacheDir.mkdirs()) { - if (mCacheDir.exists()) { - // spurious failure; probably racing with another process for this app - return mCacheDir; - } - Log.w(TAG, "Unable to create cache directory " + mCacheDir.getAbsolutePath()); - return null; - } - FileUtils.setPermissions( - mCacheDir.getPath(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, - -1, -1); - } + return createFilesDirLocked(mCacheDir); } - return mCacheDir; } @Override diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java index f50c93b3cc0e..132606434100 100644 --- a/core/java/android/app/EnterTransitionCoordinator.java +++ b/core/java/android/app/EnterTransitionCoordinator.java @@ -282,7 +282,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState, mSharedElementNames); setTransitionAlpha(mSharedElements, 1); - ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState = + ArrayList<SharedElementOriginalState> originalImageViewState = setSharedElementState(sharedElementState, sharedElementSnapshots); requestLayoutForSharedElements(); @@ -294,7 +294,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator { startEnterTransition(transition); } - setOriginalImageViewState(originalImageViewState); + setOriginalSharedElementState(mSharedElements, originalImageViewState); if (mResultReceiver != null) { // We can't trust that the view will disappear on the same frame that the shared diff --git a/core/java/android/app/PackageInstallObserver.java b/core/java/android/app/PackageInstallObserver.java index 71171118b887..1b2504eefcc2 100644 --- a/core/java/android/app/PackageInstallObserver.java +++ b/core/java/android/app/PackageInstallObserver.java @@ -25,7 +25,7 @@ public class PackageInstallObserver { @Override public void packageInstalled(String basePackageName, Bundle extras, int returnCode, String msg) { - PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode); + PackageInstallObserver.this.packageInstalled(basePackageName, extras, returnCode, msg); } }; diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 886f1a64143f..e2a86e85cd53 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -247,12 +247,40 @@ public abstract class BackupAgent extends ContextWrapper { throws IOException; /** - * The default implementation backs up the entirety of the application's "owned" - * file system trees to the output. + * The application is having its entire file system contents backed up. {@code data} + * points to the backup destination, and the app has the opportunity to choose which + * files are to be stored. To commit a file as part of the backup, call the + * {@link #fullBackupFile(File, FullBackupDataOutput)} helper method. After all file + * data is written to the output, the agent returns from this method and the backup + * operation concludes. + * + * <p>Certain parts of the app's data are never backed up even if the app explicitly + * sends them to the output: + * + * <ul> + * <li>The contents of the {@link #getCacheDir()} directory</li> + * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li> + * <li>The contents of the app's shared library directory</li> + * </ul> + * + * <p>The default implementation of this method backs up the entirety of the + * application's "owned" file system trees to the output other than the few exceptions + * listed above. Apps only need to override this method if they need to impose special + * limitations on which files are being stored beyond the control that + * {@link #getNoBackupFilesDir()} offers. + * + * @param data A structured wrapper pointing to the backup destination. + * @throws IOException + * + * @see Context#getNoBackupFilesDir() + * @see #fullBackupFile(File, FullBackupDataOutput) + * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) */ public void onFullBackup(FullBackupDataOutput data) throws IOException { ApplicationInfo appInfo = getApplicationInfo(); + // Note that we don't need to think about the no_backup dir because it's outside + // all of the ones we will be traversing String rootDir = new File(appInfo.dataDir).getCanonicalPath(); String filesDir = getFilesDir().getCanonicalPath(); String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); @@ -311,6 +339,10 @@ public abstract class BackupAgent extends ContextWrapper { * to place it with the proper location and permissions on the device where the * data is restored. * + * <p class="note">It is safe to explicitly back up files underneath your application's + * {@link #getNoBackupFilesDir()} directory, and they will be restored to that + * location correctly. + * * @param file The file to be backed up. The file must exist and be readable by * the caller. * @param output The destination to which the backed-up file data will be sent. @@ -319,6 +351,7 @@ public abstract class BackupAgent extends ContextWrapper { // Look up where all of our various well-defined dir trees live on this device String mainDir; String filesDir; + String nbFilesDir; String dbDir; String spDir; String cacheDir; @@ -331,6 +364,7 @@ public abstract class BackupAgent extends ContextWrapper { try { mainDir = new File(appInfo.dataDir).getCanonicalPath(); filesDir = getFilesDir().getCanonicalPath(); + nbFilesDir = getNoBackupFilesDir().getCanonicalPath(); dbDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); spDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); cacheDir = getCacheDir().getCanonicalPath(); @@ -354,8 +388,10 @@ public abstract class BackupAgent extends ContextWrapper { return; } - if (filePath.startsWith(cacheDir) || filePath.startsWith(libDir)) { - Log.w(TAG, "lib and cache files are not backed up"); + if (filePath.startsWith(cacheDir) + || filePath.startsWith(libDir) + || filePath.startsWith(nbFilesDir)) { + Log.w(TAG, "lib, cache, and no_backup files are not backed up"); return; } @@ -508,6 +544,8 @@ public abstract class BackupAgent extends ContextWrapper { mode = -1; // < 0 is a token to skip attempting a chmod() } } + } else if (domain.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) { + basePath = getNoBackupFilesDir().getCanonicalPath(); } else { // Not a supported location Log.i(TAG, "Unrecognized domain " + domain); diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 6ebb6c45de64..e5b47c652ccb 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -40,6 +40,7 @@ public class FullBackup { public static final String OBB_TREE_TOKEN = "obb"; public static final String ROOT_TREE_TOKEN = "r"; public static final String DATA_TREE_TOKEN = "f"; + public static final String NO_BACKUP_TREE_TOKEN = "nb"; public static final String DATABASE_TREE_TOKEN = "db"; public static final String SHAREDPREFS_TREE_TOKEN = "sp"; public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef"; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 535aaa1a936b..a52cbdd85a6e 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -647,6 +647,26 @@ public abstract class Context { public abstract File getFilesDir(); /** + * Returns the absolute path to the directory on the filesystem similar to + * {@link #getFilesDir()}. The difference is that files placed under this + * directory will be excluded from automatic backup to remote storage. See + * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion + * of the automatic backup mechanism in Android. + * + * <p>No permissions are required to read or write to the returned path, since this + * path is internal storage. + * + * @return The path of the directory holding application files that will not be + * automatically backed up to remote storage. + * + * @see #openFileOutput + * @see #getFileStreamPath + * @see #getDir + * @see android.app.backup.BackupAgent + */ + public abstract File getNoBackupFilesDir(); + + /** * Returns the absolute path to the directory on the primary external filesystem * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() * Environment.getExternalStorageDirectory()}) where the application can diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index dbf9122e454d..13eed07dbef0 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -200,7 +200,12 @@ public class ContextWrapper extends Context { public File getFilesDir() { return mBase.getFilesDir(); } - + + @Override + public File getNoBackupFilesDir() { + return mBase.getNoBackupFilesDir(); + } + @Override public File getExternalFilesDir(String type) { return mBase.getExternalFilesDir(type); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 58d3526c0b29..3a98f5d53e3c 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -429,4 +429,9 @@ interface IPackageManager { boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId); boolean getBlockUninstallForUser(String packageName, int userId); + + IBinder getKeySetByAlias(String packageName, String alias); + IBinder getSigningKeySet(String packageName); + boolean isPackageSignedByKeySet(String packageName, IBinder ks); + boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks); } diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java index 0ef09a432528..fcdaa1867391 100644 --- a/core/java/android/content/pm/KeySet.java +++ b/core/java/android/content/pm/KeySet.java @@ -16,19 +16,36 @@ package android.content.pm; -import android.os.Binder; +import android.os.IBinder; -/** @hide */ +/** + * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml + * file for the application. A {@code KeySet} can be used explicitly to + * represent a trust relationship with other applications on the device. + */ public class KeySet { - private Binder token; + private IBinder token; /** @hide */ - public KeySet(Binder token) { + public KeySet(IBinder token) { + if (token == null) { + throw new NullPointerException("null value for KeySet IBinder token"); + } this.token = token; } - Binder getToken() { + /** @hide */ + public IBinder getToken() { return token; } + + @Override + public boolean equals(Object o) { + if (o instanceof KeySet) { + KeySet ks = (KeySet) o; + return token == ks.token; + } + return false; + } }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 03d47019e3ed..91ebbbfe6193 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3602,6 +3602,33 @@ public abstract class PackageManager { public abstract boolean isSafeMode(); /** + * Return the {@link KeySet} associated with the String alias for this + * application. + * + * @param alias The alias for a given {@link KeySet} as defined in the + * application's AndroidManifest.xml. + */ + public abstract KeySet getKeySetByAlias(String packageName, String alias); + + /** Return the signing {@link KeySet} for this application. */ + public abstract KeySet getSigningKeySet(String packageName); + + /** + * Return whether the package denoted by packageName has been signed by all + * of the keys specified by the {@link KeySet} ks. This will return true if + * the package has been signed by additional keys (a superset) as well. + * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}. + */ + public abstract boolean isSignedBy(String packageName, KeySet ks); + + /** + * Return whether the package denoted by packageName has been signed by all + * of, and only, the keys specified by the {@link KeySet} ks. Compare to + * {@link #isSignedBy(String packageName, KeySet ks)}. + */ + public abstract boolean isSignedByExactly(String packageName, KeySet ks); + + /** * Attempts to move package resources from internal to external media or vice versa. * Since this may take a little while, the result will * be posted back to the given observer. This call may fail if the calling context diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index e9793c48fab7..5f29e5c50472 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -17,6 +17,7 @@ package android.hardware.camera2.legacy; import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; @@ -463,6 +464,24 @@ public class LegacyCameraDevice implements AutoCloseable { return ids.contains(id); } + static void setSurfaceOrientation(Surface surface, int facing, int sensorOrientation) + throws BufferQueueAbandonedException { + checkNotNull(surface); + LegacyExceptionUtils.throwOnError(nativeSetSurfaceOrientation(surface, facing, + sensorOrientation)); + } + + static Size getTextureSize(SurfaceTexture surfaceTexture) + throws BufferQueueAbandonedException { + checkNotNull(surfaceTexture); + + int[] dimens = new int[2]; + LegacyExceptionUtils.throwOnError(nativeDetectTextureDimens(surfaceTexture, + /*out*/dimens)); + + return new Size(dimens[0], dimens[1]); + } + private static native int nativeDetectSurfaceType(Surface surface); private static native int nativeDetectSurfaceDimens(Surface surface, @@ -479,4 +498,11 @@ public class LegacyCameraDevice implements AutoCloseable { private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); private static native long nativeGetSurfaceId(Surface surface); + + private static native int nativeSetSurfaceOrientation(Surface surface, int facing, + int sensorOrientation); + + private static native int nativeDetectTextureDimens(SurfaceTexture surfaceTexture, + /*out*/int[/*2*/] dimens); + } diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index e6d84c564826..7d1be3b9c67a 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -292,10 +292,13 @@ public class RequestThreadManager { mInFlightPreview = null; mInFlightJpeg = null; + int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING); + int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); if (outputs != null) { for (Surface s : outputs) { try { int format = LegacyCameraDevice.detectSurfaceType(s); + LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation); switch (format) { case CameraMetadataNative.NATIVE_JPEG_FORMAT: mCallbackOutputs.add(s); diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index daa64c05e8b3..fdf9ba06d41d 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -1,18 +1,18 @@ /* -* 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. -*/ + * 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.hardware.camera2.legacy; import android.graphics.ImageFormat; @@ -98,14 +98,14 @@ public class SurfaceTextureRenderer { */ private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;\n" + - "uniform mat4 uSTMatrix;\n" + - "attribute vec4 aPosition;\n" + - "attribute vec4 aTextureCoord;\n" + - "varying vec2 vTextureCoord;\n" + - "void main() {\n" + - " gl_Position = uMVPMatrix * aPosition;\n" + - " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + - "}\n"; + "uniform mat4 uSTMatrix;\n" + + "attribute vec4 aPosition;\n" + + "attribute vec4 aTextureCoord;\n" + + "varying vec2 vTextureCoord;\n" + + "void main() {\n" + + " gl_Position = uMVPMatrix * aPosition;\n" + + " vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" + + "}\n"; /** * This fragment shader simply draws the color in the 2D texture at @@ -113,12 +113,12 @@ public class SurfaceTextureRenderer { */ private static final String FRAGMENT_SHADER = "#extension GL_OES_EGL_image_external : require\n" + - "precision mediump float;\n" + - "varying vec2 vTextureCoord;\n" + - "uniform samplerExternalOES sTexture;\n" + - "void main() {\n" + - " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + - "}\n"; + "precision mediump float;\n" + + "varying vec2 vTextureCoord;\n" + + "uniform samplerExternalOES sTexture;\n" + + "void main() {\n" + + " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" + + "}\n"; private float[] mMVPMatrix = new float[GL_MATRIX_SIZE]; private float[] mSTMatrix = new float[GL_MATRIX_SIZE]; @@ -189,12 +189,56 @@ public class SurfaceTextureRenderer { return program; } - private void drawFrame(SurfaceTexture st) { + private void drawFrame(SurfaceTexture st, int width, int height) { checkGlError("onDrawFrame start"); st.getTransformMatrix(mSTMatrix); + Size dimens; + try { + dimens = LegacyCameraDevice.getTextureSize(st); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + // Should never hit this. + throw new IllegalStateException("Surface abandoned, skipping drawFrame...", e); + } + + Matrix.setIdentityM(mMVPMatrix, /*smOffset*/0); + + float texWidth = dimens.getWidth(); + float texHeight = dimens.getHeight(); + + if (texWidth <= 0 || texHeight <= 0) { + throw new IllegalStateException("Illegal intermediate texture with dimension of 0"); + } + + // Find largest scaling factor from the intermediate texture dimension to the + // output surface dimension. Scaling the intermediate texture by this allows + // us to letterbox/pillerbox the output surface into the intermediate texture. + float widthRatio = width / texWidth; + float heightRatio = height / texHeight; + float actual = (widthRatio < heightRatio) ? heightRatio : widthRatio; + + if (DEBUG) { + Log.d(TAG, "Scaling factor " + actual + " used for " + width + "x" + height + + " surface, intermediate buffer size is " + texWidth + "x" + texHeight); + } + + // Set the viewport height and width to be the scaled intermediate texture dimensions. + int viewportW = (int) (actual * texWidth); + int viewportH = (int) (actual * texHeight); + + // Set the offset of the viewport so that the output surface is centered in the viewport. + float dx = (width - viewportW) / 2f; + float dy = (height - viewportH) / 2f; + if (DEBUG) { - GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + Log.d(TAG, "Translation " + dx + "," + dy + " used for " + width + "x" + height + + " surface"); + } + + GLES20.glViewport((int) dx, (int) dy, viewportW, viewportH); + + if (DEBUG) { + GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); } @@ -218,7 +262,6 @@ public class SurfaceTextureRenderer { GLES20.glEnableVertexAttribArray(maTextureHandle); checkGlError("glEnableVertexAttribArray maTextureHandle"); - Matrix.setIdentityM(mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, /*count*/ 1, /*transpose*/ false, mMVPMatrix, /*offset*/ 0); GLES20.glUniformMatrix4fv(muSTMatrixHandle, /*count*/ 1, /*transpose*/ false, mSTMatrix, @@ -589,18 +632,19 @@ public class SurfaceTextureRenderer { for (EGLSurfaceHolder holder : mSurfaces) { if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) { makeCurrent(holder.eglSurface); - drawFrame(mSurfaceTexture); + drawFrame(mSurfaceTexture, holder.width, holder.height); swapBuffers(holder.eglSurface); } } for (EGLSurfaceHolder holder : mConversionSurfaces) { if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) { makeCurrent(holder.eglSurface); - drawFrame(mSurfaceTexture); + drawFrame(mSurfaceTexture, holder.width, holder.height); mPBufferPixels.clear(); - GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, GLES20.GL_RGBA, - GLES20.GL_UNSIGNED_BYTE, mPBufferPixels); + GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, + GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels); checkGlError("glReadPixels"); + try { int format = LegacyCameraDevice.detectSurfaceType(holder.surface); LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(), diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 7fab80801448..b554548e0232 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -461,6 +461,11 @@ public final class ViewRootImpl implements ViewParent, } } + // Compute surface insets required to draw at specified Z value. + // TODO: Use real shadow insets for a constant max Z. + final int surfaceInset = (int) Math.ceil(view.getZ() * 2); + attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset); + CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo(); mTranslator = compatibilityInfo.getTranslator(); mDisplayAdjustments.setActivityToken(attrs.token); @@ -1713,8 +1718,8 @@ public final class ViewRootImpl implements ViewParent, if (hwInitialized || mWidth != mAttachInfo.mHardwareRenderer.getWidth() || mHeight != mAttachInfo.mHardwareRenderer.getHeight()) { - final Rect shadowInsets = params != null ? params.shadowInsets : null; - mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, shadowInsets); + final Rect surfaceInsets = params != null ? params.surfaceInsets : null; + mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight, surfaceInsets); if (!hwInitialized) { mAttachInfo.mHardwareRenderer.invalidate(mSurface); mFullRedrawNeeded = true; @@ -2371,7 +2376,7 @@ public final class ViewRootImpl implements ViewParent, } final WindowManager.LayoutParams params = mWindowAttributes; - final Rect surfaceInsets = params != null ? params.shadowInsets : null; + final Rect surfaceInsets = params != null ? params.surfaceInsets : null; boolean animating = mScroller != null && mScroller.computeScrollOffset(); final int curScrollY; if (animating) { @@ -3155,7 +3160,7 @@ public final class ViewRootImpl implements ViewParent, mFullRedrawNeeded = true; try { final WindowManager.LayoutParams lp = mWindowAttributes; - final Rect surfaceInsets = lp != null ? lp.shadowInsets : null; + final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null; mAttachInfo.mHardwareRenderer.initializeIfNeeded( mWidth, mHeight, mSurface, surfaceInsets); } catch (OutOfResourcesException e) { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index aa71ed8c74b6..c169d353c570 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -23,6 +23,8 @@ import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.drawable.Drawable; +import android.media.session.MediaController; +import android.media.session.MediaSession; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; @@ -157,7 +159,7 @@ public abstract class Window { private static final String PROPERTY_HARDWARE_UI = "persist.sys.ui.hw"; private final Context mContext; - + private TypedArray mWindowStyle; private Callback mCallback; private OnWindowDismissedCallback mOnWindowDismissedCallback; @@ -181,7 +183,7 @@ public abstract class Window { private int mDefaultWindowFormat = PixelFormat.OPAQUE; private boolean mHasSoftInputMode = false; - + private boolean mDestroyed; // The current window attributes. @@ -227,7 +229,7 @@ public abstract class Window { * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent event); - + /** * Called to process trackball events. At the very least your * implementation must call @@ -313,14 +315,14 @@ public abstract class Window { * Called when a panel's menu is opened by the user. This may also be * called when the menu is changing from one type to another (for * example, from the icon menu to the expanded menu). - * + * * @param featureId The panel that the menu is in. * @param menu The menu that is opened. * @return Return true to allow the menu to open, or false to prevent * the menu from opening. */ public boolean onMenuOpened(int featureId, Menu menu); - + /** * Called when a panel's menu item has been selected by the user. * @@ -364,31 +366,31 @@ public abstract class Window { * for more information. */ public void onAttachedToWindow(); - + /** * Called when the window has been attached to the window manager. * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()} * for more information. */ public void onDetachedFromWindow(); - + /** * Called when a panel is being closed. If another logical subsequent * panel is being opened (and this panel is being closed to make room for the subsequent * panel), this method will NOT be called. - * + * * @param featureId The panel that is being displayed. * @param menu If onCreatePanelView() returned null, this is the Menu * being displayed in the panel. */ public void onPanelClosed(int featureId, Menu menu); - + /** * Called when the user signals the desire to start a search. - * + * * @return true if search launched, false if activity refuses (blocks) - * - * @see android.app.Activity#onSearchRequested() + * + * @see android.app.Activity#onSearchRequested() */ public boolean onSearchRequested(); @@ -457,7 +459,7 @@ public abstract class Window { return mWindowStyle; } } - + /** * Set the container for this window. If not set, the DecorWindow * operates as a top-level window; otherwise, it negotiates with the @@ -488,7 +490,7 @@ public abstract class Window { public final boolean hasChildren() { return mHasChildren; } - + /** @hide */ public final void destroy() { mDestroyed = true; @@ -622,14 +624,14 @@ public abstract class Window { * callback will be used to tell you about state changes to the surface. */ public abstract void takeSurface(SurfaceHolder.Callback2 callback); - + /** * Take ownership of this window's InputQueue. The window will no * longer read and dispatch input events from the queue; it is your * responsibility to do so. */ public abstract void takeInputQueue(InputQueue.Callback callback); - + /** * Return whether this window is being displayed with a floating style * (based on the {@link android.R.attr#windowIsFloating} attribute in @@ -740,7 +742,7 @@ public abstract class Window { } dispatchWindowAttributesChanged(attrs); } - + /** * Convenience function to set the flag bits as specified in flags, as * per {@link #setFlags}. @@ -756,7 +758,7 @@ public abstract class Window { public void addPrivateFlags(int flags) { setPrivateFlags(flags, flags); } - + /** * Convenience function to clear the flag bits as specified in flags, as * per {@link #setFlags}. @@ -772,7 +774,7 @@ public abstract class Window { * Set the flags of the window, as per the * {@link WindowManager.LayoutParams WindowManager.LayoutParams} * flags. - * + * * <p>Note that some flags must be set before the window decoration is * created (by the first call to * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)} or @@ -859,20 +861,20 @@ public abstract class Window { protected final int getForcedWindowFlags() { return mForcedWindowFlags; } - + /** * Has the app specified their own soft input mode? */ protected final boolean hasSoftInputMode() { return mHasSoftInputMode; } - + /** @hide */ public void setCloseOnTouchOutside(boolean close) { mCloseOnTouchOutside = close; mSetCloseOnTouchOutside = true; } - + /** @hide */ public void setCloseOnTouchOutsideIfNotSet(boolean close) { if (!mSetCloseOnTouchOutside) { @@ -880,10 +882,10 @@ public abstract class Window { mSetCloseOnTouchOutside = true; } } - + /** @hide */ public abstract void alwaysReadCloseOnTouchAttr(); - + /** @hide */ public boolean shouldCloseOnTouch(Context context, MotionEvent event) { if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN @@ -892,7 +894,7 @@ public abstract class Window { } return false; } - + private boolean isOutOfBounds(Context context, MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); @@ -902,7 +904,7 @@ public abstract class Window { || (x > (decorView.getWidth()+slop)) || (y > (decorView.getHeight()+slop)); } - + /** * Enable extended screen features. This must be called before * setContentView(). May be called as many times as desired as long as it @@ -989,7 +991,7 @@ public abstract class Window { * of the window that can not, from this point forward, be changed: the * features that have been requested with {@link #requestFeature(int)}, * and certain window flags as described in {@link #setFlags(int, int)}. - * + * * @param view The desired content to display. * @param params Layout parameters for the view. */ @@ -1037,7 +1039,7 @@ public abstract class Window { public abstract void togglePanel(int featureId, KeyEvent event); public abstract void invalidatePanelMenu(int featureId); - + public abstract boolean performPanelShortcut(int featureId, int keyCode, KeyEvent event, @@ -1052,17 +1054,17 @@ public abstract class Window { /** * Should be called when the configuration is changed. - * + * * @param newConfig The new configuration. */ public abstract void onConfigurationChanged(Configuration newConfig); - + /** * Change the background of this window to a Drawable resource. Setting the * background to null will make the window be opaque. To make the window * transparent, you can use an empty drawable (for instance a ColorDrawable * with the color 0 or the system drawable android:drawable/empty.) - * + * * @param resid The resource identifier of a drawable resource which will be * installed as the new background. */ @@ -1173,7 +1175,7 @@ public abstract class Window { * */ public abstract boolean superDispatchTouchEvent(MotionEvent event); - + /** * Used by custom windows, such as Dialog, to pass the trackball event * further down the view hierarchy. Application developers should @@ -1181,7 +1183,7 @@ public abstract class Window { * */ public abstract boolean superDispatchTrackballEvent(MotionEvent event); - + /** * Used by custom windows, such as Dialog, to pass the generic motion event * further down the view hierarchy. Application developers should @@ -1194,11 +1196,11 @@ public abstract class Window { * Retrieve the top-level window decor view (containing the standard * window frame/decorations and the client's content inside of that), which * can be added as a window to the window manager. - * + * * <p><em>Note that calling this function for the first time "locks in" * various window characteristics as described in * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p> - * + * * @return Returns the top-level window decor view. */ public abstract View getDecorView(); @@ -1206,16 +1208,16 @@ public abstract class Window { /** * Retrieve the current decor view, but only if it has already been created; * otherwise returns null. - * + * * @return Returns the top-level window decor or null. * @see #getDecorView */ public abstract View peekDecorView(); public abstract Bundle saveHierarchyState(); - + public abstract void restoreHierarchyState(Bundle savedInstanceState); - + protected abstract void onActive(); /** @@ -1233,10 +1235,10 @@ public abstract class Window { { return mFeatures; } - + /** * Query for the availability of a certain feature. - * + * * @param feature The feature ID to check * @return true if the feature is enabled, false otherwise. */ @@ -1290,9 +1292,9 @@ public abstract class Window { * @param event the {@link android.view.KeyEvent} to use to help check. */ public abstract boolean isShortcutKey(int keyCode, KeyEvent event); - + /** - * @see android.app.Activity#setVolumeControlStream(int) + * @see android.app.Activity#setVolumeControlStream(int) */ public abstract void setVolumeControlStream(int streamType); @@ -1302,6 +1304,19 @@ public abstract class Window { public abstract int getVolumeControlStream(); /** + * @see android.app.Activity#setMediaController(android.media.session.MediaController) + */ + public void setMediaController(MediaController controller) { + } + + /** + * @see android.app.Activity#getMediaController() + */ + public MediaController getMediaController() { + return null; + } + + /** * Set extra options that will influence the UI for this window. * @param uiOptions Flags specifying extra options for this window. */ diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index c06b5d8f50a9..034778ff397a 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1298,7 +1298,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public Rect shadowInsets = new Rect(); + public Rect surfaceInsets = new Rect(); /** * The desired bitmap format. May be one of the constants in @@ -1580,10 +1580,10 @@ public interface WindowManager extends ViewManager { out.writeInt(hasSystemUiListeners ? 1 : 0); out.writeInt(inputFeatures); out.writeLong(userActivityTimeout); - out.writeInt(shadowInsets.left); - out.writeInt(shadowInsets.top); - out.writeInt(shadowInsets.right); - out.writeInt(shadowInsets.bottom); + out.writeInt(surfaceInsets.left); + out.writeInt(surfaceInsets.top); + out.writeInt(surfaceInsets.right); + out.writeInt(surfaceInsets.bottom); } public static final Parcelable.Creator<LayoutParams> CREATOR @@ -1626,7 +1626,10 @@ public interface WindowManager extends ViewManager { hasSystemUiListeners = in.readInt() != 0; inputFeatures = in.readInt(); userActivityTimeout = in.readLong(); - shadowInsets.set(in.readInt(), in.readInt(), in.readInt(), in.readInt()); + surfaceInsets.left = in.readInt(); + surfaceInsets.top = in.readInt(); + surfaceInsets.right = in.readInt(); + surfaceInsets.bottom = in.readInt(); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -1658,7 +1661,7 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int TRANSLUCENT_FLAGS_CHANGED = 1<<19; /** {@hide} */ - public static final int SHADOW_INSETS_CHANGED = 1<<20; + public static final int SURFACE_INSETS_CHANGED = 1<<20; /** {@hide} */ public static final int EVERYTHING_CHANGED = 0xffffffff; @@ -1794,9 +1797,9 @@ public interface WindowManager extends ViewManager { changes |= USER_ACTIVITY_TIMEOUT_CHANGED; } - if (!shadowInsets.equals(o.shadowInsets)) { - shadowInsets.set(o.shadowInsets); - changes |= SHADOW_INSETS_CHANGED; + if (!surfaceInsets.equals(o.surfaceInsets)) { + surfaceInsets.set(o.surfaceInsets); + changes |= SURFACE_INSETS_CHANGED; } return changes; @@ -1898,8 +1901,8 @@ public interface WindowManager extends ViewManager { if (userActivityTimeout >= 0) { sb.append(" userActivityTimeout=").append(userActivityTimeout); } - if (!shadowInsets.equals(Insets.NONE)) { - sb.append(" shadowInsets=").append(shadowInsets); + if (!surfaceInsets.equals(Insets.NONE)) { + sb.append(" surfaceInsets=").append(surfaceInsets); } sb.append('}'); return sb.toString(); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 9701c6fce6ba..aa0b94f0208c 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -36,7 +36,6 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.util.LongSparseArray; -import android.util.MathUtils; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.StateSet; @@ -61,8 +60,6 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.inputmethod.BaseInputConnection; @@ -7269,290 +7266,4 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } - - /** - * Abstract position scroller that handles sub-position scrolling but has no - * understanding of layout. - */ - abstract class AbsSubPositionScroller extends AbsPositionScroller { - private static final int DURATION_AUTO = -1; - - private static final int DURATION_AUTO_MIN = 100; - private static final int DURATION_AUTO_MAX = 500; - - private final SubScroller mSubScroller = new SubScroller(); - - /** - * The target offset in pixels between the top of the list and the top - * of the target position. - */ - private int mOffset; - - /** - * Scroll the minimum amount to get the target view entirely on-screen. - */ - private void scrollToPosition(final int targetPosition, final boolean useOffset, - final int offset, final int boundPosition, final int duration) { - stop(); - - if (mDataChanged) { - // Wait until we're back in a stable state to try this. - mPositionScrollAfterLayout = new Runnable() { - @Override - public void run() { - scrollToPosition( - targetPosition, useOffset, offset, boundPosition, duration); - } - }; - return; - } - - if (mAdapter == null) { - // Can't scroll anywhere without an adapter. - return; - } - - final int itemCount = getCount(); - final int clampedPosition = MathUtils.constrain(targetPosition, 0, itemCount - 1); - final int clampedBoundPosition = MathUtils.constrain(boundPosition, -1, itemCount - 1); - final int firstPosition = getFirstVisiblePosition(); - final int lastPosition = firstPosition + getChildCount(); - final int targetRow = getRowForPosition(clampedPosition); - final int firstRow = getRowForPosition(firstPosition); - final int lastRow = getRowForPosition(lastPosition); - if (useOffset || targetRow <= firstRow) { - // Offset so the target row is top-aligned. - mOffset = offset; - } else if (targetRow >= lastRow - 1) { - // Offset so the target row is bottom-aligned. - final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - mOffset = getHeightForPosition(clampedPosition) - listHeight; - } else { - // Don't scroll, target is entirely on-screen. - return; - } - - float endSubRow = targetRow; - if (clampedBoundPosition != INVALID_POSITION) { - final int boundRow = getRowForPosition(clampedBoundPosition); - if (boundRow >= firstRow && boundRow < lastRow && boundRow != targetRow) { - endSubRow = computeBoundSubRow(targetRow, boundRow); - } - } - - final View firstChild = getChildAt(0); - if (firstChild == null) { - return; - } - - final int firstChildHeight = firstChild.getHeight(); - final float startOffsetRatio; - if (firstChildHeight == 0) { - startOffsetRatio = 0; - } else { - startOffsetRatio = -firstChild.getTop() / (float) firstChildHeight; - } - - final float startSubRow = MathUtils.constrain( - firstRow + startOffsetRatio, 0, getCount()); - if (startSubRow == endSubRow && mOffset == 0) { - // Don't scroll, target is already in position. - return; - } - - final int durationMillis; - if (duration == DURATION_AUTO) { - final float subRowDelta = Math.abs(startSubRow - endSubRow); - durationMillis = (int) MathUtils.lerp( - DURATION_AUTO_MIN, DURATION_AUTO_MAX, subRowDelta / getCount()); - } else { - durationMillis = duration; - } - - mSubScroller.startScroll(startSubRow, endSubRow, durationMillis); - - postOnAnimation(mAnimationFrame); - } - - /** - * Given a target row and offset, computes the sub-row position that - * aligns with the top of the list. If the offset is negative, the - * resulting sub-row will be smaller than the target row. - */ - private float resolveOffset(int targetRow, int offset) { - // Compute the target sub-row position by finding the actual row - // indicated by the target and offset. - int remainingOffset = offset; - int targetHeight = getHeightForRow(targetRow); - if (offset < 0) { - // Subtract row heights until we find the right row. - while (targetRow > 0 && remainingOffset < 0) { - remainingOffset += targetHeight; - targetRow--; - targetHeight = getHeightForRow(targetRow); - } - } else if (offset > 0) { - // Add row heights until we find the right row. - while (targetRow < getCount() - 1 && remainingOffset > targetHeight) { - remainingOffset -= targetHeight; - targetRow++; - targetHeight = getHeightForRow(targetRow); - } - } - - final float targetOffsetRatio; - if (remainingOffset < 0 || targetHeight == 0) { - targetOffsetRatio = 0; - } else { - targetOffsetRatio = remainingOffset / (float) targetHeight; - } - - return targetRow + targetOffsetRatio; - } - - private float computeBoundSubRow(int targetRow, int boundRow) { - final float targetSubRow = resolveOffset(targetRow, mOffset); - mOffset = 0; - - // The target row is below the bound row, so the end position would - // push the bound position above the list. Abort! - if (targetSubRow >= boundRow) { - return boundRow; - } - - // Compute the closest possible sub-position that wouldn't push the - // bound position's view further below the list. - final int listHeight = getHeight() - getPaddingTop() - getPaddingBottom(); - final int boundHeight = getHeightForRow(boundRow); - final float boundSubRow = resolveOffset(boundRow, -listHeight + boundHeight); - - return Math.max(boundSubRow, targetSubRow); - } - - @Override - public void start(int position) { - scrollToPosition(position, false, 0, INVALID_POSITION, DURATION_AUTO); - } - - @Override - public void start(int position, int boundPosition) { - scrollToPosition(position, false, 0, boundPosition, DURATION_AUTO); - } - - @Override - public void startWithOffset(int position, int offset) { - scrollToPosition(position, true, offset, INVALID_POSITION, DURATION_AUTO); - } - - @Override - public void startWithOffset(int position, int offset, int duration) { - scrollToPosition(position, true, offset, INVALID_POSITION, duration); - } - - @Override - public void stop() { - removeCallbacks(mAnimationFrame); - } - - /** - * Returns the height of a row, which is computed as the maximum height of - * the items in the row. - * - * @param row the row index - * @return row height in pixels - */ - public abstract int getHeightForRow(int row); - - /** - * Returns the row for the specified item position. - * - * @param position the item position - * @return the row index - */ - public abstract int getRowForPosition(int position); - - /** - * Returns the first item position within the specified row. - * - * @param row the row - * @return the position of the first item in the row - */ - public abstract int getFirstPositionForRow(int row); - - private void onAnimationFrame() { - final boolean shouldPost = mSubScroller.computePosition(); - final float subRow = mSubScroller.getPosition(); - - final int row = (int) subRow; - final int position = getFirstPositionForRow(row); - if (position >= getCount()) { - // Invalid position, abort scrolling. - return; - } - - final int rowHeight = getHeightForRow(row); - final int offset = (int) (rowHeight * (subRow - row)); - final int addOffset = (int) (mOffset * mSubScroller.getInterpolatedValue()); - setSelectionFromTop(position, -offset - addOffset); - - if (shouldPost) { - postOnAnimation(mAnimationFrame); - } - } - - private Runnable mAnimationFrame = new Runnable() { - @Override - public void run() { - onAnimationFrame(); - } - }; - } - - /** - * Scroller capable of returning floating point positions. - */ - static class SubScroller { - private static final Interpolator INTERPOLATOR = new AccelerateDecelerateInterpolator(); - - private float mStartPosition; - private float mEndPosition; - private long mStartTime; - private long mDuration; - - private float mPosition; - private float mInterpolatedValue; - - public void startScroll(float startPosition, float endPosition, int duration) { - mStartPosition = startPosition; - mEndPosition = endPosition; - mDuration = duration; - - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mPosition = startPosition; - mInterpolatedValue = 0; - } - - public boolean computePosition() { - final long elapsed = AnimationUtils.currentAnimationTimeMillis() - mStartTime; - final float value; - if (mDuration <= 0) { - value = 1; - } else { - value = MathUtils.constrain(elapsed / (float) mDuration, 0, 1); - } - - mInterpolatedValue = INTERPOLATOR.getInterpolation(value); - mPosition = (mEndPosition - mStartPosition) * mInterpolatedValue + mStartPosition; - - return elapsed < mDuration; - } - - public float getPosition() { - return mPosition; - } - - public float getInterpolatedValue() { - return mInterpolatedValue; - } - } } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 93810b3f6bf3..33cc66e55bc2 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1029,11 +1029,6 @@ public class GridView extends AbsListView { } @Override - AbsPositionScroller createPositionScroller() { - return new GridViewPositionScroller(); - } - - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Sets up mListPadding super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -2392,33 +2387,4 @@ public class GridView extends AbsListView { column, 1, row, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } - - /** - * Sub-position scroller that understands the layout of a GridView. - */ - class GridViewPositionScroller extends AbsSubPositionScroller { - @Override - public int getRowForPosition(int position) { - return position / mNumColumns; - } - - @Override - public int getFirstPositionForRow(int row) { - return row * mNumColumns; - } - - @Override - public int getHeightForRow(int row) { - final int firstRowPosition = row * mNumColumns; - final int lastRowPosition = Math.min(getCount(), firstRowPosition + mNumColumns); - int maxHeight = 0; - for (int i = firstRowPosition; i < lastRowPosition; i++) { - final int height = getHeightForPosition(i); - if (height > maxHeight) { - maxHeight = height; - } - } - return maxHeight; - } - } } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 1baeca880e9a..9db1e057ee7e 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3872,11 +3872,6 @@ public class ListView extends AbsListView { } @Override - AbsPositionScroller createPositionScroller() { - return new ListViewPositionScroller(); - } - - @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(ListView.class.getName()); @@ -3905,24 +3900,4 @@ public class ListView extends AbsListView { 0, 1, position, 1, isHeading, isSelected); info.setCollectionItemInfo(itemInfo); } - - /** - * Sub-position scroller that understands the layout of a ListView. - */ - class ListViewPositionScroller extends AbsSubPositionScroller { - @Override - public int getRowForPosition(int position) { - return position; - } - - @Override - public int getFirstPositionForRow(int row) { - return row; - } - - @Override - public int getHeightForRow(int row) { - return getHeightForPosition(row); - } - } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index a35d4470aad4..41d3e3208ee2 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1096,10 +1096,6 @@ public class PopupWindow { p.softInputMode = mSoftInputMode; p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); - // TODO: Use real shadow insets once that algorithm is finalized. - final int shadowInset = (int) Math.ceil(mElevation * 2); - p.shadowInsets.set(shadowInset, shadowInset, shadowInset, shadowInset); - return p; } diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp index 9621bb25d772..d2f5b5d6d633 100644 --- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp +++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp @@ -19,17 +19,20 @@ #include <utils/Log.h> #include <utils/Errors.h> #include <utils/Trace.h> +#include <camera/CameraUtils.h> #include "jni.h" #include "JNIHelp.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" +#include "android_runtime/android_graphics_SurfaceTexture.h" #include <gui/Surface.h> #include <gui/IGraphicBufferProducer.h> #include <ui/GraphicBuffer.h> #include <system/window.h> #include <hardware/camera3.h> +#include <system/camera_metadata.h> #include <stdint.h> #include <inttypes.h> @@ -335,6 +338,25 @@ static sp<ANativeWindow> getNativeWindow(JNIEnv* env, jobject surface) { return anw; } +static sp<ANativeWindow> getNativeWindowFromTexture(JNIEnv* env, jobject surfaceTexture) { + sp<ANativeWindow> anw; + if (surfaceTexture) { + anw = android_SurfaceTexture_getNativeWindow(env, surfaceTexture); + if (env->ExceptionCheck()) { + return NULL; + } + } else { + jniThrowNullPointerException(env, "surfaceTexture"); + return NULL; + } + if (anw == NULL) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "SurfaceTexture had no valid native window."); + return NULL; + } + return anw; +} + static sp<Surface> getSurface(JNIEnv* env, jobject surface) { sp<Surface> s; if (surface) { @@ -376,6 +398,17 @@ static jint LegacyCameraDevice_nativeDetectSurfaceType(JNIEnv* env, jobject thiz static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject thiz, jobject surface, jintArray dimens) { ALOGV("nativeGetSurfaceDimens"); + + if (dimens == NULL) { + ALOGE("%s: Null dimens argument passed to nativeDetectSurfaceDimens", __FUNCTION__); + return BAD_VALUE; + } + + if (env->GetArrayLength(dimens) < 2) { + ALOGE("%s: Invalid length of dimens argument in nativeDetectSurfaceDimens", __FUNCTION__); + return BAD_VALUE; + } + sp<ANativeWindow> anw; if ((anw = getNativeWindow(env, surface)) == NULL) { ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); @@ -398,6 +431,37 @@ static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject th return NO_ERROR; } +static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz, + jobject surfaceTexture, jintArray dimens) { + ALOGV("nativeDetectTextureDimens"); + sp<ANativeWindow> anw; + if ((anw = getNativeWindowFromTexture(env, surfaceTexture)) == NULL) { + ALOGE("%s: Could not retrieve native window from SurfaceTexture.", __FUNCTION__); + return BAD_VALUE; + } + + int32_t dimenBuf[2]; + status_t err = anw->query(anw.get(), NATIVE_WINDOW_WIDTH, dimenBuf); + if(err != NO_ERROR) { + ALOGE("%s: Error while querying SurfaceTexture width %s (%d)", __FUNCTION__, + strerror(-err), err); + return err; + } + + err = anw->query(anw.get(), NATIVE_WINDOW_HEIGHT, dimenBuf + 1); + if(err != NO_ERROR) { + ALOGE("%s: Error while querying SurfaceTexture height %s (%d)", __FUNCTION__, + strerror(-err), err); + return err; + } + + env->SetIntArrayRegion(dimens, /*start*/0, /*length*/ARRAY_SIZE(dimenBuf), dimenBuf); + if (env->ExceptionCheck()) { + return BAD_VALUE; + } + return NO_ERROR; +} + static jint LegacyCameraDevice_nativeConfigureSurface(JNIEnv* env, jobject thiz, jobject surface, jint width, jint height, jint pixelFormat) { ALOGV("nativeConfigureSurface"); @@ -504,6 +568,42 @@ static jlong LegacyCameraDevice_nativeGetSurfaceId(JNIEnv* env, jobject thiz, jo return reinterpret_cast<jlong>(b.get()); } +static jint LegacyCameraDevice_nativeSetSurfaceOrientation(JNIEnv* env, jobject thiz, + jobject surface, jint facing, jint orientation) { + ALOGV("nativeSetSurfaceOrientation"); + sp<ANativeWindow> anw; + if ((anw = getNativeWindow(env, surface)) == NULL) { + ALOGE("%s: Could not retrieve native window from surface.", __FUNCTION__); + return BAD_VALUE; + } + + status_t err = NO_ERROR; + CameraMetadata staticMetadata; + + int32_t orientVal = static_cast<int32_t>(orientation); + uint8_t facingVal = static_cast<uint8_t>(facing); + staticMetadata.update(ANDROID_SENSOR_ORIENTATION, &orientVal, 1); + staticMetadata.update(ANDROID_LENS_FACING, &facingVal, 1); + + int32_t transform = 0; + + if ((err = CameraUtils::getRotationTransform(staticMetadata, /*out*/&transform)) != OK) { + ALOGE("%s: Invalid rotation transform %s (%d)", __FUNCTION__, strerror(-err), + err); + return err; + } + + ALOGV("%s: Setting buffer sticky transform to %d", __FUNCTION__, transform); + + if ((err = native_window_set_buffers_sticky_transform(anw.get(), transform)) != OK) { + ALOGE("%s: Unable to configure surface transform, error %s (%d)", __FUNCTION__, + strerror(-err), err); + return err; + } + + return NO_ERROR; +} + } // extern "C" static JNINativeMethod gCameraDeviceMethods[] = { @@ -528,6 +628,12 @@ static JNINativeMethod gCameraDeviceMethods[] = { { "nativeGetSurfaceId", "(Landroid/view/Surface;)J", (void *)LegacyCameraDevice_nativeGetSurfaceId }, + { "nativeDetectTextureDimens", + "(Landroid/graphics/SurfaceTexture;[I)I", + (void *)LegacyCameraDevice_nativeDetectTextureDimens }, + { "nativeSetSurfaceOrientation", + "(Landroid/view/Surface;II)I", + (void *)LegacyCameraDevice_nativeSetSurfaceOrientation }, }; // Get all the required offsets in java class and register native functions diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 989b60eef0d2..64366e5a0467 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -439,8 +439,11 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra if (!is_system_server) { int rc = createProcessGroup(uid, getpid()); if (rc != 0) { - ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc)); - RuntimeAbort(env); + if (rc == -EROFS) { + ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?"); + } else { + ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc)); + } } } diff --git a/core/res/res/layout/alert_dialog_material.xml b/core/res/res/layout/alert_dialog_material.xml index 93acc3f5cd8d..6a435b25d927 100644 --- a/core/res/res/layout/alert_dialog_material.xml +++ b/core/res/res/layout/alert_dialog_material.xml @@ -20,13 +20,7 @@ android:id="@+id/parentPanel" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" - android:background="@drawable/dialog_background_material" - android:translationZ="@dimen/floating_window_z" - android:layout_marginLeft="@dimen/floating_window_margin_left" - android:layout_marginTop="@dimen/floating_window_margin_top" - android:layout_marginRight="@dimen/floating_window_margin_right" - android:layout_marginBottom="@dimen/floating_window_margin_bottom"> + android:orientation="vertical"> <LinearLayout android:id="@+id/topPanel" android:layout_width="match_parent" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 5fdadb7fdd0e..ed228e4ac563 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1826,6 +1826,9 @@ Activity Transition. Corresponds to {@link android.view.Window#setTransitionBackgroundFadeDuration(long)}. --> <attr name="windowTransitionBackgroundFadeDuration" /> + + <!-- Elevation to use for the window. --> + <attr name="windowElevation" format="dimension" /> </declare-styleable> <!-- The set of attributes that describe a AlertDialog's theme. --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a0ca06ef9621..fee5c4335446 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2233,6 +2233,9 @@ <public type="attr" name="buttonBarNegativeButtonStyle" /> <public type="attr" name="popupElevation" /> <public type="attr" name="actionBarPopupTheme" /> + <public type="attr" name="multiArch" /> + <public type="attr" name="touchscreenBlocksFocus" /> + <public type="attr" name="windowElevation" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> @@ -2509,6 +2512,4 @@ <!-- A transition that moves views in or out of the scene to or from the left edge when a view visibility changes. --> <public type="transition" name="slide_left"/> - <public type="attr" name="multiArch" /> - <public type="attr" name="touchscreenBlocksFocus" /> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 193d3c03e9f9..2ecaa5c26b88 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3627,13 +3627,13 @@ <!-- See SIM_REMOVED_DIALOG. This is the title of that dialog. --> <string name="sim_removed_title">SIM card removed</string> <!-- See SIM_REMOVED_DIALOG. This is the message of that dialog. --> - <string name="sim_removed_message">The mobile network will be unavailable until you restart with a valid SIM card inserted.</string> + <string name="sim_removed_message">The cellular network will be unavailable until you restart with a valid SIM card inserted.</string> <!-- See SIM_REMOVED_DIALOG. This is the button of that dialog. --> <string name="sim_done_button">Done</string> <!-- See SIM_ADDED_DIALOG. This is the title of that dialog. --> <string name="sim_added_title">SIM card added</string> <!-- See SIM_ADDED_DIALOG. This is the message of that dialog. --> - <string name="sim_added_message">Restart your device to access the mobile network.</string> + <string name="sim_added_message">Restart your device to access the cellular network.</string> <!-- See SIM_ADDED_DIALOG. This is the button of that dialog. --> <string name="sim_restart_button">Restart</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 0eceae607926..f5d7f5f8483e 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -824,15 +824,9 @@ please see styles_device_defaults.xml. <item name="textColorLink">?textColorLinkInverse</item> </style> - <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme"> - </style> - - <style name="TextAppearance.Theme.Dialog.AppError"> - <item name="textColor">#ffffc0c0</item> - </style> + <style name="TextAppearance.Theme.Dialog" parent="TextAppearance.Theme" /> - <style name="TextAppearance.Widget"> - </style> + <style name="TextAppearance.Widget" /> <style name="TextAppearance.Widget.Button" parent="TextAppearance.Small.Inverse"> <item name="textColor">@color/primary_text_light_nodisable</item> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 6ada97586ae9..a519c37acce8 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -820,21 +820,14 @@ please see themes_device_defaults.xml. <eat-comment /> <!-- Theme for the dialog shown when an app crashes or ANRs. --> - <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog"> - <item name="windowFrame">@null</item> - <item name="windowTitleStyle">@style/DialogWindowTitle</item> - <item name="windowBackground">@color/transparent</item> - <item name="windowIsFloating">true</item> - <item name="windowContentOverlay">@null</item> + <style name="Theme.Dialog.AppError" parent="Theme.DeviceDefault.Light.Dialog.Alert"> <item name="windowContentTransitions">false</item> - <item name="textAppearance">@style/TextAppearance.Theme.Dialog.AppError</item> <item name="windowCloseOnTouchOutside">false</item> </style> <!-- Special theme for the recent apps dialog, to allow customization with overlays. --> <style name="Theme.Dialog.RecentApplications" parent="Theme.DeviceDefault.Light.Dialog"> - <item name="windowFrame">@null</item> <item name="windowBackground">@color/transparent</item> <item name="windowAnimationStyle">@style/Animation.RecentApplications</item> <item name="textColor">@color/secondary_text_nofocus</item> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 47f3778b02d8..efc92d95d017 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -1011,7 +1011,8 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Dialog"> <item name="windowFrame">@null</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item> - <item name="windowBackground">@drawable/dialog_background_shadow_material</item> + <item name="windowBackground">@drawable/dialog_background_material</item> + <item name="windowElevation">@dimen/floating_window_z</item> <item name="windowIsFloating">true</item> <item name="windowContentOverlay">@null</item> <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item> @@ -1077,6 +1078,7 @@ please see themes_device_defaults.xml. its pixels. --> <style name="Theme.Material.Dialog.NoFrame"> <item name="windowBackground">@color/transparent</item> + <item name="windowElevation">0dp</item> <item name="windowAnimationStyle">@null</item> <item name="backgroundDimEnabled">false</item> <item name="windowIsTranslucent">true</item> @@ -1085,7 +1087,6 @@ please see themes_device_defaults.xml. </style> <style name="Theme.Material.Dialog.BaseAlert"> - <item name="windowBackground">@color/transparent</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item> <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item> <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item> @@ -1100,6 +1101,7 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Dialog.BaseTimePicker"> <item name="windowBackground">@color/transparent</item> + <item name="windowElevation">0dp</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item> <item name="windowContentOverlay">@null</item> </style> @@ -1131,7 +1133,8 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Light.Dialog"> <item name="windowFrame">@null</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item> - <item name="windowBackground">@drawable/dialog_background_shadow_material</item> + <item name="windowBackground">@drawable/dialog_background_material</item> + <item name="windowElevation">@dimen/floating_window_z</item> <item name="windowIsFloating">true</item> <item name="windowContentOverlay">@null</item> <item name="windowAnimationStyle">@style/Animation.Material.Dialog</item> @@ -1203,7 +1206,6 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.NoActionBar" /> <style name="Theme.Material.Light.Dialog.BaseAlert"> - <item name="windowBackground">@color/transparent</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item> <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item> <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item> @@ -1218,6 +1220,7 @@ please see themes_device_defaults.xml. <style name="Theme.Material.Light.Dialog.BaseTimePicker"> <item name="windowBackground">@color/transparent</item> + <item name="windowElevation">0dp</item> <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item> </style> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index a2cc40cee252..b5241779a88a 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1257,4 +1257,11 @@ <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.android.frameworks.coretests" android:label="Frameworks Core Tests" /> + <key-sets> + <key-set android:name="A" > + <public-key android:name="keyA" + android:value="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJoN1Nsgqf0V4C/bbN8wo8O2X/S5D76+5Mb9mlIsHkUTUTbHCNk+LxHIUYLm89YbP9zImrV0bUHLUAZUyoMUCiMCAwEAAQ=="/> + </key-set> + <upgrade-key-set android:name="A"/> + </key-sets> </manifest> diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk index e44ac6c277f3..306dc90118f7 100644 --- a/core/tests/coretests/apks/keyset/Android.mk +++ b/core/tests/coretests/apks/keyset/Android.mk @@ -88,4 +88,21 @@ LOCAL_PACKAGE_NAME := keyset_sau_ub LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml +include $(FrameworkCoreTests_BUILD_PACKAGE) + +#apks signed by platform only +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := keyset_splat_api +LOCAL_CERTIFICATE := platform +LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml +include $(FrameworkCoreTests_BUILD_PACKAGE) + +#apks signed by platform and keyset_A +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := keyset_splata_api +LOCAL_CERTIFICATE := platform +LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A +LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml new file mode 100644 index 000000000000..4c7e968c2722 --- /dev/null +++ b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.keysets_api"> + <application android:hasCode="false"> + </application> +</manifest> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 024442535931..3a80309690b0 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -26,6 +26,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.KeySet; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -33,6 +34,7 @@ import android.content.pm.PackageParser.PackageParserException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; @@ -3328,6 +3330,174 @@ public class PackageManagerTests extends AndroidTestCase { } /** + * The following tests are related to testing KeySets-based API + */ + + /* + * testGetSigningKeySetNull - ensure getSigningKeySet() returns null on null + * input and when calling a package other than that which made the call. + */ + public void testGetSigningKeySet() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet ks; + try { + ks = pm.getSigningKeySet(null); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getSigningKeySet("keysets.test.bogus.package"); + assertTrue(false); // should have thrown + } catch (IllegalArgumentException e) { + } + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + try { + ks = pm.getSigningKeySet(otherPkgName); + assertTrue(false); // should have thrown + } catch (SecurityException e) { + } + cleanUpInstall(otherPkgName); + ks = pm.getSigningKeySet(mContext.getPackageName()); + assertNotNull(ks); + } + + /* + * testGetKeySetByAlias - same as getSigningKeySet, but for keysets defined + * by this package. + */ + public void testGetKeySetByAlias() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet ks; + try { + ks = pm.getKeySetByAlias(null, null); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getKeySetByAlias(null, "keysetBogus"); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getKeySetByAlias("keysets.test.bogus.package", null); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getKeySetByAlias("keysets.test.bogus.package", "A"); + assertTrue(false); // should have thrown + } catch(IllegalArgumentException e) { + } + try { + ks = pm.getKeySetByAlias(mPkgName, "keysetBogus"); + assertTrue(false); // should have thrown + } catch(IllegalArgumentException e) { + } + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + try { + ks = pm.getKeySetByAlias(otherPkgName, "A"); + assertTrue(false); // should have thrown + } catch (SecurityException e) { + } + cleanUpInstall(otherPkgName); + ks = pm.getKeySetByAlias(mPkgName, "A"); + assertNotNull(ks); + } + + public void testIsSignedBy() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet mSigningKS = pm.getSigningKeySet(mPkgName); + KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A"); + + try { + assertFalse(pm.isSignedBy(null, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(null, mSigningKS)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(mPkgName, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy("keysets.test.bogus.package", mDefinedKS)); + } catch(IllegalArgumentException e) { + } + assertFalse(pm.isSignedBy(mPkgName, mDefinedKS)); + assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder()))); + assertTrue(pm.isSignedBy(mPkgName, mSigningKS)); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS)); + assertTrue(pm.isSignedBy(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS)); + assertTrue(pm.isSignedBy(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + } + + public void testIsSignedByExactly() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet mSigningKS = pm.getSigningKeySet(mPkgName); + KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A"); + try { + assertFalse(pm.isSignedBy(null, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(null, mSigningKS)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(mPkgName, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedByExactly("keysets.test.bogus.package", mDefinedKS)); + } catch(IllegalArgumentException e) { + } + assertFalse(pm.isSignedByExactly(mPkgName, mDefinedKS)); + assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder()))); + assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS)); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS)); + assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS)); + assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + } + + + + /** * The following tests are related to testing the checkSignatures api. */ private void checkSignatures(int apk1, int apk2, int expMatchResult) throws Exception { diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 1cecef329867..b5fc62895946 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -209,6 +209,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { if (drawableRes != 0) { mAnimatedVectorState.mVectorDrawable = (VectorDrawable) res.getDrawable( drawableRes, theme).mutate(); + mAnimatedVectorState.mVectorDrawable.setAllowCaching(false); } a.recycle(); } else if (TARGET.equals(tagName)) { @@ -258,6 +259,7 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable { mChangingConfigurations = copy.mChangingConfigurations; // TODO: Make sure the constant state are handled correctly. mVectorDrawable = new VectorDrawable(); + mVectorDrawable.setAllowCaching(false); mAnimators = new ArrayList<Animator>(); } } diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java index 8783994cf7ed..8c907b2b4c84 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -18,6 +18,7 @@ import android.content.res.ColorStateList; import android.content.res.Resources; import android.content.res.Resources.Theme; import android.content.res.TypedArray; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; @@ -142,6 +143,10 @@ public class VectorDrawable extends Drawable { private boolean mMutated; + // AnimatedVectorDrawable needs to turn off the cache all the time, otherwise, + // caching the bitmap by default is allowed. + private boolean mAllowCaching = true; + public VectorDrawable() { mVectorState = new VectorDrawableState(); } @@ -183,7 +188,23 @@ public class VectorDrawable extends Drawable { final int saveCount = canvas.save(); final Rect bounds = getBounds(); canvas.translate(bounds.left, bounds.top); - mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height()); + + if (!mAllowCaching) { + mVectorState.mVPathRenderer.draw(canvas, bounds.width(), bounds.height()); + } else { + Bitmap bitmap = mVectorState.mCachedBitmap; + if (bitmap == null || !mVectorState.canReuseCache(bounds.width(), + bounds.height())) { + bitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), + Bitmap.Config.ARGB_8888); + Canvas tmpCanvas = new Canvas(bitmap); + mVectorState.mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height()); + mVectorState.mCachedBitmap = bitmap; + + mVectorState.updateCacheStates(); + } + canvas.drawBitmap(bitmap, null, bounds, null); + } canvas.restoreToCount(saveCount); } @@ -444,6 +465,10 @@ public class VectorDrawable extends Drawable { return super.getChangingConfigurations() | mVectorState.mChangingConfigurations; } + void setAllowCaching(boolean allowCaching) { + mAllowCaching = allowCaching; + } + private static class VectorDrawableState extends ConstantState { int[] mThemeAttrs; int mChangingConfigurations; @@ -451,6 +476,12 @@ public class VectorDrawable extends Drawable { ColorStateList mTint; Mode mTintMode; + Bitmap mCachedBitmap; + int[] mCachedThemeAttrs; + ColorStateList mCachedTint; + Mode mCachedTintMode; + int mCachedRootAlpha; + // Deep copy for mutate() or implicitly mutate. public VectorDrawableState(VectorDrawableState copy) { if (copy != null) { @@ -462,6 +493,27 @@ public class VectorDrawable extends Drawable { } } + public boolean canReuseCache(int width, int height) { + if (mCachedThemeAttrs == mThemeAttrs + && mCachedTint == mTint + && mCachedTintMode == mTintMode + && width == mCachedBitmap.getWidth() + && height == mCachedBitmap.getHeight() + && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) { + return true; + } + return false; + } + + public void updateCacheStates() { + // Use shallow copy here and shallow comparison in canReuseCache(), + // likely hit cache miss more, but practically not much difference. + mCachedThemeAttrs = mThemeAttrs; + mCachedTint = mTint; + mCachedTintMode = mTintMode; + mCachedRootAlpha = mVPathRenderer.getRootAlpha(); + } + public VectorDrawableState() { mVPathRenderer = new VPathRenderer(); } diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index 73bc61a5875c..740a9d3a4f57 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -682,9 +682,13 @@ public class RemoteControlClient // USE_SESSIONS if (mSession != null) { int pbState = PlaybackState.getStateFromRccState(state); - mSessionPlaybackState.setState(pbState, hasPosition ? - mPlaybackPositionMs : PlaybackState.PLAYBACK_POSITION_UNKNOWN, - playbackSpeed); + long position = hasPosition ? mPlaybackPositionMs + : PlaybackState.PLAYBACK_POSITION_UNKNOWN; + + PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState); + bob.setState(pbState, position, playbackSpeed, SystemClock.elapsedRealtime()); + bob.setErrorMessage(null); + mSessionPlaybackState = bob.build(); mSession.setPlaybackState(mSessionPlaybackState); } } @@ -745,8 +749,9 @@ public class RemoteControlClient // USE_SESSIONS if (mSession != null) { - mSessionPlaybackState.setActions(PlaybackState - .getActionsFromRccControlFlags(transportControlFlags)); + PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState); + bob.setActions(PlaybackState.getActionsFromRccControlFlags(transportControlFlags)); + mSessionPlaybackState = bob.build(); mSession.setPlaybackState(mSessionPlaybackState); } } @@ -946,7 +951,7 @@ public class RemoteControlClient /** * Cache for the current playback state using Session APIs. */ - private final PlaybackState mSessionPlaybackState = new PlaybackState(); + private PlaybackState mSessionPlaybackState = null; /** * Cache for metadata using Session APIs. This is re-initialized in apply(). diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java index 1f5b21698344..9ea3f26f609a 100644 --- a/media/java/android/media/RemoteController.java +++ b/media/java/android/media/RemoteController.java @@ -1020,7 +1020,7 @@ public final class RemoteController l.onClientPlaybackStateUpdate(playstate); } else { l.onClientPlaybackStateUpdate(playstate, state.getLastPositionUpdateTime(), - state.getPosition(), state.getPlaybackRate()); + state.getPosition(), state.getPlaybackSpeed()); } if (state != null) { l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index 9ae243687e15..f7e71768b453 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -184,41 +184,29 @@ public final class PlaybackState implements Parcelable { */ public final static long PLAYBACK_POSITION_UNKNOWN = -1; - private int mState; - private long mPosition; - private long mBufferPosition; - private float mRate; - private long mActions; - private CharSequence mErrorMessage; - private long mUpdateTime; - - /** - * Create an empty PlaybackState. At minimum a state and actions should be - * set before publishing a PlaybackState. - */ - public PlaybackState() { - } - - /** - * Create a new PlaybackState from an existing PlaybackState. All fields - * will be copied to the new state. - * - * @param from The PlaybackState to duplicate - */ - public PlaybackState(PlaybackState from) { - mState = from.mState; - mPosition = from.mPosition; - mRate = from.mRate; - mUpdateTime = from.mUpdateTime; - mBufferPosition = from.mBufferPosition; - mActions = from.mActions; - mErrorMessage = from.mErrorMessage; + private final int mState; + private final long mPosition; + private final long mBufferPosition; + private final float mSpeed; + private final long mActions; + private final CharSequence mErrorMessage; + private final long mUpdateTime; + + private PlaybackState(int state, long position, long updateTime, float speed, + long bufferPosition, long actions, CharSequence error) { + mState = state; + mPosition = position; + mSpeed = speed; + mUpdateTime = updateTime; + mBufferPosition = bufferPosition; + mActions = actions; + mErrorMessage = error; } private PlaybackState(Parcel in) { mState = in.readInt(); mPosition = in.readLong(); - mRate = in.readFloat(); + mSpeed = in.readFloat(); mUpdateTime = in.readLong(); mBufferPosition = in.readLong(); mActions = in.readLong(); @@ -232,7 +220,7 @@ public final class PlaybackState implements Parcelable { bob.append("state=").append(mState); bob.append(", position=").append(mPosition); bob.append(", buffered position=").append(mBufferPosition); - bob.append(", rate=").append(mRate); + bob.append(", speed=").append(mSpeed); bob.append(", updated=").append(mUpdateTime); bob.append(", actions=").append(mActions); bob.append(", error=").append(mErrorMessage); @@ -249,7 +237,7 @@ public final class PlaybackState implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mState); dest.writeLong(mPosition); - dest.writeFloat(mRate); + dest.writeFloat(mSpeed); dest.writeLong(mUpdateTime); dest.writeLong(mBufferPosition); dest.writeLong(mActions); @@ -271,41 +259,6 @@ public final class PlaybackState implements Parcelable { public int getState() { return mState; } - - /** - * Set the current state of playback. - * <p> - * The position must be in ms and indicates the current playback position - * within the track. If the position is unknown use - * {@link #PLAYBACK_POSITION_UNKNOWN}. - * <p> - * The rate is a multiple of normal playback and should be 0 when paused and - * negative when rewinding. Normal playback rate is 1.0. - * <p> - * The state must be one of the following: - * <ul> - * <li> {@link PlaybackState#STATE_NONE}</li> - * <li> {@link PlaybackState#STATE_STOPPED}</li> - * <li> {@link PlaybackState#STATE_PLAYING}</li> - * <li> {@link PlaybackState#STATE_PAUSED}</li> - * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li> - * <li> {@link PlaybackState#STATE_REWINDING}</li> - * <li> {@link PlaybackState#STATE_BUFFERING}</li> - * <li> {@link PlaybackState#STATE_ERROR}</li> - * </ul> - * - * @param state The current state of playback. - * @param position The position in the current track in ms. - * @param playbackRate The current rate of playback as a multiple of normal - * playback. - */ - public void setState(int state, long position, float playbackRate) { - this.mState = state; - this.mPosition = position; - this.mRate = playbackRate; - mUpdateTime = SystemClock.elapsedRealtime(); - } - /** * Get the current playback position in ms. */ @@ -323,23 +276,14 @@ public final class PlaybackState implements Parcelable { } /** - * Set the current buffer position in ms. This is the farthest playback - * point that can be reached from the current position using only buffered - * content. - */ - public void setBufferPosition(long bufferPosition) { - mBufferPosition = bufferPosition; - } - - /** - * Get the current playback rate as a multiple of normal playback. This + * Get the current playback speed as a multiple of normal playback. This * should be negative when rewinding. A value of 1 means normal playback and * 0 means paused. * - * @return The current rate of playback. + * @return The current speed of playback. */ - public float getPlaybackRate() { - return mRate; + public float getPlaybackSpeed() { + return mSpeed; } /** @@ -362,25 +306,6 @@ public final class PlaybackState implements Parcelable { } /** - * Set the current capabilities available on this session. This should use a - * bitmask of the available capabilities. - * <ul> - * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li> - * <li> {@link PlaybackState#ACTION_REWIND}</li> - * <li> {@link PlaybackState#ACTION_PLAY}</li> - * <li> {@link PlaybackState#ACTION_PAUSE}</li> - * <li> {@link PlaybackState#ACTION_STOP}</li> - * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li> - * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li> - * <li> {@link PlaybackState#ACTION_SEEK_TO}</li> - * <li> {@link PlaybackState#ACTION_SET_RATING}</li> - * </ul> - */ - public void setActions(long capabilities) { - mActions = capabilities; - } - - /** * Get a user readable error message. This should be set when the state is * {@link PlaybackState#STATE_ERROR}. */ @@ -393,21 +318,12 @@ public final class PlaybackState implements Parcelable { * position has never been set this will return 0; * * @return The last time the position was updated. - * @hide */ public long getLastPositionUpdateTime() { return mUpdateTime; } /** - * Set a user readable error message. This should be set when the state is - * {@link PlaybackState#STATE_ERROR}. - */ - public void setErrorMessage(CharSequence errorMessage) { - mErrorMessage = errorMessage; - } - - /** * Get the {@link PlaybackState} state for the given * {@link RemoteControlClient} state. * @@ -574,4 +490,175 @@ public final class PlaybackState implements Parcelable { return new PlaybackState[size]; } }; + + /** + * Builder for {@link PlaybackState} objects. + */ + public static final class Builder { + private int mState; + private long mPosition; + private long mBufferPosition; + private float mSpeed; + private long mActions; + private CharSequence mErrorMessage; + private long mUpdateTime; + + /** + * Creates an initially empty state builder. + */ + public Builder() { + } + + /** + * Creates a builder with the same initial values as those in the from + * state. + * + * @param from The state to use for initializing the builder. + */ + public Builder(PlaybackState from) { + if (from == null) { + return; + } + mState = from.mState; + mPosition = from.mPosition; + mBufferPosition = from.mBufferPosition; + mSpeed = from.mSpeed; + mActions = from.mActions; + mErrorMessage = from.mErrorMessage; + mUpdateTime = from.mUpdateTime; + } + + /** + * Set the current state of playback. + * <p> + * The position must be in ms and indicates the current playback + * position within the track. If the position is unknown use + * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown + * position the time at which the position was updated must be provided. + * It is okay to use {@link SystemClock#elapsedRealtime()} if the + * current position was just retrieved. + * <p> + * The speed is a multiple of normal playback and should be 0 when + * paused and negative when rewinding. Normal playback speed is 1.0. + * <p> + * The state must be one of the following: + * <ul> + * <li> {@link PlaybackState#STATE_NONE}</li> + * <li> {@link PlaybackState#STATE_STOPPED}</li> + * <li> {@link PlaybackState#STATE_PLAYING}</li> + * <li> {@link PlaybackState#STATE_PAUSED}</li> + * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li> + * <li> {@link PlaybackState#STATE_REWINDING}</li> + * <li> {@link PlaybackState#STATE_BUFFERING}</li> + * <li> {@link PlaybackState#STATE_ERROR}</li> + * </ul> + * + * @param state The current state of playback. + * @param position The position in the current track in ms. + * @param playbackSpeed The current speed of playback as a multiple of + * normal playback. + * @param updateTime The time in the {@link SystemClock#elapsedRealtime} + * timebase that the position was updated at. + * @return this + */ + public Builder setState(int state, long position, float playbackSpeed, long updateTime) { + mState = state; + mPosition = position; + mUpdateTime = updateTime; + mSpeed = playbackSpeed; + return this; + } + + /** + * Set the current state of playback. + * <p> + * The position must be in ms and indicates the current playback + * position within the track. If the position is unknown use + * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to + * the current {@link SystemClock#elapsedRealtime()}. + * <p> + * The speed is a multiple of normal playback and should be 0 when + * paused and negative when rewinding. Normal playback speed is 1.0. + * <p> + * The state must be one of the following: + * <ul> + * <li> {@link PlaybackState#STATE_NONE}</li> + * <li> {@link PlaybackState#STATE_STOPPED}</li> + * <li> {@link PlaybackState#STATE_PLAYING}</li> + * <li> {@link PlaybackState#STATE_PAUSED}</li> + * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li> + * <li> {@link PlaybackState#STATE_REWINDING}</li> + * <li> {@link PlaybackState#STATE_BUFFERING}</li> + * <li> {@link PlaybackState#STATE_ERROR}</li> + * </ul> + * + * @param state The current state of playback. + * @param position The position in the current track in ms. + * @param playbackSpeed The current speed of playback as a multiple of + * normal playback. + * @return this + */ + public Builder setState(int state, long position, float playbackSpeed) { + return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); + } + + /** + * Set the current actions available on this session. This should use a + * bitmask of possible actions. + * <ul> + * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li> + * <li> {@link PlaybackState#ACTION_REWIND}</li> + * <li> {@link PlaybackState#ACTION_PLAY}</li> + * <li> {@link PlaybackState#ACTION_PAUSE}</li> + * <li> {@link PlaybackState#ACTION_STOP}</li> + * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li> + * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li> + * <li> {@link PlaybackState#ACTION_SEEK_TO}</li> + * <li> {@link PlaybackState#ACTION_SET_RATING}</li> + * </ul> + * + * @param actions The set of actions allowed. + * @return this + */ + public Builder setActions(long actions) { + mActions = actions; + return this; + } + + /** + * Set the current buffer position in ms. This is the farthest playback + * point that can be reached from the current position using only + * buffered content. + * + * @param bufferPosition The position in ms that playback is buffered + * to. + * @return this + */ + public Builder setBufferPosition(long bufferPosition) { + mBufferPosition = bufferPosition; + return this; + } + + /** + * Set a user readable error message. This should be set when the state + * is {@link PlaybackState#STATE_ERROR}. + * + * @param error The error message for display to the user. + * @return this + */ + public Builder setErrorMessage(CharSequence error) { + mErrorMessage = error; + return this; + } + + /** + * Build and return the PlaybackState instance with these values. + * + * @return A new state instance. + */ + public PlaybackState build() { + return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferPosition, + mActions, mErrorMessage); + } + } } diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp index 1685a44778b6..56467403179d 100644 --- a/media/jni/android_media_MediaRecorder.cpp +++ b/media/jni/android_media_MediaRecorder.cpp @@ -157,6 +157,10 @@ static void android_media_MediaRecorder_setCamera(JNIEnv* env, jobject thiz, job return; } sp<Camera> c = get_native_camera(env, camera, NULL); + if (c == NULL) { + // get_native_camera will throw an exception in this case + return; + } sp<MediaRecorder> mr = getMediaRecorder(env, thiz); process_media_recorder_call(env, mr->setCamera(c->remote(), c->getRecordingProxy()), "java/lang/RuntimeException", "setCamera failed."); diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java index eaf268d89382..094edf8b5042 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java @@ -389,6 +389,7 @@ public final class PageAdapter extends Adapter { mSelectedPages = selectedPages; mSelectedPageCount = PageRangeUtils.getNormalizedPageCount( mSelectedPages, mDocumentPageCount); + updatePreviewAreaAndPageSize(); notifyDataSetChanged(); } return mSelectedPages; diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b79dbbeb77f0..d4feccd0120a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -516,6 +516,10 @@ <string name="quick_settings_time_label">Time</string> <!-- QuickSettings: User [CHAR LIMIT=NONE] --> <string name="quick_settings_user_label">Me</string> + <!-- QuickSettings: Title of the user detail panel [CHAR LIMIT=NONE] --> + <string name="quick_settings_user_title">User</string> + <!-- QuickSettings: Label on the item for adding a new user [CHAR LIMIT=NONE] --> + <string name="quick_settings_user_new_user">New user</string> <!-- QuickSettings: Wifi [CHAR LIMIT=NONE] --> <string name="quick_settings_wifi_label">Wi-Fi</string> <!-- QuickSettings: Wifi (Not connected) [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index a9a606f25e5f..b6d7d7e28fcf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -64,11 +64,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta Context mContext; SystemServicesProxy mSystemServicesProxy; - - // Recents service binding Handler mHandler; - boolean mBootCompleted = false; - boolean mStartAnimationTriggered = false; + boolean mBootCompleted; + boolean mStartAnimationTriggered; // Task launching RecentsConfiguration mConfig; @@ -95,9 +93,7 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta } public void onStart() { - if (Console.Enabled) { - Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|start]"); - } + // Do nothing } public void onBootCompleted() { @@ -106,9 +102,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta /** Shows the recents */ public void onShowRecents(boolean triggeredFromAltTab, View statusBarView) { - if (Console.Enabled) { - Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|showRecents]"); - } mStatusBarView = statusBarView; mTriggeredFromAltTab = triggeredFromAltTab; @@ -121,10 +114,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta /** Hides the recents */ public void onHideRecents(boolean triggeredFromAltTab) { - if (Console.Enabled) { - Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]"); - } - if (mBootCompleted) { if (isRecentsTopMost(getTopMostTask(), null)) { // Notify recents to hide itself @@ -139,13 +128,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta /** Toggles the alternate recents activity */ public void onToggleRecents(View statusBarView) { - if (Console.Enabled) { - Console.logStartTracingTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey); - Console.logStartTracingTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey); - Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|toggleRecents]", ""); - } mStatusBarView = statusBarView; mTriggeredFromAltTab = false; @@ -223,14 +205,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta intent.setPackage(mContext.getPackageName()); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mContext.sendBroadcast(intent); - - // Time this path - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents"); - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents"); - } mLastToggleTime = System.currentTimeMillis(); return; } else { @@ -395,11 +369,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta startAlternateRecentsActivity(topTask, opts, null); } } - - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity"); - } mLastToggleTime = System.currentTimeMillis(); } @@ -417,10 +386,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab); intent.putExtra(EXTRA_TRIGGERED_FROM_TASK_ID, (topTask != null) ? topTask.id : -1); if (opts != null) { - mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle( - UserHandle.USER_CURRENT)); + mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT); } else { - mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + mContext.startActivityAsUser(intent, UserHandle.CURRENT); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index e7ac2e1ec568..c49e244a3265 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -52,43 +52,6 @@ public class Constants { } } - public static class Log { - public static class App { - public static final String TimeRecentsStartupKey = "startup"; - public static final String TimeRecentsLaunchKey = "launchTask"; - public static final String TimeRecentsScreenshotTransitionKey = "screenshot"; - public static final boolean TimeRecentsStartup = false; - public static final boolean TimeRecentsLaunchTask = false; - public static final boolean TimeRecentsScreenshotTransition = false; - - - public static final boolean RecentsComponent = false; - public static final boolean TaskDataLoader = false; - public static final boolean SystemUIHandshake = false; - public static final boolean TimeSystemCalls = false; - public static final boolean Memory = false; - public static final boolean Search = false; - } - - public static class UI { - public static final boolean Draw = false; - public static final boolean ClickEvents = false; - public static final boolean TouchEvents = false; - public static final boolean MeasureAndLayout = false; - public static final boolean HwLayers = false; - public static final boolean Focus = false; - } - - public static class TaskStack { - public static final boolean SynchronizeViewsWithModel = false; - } - - public static class ViewPool { - public static final boolean PoolCallbacks = false; - } - } - - /** XXX: We are going to move almost all of these into a resource once they are nailed down. */ public static class Values { public static class App { public static int AppWidgetHostId = 1024; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 1e581c1daf4a..29a02623552f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -52,16 +52,23 @@ import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; -/* Activity */ +/** + * The main Recents activity that is started from AlternateRecentsComponent. + */ public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks, FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks { + // Actions and Extras sent from AlternateRecentsComponent final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab"; final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation"; final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity"; final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity"; + RecentsConfiguration mConfig; + boolean mVisible; + + // Top level views RecentsView mRecentsView; SystemBarScrimViews mScrimViews; ViewStub mEmptyViewStub; @@ -69,29 +76,29 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView ViewStub mFullscreenOverlayStub; FullscreenTransitionOverlayView mFullScreenOverlayView; - RecentsConfiguration mConfig; - + // Search AppWidget RecentsAppWidgetHost mAppWidgetHost; AppWidgetProviderInfo mSearchAppWidgetInfo; AppWidgetHostView mSearchAppWidgetHostView; - boolean mVisible; // Runnables to finish the Recents activity - FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true); + FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(); FinishRecentsRunnable mFinishLaunchHomeRunnable; /** - * A Runnable to finish Recents either with/without a transition, and either by calling finish() - * or just launching the specified intent. + * A common Runnable to finish Recents either by calling finish() (with a custom animation) or + * launching Home with some ActivityOptions. Generally we always launch home when we exit + * Recents rather than just finishing the activity since we don't know what is behind Recents in + * the task stack. The only case where we finish() directly is when we are cancelling the full + * screen transition from the app. */ class FinishRecentsRunnable implements Runnable { - boolean mUseCustomFinishTransition; Intent mLaunchIntent; ActivityOptions mLaunchOpts; - public FinishRecentsRunnable(boolean withTransition) { - mUseCustomFinishTransition = withTransition; + public FinishRecentsRunnable() { + // Do nothing } /** @@ -111,77 +118,66 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Finish Recents if (mLaunchIntent != null) { if (mLaunchOpts != null) { - startActivityAsUser(mLaunchIntent, new UserHandle(UserHandle.USER_CURRENT)); + startActivityAsUser(mLaunchIntent, UserHandle.CURRENT); } else { - startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), - new UserHandle(UserHandle.USER_CURRENT)); + startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT); } } else { finish(); - if (mUseCustomFinishTransition) { - overridePendingTransition(R.anim.recents_to_launcher_enter, - R.anim.recents_to_launcher_exit); - } + overridePendingTransition(R.anim.recents_to_launcher_enter, + R.anim.recents_to_launcher_exit); } } } - // Broadcast receiver to handle messages from AlternateRecentsComponent + /** + * Broadcast receiver to handle messages from AlternateRecentsComponent. + */ final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, - "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed); - } if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) { if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) { - // Dismiss recents, launching the focused task - dismissRecentsIfVisible(); + // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app + dismissRecentsToFocusedTaskOrHome(false); } else { - // If we are mid-animation into Recents, then reverse it and finish - if (mFullScreenOverlayView == null || - !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) { - // Otherwise, either finish Recents, or launch Home directly - ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context, - null, mFinishLaunchHomeRunnable, null); - mRecentsView.startExitToHomeAnimation( - new ViewAnimation.TaskViewExitContext(exitTrigger)); - } + // Otherwise, dismiss Recents to Home + dismissRecentsToHome(true); } } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) { - // Try and unfilter and filtered stacks - if (!mRecentsView.unfilterFilteredStacks()) { - // If there are no filtered stacks, dismiss recents and launch the first task - dismissRecentsIfVisible(); - } + // If we are toggling Recents, then first unfilter any filtered stacks first + dismissRecentsToFocusedTaskOrHome(true); } else if (action.equals(ACTION_START_ENTER_ANIMATION)) { // Try and start the enter animation (or restart it on configuration changed) ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null); mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext( mFullScreenOverlayView, t)); - // Call our callback onEnterAnimationTriggered(); } } }; - // Broadcast receiver to handle messages from the system + /** + * Broadcast receiver to handle messages from the system + */ final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(Intent.ACTION_SCREEN_OFF) && mVisible) { - mFinishLaunchHomeRunnable.run(); + if (action.equals(Intent.ACTION_SCREEN_OFF)) { + // When the screen turns off, dismiss Recents to Home + dismissRecentsToHome(false); } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) { - // Refresh the search widget + // When the search activity changes, update the Search widget refreshSearchWidget(); } } }; - // Debug trigger + /** + * A custom debug trigger to listen for a debug key chord. + */ final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() { @Override public void run() { @@ -211,15 +207,17 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mConfig.launchedToTaskId = launchIntent.getIntExtra( AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1); - // Add the default no-recents layout - if (mEmptyView == null) { - mEmptyView = mEmptyViewStub.inflate(); - } + // Update the top level view's visibilities if (mConfig.launchedWithNoRecentTasks) { + if (mEmptyView == null) { + mEmptyView = mEmptyViewStub.inflate(); + } mEmptyView.setVisibility(View.VISIBLE); mRecentsView.setSearchBarVisibility(View.GONE); } else { - mEmptyView.setVisibility(View.GONE); + if (mEmptyView != null) { + mEmptyView.setVisibility(View.GONE); + } if (mRecentsView.hasSearchBar()) { mRecentsView.setSearchBarVisibility(View.VISIBLE); } else { @@ -227,7 +225,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } } - // Show the scrim if we animate into Recents without window transitions + // Animate the SystemUI scrims into view mScrimViews.prepareEnterRecentsAnimation(); } @@ -250,12 +248,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId); appWidgetId = -1; } - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, - "[RecentsActivity|onCreate|settings|appWidgetId]", - "Id: " + appWidgetId, - Console.AnsiBlue); - } } // If there is no id, then bind a new search app widget @@ -263,13 +255,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView Pair<Integer, AppWidgetProviderInfo> widgetInfo = ssp.bindSearchAppWidget(mAppWidgetHost); if (widgetInfo != null) { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, - "[RecentsActivity|onCreate|searchWidget]", - "Id: " + widgetInfo.first + " Info: " + widgetInfo.second, - Console.AnsiBlue); - } - // Save the app widget id into the settings mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first); mSearchAppWidgetInfo = widgetInfo.second; @@ -283,12 +268,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView if (Constants.DebugFlags.App.EnableSearchLayout) { int appWidgetId = mConfig.searchBarAppWidgetId; if (appWidgetId >= 0) { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, - "[RecentsActivity|onCreate|addSearchAppWidgetView]", - "Id: " + appWidgetId, - Console.AnsiBlue); - } mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId, mSearchAppWidgetInfo); Bundle opts = new Bundle(); @@ -305,28 +284,50 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } /** Dismisses recents if we are already visible and the intent is to toggle the recents view */ - boolean dismissRecentsIfVisible() { + boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) { if (mVisible) { - // If we are mid-animation into Recents, then reverse it and finish - if (mFullScreenOverlayView == null || - !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) { - // If we have a focused task, then launch that task - if (!mRecentsView.launchFocusedTask()) { - if (mConfig.launchedFromHome) { - // Just start the animation out of recents - ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, - null, mFinishLaunchHomeRunnable, null); - mRecentsView.startExitToHomeAnimation( - new ViewAnimation.TaskViewExitContext(exitTrigger)); - } else { - // Otherwise, try and launch the first task - if (!mRecentsView.launchFirstTask()) { - // If there are no tasks, then just finish recents - mFinishLaunchHomeRunnable.run(); - } - } - } + // If we are mid-animation into Recents, reverse the animation now + if (mFullScreenOverlayView != null && + mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true; + // If we currently have filtered stacks, then unfilter those first + if (checkFilteredStackState && + mRecentsView.unfilterFilteredStacks()) return true; + // If we have a focused Task, launch that Task now + if (mRecentsView.launchFocusedTask()) return true; + // If we launched from Home, then return to Home + if (mConfig.launchedFromHome) { + dismissRecentsToHomeRaw(true); + return true; } + // Otherwise, try and return to the first Task in the stack + if (mRecentsView.launchFirstTask()) return true; + // If none of the other cases apply, then just go Home + dismissRecentsToHomeRaw(true); + return true; + } + return false; + } + + /** Dismisses Recents directly to Home. */ + void dismissRecentsToHomeRaw(boolean animated) { + if (animated) { + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + null, mFinishLaunchHomeRunnable, null); + mRecentsView.startExitToHomeAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } else { + mFinishLaunchHomeRunnable.run(); + } + } + + /** Dismisses Recents directly to Home if we currently aren't transitioning. */ + boolean dismissRecentsToHome(boolean animated) { + if (mVisible) { + // If we are mid-animation into Recents, reverse the animation now + if (mFullScreenOverlayView != null && + mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) return true; + // Return to Home + dismissRecentsToHomeRaw(animated); return true; } return false; @@ -336,13 +337,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (Console.Enabled) { - Console.logDivider(Constants.Log.App.SystemUIHandshake); - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]", - getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed); - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "onCreate"); - } // Initialize the loader and the configuration RecentsTaskLoader.initialize(this); @@ -410,11 +404,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } void onConfigurationChange() { + // Update RecentsConfiguration + mConfig = RecentsConfiguration.reinitialize(this); + // Try and start the enter animation (or restart it on configuration changed) ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null); mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext( mFullScreenOverlayView, t)); - // Call our callback onEnterAnimationTriggered(); } @@ -423,18 +419,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView super.onNewIntent(intent); setIntent(intent); - if (Console.Enabled) { - Console.logDivider(Constants.Log.App.SystemUIHandshake); - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]", - intent.getAction() + " visible: " + mVisible, Console.AnsiRed); - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "onNewIntent"); - } - - // Initialize the loader and the configuration - RecentsTaskLoader.initialize(this); - mConfig = RecentsConfiguration.reinitialize(this); - // Update the recent tasks updateRecentsTasks(intent); @@ -446,10 +430,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override protected void onStart() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "", - Console.AnsiRed); - } super.onStart(); // Register the broadcast receiver to handle messages from our service @@ -462,10 +442,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override protected void onResume() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "", - Console.AnsiRed); - } super.onResume(); // Start listening for widget package changes if there is one bound, post it since we don't @@ -484,64 +460,28 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView }, 1); } + // Mark Recents as visible mVisible = true; } @Override - public void onAttachedToWindow() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, - "[RecentsActivity|onAttachedToWindow]", "", - Console.AnsiRed); - } - super.onAttachedToWindow(); - } - - @Override - public void onDetachedFromWindow() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, - "[RecentsActivity|onDetachedFromWindow]", "", - Console.AnsiRed); - } - super.onDetachedFromWindow(); - } - - @Override - protected void onPause() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "", - Console.AnsiRed); - } - super.onPause(); - } - - @Override protected void onStop() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "", - Console.AnsiRed); - } super.onStop(); // Unregister the RecentsService receiver unregisterReceiver(mServiceBroadcastReceiver); // Stop listening for widget package changes if there was one bound - if (mConfig.searchBarAppWidgetId >= 0) { + if (mAppWidgetHost.isListening()) { mAppWidgetHost.stopListening(); } } @Override protected void onDestroy() { - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "", - Console.AnsiRed); - } super.onDestroy(); - // Unregister the screen off receiver + // Unregister the system broadcast receivers unregisterReceiver(mSystemBroadcastReceiver); RecentsTaskLoader.getInstance().unregisterReceivers(); } @@ -583,26 +523,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Test mode where back does not do anything if (mConfig.debugModeEnabled) return; - // If we are mid-animation into Recents, then reverse it and finish - if (mFullScreenOverlayView == null || - !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) { - // If we are currently filtering in any stacks, unfilter them first - if (!mRecentsView.unfilterFilteredStacks()) { - if (mConfig.launchedFromHome) { - // Just start the animation out of recents - ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, - null, mFinishLaunchHomeRunnable, null); - mRecentsView.startExitToHomeAnimation( - new ViewAnimation.TaskViewExitContext(exitTrigger)); - } else { - // Otherwise, try and launch the first task - if (!mRecentsView.launchFirstTask()) { - // If there are no tasks, then just finish recents - mFinishLaunchHomeRunnable.run(); - } - } - } - } + // Dismiss Recents to the focused Task or Home + dismissRecentsToFocusedTaskOrHome(true); } /** Called when debug mode is triggered */ @@ -623,7 +545,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** Called when the enter recents animation is triggered. */ public void onEnterAnimationTriggered() { - // Animate the scrims in + // Animate the SystemUI scrim views mScrimViews.startEnterRecentsAnimation(); } @@ -644,7 +566,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onExitToHomeAnimationTriggered() { - // Animate the scrims out + // Animate the SystemUI scrim views out mScrimViews.startExitRecentsAnimation(); } @@ -664,7 +586,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void refreshSearchWidget() { - // Load the Search widget again bindSearchBarAppWidget(); addSearchBarAppWidgetView(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java index 43d7a54e8ff7..a63e167acf11 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java @@ -33,6 +33,7 @@ public class RecentsAppWidgetHost extends AppWidgetHost { Context mContext; RecentsAppWidgetHostCallbacks mCb; RecentsConfiguration mConfig; + boolean mIsListening; public RecentsAppWidgetHost(Context context, int hostId) { super(context, hostId); @@ -42,6 +43,7 @@ public class RecentsAppWidgetHost extends AppWidgetHost { public void startListening(RecentsAppWidgetHostCallbacks cb) { mCb = cb; + mIsListening = true; super.startListening(); } @@ -51,6 +53,11 @@ public class RecentsAppWidgetHost extends AppWidgetHost { // Ensure that we release any references to the callbacks mCb = null; mContext = null; + mIsListening = false; + } + + public boolean isListening() { + return mIsListening; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index e62d9891066c..439765eaf8f0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -35,6 +35,7 @@ import com.android.systemui.recents.misc.Console; * NOTE: We should not hold any references to a Context from a static instance */ public class RecentsConfiguration { static RecentsConfiguration sInstance; + static int sPrevConfigurationHashCode; DisplayMetrics mDisplayMetrics; @@ -138,7 +139,11 @@ public class RecentsConfiguration { if (sInstance == null) { sInstance = new RecentsConfiguration(context); } - sInstance.update(context); + int configHashCode = context.getResources().getConfiguration().hashCode(); + if (sPrevConfigurationHashCode != configHashCode) { + sInstance.update(context); + sPrevConfigurationHashCode = configHashCode; + } return sInstance; } @@ -179,10 +184,8 @@ public class RecentsConfiguration { transposeRecentsLayoutWithOrientation = res.getBoolean(R.bool.recents_transpose_layout_with_orientation); - // Search bar + // Search Bar searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); - - // Update the search widget id searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1); // Task stack @@ -242,12 +245,6 @@ public class RecentsConfiguration { // Nav bar scrim navBarScrimEnterDuration = res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration); - - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, - "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait", - Console.AnsiGreen); - } } /** Updates the system insets */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index b8beda6f6858..ced404309466 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -80,6 +81,8 @@ public class SystemServicesProxy { ComponentName mAssistComponent; Bitmap mDummyIcon; + int mDummyThumbnailWidth; + int mDummyThumbnailHeight; Paint mBgProtectionPaint; Canvas mBgProtectionCanvas; @@ -96,6 +99,13 @@ public class SystemServicesProxy { mDisplay = mWm.getDefaultDisplay(); mRecentsPackage = context.getPackageName(); + // Get the dummy thumbnail width/heights + Resources res = context.getResources(); + int wId = com.android.internal.R.dimen.thumbnail_width; + int hId = com.android.internal.R.dimen.thumbnail_height; + mDummyThumbnailWidth = res.getDimensionPixelSize(wId); + mDummyThumbnailHeight = res.getDimensionPixelSize(hId); + // Create the protection paints mBgProtectionPaint = new Paint(); mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); @@ -213,7 +223,8 @@ public class SystemServicesProxy { // If we are mocking, then just return a dummy thumbnail if (Constants.DebugFlags.App.EnableSystemServicesProxy) { - Bitmap thumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight, + Bitmap.Config.ARGB_8888); thumbnail.eraseColor(0xff333333); return thumbnail; } @@ -239,6 +250,8 @@ public class SystemServicesProxy { */ public static Bitmap getThumbnail(ActivityManager activityManager, int taskId) { ActivityManager.TaskThumbnail taskThumbnail = activityManager.getTaskThumbnail(taskId); + if (taskThumbnail == null) return null; + Bitmap thumbnail = taskThumbnail.mainThumbnail; ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor; if (thumbnail == null && descriptor != null) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index cbb8892dea94..71979c4f7015 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -29,7 +29,6 @@ import android.os.HandlerThread; import android.os.UserHandle; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; -import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.SystemServicesProxy; import java.util.Collections; @@ -44,9 +43,6 @@ class TaskResourceLoadQueue { /** Adds a new task to the load queue */ void addTask(Task t) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|addTask]"); - } if (!mQueue.contains(t)) { mQueue.add(t); } @@ -60,25 +56,16 @@ class TaskResourceLoadQueue { * force reloaded. */ Task nextTask() { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|nextTask]"); - } return mQueue.poll(); } /** Removes a task from the load queue */ void removeTask(Task t) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|removeTask]"); - } mQueue.remove(t); } /** Clears all the tasks from the load queue */ void clearTasks() { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, " [TaskResourceLoadQueue|clearTasks]"); - } mQueue.clear(); } @@ -124,9 +111,6 @@ class TaskResourceLoader implements Runnable { /** Restarts the loader thread */ void start(Context context) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|start]"); - } mContext = context; mCancelled = false; mSystemServicesProxy = new SystemServicesProxy(context); @@ -138,9 +122,6 @@ class TaskResourceLoader implements Runnable { /** Requests the loader thread to stop after the current iteration */ void stop() { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, "[TaskResourceLoader|stop]"); - } // Mark as cancelled for the thread to pick up mCancelled = true; mSystemServicesProxy = null; @@ -154,25 +135,13 @@ class TaskResourceLoader implements Runnable { @Override public void run() { while (true) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[TaskResourceLoader|run|" + Thread.currentThread().getId() + "]"); - } if (mCancelled) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[TaskResourceLoader|cancel|" + Thread.currentThread().getId() + "]"); - } // We have to unset the context here, since the background thread may be using it // when we call stop() mContext = null; // If we are cancelled, then wait until we are started again synchronized(mLoadThread) { try { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[TaskResourceLoader|waitOnLoadThreadCancelled]"); - } mLoadThread.wait(); } catch (InterruptedException ie) { ie.printStackTrace(); @@ -186,11 +155,6 @@ class TaskResourceLoader implements Runnable { if (t != null) { Drawable cachedIcon = mApplicationIconCache.getCheckLastActiveTime(t.key); Bitmap cachedThumbnail = mThumbnailCache.getCheckLastActiveTime(t.key); - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - " [TaskResourceLoader|load]", - t + " icon: " + cachedIcon + " thumbnail: " + cachedThumbnail); - } // Load the application icon if it is stale or we haven't cached one yet if (cachedIcon == null) { Drawable icon = null; @@ -198,10 +162,6 @@ class TaskResourceLoader implements Runnable { t.userId); if (info != null) { icon = ssp.getActivityIcon(info, t.userId); - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - " [TaskResourceLoader|loadedIcon]", icon); - } } // If we can't load the icon, then set the default application icon into the // cache. This will remain until the task's last active time is updated. @@ -213,10 +173,6 @@ class TaskResourceLoader implements Runnable { Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id); if (thumbnail != null) { thumbnail.setHasAlpha(false); - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - " [TaskResourceLoader|loadedThumbnail]", thumbnail); - } } // Even if we can't load the icon, we set the default thumbnail into the // cache. This will remain until the task's last active time is updated. @@ -240,10 +196,6 @@ class TaskResourceLoader implements Runnable { if (!mCancelled && mLoadQueue.isEmpty()) { synchronized(mLoadQueue) { try { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[TaskResourceLoader|waitOnLoadQueue]"); - } mWaitingOnLoadQueue = true; mLoadQueue.wait(); mWaitingOnLoadQueue = false; @@ -290,12 +242,6 @@ public class RecentsTaskLoader { int thumbnailCacheSize = Constants.DebugFlags.App.DisableBackgroundCache ? 1 : mMaxThumbnailCacheSize; - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize + - " iconCache: " + iconCacheSize); - } - // Create the default assets Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); icon.eraseColor(0x00000000); @@ -315,13 +261,6 @@ public class RecentsTaskLoader { mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, mDefaultThumbnail, mDefaultApplicationIcon); - - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|defaultBitmaps]", - "icon: " + mDefaultApplicationIcon + - " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed); - } } /** Initializes the recents task loader */ @@ -343,29 +282,15 @@ public class RecentsTaskLoader { } private static List<ActivityManager.RecentTaskInfo> getRecentTasks(SystemServicesProxy ssp) { - long t1 = System.currentTimeMillis(); - List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier()); Collections.reverse(tasks); - if (Console.Enabled) { - Console.log(Constants.Log.App.TimeSystemCalls, - "[RecentsTaskLoader|getRecentTasks]", - "" + (System.currentTimeMillis() - t1) + "ms"); - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|tasks]", "" + tasks.size()); - } return tasks; } /** Reload the set of recent tasks */ public SpaceNode reload(Context context, int preloadCount) { - long t1 = System.currentTimeMillis(); - - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|reload]"); - } RecentsConfiguration config = RecentsConfiguration.getInstance(); Resources res = context.getResources(); LinkedHashSet<Task> tasksToLoad = new LinkedHashSet<Task>(); @@ -378,7 +303,6 @@ public class RecentsTaskLoader { List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(ssp); // Add each task to the task stack - t1 = System.currentTimeMillis(); int taskCount = tasks.size(); for (int i = 0; i < taskCount; i++) { ActivityManager.RecentTaskInfo t = tasks.get(i); @@ -408,12 +332,6 @@ public class RecentsTaskLoader { // Preload the specified number of apps if (i >= (taskCount - preloadCount)) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|preloadTask]", - "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName()); - } - // Load the icon from the cache if possible task.applicationIcon = mApplicationIconCache.getCheckLastActiveTime(task.key); if (task.applicationIcon == null) { @@ -436,10 +354,6 @@ public class RecentsTaskLoader { // Load the thumbnail (if possible and not the foremost task, from the cache) task.thumbnail = mThumbnailCache.getCheckLastActiveTime(task.key); if (task.thumbnail == null) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|loadingTaskThumbnail]"); - } if (isForemostTask) { // We force loading the thumbnail icon for the foremost task task.thumbnail = ssp.getTaskThumbnail(task.key.id); @@ -460,17 +374,8 @@ public class RecentsTaskLoader { } // Add the task to the stack - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName()); - } stack.addTask(task); } - if (Console.Enabled) { - Console.log(Constants.Log.App.TimeSystemCalls, - "[RecentsTaskLoader|getAllTaskTopThumbnail]", - "" + (System.currentTimeMillis() - t1) + "ms"); - } // Simulate the groupings that we describe stack.createAffiliatedGroupings(); @@ -512,12 +417,6 @@ public class RecentsTaskLoader { Drawable applicationIcon = mApplicationIconCache.get(t.key); Bitmap thumbnail = mThumbnailCache.get(t.key); - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]", - t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail + - " thumbnailCacheSize: " + mThumbnailCache.size()); - } - boolean requiresLoad = false; if (applicationIcon == null) { applicationIcon = mDefaultApplicationIcon; @@ -535,23 +434,12 @@ public class RecentsTaskLoader { /** Releases the task resource data back into the pool. */ public void unloadTaskData(Task t) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|unloadTask]", t + - " thumbnailCacheSize: " + mThumbnailCache.size()); - } - mLoadQueue.removeTask(t); t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); } /** Completely removes the resource data from the pool. */ public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, - "[RecentsTaskLoader|deleteTask]", t); - } - mLoadQueue.removeTask(t); mThumbnailCache.remove(t.key); mApplicationIconCache.remove(t.key); @@ -562,9 +450,6 @@ public class RecentsTaskLoader { /** Stops the task loader and clears all pending tasks */ void stopLoader() { - if (Console.Enabled) { - Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stopLoader]"); - } mLoader.stop(); mLoadQueue.clearTasks(); } @@ -585,11 +470,6 @@ public class RecentsTaskLoader { * out of memory. */ public void onTrimMemory(int level) { - if (Console.Enabled) { - Console.log(Constants.Log.App.Memory, "[RecentsTaskLoader|onTrimMemory]", - Console.trimMemoryLevelToString(level)); - } - switch (level) { case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN: // Stop the loader immediately when the UI is no longer visible diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java index 57b8ea43848f..63f59be31651 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullscreenTransitionOverlayView.java @@ -22,8 +22,6 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -34,9 +32,7 @@ import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import com.android.systemui.R; -import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; -import com.android.systemui.recents.misc.Console; /** @@ -152,11 +148,6 @@ public class FullscreenTransitionOverlayView extends FrameLayout { public void prepareAnimateOnEnterRecents(Bitmap screenshot) { if (!mConfig.launchedFromAppWithScreenshot) return; - if (Console.Enabled) { - Console.logStartTracingTime(Constants.Log.App.TimeRecentsScreenshotTransition, - Constants.Log.App.TimeRecentsScreenshotTransitionKey); - } - setClipTop(0); setClipBottom(getMeasuredHeight()); setDim(0); @@ -180,11 +171,6 @@ public class FullscreenTransitionOverlayView extends FrameLayout { /** Animates this view as it enters recents */ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx, final Runnable postAnimRunnable) { - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition, - Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Starting"); - } - // Cancel the current animation if (mEnterAnimation != null) { mEnterAnimation.removeAllListeners(); @@ -226,11 +212,6 @@ public class FullscreenTransitionOverlayView extends FrameLayout { mCb.onEnterAnimationComplete(); // Run the given post-anim runnable postAnimRunnable.run(); - - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition, - Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed"); - } } }); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 73bbf86165c6..7bb614453da5 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -141,20 +141,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV TaskView tv = (TaskView) stackView.getChildAt(j); Task task = tv.getTask(); if (tv.isFocusedTask()) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]", - "Found focused Task"); - } onTaskViewClicked(stackView, tv, stack, task, false); return true; } } } } - if (Console.Enabled) { - Console.log(Constants.Log.UI.Focus, "[RecentsView|launchFocusedTask]", - "No Tasks focused"); - } return false; } @@ -228,12 +220,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV if (searchBar != null) { mSearchBar = searchBar; addView(mSearchBar); - - if (Console.Enabled) { - Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsView|setSearchBar]", - "" + (mSearchBar.getVisibility() == View.VISIBLE), - Console.AnsiBlue); - } } } } @@ -260,13 +246,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV int height = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|measure]", - "width: " + width + " height: " + height, Console.AnsiGreen); - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onMeasure"); - } - // Get the search bar bounds and measure the search bar layout if (mSearchBar != null) { Rect searchBarSpaceBounds = new Rect(); @@ -303,13 +282,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, "[RecentsView|layout]", - new Rect(left, top, right, bottom) + " changed: " + changed, Console.AnsiGreen); - Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, - Constants.Log.App.TimeRecentsStartupKey, "RecentsView.onLayout"); - } - // Get the search bar bounds so that we lay it out if (mSearchBar != null) { Rect searchBarSpaceBounds = new Rect(); @@ -351,11 +323,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, - "[RecentsView|fitSystemWindows]", "insets: " + insets, Console.AnsiGreen); - } - // Update the configuration with the latest system insets and trigger a relayout mConfig.updateSystemInsets(insets.getSystemWindowInsets()); requestLayout(); @@ -482,11 +449,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV final Runnable launchRunnable = new Runnable() { @Override public void run() { - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity"); - } - if (task.isActive) { // Bring an active task to the foreground RecentsTaskLoader.getInstance().getSystemServicesProxy() @@ -515,19 +477,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // And clean up the old task onTaskViewDismissed(task); } - - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "startActivity"); - } } }; - if (Console.Enabled) { - Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, - Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched"); - } - // Launch the app right away if there is no task view, otherwise, animate the icon out first if (tv == null) { post(launchRunnable); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java index 8409227a871e..e0298ab58d94 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java @@ -28,8 +28,6 @@ import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.animation.LinearInterpolator; -import com.android.systemui.recents.misc.Console; -import com.android.systemui.recents.Constants; /** * This class facilitates swipe to dismiss. It defines an interface to be implemented by the @@ -178,11 +176,6 @@ public class SwipeHelper { } public boolean onInterceptTouchEvent(MotionEvent ev) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.TouchEvents, - "[SwipeHelper|interceptTouchEvent]", - Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue); - } final int action = ev.getAction(); switch (action) { @@ -294,12 +287,6 @@ public class SwipeHelper { } public boolean onTouchEvent(MotionEvent ev) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.TouchEvents, - "[SwipeHelper|touchEvent]", - Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue); - } - if (!mDragging) { if (!onInterceptTouchEvent(ev)) { return mCanCurrViewBeDimissed; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index adc808a733fa..7b5216319134 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -167,10 +167,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal requestSynchronizeStackViewsWithModel(0); } void requestSynchronizeStackViewsWithModel(int duration) { - if (Console.Enabled) { - Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel, - "[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow); - } if (!mStackViewsDirty) { invalidate(mStackAlgorithm.mStackRect); } @@ -266,11 +262,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (visibleRangeOut != null) { visibleRangeOut[0] = frontMostVisibleIndex; visibleRangeOut[1] = backMostVisibleIndex; - if (Console.Enabled) { - Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel, - "[TaskStackView|updateStackTransforms]", - "Back: " + backMostVisibleIndex + " Front: " + frontMostVisibleIndex); - } } } @@ -290,11 +281,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** Synchronizes the views with the model */ void synchronizeStackViewsWithModel() { - if (Console.Enabled) { - Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel, - "[TaskStackView|synchronizeViewsWithModel]", - "mStackViewsDirty: " + mStackViewsDirty, Console.AnsiYellow); - } if (mStackViewsDirty) { // Get all the task transforms ArrayList<Task> tasks = mStack.getTasks(); @@ -344,11 +330,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mStackViewsAnimationDuration); } - if (Console.Enabled) { - Console.log(Constants.Log.TaskStack.SynchronizeViewsWithModel, - " [TaskStackView|viewChildren]", "" + getChildCount()); - } - mStackViewsAnimationDuration = 0; mStackViewsDirty = false; } @@ -537,11 +518,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mMaxScroll = mStackAlgorithm.mMaxScroll; // Debug logging - if (Constants.Log.UI.MeasureAndLayout) { - Console.log(" [TaskStack|minScroll] " + mMinScroll); - Console.log(" [TaskStack|maxScroll] " + mMaxScroll); - } - if (boundScrollToNewMinMax) { boundScroll(); } @@ -563,9 +539,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** Focuses the task at the specified index in the stack */ void focusTask(int taskIndex, boolean scrollToNewPosition) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "" + taskIndex); - } if (0 <= taskIndex && taskIndex < mStack.getTaskCount()) { mFocusedTaskIndex = taskIndex; @@ -575,9 +548,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal Runnable postScrollRunnable = null; if (tv != null) { tv.setFocusedTask(); - if (Console.Enabled) { - Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", "Requesting focus"); - } } else { postScrollRunnable = new Runnable() { @Override @@ -586,10 +556,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView tv = getChildViewForTask(t); if (tv != null) { tv.setFocusedTask(); - if (Console.Enabled) { - Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusTask]", - "Requesting focus after scroll animation"); - } } } }; @@ -611,11 +577,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** Focuses the next task in the stack */ void focusNextTask(boolean forward) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.Focus, "[TaskStackView|focusNextTask]", "" + - mFocusedTaskIndex); - } - // Find the next index to focus int numTasks = mStack.getTaskCount(); if (mFocusedTaskIndex < 0) { @@ -630,24 +591,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** Enables the hw layers and increments the hw layer requirement ref count */ void addHwLayersRefCount(String reason) { - if (Console.Enabled) { - int refCount = mHwLayersTrigger.getCount(); - Console.log(Constants.Log.UI.HwLayers, - "[TaskStackView|addHwLayersRefCount] refCount: " + - refCount + "->" + (refCount + 1) + " " + reason); - } mHwLayersTrigger.increment(); } /** Decrements the hw layer requirement ref count and disables the hw layers when we don't need them anymore. */ void decHwLayersRefCount(String reason) { - if (Console.Enabled) { - int refCount = mHwLayersTrigger.getCount(); - Console.log(Constants.Log.UI.HwLayers, - "[TaskStackView|decHwLayersRefCount] refCount: " + - refCount + "->" + (refCount - 1) + " " + reason); - } mHwLayersTrigger.decrement(); } @@ -676,10 +625,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void dispatchDraw(Canvas canvas) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.Draw, "[TaskStackView|dispatchDraw]", "", - Console.AnsiPurple); - } synchronizeStackViewsWithModel(); clipTaskViews(); super.dispatchDraw(canvas); @@ -703,25 +648,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, "[TaskStackView|measure]", - "width: " + width + " height: " + height + - " awaitingFirstLayout: " + mAwaitingFirstLayout, Console.AnsiGreen); - } // Compute our stack/task rects Rect taskStackBounds = new Rect(); mConfig.getTaskStackBounds(width, height, taskStackBounds); computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom); - // Debug logging - if (Constants.Log.UI.MeasureAndLayout) { - Console.log(" [TaskStack|fullRect] " + mStackAlgorithm.mRect); - Console.log(" [TaskStack|stackRect] " + mStackAlgorithm.mStackRect); - Console.log(" [TaskStack|stackRectSansPeek] " + mStackAlgorithm.mStackRectSansPeek); - Console.log(" [TaskStack|taskRect] " + mStackAlgorithm.mTaskRect); - } - // If this is the first layout, then scroll to the front of the stack and synchronize the // stack views immediately if (mAwaitingFirstLayout) { @@ -749,19 +681,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.MeasureAndLayout, "[TaskStackView|layout]", - "" + new Rect(left, top, right, bottom), Console.AnsiGreen); - } - - // Debug logging - if (Constants.Log.UI.MeasureAndLayout) { - Console.log(" [TaskStack|fullRect] " + mStackAlgorithm.mRect); - Console.log(" [TaskStack|stackRect] " + mStackAlgorithm.mStackRect); - Console.log(" [TaskStack|stackRectSansPeek] " + mStackAlgorithm.mStackRectSansPeek); - Console.log(" [TaskStack|taskRect] " + mStackAlgorithm.mTaskRect); - } - // Layout each of the children int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { @@ -1021,19 +940,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public TaskView createView(Context context) { - if (Console.Enabled) { - Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|createPoolView]"); - } return (TaskView) mInflater.inflate(R.layout.recents_task_view, this, false); } @Override public void prepareViewToEnterPool(TaskView tv) { Task task = tv.getTask(); - if (Console.Enabled) { - Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|returnToPool]", - tv.getTask() + " tv: " + tv); - } // Report that this tasks's data is no longer being used RecentsTaskLoader.getInstance().unloadTaskData(task); @@ -1050,11 +962,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void prepareViewToLeavePool(TaskView tv, Task task, boolean isNewView) { - if (Console.Enabled) { - Console.log(Constants.Log.ViewPool.PoolCallbacks, "[TaskStackView|leavePool]", - "isNewView: " + isNewView); - } - // Rebind the task and request that this task's data be filled into the TaskView tv.onTaskBound(task); RecentsTaskLoader.getInstance().loadTaskData(task); @@ -1083,10 +990,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Add/attach the view to the hierarchy - if (Console.Enabled) { - Console.log(Constants.Log.ViewPool.PoolCallbacks, " [TaskStackView|insertIndex]", - "" + insertIndex); - } if (isNewView) { addView(tv, insertIndex); @@ -1112,11 +1015,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void onTaskViewAppIconClicked(TaskView tv) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Icon]", - tv.getTask() + " is currently filtered: " + mStack.hasFilteredTasks(), - Console.AnsiCyan); - } if (Constants.DebugFlags.App.EnableTaskFiltering) { if (mStack.hasFilteredTasks()) { mStack.unfilterTasks(); @@ -1135,11 +1033,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.ClickEvents, "[TaskStack|Clicked|Thumbnail]", - task + " cb: " + mCb); - } - // Cancel any doze triggers mUIDozeTrigger.stopDozing(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 15ace134b35d..bd4ea90fb799 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -22,7 +22,6 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewParent; -import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.Constants; /* Handles touch events for a TaskStackView. */ @@ -100,12 +99,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { /** Touch preprocessing for handling below */ public boolean onInterceptTouchEvent(MotionEvent ev) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.TouchEvents, - "[TaskStackViewTouchHandler|interceptTouchEvent]", - Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue); - } - // Return early if we have no children boolean hasChildren = (mSv.getChildCount() > 0); if (!hasChildren) { @@ -186,12 +179,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { /** Handles touch events once we have intercepted them */ public boolean onTouchEvent(MotionEvent ev) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.TouchEvents, - "[TaskStackViewTouchHandler|touchEvent]", - Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue); - } - // Short circuit if we have no children boolean hasChildren = (mSv.getChildCount() > 0); if (!hasChildren) { @@ -290,16 +277,6 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { int overscrollRange = (int) (Math.min(1f, Math.abs((float) velocity / mMaximumVelocity)) * Constants.Values.TaskStackView.TaskStackOverscrollRange); - - if (Console.Enabled) { - Console.log(Constants.Log.UI.TouchEvents, - "[TaskStackViewTouchHandler|fling]", - "scroll: " + mSv.getStackScroll() + " velocity: " + velocity + - " maxVelocity: " + mMaximumVelocity + - " overscrollRange: " + overscrollRange, - Console.AnsiGreen); - } - // Fling scroll mSv.mScroller.fling(0, mSv.getStackScroll(), 0, -velocity, diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index ab148632698c..7e3004737f7f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -175,11 +175,6 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On /** Synchronizes this view's properties with the task's transform */ void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration) { - if (Console.Enabled) { - Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]", - "duration: " + duration, Console.AnsiPurple); - } - // Update the bar view mBarView.updateViewPropertiesToTaskTransform(toTransform, duration); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index adfa1f239dec..f431fdb2b9ab 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -49,6 +49,9 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.AudioManager; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -205,6 +208,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private Drawable mBackgroundDrawable; + private float mElevation; + private int mFrameResource = 0; private int mTextColor = 0; @@ -224,6 +229,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mClosingActionMenu; private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE; + private MediaController mMediaController; private AudioManager mAudioManager; private KeyguardManager mKeyguardManager; @@ -1688,15 +1694,42 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_DOWN: { + int direction = keyCode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE + : AudioManager.ADJUST_LOWER; + // If we have a session send it the volume command, otherwise + // use the suggested stream. + if (mMediaController != null) { + mMediaController.adjustVolumeBy(direction, AudioManager.FLAG_SHOW_UI); + } else { + MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy( + mVolumeControlStreamType, direction, AudioManager.FLAG_SHOW_UI); + } + return true; + } case KeyEvent.KEYCODE_VOLUME_MUTE: { - // Similar code is in PhoneFallbackEventHandler in case the window - // doesn't have one of these. In this case, we execute it here and - // eat the event instead, because we have mVolumeControlStreamType - // and they don't. getAudioManager().handleKeyDown(event, mVolumeControlStreamType); return true; } + // These are all the recognized media key codes in + // KeyEvent.isMediaKey() + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + if (mMediaController != null) { + if (mMediaController.dispatchMediaButtonEvent(event)) { + return true; + } + } + } case KeyEvent.KEYCODE_MENU: { onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event); @@ -1750,7 +1783,19 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: - case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_DOWN: { + // If we have a session send it the volume command, otherwise + // use the suggested stream. + if (mMediaController != null) { + mMediaController.adjustVolumeBy(0, AudioManager.FLAG_PLAY_SOUND + | AudioManager.FLAG_VIBRATE); + } else { + MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy( + mVolumeControlStreamType, 0, + AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE); + } + return true; + } case KeyEvent.KEYCODE_VOLUME_MUTE: { // Similar code is in PhoneFallbackEventHandler in case the window // doesn't have one of these. In this case, we execute it here and @@ -1759,6 +1804,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { getAudioManager().handleKeyUp(event, mVolumeControlStreamType); return true; } + // These are all the recognized media key codes in + // KeyEvent.isMediaKey() + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_MUTE: + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_STOP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + case KeyEvent.KEYCODE_MEDIA_REWIND: + case KeyEvent.KEYCODE_MEDIA_RECORD: + case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: { + if (mMediaController != null) { + if (mMediaController.dispatchMediaButtonEvent(event)) { + return true; + } + } + } case KeyEvent.KEYCODE_MENU: { onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId, @@ -3189,6 +3253,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { + Integer.toHexString(mFrameResource)); } } + mElevation = a.getDimension(com.android.internal.R.styleable.Window_windowElevation, 0); mTextColor = a.getColor(com.android.internal.R.styleable.Window_textColor, 0xFF000000); } @@ -3278,28 +3343,31 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { // Remaining setup -- of background and title -- that only applies // to top-level windows. if (getContainer() == null) { - Drawable drawable = mBackgroundDrawable; + final Drawable background; if (mBackgroundResource != 0) { - drawable = getContext().getDrawable(mBackgroundResource); + background = getContext().getDrawable(mBackgroundResource); + } else { + background = mBackgroundDrawable; } - mDecor.setWindowBackground(drawable); - drawable = null; + mDecor.setWindowBackground(background); + + final Drawable frame; if (mFrameResource != 0) { - drawable = getContext().getDrawable(mFrameResource); + frame = getContext().getDrawable(mFrameResource); + } else { + frame = null; } - mDecor.setWindowFrame(drawable); + mDecor.setWindowFrame(frame); - // System.out.println("Text=" + Integer.toHexString(mTextColor) + - // " Sel=" + Integer.toHexString(mTextSelectedColor) + - // " Title=" + Integer.toHexString(mTitleColor)); - - if (mTitleColor == 0) { - mTitleColor = mTextColor; - } + mDecor.setElevation(mElevation); if (mTitle != null) { setTitle(mTitle); } + + if (mTitleColor == 0) { + mTitleColor = mTextColor; + } setTitleColor(mTitleColor); } @@ -3773,6 +3841,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return mVolumeControlStreamType; } + @Override + public void setMediaController(MediaController controller) { + mMediaController = controller; + } + + @Override + public MediaController getMediaController() { + return mMediaController; + } + private boolean isTranslucent() { TypedArray a = getWindowStyle(); return a.getBoolean(a.getResourceId( diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 5bfde4dbaadb..c3a9dbe94331 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -3922,6 +3922,11 @@ public class BackupManagerService extends IBackupManager.Stub { break; } + // Is it a *file* we need to drop? + if (!isRestorableFile(info)) { + okay = false; + } + // If the policy is satisfied, go ahead and set up to pipe the // data to the agent. if (DEBUG && okay && mAgent != null) { @@ -4082,9 +4087,9 @@ public class BackupManagerService extends IBackupManager.Stub { } } - // Problems setting up the agent communication, or an already- - // ignored package: skip to the next tar stream entry by - // reading and discarding this file. + // Problems setting up the agent communication, an explicitly + // dropped file, or an already-ignored package: skip to the + // next stream entry by reading and discarding this file. if (!okay) { if (DEBUG) Slog.d(TAG, "[discarding file content]"); long bytesToConsume = (info.size + 511) & ~511; @@ -4691,6 +4696,31 @@ public class BackupManagerService extends IBackupManager.Stub { return info; } + private boolean isRestorableFile(FileMetadata info) { + if (FullBackup.CACHE_TREE_TOKEN.equals(info.domain)) { + if (MORE_DEBUG) { + Slog.i(TAG, "Dropping cache file path " + info.path); + } + return false; + } + + if (FullBackup.ROOT_TREE_TOKEN.equals(info.domain)) { + // It's possible this is "no-backup" dir contents in an archive stream + // produced on a device running a version of the OS that predates that + // API. Respect the no-backup intention and don't let the data get to + // the app. + if (info.path.startsWith("no_backup/")) { + if (MORE_DEBUG) { + Slog.i(TAG, "Dropping no_backup file path " + info.path); + } + return false; + } + } + + // Otherwise we think this file is good to go + return true; + } + private void HEXLOG(byte[] block) { int offset = 0; int todo = block.length; diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index 822215526fcd..b3419c114250 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -62,6 +62,7 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; @@ -70,6 +71,7 @@ import android.provider.Telephony.Sms.Intents; import android.telephony.SmsMessage; import android.telephony.TelephonyManager; import android.telephony.gsm.GsmCellLocation; +import android.text.TextUtils; import android.util.Log; import android.util.NtpTrustedTime; @@ -85,6 +87,8 @@ import java.util.Date; import java.util.Map.Entry; import java.util.Properties; +import libcore.io.IoUtils; + /** * A GPS implementation of LocationProvider used by LocationManager. * @@ -201,7 +205,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final int AGPS_SETID_TYPE_IMSI = 1; private static final int AGPS_SETID_TYPE_MSISDN = 2; - private static final String PROPERTIES_FILE = "/etc/gps.conf"; + private static final String PROPERTIES_FILE_PREFIX = "/etc/gps"; + private static final String PROPERTIES_FILE_SUFFIX = ".conf"; + private static final String DEFAULT_PROPERTIES_FILE = PROPERTIES_FILE_PREFIX + PROPERTIES_FILE_SUFFIX; private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L; private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L; @@ -441,6 +447,44 @@ public class GpsLocationProvider implements LocationProviderInterface { return native_is_supported(); } + private boolean loadPropertiesFile(String filename) { + mProperties = new Properties(); + try { + File file = new File(filename); + FileInputStream stream = null; + try { + stream = new FileInputStream(file); + mProperties.load(stream); + } finally { + IoUtils.closeQuietly(stream); + } + + mSuplServerHost = mProperties.getProperty("SUPL_HOST"); + String portString = mProperties.getProperty("SUPL_PORT"); + if (mSuplServerHost != null && portString != null) { + try { + mSuplServerPort = Integer.parseInt(portString); + } catch (NumberFormatException e) { + Log.e(TAG, "unable to parse SUPL_PORT: " + portString); + } + } + + mC2KServerHost = mProperties.getProperty("C2K_HOST"); + portString = mProperties.getProperty("C2K_PORT"); + if (mC2KServerHost != null && portString != null) { + try { + mC2KServerPort = Integer.parseInt(portString); + } catch (NumberFormatException e) { + Log.e(TAG, "unable to parse C2K_PORT: " + portString); + } + } + } catch (IOException e) { + Log.w(TAG, "Could not open GPS configuration file " + filename); + return false; + } + return true; + } + public GpsLocationProvider(Context context, ILocationManager ilocationManager, Looper looper) { mContext = context; @@ -469,34 +513,15 @@ public class GpsLocationProvider implements LocationProviderInterface { mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( BatteryStats.SERVICE_NAME)); - mProperties = new Properties(); - try { - File file = new File(PROPERTIES_FILE); - FileInputStream stream = new FileInputStream(file); - mProperties.load(stream); - stream.close(); - - mSuplServerHost = mProperties.getProperty("SUPL_HOST"); - String portString = mProperties.getProperty("SUPL_PORT"); - if (mSuplServerHost != null && portString != null) { - try { - mSuplServerPort = Integer.parseInt(portString); - } catch (NumberFormatException e) { - Log.e(TAG, "unable to parse SUPL_PORT: " + portString); - } - } + boolean propertiesLoaded = false; + final String gpsHardware = SystemProperties.get("ro.hardware.gps"); + if (!TextUtils.isEmpty(gpsHardware)) { + final String propFilename = PROPERTIES_FILE_PREFIX + "." + gpsHardware + PROPERTIES_FILE_SUFFIX; + propertiesLoaded = loadPropertiesFile(propFilename); + } - mC2KServerHost = mProperties.getProperty("C2K_HOST"); - portString = mProperties.getProperty("C2K_PORT"); - if (mC2KServerHost != null && portString != null) { - try { - mC2KServerPort = Integer.parseInt(portString); - } catch (NumberFormatException e) { - Log.e(TAG, "unable to parse C2K_PORT: " + portString); - } - } - } catch (IOException e) { - Log.w(TAG, "Could not open GPS configuration file " + PROPERTIES_FILE); + if (!propertiesLoaded) { + loadPropertiesFile(DEFAULT_PROPERTIES_FILE); } // construct handler, listen for events diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 341c7a90d556..01a21f406559 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -505,16 +505,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { || state.getState() == PlaybackState.STATE_FAST_FORWARDING || state.getState() == PlaybackState.STATE_REWINDING) { long updateTime = state.getLastPositionUpdateTime(); + long currentTime = SystemClock.elapsedRealtime(); if (updateTime > 0) { - long position = (long) (state.getPlaybackRate() - * (SystemClock.elapsedRealtime() - updateTime)) + state.getPosition(); + long position = (long) (state.getPlaybackSpeed() + * (currentTime - updateTime)) + state.getPosition(); if (duration >= 0 && position > duration) { position = duration; } else if (position < 0) { position = 0; } - result = new PlaybackState(state); - result.setState(state.getState(), position, state.getPlaybackRate()); + PlaybackState.Builder builder = new PlaybackState.Builder(state); + builder.setState(state.getState(), position, state.getPlaybackSpeed(), + currentTime); + result = builder.build(); } } } diff --git a/services/core/java/com/android/server/pm/KeySetHandle.java b/services/core/java/com/android/server/pm/KeySetHandle.java new file mode 100644 index 000000000000..640feb398489 --- /dev/null +++ b/services/core/java/com/android/server/pm/KeySetHandle.java @@ -0,0 +1,22 @@ +/* + * Copyright 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 com.android.server.pm; + +import android.os.Binder; + +public class KeySetHandle extends Binder { +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index c19951fe4acc..37bedf3da53c 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import android.content.pm.KeySet; import android.content.pm.PackageParser; import android.os.Binder; import android.util.ArraySet; @@ -52,7 +51,7 @@ public class KeySetManagerService { /** Sentinel value returned when public key is not found. */ protected static final long PUBLIC_KEY_NOT_FOUND = -1; - private final LongSparseArray<KeySet> mKeySets; + private final LongSparseArray<KeySetHandle> mKeySets; private final LongSparseArray<PublicKey> mPublicKeys; @@ -65,7 +64,7 @@ public class KeySetManagerService { private static long lastIssuedKeyId = 0; public KeySetManagerService(Map<String, PackageSetting> packages) { - mKeySets = new LongSparseArray<KeySet>(); + mKeySets = new LongSparseArray<KeySetHandle>(); mPublicKeys = new LongSparseArray<PublicKey>(); mKeySetMapping = new LongSparseArray<ArraySet<Long>>(); mPackages = packages; @@ -82,7 +81,7 @@ public class KeySetManagerService { * * Note that this can return true for multiple KeySets. */ - public boolean packageIsSignedByLPr(String packageName, KeySet ks) { + public boolean packageIsSignedByLPr(String packageName, KeySetHandle ks) { PackageSetting pkg = mPackages.get(packageName); if (pkg == null) { throw new NullPointerException("Invalid package name"); @@ -91,16 +90,42 @@ public class KeySetManagerService { throw new NullPointerException("Package has no KeySet data"); } long id = getIdByKeySetLPr(ks); + if (id == KEYSET_NOT_FOUND) { + return false; + } return pkg.keySetData.packageIsSignedBy(id); } /** + * Determine if a package is signed by the given KeySet. + * + * Returns false if the package was not signed by all the + * keys in the KeySet, or if the package was signed by keys + * not in the KeySet. + * + * Note that this can return only for one KeySet. + */ + public boolean packageIsSignedByExactlyLPr(String packageName, KeySetHandle ks) { + PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new NullPointerException("Invalid package name"); + } + if (pkg.keySetData == null + || pkg.keySetData.getProperSigningKeySet() + == PackageKeySetData.KEYSET_UNASSIGNED) { + throw new NullPointerException("Package has no KeySet data"); + } + long id = getIdByKeySetLPr(ks); + return pkg.keySetData.getProperSigningKeySet() == id; + } + + /** * 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. */ public void addDefinedKeySetToPackageLPw(String packageName, - Set<PublicKey> keys, String alias) { + ArraySet<PublicKey> keys, String alias) { if ((packageName == null) || (keys == null) || (alias == null)) { Slog.w(TAG, "Got null argument for a defined keyset, ignoring!"); return; @@ -110,7 +135,7 @@ public class KeySetManagerService { throw new NullPointerException("Unknown package"); } // Add to KeySets, then to package - KeySet ks = addKeySetLPw(keys); + KeySetHandle ks = addKeySetLPw(keys); long id = getIdByKeySetLPr(ks); pkg.keySetData.addDefinedKeySet(id, alias); } @@ -137,19 +162,18 @@ public class KeySetManagerService { * was signed by the provided KeySet. */ public void addSigningKeySetToPackageLPw(String packageName, - Set<PublicKey> signingKeys) { + ArraySet<PublicKey> signingKeys) { if ((packageName == null) || (signingKeys == null)) { Slog.w(TAG, "Got null argument for a signing keyset, ignoring!"); return; } // add the signing KeySet - KeySet ks = addKeySetLPw(signingKeys); + KeySetHandle ks = addKeySetLPw(signingKeys); long id = getIdByKeySetLPr(ks); - Set<Long> publicKeyIds = mKeySetMapping.get(id); + ArraySet<Long> publicKeyIds = mKeySetMapping.get(id); if (publicKeyIds == null) { throw new NullPointerException("Got invalid KeySet id"); } - // attach it to the package PackageSetting pkg = mPackages.get(packageName); if (pkg == null) { @@ -160,7 +184,7 @@ public class KeySetManagerService { // KeySet id to the package's signing KeySets for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) { long keySetID = mKeySets.keyAt(keySetIndex); - Set<Long> definedKeys = mKeySetMapping.get(keySetID); + ArraySet<Long> definedKeys = mKeySetMapping.get(keySetID); if (publicKeyIds.containsAll(definedKeys)) { pkg.keySetData.addSigningKeySet(keySetID); } @@ -171,9 +195,9 @@ public class KeySetManagerService { * Fetches the stable identifier associated with the given KeySet. Returns * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found. */ - private long getIdByKeySetLPr(KeySet ks) { + private long getIdByKeySetLPr(KeySetHandle ks) { for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) { - KeySet value = mKeySets.valueAt(keySetIndex); + KeySetHandle value = mKeySets.valueAt(keySetIndex); if (ks.equals(value)) { return mKeySets.keyAt(keySetIndex); } @@ -187,25 +211,24 @@ public class KeySetManagerService { * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't * identify a {@link KeySet}. */ - public KeySet getKeySetByIdLPr(long id) { + public KeySetHandle getKeySetByIdLPr(long id) { return mKeySets.get(id); } /** - * Fetches the {@link KeySet} that a given package refers to by the provided alias. - * - * @throws IllegalArgumentException if the package has no keyset data. - * @throws NullPointerException if the package is unknown. + * Fetches the {@link KeySetHandle} that a given package refers to by the + * provided alias. Returns null if the package is unknown or does not have a + * KeySet corresponding to that alias. */ - public KeySet getKeySetByAliasAndPackageNameLPr(String packageName, String alias) { + public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) { PackageSetting p = mPackages.get(packageName); - if (p == null) { - throw new NullPointerException("Unknown package"); + if (p == null || p.keySetData == null) { + return null; } - if (p.keySetData == null) { - throw new IllegalArgumentException("Package has no keySet data"); + Long keySetId = p.keySetData.getAliases().get(alias); + if (keySetId == null) { + throw new IllegalArgumentException("Unknown KeySet alias: " + alias); } - long keySetId = p.keySetData.getAliases().get(alias); return mKeySets.get(keySetId); } @@ -214,7 +237,7 @@ public class KeySetManagerService { * KeySet id. * * Returns {@code null} if the identifier doesn't - * identify a {@link KeySet}. + * identify a {@link KeySetHandle}. */ public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) { if(mKeySetMapping.get(id) == null) { @@ -228,36 +251,32 @@ public class KeySetManagerService { } /** - * Fetches all the known {@link KeySet KeySets} that signed the given + * Fetches the proper {@link KeySetHandle KeySet} that signed the given * package. * * @throws IllegalArgumentException if the package has no keyset data. * @throws NullPointerException if the package is unknown. */ - public Set<KeySet> getSigningKeySetsByPackageNameLPr(String packageName) { - Set<KeySet> signingKeySets = new ArraySet<KeySet>(); + public KeySetHandle getSigningKeySetByPackageNameLPr(String packageName) { PackageSetting p = mPackages.get(packageName); - if (p == null) { - throw new NullPointerException("Unknown package"); - } - if (p.keySetData == null || p.keySetData.getSigningKeySets() == null) { - throw new IllegalArgumentException("Package has no keySet data"); - } - for (long l : p.keySetData.getSigningKeySets()) { - signingKeySets.add(mKeySets.get(l)); + if (p == null + || p.keySetData == null + || p.keySetData.getProperSigningKeySet() + == PackageKeySetData.KEYSET_UNASSIGNED) { + return null; } - return signingKeySets; + return mKeySets.get(p.keySetData.getProperSigningKeySet()); } /** - * Fetches all the known {@link KeySet KeySets} that may upgrade the given + * Fetches all the known {@link KeySetHandle KeySets} that may upgrade the given * package. * * @throws IllegalArgumentException if the package has no keyset data. * @throws NullPointerException if the package is unknown. */ - public ArraySet<KeySet> getUpgradeKeySetsByPackageNameLPr(String packageName) { - ArraySet<KeySet> upgradeKeySets = new ArraySet<KeySet>(); + public ArraySet<KeySetHandle> getUpgradeKeySetsByPackageNameLPr(String packageName) { + ArraySet<KeySetHandle> upgradeKeySets = new ArraySet<KeySetHandle>(); PackageSetting p = mPackages.get(packageName); if (p == null) { throw new NullPointerException("Unknown package"); @@ -287,7 +306,7 @@ public class KeySetManagerService { * * Throws if the provided set is {@code null}. */ - private KeySet addKeySetLPw(Set<PublicKey> keys) { + private KeySetHandle addKeySetLPw(ArraySet<PublicKey> keys) { if (keys == null) { throw new NullPointerException("Provided keys cannot be null"); } @@ -305,7 +324,7 @@ public class KeySetManagerService { } // create the KeySet object - KeySet ks = new KeySet(new Binder()); + KeySetHandle ks = new KeySetHandle(); // get the first unoccupied slot in mKeySets long id = getFreeKeySetIDLPw(); // add the KeySet object to it @@ -318,7 +337,7 @@ public class KeySetManagerService { if (p.keySetData != null) { long pProperSigning = p.keySetData.getProperSigningKeySet(); if (pProperSigning != PackageKeySetData.KEYSET_UNASSIGNED) { - Set<Long> pSigningKeys = mKeySetMapping.get(pProperSigning); + ArraySet<Long> pSigningKeys = mKeySetMapping.get(pProperSigning); if (pSigningKeys.containsAll(addedKeyIds)) { p.keySetData.addSigningKeySet(id); } @@ -353,7 +372,7 @@ public class KeySetManagerService { */ private long getIdFromKeyIdsLPr(Set<Long> publicKeyIds) { for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) { - Set<Long> value = mKeySetMapping.valueAt(keyMapIndex); + ArraySet<Long> value = mKeySetMapping.valueAt(keyMapIndex); if (value.equals(publicKeyIds)) { return mKeySetMapping.keyAt(keyMapIndex); } @@ -582,7 +601,7 @@ public class KeySetManagerService { serializer.startTag(null, "keysets"); for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) { long id = mKeySetMapping.keyAt(keySetIndex); - Set<Long> keys = mKeySetMapping.valueAt(keySetIndex); + ArraySet<Long> keys = mKeySetMapping.valueAt(keySetIndex); serializer.startTag(null, "keyset"); serializer.attribute(null, "identifier", Long.toString(id)); for (long keyId : keys) { @@ -662,7 +681,7 @@ public class KeySetManagerService { final String tagName = parser.getName(); if (tagName.equals("keyset")) { currentKeySetId = readIdentifierLPw(parser); - mKeySets.put(currentKeySetId, new KeySet(new Binder())); + mKeySets.put(currentKeySetId, new KeySetHandle()); mKeySetMapping.put(currentKeySetId, new ArraySet<Long>()); } else if (tagName.equals("key-id")) { long id = readIdentifierLPw(parser); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index c399fa2550bb..41ab66ac7ee1 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -177,7 +177,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void setClientProgress(int progress) { mClientProgress = progress; - mProgress = MathUtils.constrain((mClientProgress * 8 * 100) / (params.progressMax * 10), 0, 80); + mProgress = MathUtils.constrain( + (int) (((float) mClientProgress) / ((float) params.progressMax)) * 80, 0, 80); mCallback.onSessionProgress(this, mProgress); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 101ef9204729..cfba19c6ff50 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9331,14 +9331,18 @@ public class PackageManagerService extends IPackageManager.Stub { return false; } else { final File beforeCodeFile = codeFile; - final File afterCodeFile = new File(mAppInstallDir, - getNextCodePath(oldCodePath, pkg.packageName, null)); + final File afterCodeFile = getNextCodePath(pkg.packageName); Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile); - if (!beforeCodeFile.renameTo(afterCodeFile)) { + try { + Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); + } catch (ErrnoException e) { + Slog.d(TAG, "Failed to rename", e); return false; } + if (!SELinux.restoreconRecursive(afterCodeFile)) { + Slog.d(TAG, "Failed to restorecon"); return false; } @@ -9811,6 +9815,16 @@ public class PackageManagerService extends IPackageManager.Stub { return prefix + idxStr; } + private File getNextCodePath(String packageName) { + int suffix = 1; + File result; + do { + result = new File(mAppInstallDir, packageName + "-" + suffix); + suffix++; + } while (result.exists()); + return result; + } + // Utility method used to ignore ADD/REMOVE events // by directory observer. private static boolean ignoreCodePath(String fullPathStr) { @@ -13255,4 +13269,83 @@ public class PackageManagerService extends IPackageManager.Stub { } return mUserNeedsBadging.valueAt(index); } + + @Override + public KeySetHandle getKeySetByAlias(String packageName, String alias) { + if (packageName == null || alias == null) { + return null; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (pkg.applicationInfo.uid != Binder.getCallingUid() + && Process.SYSTEM_UID != Binder.getCallingUid()) { + throw new SecurityException("May not access KeySets defined by" + + " aliases in other applications."); + } + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias); + } + } + + @Override + public KeySetHandle getSigningKeySet(String packageName) { + if (packageName == null) { + return null; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (pkg.applicationInfo.uid != Binder.getCallingUid() + && Process.SYSTEM_UID != Binder.getCallingUid()) { + throw new SecurityException("May not access signing KeySet of other apps."); + } + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.getSigningKeySetByPackageNameLPr(packageName); + } + } + + @Override + public boolean isPackageSignedByKeySet(String packageName, IBinder ks) { + if (packageName == null || ks == null) { + return false; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (ks instanceof KeySetHandle) { + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ks); + } + return false; + } + } + + @Override + public boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks) { + if (packageName == null || ks == null) { + return false; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (ks instanceof KeySetHandle) { + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ks); + } + return false; + } + } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 8387b6522cee..b24072f54f35 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -717,10 +717,10 @@ class WindowStateAnimator { float top = w.mFrame.top + w.mYOffset; // Adjust for surface insets. - width += attrs.shadowInsets.left + attrs.shadowInsets.right; - height += attrs.shadowInsets.top + attrs.shadowInsets.bottom; - left -= attrs.shadowInsets.left; - top -= attrs.shadowInsets.top; + width += attrs.surfaceInsets.left + attrs.surfaceInsets.right; + height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom; + left -= attrs.surfaceInsets.left; + top -= attrs.surfaceInsets.top; if (DEBUG_VISIBILITY) { Slog.v(TAG, "Creating surface in session " @@ -1140,19 +1140,12 @@ class WindowStateAnimator { void applyDecorRect(final Rect decorRect) { final WindowState w = mWin; - int width = w.mFrame.width(); - int height = w.mFrame.height(); + final int width = w.mFrame.width(); + final int height = w.mFrame.height(); // Compute the offset of the window in relation to the decor rect. - int left = w.mXOffset + w.mFrame.left; - int top = w.mYOffset + w.mFrame.top; - - // Adjust for surface insets. - final WindowManager.LayoutParams attrs = w.mAttrs; - width += attrs.shadowInsets.left + attrs.shadowInsets.right; - height += attrs.shadowInsets.top + attrs.shadowInsets.bottom; - left -= attrs.shadowInsets.left; - top -= attrs.shadowInsets.top; + final int left = w.mXOffset + w.mFrame.left; + final int top = w.mYOffset + w.mFrame.top; // Initialize the decor rect to the entire frame. w.mSystemDecorRect.set(0, 0, width, height); @@ -1182,7 +1175,6 @@ class WindowStateAnimator { if (displayContent == null) { return; } - DisplayInfo displayInfo = displayContent.getDisplayInfo(); // Need to recompute a new system decor rect each time. if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { @@ -1192,6 +1184,7 @@ class WindowStateAnimator { } else if (!w.isDefaultDisplay()) { // On a different display there is no system decor. Crop the window // by the screen boundaries. + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height()); w.mSystemDecorRect.intersect(-w.mCompatFrame.left, -w.mCompatFrame.top, displayInfo.logicalWidth - w.mCompatFrame.left, @@ -1202,44 +1195,52 @@ class WindowStateAnimator { // windows need to be cropped by the screen, so they don't cover // the universe background. if (mAnimator.mUniverseBackground == null) { - w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), - w.mCompatFrame.height()); + w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height()); } else { applyDecorRect(mService.mScreenRect); } } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND || w.mDecorFrame.isEmpty()) { // The universe background isn't cropped, nor windows without policy decor. - w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), - w.mCompatFrame.height()); + w.mSystemDecorRect.set(0, 0, w.mCompatFrame.width(), w.mCompatFrame.height()); } else { // Crop to the system decor specified by policy. applyDecorRect(w.mDecorFrame); } - // By default, the clip rect is the system decor rect - Rect clipRect = w.mSystemDecorRect; - if (mHasClipRect) { + // By default, the clip rect is the system decor. + final Rect clipRect = mTmpClipRect; + clipRect.set(w.mSystemDecorRect); - // If we have an animated clip rect, intersect it with the system decor rect - // NOTE: We are adding a temporary workaround due to the status bar not always reporting - // the correct system decor rect. In such cases, we take into account the specified - // content insets as well. - int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top); - mTmpClipRect.set(w.mSystemDecorRect); - // Don't apply the workaround to apps explicitly requesting fullscreen layout. + // Expand the clip rect for surface insets. + final WindowManager.LayoutParams attrs = w.mAttrs; + clipRect.left -= attrs.surfaceInsets.left; + clipRect.top -= attrs.surfaceInsets.top; + clipRect.right += attrs.surfaceInsets.right; + clipRect.bottom += attrs.surfaceInsets.bottom; + + // If we have an animated clip rect, intersect it with the clip rect. + if (mHasClipRect) { + // NOTE: We are adding a temporary workaround due to the status bar + // not always reporting the correct system decor rect. In such + // cases, we take into account the specified content insets as well. if ((w.mSystemUiVisibility & SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) == SYSTEM_UI_FLAGS_LAYOUT_STABLE_FULLSCREEN) { - mTmpClipRect.intersect(mClipRect); + // Don't apply the workaround to apps explicitly requesting + // fullscreen layout. + clipRect.intersect(mClipRect); } else { - mTmpClipRect.offset(0, -offsetTop); - mTmpClipRect.intersect(mClipRect); - mTmpClipRect.offset(0, offsetTop); + final int offsetTop = Math.max(clipRect.top, w.mContentInsets.top); + clipRect.offset(0, -offsetTop); + clipRect.intersect(mClipRect); + clipRect.offset(0, offsetTop); } - clipRect = mTmpClipRect; - } + // The clip rect was generated assuming (0,0) as the window origin, + // so we need to translate to match the actual surface coordinates. + clipRect.offset(attrs.surfaceInsets.left, attrs.surfaceInsets.top); + if (!clipRect.equals(mLastClipRect)) { mLastClipRect.set(clipRect); try { @@ -1285,10 +1286,10 @@ class WindowStateAnimator { // Adjust for surface insets. final LayoutParams attrs = w.getAttrs(); - width += attrs.shadowInsets.left + attrs.shadowInsets.right; - height += attrs.shadowInsets.top + attrs.shadowInsets.bottom; - left -= attrs.shadowInsets.left; - top -= attrs.shadowInsets.top; + width += attrs.surfaceInsets.left + attrs.surfaceInsets.right; + height += attrs.surfaceInsets.top + attrs.surfaceInsets.bottom; + left -= attrs.surfaceInsets.left; + top -= attrs.surfaceInsets.top; final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top; if (surfaceMoved) { diff --git a/telecomm/java/android/telecomm/Call.java b/telecomm/java/android/telecomm/Call.java new file mode 100644 index 000000000000..ba7e253fc824 --- /dev/null +++ b/telecomm/java/android/telecomm/Call.java @@ -0,0 +1,710 @@ +/* + * 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.telecomm; + +import android.net.Uri; +import android.os.RemoteException; +import android.telephony.DisconnectCause; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * Represents an ongoing phone call that the in-call app should present to the user. + */ +public final class Call { + /** + * The state of a {@code Call} when newly created. + */ + public static final int STATE_NEW = 0; + + /** + * The state of an outgoing {@code Call} when dialing the remote number, but not yet connected. + */ + public static final int STATE_DIALING = 1; + + /** + * The state of an incoming {@code Call} when ringing locally, but not yet connected. + */ + public static final int STATE_RINGING = 2; + + /** + * The state of a {@code Call} when in a holding state. + */ + public static final int STATE_HOLDING = 3; + + /** + * The state of a {@code Call} when actively supporting conversation. + */ + public static final int STATE_ACTIVE = 4; + + /** + * The state of a {@code Call} when no further voice or other communication is being + * transmitted, the remote side has been or will inevitably be informed that the {@code Call} + * is no longer active, and the local data transport has or inevitably will release resources + * associated with this {@code Call}. + */ + public static final int STATE_DISCONNECTED = 7; + + public static class Details { + private final Uri mHandle; + private final int mHandlePresentation; + private final String mCallerDisplayName; + private final int mCallerDisplayNamePresentation; + private final PhoneAccount mAccount; + private final int mCapabilities; + private final int mDisconnectCauseCode; + private final String mDisconnectCauseMsg; + private final long mConnectTimeMillis; + private final GatewayInfo mGatewayInfo; + + /** + * @return The handle (e.g., phone number) to which the {@code Call} is currently + * connected. + */ + public Uri getHandle() { + return mHandle; + } + + /** + * @return The presentation requirements for the handle. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ + public int getHandlePresentation() { + return mHandlePresentation; + } + + /** + * @return The display name for the caller. + */ + public String getCallerDisplayName() { + return mCallerDisplayName; + } + + /** + * @return The presentation requirements for the caller display name. See + * {@link android.telecomm.CallPropertyPresentation} for valid values. + */ + public int getCallerDisplayNamePresentation() { + return mCallerDisplayNamePresentation; + } + + /** + * @return The {@code PhoneAccount} whereby the {@code Call} is currently being routed. + */ + public PhoneAccount getAccount() { + return mAccount; + } + + /** + * @return A bitmask of the capabilities of the {@code Call}, as defined in + * {@link CallCapabilities}. + */ + public int getCapabilities() { + return mCapabilities; + } + + /** + * @return For a {@link #STATE_DISCONNECTED} {@code Call}, the disconnect cause expressed + * as a code chosen from among those declared in {@link DisconnectCause}. + */ + public int getDisconnectCauseCode() { + return mDisconnectCauseCode; + } + + /** + * @return For a {@link #STATE_DISCONNECTED} {@code Call}, an optional reason for + * disconnection expressed as a free text message. + */ + public String getDisconnectCauseMsg() { + return mDisconnectCauseMsg; + } + + /** + * @return The time the {@code Call} has been connected. This information is updated + * periodically, but user interfaces should not rely on this to display any "call time + * clock". + */ + public long getConnectTimeMillis() { + return mConnectTimeMillis; + } + + /** + * @return Information about any calling gateway the {@code Call} may be using. + */ + public GatewayInfo getGatewayInfo() { + return mGatewayInfo; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Details) { + Details d = (Details) o; + return + Objects.equals(mHandle, d.mHandle) && + Objects.equals(mHandlePresentation, d.mHandlePresentation) && + Objects.equals(mCallerDisplayName, d.mCallerDisplayName) && + Objects.equals(mCallerDisplayNamePresentation, + d.mCallerDisplayNamePresentation) && + Objects.equals(mAccount, d.mAccount) && + Objects.equals(mCapabilities, d.mCapabilities) && + Objects.equals(mDisconnectCauseCode, d.mDisconnectCauseCode) && + Objects.equals(mDisconnectCauseMsg, d.mDisconnectCauseMsg) && + Objects.equals(mConnectTimeMillis, d.mConnectTimeMillis) && + Objects.equals(mGatewayInfo, d.mGatewayInfo); + } + return false; + } + + @Override + public int hashCode() { + return + Objects.hashCode(mHandle) + + Objects.hashCode(mHandlePresentation) + + Objects.hashCode(mCallerDisplayName) + + Objects.hashCode(mCallerDisplayNamePresentation) + + Objects.hashCode(mAccount) + + Objects.hashCode(mCapabilities) + + Objects.hashCode(mDisconnectCauseCode) + + Objects.hashCode(mDisconnectCauseMsg) + + Objects.hashCode(mConnectTimeMillis) + + Objects.hashCode(mGatewayInfo); + } + + /** {@hide} */ + public Details( + Uri handle, + int handlePresentation, + String callerDisplayName, + int callerDisplayNamePresentation, + PhoneAccount account, + int capabilities, + int disconnectCauseCode, + String disconnectCauseMsg, + long connectTimeMillis, + GatewayInfo gatewayInfo) { + mHandle = handle; + mHandlePresentation = handlePresentation; + mCallerDisplayName = callerDisplayName; + mCallerDisplayNamePresentation = callerDisplayNamePresentation; + mAccount = account; + mCapabilities = capabilities; + mDisconnectCauseCode = disconnectCauseCode; + mDisconnectCauseMsg = disconnectCauseMsg; + mConnectTimeMillis = connectTimeMillis; + mGatewayInfo = gatewayInfo; + } + } + + public static abstract class Listener { + /** + * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. + * + * TODO(ihab): Provide previous state also? + * + * @param call The {@code Call} invoking this method. + * @param state The new state of the {@code Call}. + */ + public void onStateChanged(Call call, int state) {} + + /** + * Invoked when the parent of this {@code Call} has changed. See {@link #getParent()}. + * + * @param call The {@code Call} invoking this method. + * @param parent The new parent of the {@code Call}. + */ + public void onParentChanged(Call call, Call parent) {} + + /** + * Invoked when the children of this {@code Call} have changed. See {@link #getChildren()}. + * + * @param call The {@code Call} invoking this method. + * @param children The new children of the {@code Call}. + */ + public void onChildrenChanged(Call call, List<Call> children) {} + + /** + * Invoked when the details of this {@code Call} have changed. See {@link #getDetails()}. + * + * @param call The {@code Call} invoking this method. + * @param details A {@code Details} object describing the {@code Call}. + */ + public void onDetailsChanged(Call call, Details details) {} + + /** + * Invoked when the text messages that can be used as responses to the incoming + * {@code Call} are loaded from the relevant database. + * See {@link #getCannedTextResponses()}. + * + * @param call The {@code Call} invoking this method. + * @param cannedTextResponses The text messages useable as responses. + */ + public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) {} + + /** + * Invoked when the outgoing {@code Call} has finished dialing but is sending DTMF signals + * that were embedded into the outgoing number. + * + * @param call The {@code Call} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDial(Call call, String remainingPostDialSequence) {} + + /** + * Invoked when the post-dial sequence in the outgoing {@code Call} has reached a pause + * character. This causes the post-dial signals to stop pending user confirmation. An + * implementation should present this choice to the user and invoke + * {@link #postDialContinue(boolean)} when the user makes the choice. + * + * @param call The {@code Call} invoking this method. + * @param remainingPostDialSequence The post-dial characters that remain to be sent. + */ + public void onPostDialWait(Call call, String remainingPostDialSequence) {} + + /** + * Invoked when the {@code RemoteCallVideoProvider} of the {@code Call} has changed. + * + * @param call The {@code Call} invoking this method. + * @param callVideoProvider The {@code RemoteCallVideoProvider} associated with the + * {@code Call}. + */ + + public void onCallVideoProviderChanged(Call call, + RemoteCallVideoProvider callVideoProvider) {} + + /** + * Invoked when the {@code Call} is destroyed. Clients should refrain from cleaning + * up their UI for the {@code Call} in response to state transitions. Specifically, + * clients should not assume that a {@link #onStateChanged(Call, int)} with a state of + * {@link #STATE_DISCONNECTED} is the final notification the {@code Call} will send. Rather, + * clients should wait for this method to be invoked. + * + * @param call The {@code Call} being destroyed. + */ + public void onCallDestroyed(Call call) {} + } + + private final Phone mPhone; + private final String mTelecommCallId; + private final InCallAdapter mInCallAdapter; + private Call mParent = null; + private int mState; + private final List<Call> mChildren = new ArrayList<>(); + private final List<Call> mUnmodifiableChildren = Collections.unmodifiableList(mChildren); + private List<String> mCannedTextResponses = null; + private String mRemainingPostDialSequence; + private RemoteCallVideoProvider mCallVideoProvider; + private Details mDetails; + private final List<Listener> mListeners = new ArrayList<>(); + + /** + * Obtains the post-dial sequence remaining to be emitted by this {@code Call}, if any. + * + * @return The remaining post-dial sequence, or {@code null} if there is no post-dial sequence + * remaining or this {@code Call} is not in a post-dial state. + */ + public String getRemainingPostDialSequence() { + return mRemainingPostDialSequence; + } + + /** + * Instructs this {@link #STATE_RINGING} {@code Call} to answer. + */ + public void answer() { + mInCallAdapter.answerCall(mTelecommCallId); + } + + /** + * Instructs this {@link #STATE_RINGING} {@code Call} to reject. + * + * @param rejectWithMessage Whether to reject with a text message. + * @param textMessage An optional text message with which to respond. + */ + public void reject(boolean rejectWithMessage, String textMessage) { + mInCallAdapter.rejectCall(mTelecommCallId, rejectWithMessage, textMessage); + } + + /** + * Instructs this {@code Call} to disconnect. + */ + public void disconnect() { + mInCallAdapter.disconnectCall(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to go on hold. + */ + public void hold() { + mInCallAdapter.holdCall(mTelecommCallId); + } + + /** + * Instructs this {@link #STATE_HOLDING} call to release from hold. + */ + public void unhold() { + mInCallAdapter.unholdCall(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to play a dual-tone multi-frequency signaling (DTMF) tone. + * + * Any other currently playing DTMF tone in the specified call is immediately stopped. + * + * @param digit A character representing the DTMF digit for which to play the tone. This + * value must be one of {@code '0'} through {@code '9'}, {@code '*'} or {@code '#'}. + */ + public void playDtmfTone(char digit) { + mInCallAdapter.playDtmfTone(mTelecommCallId, digit); + } + + /** + * Instructs this {@code Call} to stop any dual-tone multi-frequency signaling (DTMF) tone + * currently playing. + * + * DTMF tones are played by calling {@link #playDtmfTone(char)}. If no DTMF tone is + * currently playing, this method will do nothing. + */ + public void stopDtmfTone() { + mInCallAdapter.stopDtmfTone(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to continue playing a post-dial DTMF string. + * + * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, + * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. + * While these tones are playing, this {@code Call} will notify listeners via + * {@link Listener#onPostDial(Call, String)}. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, this + * {@code Call} will temporarily pause playing the tones for a pre-defined period of time. + * + * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, this + * {@code Call} will pause playing the tones and notify listeners via + * {@link Listener#onPostDialWait(Call, String)}. At this point, the in-call app + * should display to the user an indication of this state and an affordance to continue + * the postdial sequence. When the user decides to continue the postdial sequence, the in-call + * app should invoke the {@link #postDialContinue(boolean)} method. + * + * @param proceed Whether or not to continue with the post-dial sequence. + */ + public void postDialContinue(boolean proceed) { + mInCallAdapter.postDialContinue(mTelecommCallId, proceed); + } + + /** + * Notifies this {@code Call} that the phone account user interface element was touched. + * + * TODO(ihab): Figure out if and how we can generalize this + */ + public void phoneAccountClicked() { + mInCallAdapter.phoneAccountClicked(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to enter a conference. + */ + public void conference() { + mInCallAdapter.conference(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to split from any conference call with which it may be + * connected. + */ + public void splitFromConference() { + mInCallAdapter.splitFromConference(mTelecommCallId); + } + + /** + * Instructs this {@code Call} to swap itself with an existing background call, if one + * such call exists. + */ + public void swapWithBackgroundCall() { + mInCallAdapter.swapWithBackgroundCall(mTelecommCallId); + } + + /** + * Obtains the parent of this {@code Call} in a conference, if any. + * + * @return The parent {@code Call}, or {@code null} if this {@code Call} is not a + * child of any conference {@code Call}s. + */ + public Call getParent() { + return mParent; + } + + /** + * Obtains the children of this conference {@code Call}, if any. + * + * @return The children of this {@code Call} if this {@code Call} is a conference, or an empty + * {@code List} otherwise. + */ + public List<Call> getChildren() { + return mUnmodifiableChildren; + } + + /** + * Obtains the state of this {@code Call}. + * + * @return A state value, chosen from the {@code STATE_*} constants. + */ + public int getState() { + return mState; + } + + /** + * Obtains a list of canned, pre-configured message responses to present to the user as + * ways of rejecting this {@code Call} using via a text message. + * + * @see #reject(boolean, String) + * + * @return A list of canned text message responses. + */ + public List<String> getCannedTextResponses() { + return mCannedTextResponses; + } + + /** + * Obtains an object that can be used to display video from this {@code Call}. + * + * @return An {@code ICallVideoProvider}. + */ + public RemoteCallVideoProvider getCallVideoProvider() { + return mCallVideoProvider; + } + + /** + * Obtains an object containing call details. + * + * @return A {@link Details} object. Depending on the state of the {@code Call}, the + * result may be {@code null}. + */ + public Details getDetails() { + return mDetails; + } + + /** + * Adds a listener to this {@code Call}. + * + * @param listener A {@code Listener}. + */ + public void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener from this {@code Call}. + * + * @param listener A {@code Listener}. + */ + public void removeListener(Listener listener) { + mListeners.remove(listener); + } + + /** {@hide} */ + Call(Phone phone, String telecommCallId, InCallAdapter inCallAdapter) { + mPhone = phone; + mTelecommCallId = telecommCallId; + mInCallAdapter = inCallAdapter; + mState = STATE_NEW; + } + + /** {@hide} */ + final String internalGetCallId() { + return mTelecommCallId; + } + + /** {@hide} */ + final void internalUpdate(InCallCall inCallCall) { + // First, we update the internal state as far as possible before firing any updates. + + Details details = new Details( + inCallCall.getHandle(), + inCallCall.getHandlePresentation(), + inCallCall.getCallerDisplayName(), + inCallCall.getCallerDisplayNamePresentation(), + inCallCall.getAccount(), + inCallCall.getCapabilities(), + inCallCall.getDisconnectCauseCode(), + inCallCall.getDisconnectCauseMsg(), + inCallCall.getConnectTimeMillis(), + inCallCall.getGatewayInfo()); + boolean detailsChanged = !Objects.equals(mDetails, details); + if (detailsChanged) { + mDetails = details; + } + + boolean cannedTextResponsesChanged = false; + if (mCannedTextResponses == null && inCallCall.getCannedSmsResponses() != null + && !inCallCall.getCannedSmsResponses().isEmpty()) { + mCannedTextResponses = Collections.unmodifiableList(inCallCall.getCannedSmsResponses()); + } + + boolean callVideoProviderChanged = false; + try { + callVideoProviderChanged = + !Objects.equals(mCallVideoProvider, inCallCall.getCallVideoProvider()); + if (callVideoProviderChanged) { + mCallVideoProvider = inCallCall.getCallVideoProvider(); + } + } catch (RemoteException e) { + } + + int state = stateFromInCallCallState(inCallCall.getState()); + boolean stateChanged = mState != state; + if (stateChanged) { + mState = state; + } + + if (inCallCall.getParentCallId() != null) { + mParent = mPhone.internalGetCallByTelecommId(inCallCall.getParentCallId()); + } + + mChildren.clear(); + if (inCallCall.getChildCallIds() != null) { + for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) { + mChildren.add(mPhone.internalGetCallByTelecommId( + inCallCall.getChildCallIds().get(i))); + } + } + + // Now we fire updates, ensuring that any client who listens to any of these notifications + // gets the most up-to-date state. + + if (stateChanged) { + fireStateChanged(mState); + } + if (detailsChanged) { + fireDetailsChanged(mDetails); + } + if (cannedTextResponsesChanged) { + fireCannedTextResponsesLoaded(mCannedTextResponses); + } + if (callVideoProviderChanged) { + fireCallVideoProviderChanged(mCallVideoProvider); + } + + // If we have transitioned to DISCONNECTED, that means we need to notify clients and + // remove ourselves from the Phone. Note that we do this after completing all state updates + // so a client can cleanly transition all their UI to the state appropriate for a + // DISCONNECTED Call while still relying on the existence of that Call in the Phone's list. + if (mState == STATE_DISCONNECTED) { + fireCallDestroyed(); + mPhone.internalRemoveCall(this); + } + } + + /** {@hide} */ + final void internalSetPostDial(String remaining) { + mRemainingPostDialSequence = remaining; + firePostDial(mRemainingPostDialSequence); + } + + /** {@hide} */ + final void internalSetPostDialWait(String remaining) { + mRemainingPostDialSequence = remaining; + firePostDialWait(mRemainingPostDialSequence); + } + + private void fireStateChanged(int newState) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onStateChanged(this, newState); + } + } + + private void fireParentChanged(Call newParent) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onParentChanged(this, newParent); + } + } + + private void fireChildrenChanged(List<Call> children) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onChildrenChanged(this, children); + } + } + + private void fireDetailsChanged(Details details) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onDetailsChanged(this, details); + } + } + + private void fireCannedTextResponsesLoaded(List<String> cannedTextResponses) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCannedTextResponsesLoaded(this, cannedTextResponses); + } + } + + private void fireCallVideoProviderChanged(RemoteCallVideoProvider callVideoProvider) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallVideoProviderChanged(this, callVideoProvider); + } + } + + private void firePostDial(String remainingPostDialSequence) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onPostDial(this, remainingPostDialSequence); + } + } + + private void firePostDialWait(String remainingPostDialSequence) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onPostDialWait(this, remainingPostDialSequence); + } + } + + private void fireCallDestroyed() { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallDestroyed(this); + } + } + + private int stateFromInCallCallState(CallState inCallCallState) { + switch (inCallCallState) { + case NEW: + return STATE_NEW; + case DIALING: + return STATE_DIALING; + case RINGING: + return STATE_RINGING; + case ACTIVE: + return STATE_ACTIVE; + case ON_HOLD: + return STATE_HOLDING; + case DISCONNECTED: + return STATE_DISCONNECTED; + case ABORTED: + return STATE_DISCONNECTED; + default: + Log.wtf(this, "Unrecognized CallState %s", inCallCallState); + return STATE_NEW; + } + } +} diff --git a/telecomm/java/android/telecomm/CallState.java b/telecomm/java/android/telecomm/CallState.java index 152c2023e7fd..a464da563105 100644 --- a/telecomm/java/android/telecomm/CallState.java +++ b/telecomm/java/android/telecomm/CallState.java @@ -48,22 +48,6 @@ public enum CallState { RINGING, /** - * Indicates that the call is active but in a "post-dial" state where Telecomm is now sending - * some dual-tone multi-frequency signaling (DTMF) tones appended to the dialed number. Normal - * transitions are to {@link #POST_DIAL_WAIT} when the post-dial string requires user - * confirmation to proceed, {@link #ACTIVE} when the post-dial tones are completed, or - * {@link #DISCONNECTED}. - */ - POST_DIAL, - - /** - * Indicates that the call was in the {@link #POST_DIAL} state but is now waiting for user - * confirmation before the remaining digits can be sent. Normal transitions are to - * {@link #POST_DIAL} when the user asks Telecomm to proceed with the post-dial sequence. - */ - POST_DIAL_WAIT, - - /** * Indicates that a call is currently connected to another party and a communication channel is * open between them. The normal transition to this state is by the user answering a * {@link #DIALING} call or a {@link #RINGING} call being answered by the other party. diff --git a/telecomm/java/android/telecomm/CallVideoClient.java b/telecomm/java/android/telecomm/CallVideoClient.java index 76b28fad2d07..fb970dc14a35 100644 --- a/telecomm/java/android/telecomm/CallVideoClient.java +++ b/telecomm/java/android/telecomm/CallVideoClient.java @@ -241,6 +241,7 @@ public abstract class CallVideoClient { * * @param callCameraCapabilities The changed camera capabilities. */ - public abstract void onHandleCameraCapabilitiesChange(CallCameraCapabilities callCameraCapabilities); + public abstract void onHandleCameraCapabilitiesChange( + CallCameraCapabilities callCameraCapabilities); } diff --git a/telecomm/java/android/telecomm/InCallAdapter.java b/telecomm/java/android/telecomm/InCallAdapter.java index d8293a5cdbe4..66cf1df98b1c 100644 --- a/telecomm/java/android/telecomm/InCallAdapter.java +++ b/telecomm/java/android/telecomm/InCallAdapter.java @@ -24,7 +24,7 @@ import com.android.internal.telecomm.IInCallAdapter; * Receives commands from {@link InCallService} implementations which should be executed by * Telecomm. When Telecomm binds to a {@link InCallService}, an instance of this class is given to * the in-call service through which it can manipulate live (active, dialing, ringing) calls. When - * the in-call service is notified of new calls ({@link InCallService#addCall}), it can use the + * the in-call service is notified of new calls, it can use the * given call IDs to execute commands such as {@link #answerCall} for incoming calls or * {@link #disconnectCall} for active calls the user would like to end. Some commands are only * appropriate for calls in certain states; please consult each method for such limitations. @@ -167,16 +167,15 @@ public final class InCallAdapter { * A post-dial DTMF string is a string of digits entered after a phone number, when dialed, * that are immediately sent as DTMF tones to the recipient as soon as the connection is made. * While these tones are playing, Telecomm will notify the {@link InCallService} that the call - * is in the {@link InCallService#setPostDial(String,String)} state. + * is in the post dial state. * * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_PAUSE} symbol, Telecomm * will temporarily pause playing the tones for a pre-defined period of time. * * If the DTMF string contains a {@link TelecommConstants#DTMF_CHARACTER_WAIT} symbol, Telecomm * will pause playing the tones and notify the {@link InCallService} that the call is in the - * {@link InCallService#setPostDialWait(String,String)} state. When the user decides to continue - * the postdial sequence, the {@link InCallService} should invoke the - * {@link #postDialContinue(String,boolean)} method. + * post dial wait state. When the user decides to continue the postdial sequence, the + * {@link InCallService} should invoke the {@link #postDialContinue(String,boolean)} method. * * @param callId The unique ID of the call for which postdial string playing should continue. * @param proceed Whether or not to continue with the post-dial sequence. diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java index 31291fba7ec7..028b6e49e34e 100644 --- a/telecomm/java/android/telecomm/InCallService.java +++ b/telecomm/java/android/telecomm/InCallService.java @@ -16,8 +16,6 @@ package android.telecomm; -import android.app.Service; -import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.Looper; @@ -31,11 +29,10 @@ import com.android.internal.telecomm.IInCallService; * This service is implemented by any app that wishes to provide the user-interface for managing * phone calls. Telecomm binds to this service while there exists a live (active or incoming) * call, and uses it to notify the in-call app of any live and and recently disconnected calls. - * TODO(santoscordon): Needs more/better description of lifecycle once the interface is better - * defined. + * * TODO(santoscordon): What happens if two or more apps on a given device implement this interface? */ -public abstract class InCallService extends Service { +public abstract class InCallService { private static final int MSG_SET_IN_CALL_ADAPTER = 1; private static final int MSG_ADD_CALL = 2; private static final int MSG_UPDATE_CALL = 3; @@ -50,21 +47,21 @@ public abstract class InCallService extends Service { public void handleMessage(Message msg) { switch (msg.what) { case MSG_SET_IN_CALL_ADAPTER: - mAdapter = new InCallAdapter((IInCallAdapter) msg.obj); - onAdapterAttached(mAdapter); + mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj)); + onPhoneCreated(mPhone); break; case MSG_ADD_CALL: - addCall((InCallCall) msg.obj); + mPhone.internalAddCall((InCallCall) msg.obj); break; case MSG_UPDATE_CALL: - updateCall((InCallCall) msg.obj); + mPhone.internalUpdateCall((InCallCall) msg.obj); break; - case MSG_SET_POST_DIAL: { + case MSG_SET_POST_DIAL: { SomeArgs args = (SomeArgs) msg.obj; try { String callId = (String) args.arg1; String remaining = (String) args.arg2; - setPostDial(callId, remaining); + mPhone.internalSetPostDial(callId, remaining); } finally { args.recycle(); } @@ -75,17 +72,17 @@ public abstract class InCallService extends Service { try { String callId = (String) args.arg1; String remaining = (String) args.arg2; - setPostDialWait(callId, remaining); + mPhone.internalSetPostDialWait(callId, remaining); } finally { args.recycle(); } break; } case MSG_ON_AUDIO_STATE_CHANGED: - onAudioStateChanged((CallAudioState) msg.obj); + mPhone.internalAudioStateChanged((CallAudioState) msg.obj); break; case MSG_BRING_TO_FOREGROUND: - bringToForeground(msg.arg1 == 1); + mPhone.internalBringToForeground(msg.arg1 == 1); break; default: break; @@ -142,85 +139,41 @@ public abstract class InCallService extends Service { } } - private final InCallServiceBinder mBinder; - - private InCallAdapter mAdapter; - - protected InCallService() { - mBinder = new InCallServiceBinder(); - } + private Phone mPhone; - @Override - public final IBinder onBind(Intent intent) { - return mBinder; - } + protected InCallService() {} - /** - * @return The attached {@link InCallAdapter} if attached, or null otherwise. - */ - protected final InCallAdapter getAdapter() { - return mAdapter; + public final IBinder getBinder() { + return new InCallServiceBinder(); } /** - * Lifecycle callback which is called when this {@link InCallService} has been attached - * to a {@link InCallAdapter}, indicating {@link #getAdapter()} is now safe to use. + * Obtain the {@code Phone} associated with this {@code InCallService}. * - * @param adapter The adapter now attached to this in-call service. + * @return The {@code Phone} object associated with this {@code InCallService}, or {@code null} + * if the {@code InCallService} is not in a state where it has an associated {@code Phone}. */ - protected void onAdapterAttached(InCallAdapter adapter) { + public Phone getPhone() { + return mPhone; } /** - * Indicates to the in-call app that a new call has been created and an appropriate - * user-interface should be built and shown to notify the user. - * - * @param call Information about the new call. - */ - protected abstract void addCall(InCallCall call); - - /** - * Call when information about a call has changed. - * - * @param call Information about the new call. - */ - protected abstract void updateCall(InCallCall call); - - /** - * Indicates to the in-call app that the specified call is active but in a "post-dial" state - * where Telecomm is now sending some dual-tone multi-frequency signaling (DTMF) tones appended - * to the dialed number. Normal transitions are to {@link #setPostDialWait(String,String)} when - * the post-dial string requires user confirmation to proceed, and {@link CallState#ACTIVE} when - * the post-dial tones are completed. - * - * @param callId The identifier of the call changing state. - * @param remaining The remaining postdial string to be dialed. - */ - protected abstract void setPostDial(String callId, String remaining); - - /** - * Indicates to the in-call app that the specified call was in the - * {@link #setPostDial(String,String)} state but is now waiting for user confirmation before the - * remaining digits can be sent. Normal transitions are to {@link #setPostDial(String,String)} - * when the user asks Telecomm to proceed with the post-dial sequence and the in-call app - * informs Telecomm of this by invoking {@link InCallAdapter#postDialContinue(String,boolean)}. - * - * @param callId The identifier of the call changing state. - * @param remaining The remaining postdial string to be dialed. - */ - protected abstract void setPostDialWait(String callId, String remaining); - - /** - * Called when the audio state changes. + * Invoked when the {@code Phone} has been created. This is a signal to the in-call experience + * to start displaying in-call information to the user. Each instance of {@code InCallService} + * will have only one {@code Phone}, and this method will be called exactly once in the + * lifetime of the {@code InCallService}. * - * @param audioState The new {@link CallAudioState}. + * @param phone The {@code Phone} object associated with this {@code InCallService}. */ - protected abstract void onAudioStateChanged(CallAudioState audioState); + public void onPhoneCreated(Phone phone) { } /** - * Brings the in-call screen to the foreground. + * Invoked when a {@code Phone} has been destroyed. This is a signal to the in-call experience + * to stop displaying in-call information to the user. This method will be called exactly once + * in the lifetime of the {@code InCallService}, and it will always be called after a previous + * call to {@link #onPhoneCreated(Phone)}. * - * @param showDialpad If true, put up the dialpad when the screen is shown. + * @param phone The {@code Phone} object associated with this {@code InCallService}. */ - protected abstract void bringToForeground(boolean showDialpad); + public void onPhoneDestroyed(Phone phone) { } } diff --git a/telecomm/java/android/telecomm/Phone.java b/telecomm/java/android/telecomm/Phone.java new file mode 100644 index 000000000000..9c97b45891b4 --- /dev/null +++ b/telecomm/java/android/telecomm/Phone.java @@ -0,0 +1,255 @@ +/* + * 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 android.telecomm; + +import android.util.ArrayMap; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A unified virtual device providing a means of voice (and other) communication on a device. + */ +public final class Phone { + + public abstract static class Listener { + /** + * Called when the audio state changes. + * + * @param phone The {@code Phone} calling this method. + * @param audioState The new {@link CallAudioState}. + */ + public void onAudioStateChanged(Phone phone, CallAudioState audioState) { } + + /** + * Called to bring the in-call screen to the foreground. The in-call experience should + * respond immediately by coming to the foreground to inform the user of the state of + * ongoing {@code Call}s. + * + * @param phone The {@code Phone} calling this method. + * @param showDialpad If true, put up the dialpad when the screen is shown. + */ + public void onBringToForeground(Phone phone, boolean showDialpad) { } + + /** + * Called when a {@code Call} has been added to this in-call session. The in-call user + * experience should add necessary state listeners to the specified {@code Call} and + * immediately start to show the user information about the existence + * and nature of this {@code Call}. Subsequent invocations of {@link #getCalls()} will + * include this {@code Call}. + * + * @param phone The {@code Phone} calling this method. + * @param call A newly added {@code Call}. + */ + public void onCallAdded(Phone phone, Call call) { } + + /** + * Called when a {@code Call} has been removed from this in-call session. The in-call user + * experience should remove any state listeners from the specified {@code Call} and + * immediately stop displaying any information about this {@code Call}. + * Subsequent invocations of {@link #getCalls()} will no longer include this {@code Call}. + * + * @param phone The {@code Phone} calling this method. + * @param call A newly removed {@code Call}. + */ + public void onCallRemoved(Phone phone, Call call) { } + } + + // A Map allows us to track each Call by its Telecomm-specified call ID + private final Map<String, Call> mCallByTelecommCallId = new ArrayMap<>(); + + // A List allows us to keep the Calls in a stable iteration order so that casually developed + // user interface components do not incur any spurious jank + private final List<Call> mCalls = new ArrayList<>(); + + // An unmodifiable view of the above List can be safely shared with subclass implementations + private final List<Call> mUnmodifiableCalls = Collections.unmodifiableList(mCalls); + + private final InCallAdapter mInCallAdapter; + + private CallAudioState mAudioState; + + private final List<Listener> mListeners = new ArrayList<>(); + + /** {@hide} */ + Phone(InCallAdapter adapter) { + mInCallAdapter = adapter; + } + + /** {@hide} */ + final void internalAddCall(InCallCall inCallCall) { + Call call = new Call(this, inCallCall.getId(), mInCallAdapter); + mCallByTelecommCallId.put(inCallCall.getId(), call); + mCalls.add(call); + checkCallTree(inCallCall); + call.internalUpdate(inCallCall); + fireCallAdded(call); + } + + /** {@hide} */ + final void internalRemoveCall(Call call) { + mCallByTelecommCallId.remove(call.internalGetCallId()); + mCalls.remove(call); + fireCallRemoved(call); + } + + /** {@hide} */ + final void internalUpdateCall(InCallCall inCallCall) { + Call call = mCallByTelecommCallId.get(inCallCall.getId()); + if (call != null) { + checkCallTree(inCallCall); + call.internalUpdate(inCallCall); + } + } + + /** {@hide} */ + final void internalSetPostDial(String callId, String remaining) { + Call call = mCallByTelecommCallId.get(callId); + if (call != null) { + call.internalSetPostDial(remaining); + } + } + + /** {@hide} */ + final void internalSetPostDialWait(String callId, String remaining) { + Call call = mCallByTelecommCallId.get(callId); + if (call != null) { + call.internalSetPostDialWait(remaining); + } + } + + /** {@hide} */ + final void internalAudioStateChanged(CallAudioState callAudioState) { + if (!Objects.equals(mAudioState, callAudioState)) { + mAudioState = callAudioState; + fireAudioStateChanged(callAudioState); + } + } + + /** {@hide} */ + final Call internalGetCallByTelecommId(String telecommId) { + return mCallByTelecommCallId.get(telecommId); + } + + /** {@hide} */ + final void internalBringToForeground(boolean showDialpad) { + fireBringToForeground(showDialpad); + } + + /** + * Adds a listener to this {@code Phone}. + * + * @param listener A {@code Listener} object. + */ + public final void addListener(Listener listener) { + mListeners.add(listener); + } + + /** + * Removes a listener from this {@code Phone}. + * + * @param listener A {@code Listener} object. + */ + public final void removeListener(Listener listener) { + mListeners.remove(listener); + } + + /** + * Obtains the current list of {@code Call}s to be displayed by this in-call experience. + * + * @return A list of the relevant {@code Call}s. + */ + public final List<Call> getCalls() { + return mUnmodifiableCalls; + } + + /** + * Sets the microphone mute state. When this request is honored, there will be change to + * the {@link #getAudioState()}. + * + * @param state {@code true} if the microphone should be muted; {@code false} otherwise. + */ + public final void setMuted(boolean state) { + mInCallAdapter.mute(state); + } + + /** + * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will + * be change to the {@link #getAudioState()}. + * + * @param route The audio route to use. + */ + public final void setAudioRoute(int route) { + mInCallAdapter.setAudioRoute(route); + } + + /** + * Obtains the current phone call audio state of the {@code Phone}. + * + * @return An object encapsulating the audio state. + */ + public final CallAudioState getAudioState() { + return mAudioState; + } + + private void fireCallAdded(Call call) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallAdded(this, call); + } + } + + private void fireCallRemoved(Call call) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onCallRemoved(this, call); + } + } + + private void fireAudioStateChanged(CallAudioState audioState) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onAudioStateChanged(this, audioState); + } + } + + private void fireBringToForeground(boolean showDialpad) { + Listener[] listeners = mListeners.toArray(new Listener[mListeners.size()]); + for (int i = 0; i < listeners.length; i++) { + listeners[i].onBringToForeground(this, showDialpad); + } + } + + private void checkCallTree(InCallCall inCallCall) { + if (inCallCall.getParentCallId() != null && + !mCallByTelecommCallId.containsKey(inCallCall.getParentCallId())) { + Log.wtf(this, "InCallCall %s has nonexistent parent %s", + inCallCall.getId(), inCallCall.getParentCallId()); + } + if (inCallCall.getChildCallIds() != null) { + for (int i = 0; i < inCallCall.getChildCallIds().size(); i++) { + if (!mCallByTelecommCallId.containsKey(inCallCall.getChildCallIds().get(i))) { + Log.wtf(this, "InCallCall %s has nonexistent child %s", + inCallCall.getId(), inCallCall.getChildCallIds().get(i)); + } + } + } + } +} diff --git a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java index 856d321dc408..a49076a2fd0c 100644 --- a/telecomm/java/android/telecomm/RemoteCallVideoProvider.java +++ b/telecomm/java/android/telecomm/RemoteCallVideoProvider.java @@ -20,63 +20,92 @@ import android.os.IBinder; import android.os.RemoteException; import android.view.Surface; -import com.android.internal.telecomm.ICallVideoClient; import com.android.internal.telecomm.ICallVideoProvider; -public class RemoteCallVideoProvider implements IBinder.DeathRecipient { +public class RemoteCallVideoProvider { private final ICallVideoProvider mCallVideoProvider; + private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { + @Override + public void binderDied() { + mCallVideoProvider.asBinder().unlinkToDeath(this, 0); + } + }; + + /** {@hide} */ RemoteCallVideoProvider(ICallVideoProvider callVideoProvider) throws RemoteException { mCallVideoProvider = callVideoProvider; - mCallVideoProvider.asBinder().linkToDeath(this, 0); - } - - @Override - public void binderDied() { - mCallVideoProvider.asBinder().unlinkToDeath(this, 0); + mCallVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0); } - public void setCallVideoClient(CallVideoClient callVideoClient) throws RemoteException { - mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder()); + public void setCallVideoClient(CallVideoClient callVideoClient) { + try { + mCallVideoProvider.setCallVideoClient(callVideoClient.getBinder()); + } catch (RemoteException e) { + } } public void setCamera(String cameraId) throws RemoteException { mCallVideoProvider.setCamera(cameraId); } - public void setPreviewSurface(Surface surface) throws RemoteException { - mCallVideoProvider.setPreviewSurface(surface); + public void setPreviewSurface(Surface surface) { + try { + mCallVideoProvider.setPreviewSurface(surface); + } catch (RemoteException e) { + } } - public void setDisplaySurface(Surface surface) throws RemoteException { - mCallVideoProvider.setDisplaySurface(surface); + public void setDisplaySurface(Surface surface) { + try { + mCallVideoProvider.setDisplaySurface(surface); + } catch (RemoteException e) { + } } - public void setDeviceOrientation(int rotation) throws RemoteException { - mCallVideoProvider.setDeviceOrientation(rotation); + public void setDeviceOrientation(int rotation) { + try { + mCallVideoProvider.setDeviceOrientation(rotation); + } catch (RemoteException e) { + } } public void setZoom(float value) throws RemoteException { mCallVideoProvider.setZoom(value); } - public void sendSessionModifyRequest(VideoCallProfile requestProfile) throws RemoteException { - mCallVideoProvider.sendSessionModifyRequest(requestProfile); + public void sendSessionModifyRequest(VideoCallProfile requestProfile) { + try { + mCallVideoProvider.sendSessionModifyRequest(requestProfile); + } catch (RemoteException e) { + } } - public void sendSessionModifyResponse(VideoCallProfile responseProfile) throws RemoteException { - mCallVideoProvider.sendSessionModifyResponse(responseProfile); + public void sendSessionModifyResponse(VideoCallProfile responseProfile) { + try { + mCallVideoProvider.sendSessionModifyResponse(responseProfile); + } catch (RemoteException e) { + } } - public void requestCameraCapabilities() throws RemoteException { - mCallVideoProvider.requestCameraCapabilities(); + public void requestCameraCapabilities() { + try { + mCallVideoProvider.requestCameraCapabilities(); + } catch (RemoteException e) { + } } - public void requestCallDataUsage() throws RemoteException { - mCallVideoProvider.requestCallDataUsage(); + public void requestCallDataUsage() { + try { + mCallVideoProvider.requestCallDataUsage(); + } catch (RemoteException e) { + } } - public void setPauseImage(String uri) throws RemoteException { - mCallVideoProvider.setPauseImage(uri); + public void setPauseImage(String uri) { + try { + mCallVideoProvider.setPauseImage(uri); + } catch (RemoteException e) { + } } }
\ No newline at end of file diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java index b9fb40cb9587..b21ea6017b84 100644 --- a/telecomm/java/android/telecomm/TelecommConstants.java +++ b/telecomm/java/android/telecomm/TelecommConstants.java @@ -51,6 +51,12 @@ public final class TelecommConstants { public static final String ACTION_CONNECTION_SERVICE = ConnectionService.class.getName(); /** + * The {@link Intent} action used to configure a {@link ConnectionService}. + */ + public static final String ACTION_CONNECTION_SERVICE_CONFIGURE = + "android.intent.action.CONNECTION_SERVICE_CONFIGURE"; + + /** * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether * the speakerphone should be automatically turned on for an outgoing call. */ diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index a54936bfb1c5..2ebce9bcaf21 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -175,6 +175,11 @@ public class MockContext extends Context { } @Override + public File getNoBackupFilesDir() { + throw new UnsupportedOperationException(); + } + + @Override public File getExternalFilesDir(String type) { throw new UnsupportedOperationException(); } diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index a14714a153c8..648c418c6891 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -31,6 +31,7 @@ import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; @@ -616,6 +617,26 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + @Override + public KeySet getKeySetByAlias(String packageName, String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public KeySet getSigningKeySet(String packageName) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSignedBy(String packageName, KeySet ks) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSignedByExactly(String packageName, KeySet ks) { + throw new UnsupportedOperationException(); + } + /** * @hide */ diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java index 802f473e69d3..9cbb455bc6eb 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java @@ -18,12 +18,14 @@ package com.android.onemedia; import android.media.MediaMetadata; import android.media.session.MediaController; +import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -43,7 +45,7 @@ public class PlayerController { protected MediaController.TransportControls mTransportControls; private final Intent mServiceIntent; - private Context mContext; + private Activity mContext; private Listener mListener; private SessionCallback mControllerCb; private MediaSessionManager mManager; @@ -51,7 +53,7 @@ public class PlayerController { private boolean mResumed; - public PlayerController(Context context, Intent serviceIntent) { + public PlayerController(Activity context, Intent serviceIntent) { mContext = context; if (serviceIntent == null) { mServiceIntent = new Intent(mContext, PlayerService.class); @@ -140,6 +142,7 @@ public class PlayerController { mBinder = null; mController = null; mTransportControls = null; + mContext.setMediaController(null); Log.d(TAG, "Disconnected from PlayerService"); if (mListener != null) { @@ -151,12 +154,15 @@ public class PlayerController { public void onServiceConnected(ComponentName name, IBinder service) { mBinder = IPlayerService.Stub.asInterface(service); Log.d(TAG, "service is " + service + " binder is " + mBinder); + MediaSession.Token token; try { - mController = MediaController.fromToken(mBinder.getSessionToken()); + token = mBinder.getSessionToken(); } catch (RemoteException e) { Log.e(TAG, "Error getting session", e); return; } + mController = MediaController.fromToken(token); + mContext.setMediaController(mController); mController.addCallback(mControllerCb, mHandler); mTransportControls = mController.getTransportControls(); Log.d(TAG, "Ready to use PlayerService"); diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java index 7c0eabeb840a..78353b20fde1 100644 --- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java +++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java @@ -28,6 +28,7 @@ import android.media.session.PlaybackState; import android.os.Bundle; import android.support.media.protocols.MediaPlayerProtocol; import android.support.media.protocols.MediaPlayerProtocol.MediaStatus; +import android.os.SystemClock; import android.util.Log; import android.view.KeyEvent; @@ -59,9 +60,9 @@ public class PlayerSession { mRenderer = new LocalRenderer(context, null); mCallback = new SessionCb(); mRenderListener = new RenderListener(); - mPlaybackState = new PlaybackState(); - mPlaybackState.setActions(PlaybackState.ACTION_PAUSE - | PlaybackState.ACTION_PLAY); + PlaybackState.Builder psBob = new PlaybackState.Builder(); + psBob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY); + mPlaybackState = psBob.build(); mRenderer.registerListener(mRenderListener); } @@ -131,7 +132,10 @@ public class PlayerSession { private void updateState(int newState) { float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0; long position = mRenderer == null ? -1 : mRenderer.getSeekPosition(); - mPlaybackState.setState(newState, position, rate); + PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); + bob.setState(newState, position, rate, SystemClock.elapsedRealtime()); + bob.setErrorMessage(null); + mPlaybackState = bob.build(); mSession.setPlaybackState(mPlaybackState); } @@ -144,10 +148,12 @@ public class PlayerSession { @Override public void onError(int type, int extra, Bundle extras, Throwable error) { Log.d(TAG, "Sending onError with type " + type + " and extra " + extra); - mPlaybackState.setState(PlaybackState.STATE_ERROR, -1, 0); + PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); + bob.setState(PlaybackState.STATE_ERROR, -1, 0, 0); if (error != null) { - mPlaybackState.setErrorMessage(error.getLocalizedMessage()); + bob.setErrorMessage(error.getLocalizedMessage()); } + mPlaybackState = bob.build(); mSession.setPlaybackState(mPlaybackState); if (mListener != null) { mListener.onPlayStateChanged(mPlaybackState); @@ -156,36 +162,41 @@ public class PlayerSession { @Override public void onStateChanged(int newState) { - if (newState != Renderer.STATE_ERROR) { - mPlaybackState.setErrorMessage(null); - } long position = -1; if (mRenderer != null) { position = mRenderer.getSeekPosition(); } + int pbState; + float rate = 0; + String errorMsg = null; switch (newState) { case Renderer.STATE_ENDED: case Renderer.STATE_STOPPED: - mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0); + pbState = PlaybackState.STATE_STOPPED; break; case Renderer.STATE_INIT: case Renderer.STATE_PREPARING: - mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0); + pbState = PlaybackState.STATE_BUFFERING; break; case Renderer.STATE_ERROR: - mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0); + pbState = PlaybackState.STATE_ERROR; break; case Renderer.STATE_PAUSED: - mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0); + pbState = PlaybackState.STATE_PAUSED; break; case Renderer.STATE_PLAYING: - mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1); + pbState = PlaybackState.STATE_PLAYING; + rate = 1; break; default: - mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0); - mPlaybackState.setErrorMessage("unkown state"); + pbState = PlaybackState.STATE_ERROR; + errorMsg = "unknown state"; break; } + PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); + bob.setState(pbState, position, rate, SystemClock.elapsedRealtime()); + bob.setErrorMessage(errorMsg); + mPlaybackState = bob.build(); mSession.setPlaybackState(mPlaybackState); if (mListener != null) { mListener.onPlayStateChanged(mPlaybackState); @@ -200,7 +211,10 @@ public class PlayerSession { public void onFocusLost() { Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED); long position = mRenderer == null ? -1 : mRenderer.getSeekPosition(); - mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0); + PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); + bob.setState(PlaybackState.STATE_PAUSED, position, 0, SystemClock.elapsedRealtime()); + bob.setErrorMessage(null); + mPlaybackState = bob.build(); mSession.setPlaybackState(mPlaybackState); if (mListener != null) { mListener.onPlayStateChanged(mPlaybackState); diff --git a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java index 2e1478bc3f54..5845e48db6e5 100644 --- a/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java +++ b/tests/OneMedia/src/com/android/onemedia/provider/OneMediaRouteProvider.java @@ -29,6 +29,9 @@ import android.os.Process; import android.support.media.protocols.MediaPlayerProtocol; import android.support.media.protocols.MediaPlayerProtocol.MediaInfo; import android.support.media.protocols.MediaPlayerProtocol.MediaStatus; +import android.os.Looper; +import android.os.ResultReceiver; +import android.os.SystemClock; import android.util.Log; import com.android.onemedia.playback.LocalRenderer; @@ -60,9 +63,9 @@ public class OneMediaRouteProvider extends MediaRouteService { mHandler = new Handler(); mRenderer = new LocalRenderer(this, null); mRenderListener = new RenderListener(); - mPlaybackState = new PlaybackState(); - mPlaybackState.setActions(PlaybackState.ACTION_PAUSE - | PlaybackState.ACTION_PLAY); + PlaybackState.Builder bob = new PlaybackState.Builder(); + bob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY); + mPlaybackState = bob.build(); mRenderer.registerListener(mRenderListener); } @@ -178,36 +181,41 @@ public class OneMediaRouteProvider extends MediaRouteService { @Override public void onStateChanged(int newState) { - if (newState != Renderer.STATE_ERROR) { - mPlaybackState.setErrorMessage(null); - } long position = -1; if (mRenderer != null) { position = mRenderer.getSeekPosition(); } + int pbState; + float rate = 0; + String errorMsg = null; switch (newState) { case Renderer.STATE_ENDED: case Renderer.STATE_STOPPED: - mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0); + pbState = PlaybackState.STATE_STOPPED; break; case Renderer.STATE_INIT: case Renderer.STATE_PREPARING: - mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0); + pbState = PlaybackState.STATE_BUFFERING; break; case Renderer.STATE_ERROR: - mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0); + pbState = PlaybackState.STATE_ERROR; break; case Renderer.STATE_PAUSED: - mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0); + pbState = PlaybackState.STATE_PAUSED; break; case Renderer.STATE_PLAYING: - mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1); + pbState = PlaybackState.STATE_PLAYING; + rate = 1; break; default: - mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0); - mPlaybackState.setErrorMessage("unkown state"); + pbState = PlaybackState.STATE_ERROR; + errorMsg = "unknown state"; break; } + PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState); + bob.setState(pbState, position, rate, SystemClock.elapsedRealtime()); + bob.setErrorMessage(errorMsg); + mPlaybackState = bob.build(); sendStatusUpdate(mPlaybackState.getState()); } @@ -218,8 +226,9 @@ public class OneMediaRouteProvider extends MediaRouteService { @Override public void onFocusLost() { - Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED); - mPlaybackState.setState(PlaybackState.STATE_PAUSED, mRenderer.getSeekPosition(), 0); + Log.d(TAG, "Focus lost, pausing"); + // Don't update state here, we'll get a separate call to + // onStateChanged when it pauses mRenderer.onPause(); } |