diff options
206 files changed, 2665 insertions, 758 deletions
diff --git a/api/current.txt b/api/current.txt index ef40053..c4256af 100644 --- a/api/current.txt +++ b/api/current.txt @@ -370,6 +370,7 @@ package android { field public static final int dialogTitle = 16843250; // 0x10101f2 field public static final int digits = 16843110; // 0x1010166 field public static final int direction = 16843217; // 0x10101d1 + field public static final int directionDescriptions = 16843695; // 0x10103af field public static final int directionPriority = 16843218; // 0x10101d2 field public static final int disableDependentsState = 16843249; // 0x10101f1 field public static final int disabledAlpha = 16842803; // 0x1010033 @@ -938,6 +939,7 @@ package android { field public static final int tag = 16842961; // 0x10100d1 field public static final int targetActivity = 16843266; // 0x1010202 field public static final int targetClass = 16842799; // 0x101002f + field public static final int targetDescriptions = 16843694; // 0x10103ae field public static final int targetDrawables = 16843654; // 0x1010386 field public static final int targetPackage = 16842785; // 0x1010021 field public static final int targetSdkVersion = 16843376; // 0x1010270 @@ -3277,6 +3279,7 @@ package android.app { method public void setArguments(android.os.Bundle); method public void setHasOptionsMenu(boolean); method public void setInitialSavedState(android.app.Fragment.SavedState); + method public void setMenuVisibility(boolean); method public void setRetainInstance(boolean); method public void setTargetFragment(android.app.Fragment, int); method public void startActivity(android.content.Intent); @@ -3342,6 +3345,7 @@ package android.app { method public abstract java.lang.CharSequence getBreadCrumbTitle(); method public abstract int getBreadCrumbTitleRes(); method public abstract int getId(); + method public abstract java.lang.String getName(); } public static abstract interface FragmentManager.OnBackStackChangedListener { @@ -4040,6 +4044,7 @@ package android.app.backup { public abstract class BackupAgent extends android.content.ContextWrapper { ctor public BackupAgent(); + method public final void fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput); method public abstract void onBackup(android.os.ParcelFileDescriptor, android.app.backup.BackupDataOutput, android.os.ParcelFileDescriptor) throws java.io.IOException; method public void onCreate(); method public void onDestroy(); @@ -10676,6 +10681,7 @@ package android.media { method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener); method public void setScreenOnWhilePlaying(boolean); method public void setTexture(android.graphics.SurfaceTexture); + method public void setSurface(android.view.Surface); method public void setVolume(float, float); method public void setWakeMode(android.content.Context, int); method public void start() throws java.lang.IllegalStateException; @@ -16052,7 +16058,7 @@ package android.provider { public final class ContactsContract { ctor public ContactsContract(); - field public static final java.lang.String ALLOW_PROFILE = "allow_profile"; + method public static boolean isProfileId(long); field public static final java.lang.String AUTHORITY = "com.android.contacts"; field public static final android.net.Uri AUTHORITY_URI; field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; @@ -16579,6 +16585,16 @@ package android.provider { field public static final android.net.Uri CONTENT_RAW_CONTACTS_URI; field public static final android.net.Uri CONTENT_URI; field public static final android.net.Uri CONTENT_VCARD_URI; + field public static final long MIN_ID = 9223372034707292160L; // 0x7fffffff80000000L + } + + public static final class ContactsContract.ProfileSyncState implements android.provider.SyncStateContract.Columns { + method public static byte[] get(android.content.ContentProviderClient, android.accounts.Account) throws android.os.RemoteException; + method public static android.util.Pair<android.net.Uri, byte[]> getWithUri(android.content.ContentProviderClient, android.accounts.Account) throws android.os.RemoteException; + method public static android.content.ContentProviderOperation newSetOperation(android.accounts.Account, byte[]); + method public static void set(android.content.ContentProviderClient, android.accounts.Account, byte[]) throws android.os.RemoteException; + field public static final java.lang.String CONTENT_DIRECTORY = "syncstate"; + field public static final android.net.Uri CONTENT_URI; } public static final class ContactsContract.QuickContact { @@ -16677,6 +16693,7 @@ package android.provider { field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/status-update"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/status-update"; field public static final android.net.Uri CONTENT_URI; + field public static final android.net.Uri PROFILE_CONTENT_URI; } public static final class ContactsContract.StreamItemPhotos implements android.provider.BaseColumns android.provider.ContactsContract.StreamItemPhotosColumns { @@ -20672,6 +20689,14 @@ package android.text.style { field protected final int mVerticalAlignment; } + public class EasyEditSpan implements android.text.ParcelableSpan { + ctor public EasyEditSpan(); + ctor public EasyEditSpan(android.os.Parcel); + method public int describeContents(); + method public int getSpanTypeId(); + method public void writeToParcel(android.os.Parcel, int); + } + public class ForegroundColorSpan extends android.text.style.CharacterStyle implements android.text.ParcelableSpan android.text.style.UpdateAppearance { ctor public ForegroundColorSpan(int); ctor public ForegroundColorSpan(android.os.Parcel); @@ -22509,6 +22534,7 @@ package android.view { } public class Surface implements android.os.Parcelable { + ctor public Surface(android.graphics.SurfaceTexture); method public int describeContents(); method public boolean isValid(); method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 1271ddd..0e3eaaa 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4206,7 +4206,6 @@ public class Activity extends ContextThemeWrapper fragment.mContainerId = containerId; fragment.mTag = tag; fragment.mInLayout = true; - fragment.mImmediateActivity = this; fragment.mFragmentManager = mFragments; fragment.onInflate(this, attrs, fragment.mSavedFragmentState); mFragments.addFragment(fragment, true); @@ -4222,7 +4221,6 @@ public class Activity extends ContextThemeWrapper // This fragment was retained from a previous instance; get it // going now. fragment.mInLayout = true; - fragment.mImmediateActivity = this; // If this fragment is newly instantiated (either right now, or // from last saved state), then give it the attributes to // initialize itself. diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java index 93330a7..e9e8e16 100644 --- a/core/java/android/app/BackStackRecord.java +++ b/core/java/android/app/BackStackRecord.java @@ -344,10 +344,6 @@ final class BackStackRecord extends FragmentTransaction implements } private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { - if (fragment.mImmediateActivity != null) { - throw new IllegalStateException("Fragment already added: " + fragment); - } - fragment.mImmediateActivity = mManager.mActivity; fragment.mFragmentManager = mManager; if (tag != null) { @@ -388,11 +384,6 @@ final class BackStackRecord extends FragmentTransaction implements } public FragmentTransaction remove(Fragment fragment) { - if (fragment.mImmediateActivity == null) { - throw new IllegalStateException("Fragment not added: " + fragment); - } - fragment.mImmediateActivity = null; - Op op = new Op(); op.cmd = OP_REMOVE; op.fragment = fragment; @@ -402,10 +393,6 @@ final class BackStackRecord extends FragmentTransaction implements } public FragmentTransaction hide(Fragment fragment) { - if (fragment.mImmediateActivity == null) { - throw new IllegalStateException("Fragment not added: " + fragment); - } - Op op = new Op(); op.cmd = OP_HIDE; op.fragment = fragment; @@ -415,10 +402,6 @@ final class BackStackRecord extends FragmentTransaction implements } public FragmentTransaction show(Fragment fragment) { - if (fragment.mImmediateActivity == null) { - throw new IllegalStateException("Fragment not added: " + fragment); - } - Op op = new Op(); op.cmd = OP_SHOW; op.fragment = fragment; @@ -428,10 +411,6 @@ final class BackStackRecord extends FragmentTransaction implements } public FragmentTransaction detach(Fragment fragment) { - //if (fragment.mImmediateActivity == null) { - // throw new IllegalStateException("Fragment not added: " + fragment); - //} - Op op = new Op(); op.cmd = OP_DETACH; op.fragment = fragment; @@ -441,10 +420,6 @@ final class BackStackRecord extends FragmentTransaction implements } public FragmentTransaction attach(Fragment fragment) { - //if (fragment.mImmediateActivity == null) { - // throw new IllegalStateException("Fragment not added: " + fragment); - //} - Op op = new Op(); op.cmd = OP_ATTACH; op.fragment = fragment; @@ -663,7 +638,6 @@ final class BackStackRecord extends FragmentTransaction implements case OP_ADD: { Fragment f = op.fragment; f.mNextAnim = op.popExitAnim; - f.mImmediateActivity = null; mManager.removeFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); @@ -671,7 +645,6 @@ final class BackStackRecord extends FragmentTransaction implements case OP_REPLACE: { Fragment f = op.fragment; f.mNextAnim = op.popExitAnim; - f.mImmediateActivity = null; mManager.removeFragment(f, FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle); @@ -679,7 +652,6 @@ final class BackStackRecord extends FragmentTransaction implements for (int i=0; i<op.removed.size(); i++) { Fragment old = op.removed.get(i); old.mNextAnim = op.popEnterAnim; - f.mImmediateActivity = mManager.mActivity; mManager.addFragment(old, false); } } @@ -687,7 +659,6 @@ final class BackStackRecord extends FragmentTransaction implements case OP_REMOVE: { Fragment f = op.fragment; f.mNextAnim = op.popEnterAnim; - f.mImmediateActivity = mManager.mActivity; mManager.addFragment(f, false); } break; case OP_HIDE: { diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index a8621f8..3a08e6d 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -402,10 +402,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // from all transactions. FragmentManager mFragmentManager; - // Set as soon as a fragment is added to a transaction (or removed), - // to be able to do validation. - Activity mImmediateActivity; - // Activity this fragment is attached to. Activity mActivity; @@ -438,7 +434,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // If set this fragment has menu items to contribute. boolean mHasMenu; - + + // Set to true to allow the fragment's menu to be shown. + boolean mMenuVisible = true; + // Used to verify that subclasses call through to super class. boolean mCalled; @@ -888,7 +887,25 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene } } } - + + /** + * Set a hint for whether this fragment's menu should be visible. This + * is useful if you know that a fragment has been placed in your view + * hierarchy so that the user can not currently seen it, so any menu items + * it has should also not be shown. + * + * @param menuVisible The default is true, meaning the fragment's menu will + * be shown as usual. If false, the user will not see the menu. + */ + public void setMenuVisibility(boolean menuVisible) { + if (mMenuVisible != menuVisible) { + mMenuVisible = menuVisible; + if (mHasMenu && isAdded() && !isHidden()) { + mFragmentManager.invalidateOptionsMenu(); + } + } + } + /** * Return the LoaderManager for this fragment, creating it if needed. */ @@ -1233,7 +1250,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mRestored = false; mBackStackNesting = 0; mFragmentManager = null; - mActivity = mImmediateActivity = null; + mActivity = null; mFragmentId = 0; mContainerId = 0; mTag = null; @@ -1421,17 +1438,14 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene writer.print(" mInLayout="); writer.println(mInLayout); writer.print(prefix); writer.print("mHidden="); writer.print(mHidden); writer.print(" mDetached="); writer.print(mDetached); - writer.print(" mRetainInstance="); writer.print(mRetainInstance); - writer.print(" mRetaining="); writer.print(mRetaining); + writer.print(" mMenuVisible="); writer.print(mMenuVisible); writer.print(" mHasMenu="); writer.println(mHasMenu); + writer.print(prefix); writer.print("mRetainInstance="); writer.print(mRetainInstance); + writer.print(" mRetaining="); writer.println(mRetaining); if (mFragmentManager != null) { writer.print(prefix); writer.print("mFragmentManager="); writer.println(mFragmentManager); } - if (mImmediateActivity != null) { - writer.print(prefix); writer.print("mImmediateActivity="); - writer.println(mImmediateActivity); - } if (mActivity != null) { writer.print(prefix); writer.print("mActivity="); writer.println(mActivity); diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 70e6866..7a6759f 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -67,6 +67,13 @@ public abstract class FragmentManager { public int getId(); /** + * Get the name that was supplied to + * {@link FragmentTransaction#addToBackStack(String) + * FragmentTransaction.addToBackStack(String)} when creating this entry. + */ + public String getName(); + + /** * Return the full bread crumb title resource identifier for the entry, * or 0 if it does not have one. */ @@ -949,7 +956,6 @@ final class FragmentManagerImpl extends FragmentManager { if (!f.mRetaining) { makeInactive(f); } else { - f.mImmediateActivity = null; f.mActivity = null; f.mFragmentManager = null; } @@ -1037,7 +1043,7 @@ final class FragmentManagerImpl extends FragmentManager { mAdded.add(fragment); fragment.mAdded = true; fragment.mRemoving = false; - if (fragment.mHasMenu) { + if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } if (moveToStateNow) { @@ -1051,7 +1057,7 @@ final class FragmentManagerImpl extends FragmentManager { final boolean inactive = !fragment.isInBackStack(); if (!fragment.mDetached || inactive) { mAdded.remove(fragment); - if (fragment.mHasMenu) { + if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.mAdded = false; @@ -1086,7 +1092,7 @@ final class FragmentManagerImpl extends FragmentManager { fragment.mView.setVisibility(View.GONE); } } - if (fragment.mAdded && fragment.mHasMenu) { + if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.onHiddenChanged(true); @@ -1106,7 +1112,7 @@ final class FragmentManagerImpl extends FragmentManager { } fragment.mView.setVisibility(View.VISIBLE); } - if (fragment.mAdded && fragment.mHasMenu) { + if (fragment.mAdded && fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.onHiddenChanged(false); @@ -1120,7 +1126,7 @@ final class FragmentManagerImpl extends FragmentManager { if (fragment.mAdded) { // We are not already in back stack, so need to remove the fragment. mAdded.remove(fragment); - if (fragment.mHasMenu) { + if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } fragment.mAdded = false; @@ -1136,7 +1142,7 @@ final class FragmentManagerImpl extends FragmentManager { if (!fragment.mAdded) { mAdded.add(fragment); fragment.mAdded = true; - if (fragment.mHasMenu) { + if (fragment.mHasMenu && fragment.mMenuVisible) { mNeedMenuInvalidate = true; } moveToState(fragment, mCurState, transition, transitionStyle); @@ -1640,7 +1646,6 @@ final class FragmentManagerImpl extends FragmentManager { "No instantiated fragment for index #" + fms.mAdded[i]); } f.mAdded = true; - f.mImmediateActivity = mActivity; if (DEBUG) Log.v(TAG, "restoreAllState: making added #" + i + ": " + f); mAdded.add(f); } @@ -1748,7 +1753,7 @@ final class FragmentManagerImpl extends FragmentManager { if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); - if (f != null && !f.mHidden && f.mHasMenu) { + if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) { show = true; f.onCreateOptionsMenu(menu, inflater); if (newMenus == null) { @@ -1778,7 +1783,7 @@ final class FragmentManagerImpl extends FragmentManager { if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); - if (f != null && !f.mHidden && f.mHasMenu) { + if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) { show = true; f.onPrepareOptionsMenu(menu); } @@ -1791,7 +1796,7 @@ final class FragmentManagerImpl extends FragmentManager { if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); - if (f != null && !f.mHidden && f.mHasMenu) { + if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) { if (f.onOptionsItemSelected(item)) { return true; } @@ -1819,7 +1824,7 @@ final class FragmentManagerImpl extends FragmentManager { if (mActive != null) { for (int i=0; i<mAdded.size(); i++) { Fragment f = mAdded.get(i); - if (f != null && !f.mHidden && f.mHasMenu) { + if (f != null && !f.mHidden && f.mHasMenu && f.mMenuVisible) { f.onOptionsMenuClosed(menu); } } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 58ea0c3..9542874 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -255,10 +255,7 @@ public abstract class BackupAgent extends ContextWrapper { * will be delivered to the backup destination along with the metadata necessary * to place it with the proper location and permissions on the device where the * data is restored. - * @hide * - * @param context The BackupAgent that is calling this method. It is an error to - * call it from something other than a running BackupAgent instance. * @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. diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 9c96883..3441217 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant.SdkConstantType; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.RemoteException; +import android.provider.Settings; import java.net.InetAddress; @@ -71,6 +72,15 @@ public class ConnectivityManager { public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; /** + * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any + * applicable {@link Settings.Secure#CONNECTIVITY_CHANGE_DELAY}. + * + * @hide + */ + public static final String CONNECTIVITY_ACTION_IMMEDIATE = + "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE"; + + /** * The lookup key for a {@link NetworkInfo} object. Retrieve with * {@link android.content.Intent#getParcelableExtra(String)}. * diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 5321c6a..1f2b342 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -125,19 +125,6 @@ public final class ContactsContract { public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter"; /** - * An optional URI parameter for selection queries that instructs the - * provider to allow the user's personal profile contact entry (if any) - * to appear in a list of contact results. It is only useful when issuing - * a query that may retrieve more than one contact. If present, the user's - * profile will always be the first entry returned. The default value is - * false. - * - * Specifying this parameter will result in a security error if the calling - * application does not have android.permission.READ_PROFILE permission. - */ - public static final String ALLOW_PROFILE = "allow_profile"; - - /** * Query parameter that should be used by the client to access a specific * {@link Directory}. The parameter value should be the _ID of the corresponding * directory, e.g. @@ -557,7 +544,7 @@ public final class ContactsContract { } /** - * A table provided for sync adapters to use for storing private sync state data. + * A table provided for sync adapters to use for storing private sync state data for contacts. * * @see SyncStateContract */ @@ -608,6 +595,60 @@ public final class ContactsContract { } } + + /** + * A table provided for sync adapters to use for storing private sync state data for the + * user's personal profile. + * + * @see SyncStateContract + */ + public static final class ProfileSyncState implements SyncStateContract.Columns { + /** + * This utility class cannot be instantiated + */ + private ProfileSyncState() {} + + public static final String CONTENT_DIRECTORY = + SyncStateContract.Constants.CONTENT_DIRECTORY; + + /** + * The content:// style URI for this table + */ + public static final Uri CONTENT_URI = + Uri.withAppendedPath(Profile.CONTENT_URI, CONTENT_DIRECTORY); + + /** + * @see android.provider.SyncStateContract.Helpers#get + */ + public static byte[] get(ContentProviderClient provider, Account account) + throws RemoteException { + return SyncStateContract.Helpers.get(provider, CONTENT_URI, account); + } + + /** + * @see android.provider.SyncStateContract.Helpers#get + */ + public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account) + throws RemoteException { + return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account); + } + + /** + * @see android.provider.SyncStateContract.Helpers#set + */ + public static void set(ContentProviderClient provider, Account account, byte[] data) + throws RemoteException { + SyncStateContract.Helpers.set(provider, CONTENT_URI, account, data); + } + + /** + * @see android.provider.SyncStateContract.Helpers#newSetOperation + */ + public static ContentProviderOperation newSetOperation(Account account, byte[] data) { + return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data); + } + } + /** * Generic columns for use by sync adapters. The specific functions of * these columns are private to the sync adapter. Other clients of the API @@ -1875,8 +1916,8 @@ public final class ContactsContract { * Constants for the user's profile data, which is represented as a single contact on * the device that represents the user. The profile contact is not aggregated * together automatically in the same way that normal contacts are; instead, each - * account on the device may contribute a single raw contact representing the user's - * personal profile data from that source. + * account (including data set, if applicable) on the device may contribute a single + * raw contact representing the user's personal profile data from that source. * </p> * <p> * Access to the profile entry through these URIs (or incidental access to parts of @@ -1950,6 +1991,31 @@ public final class ContactsContract { */ public static final Uri CONTENT_RAW_CONTACTS_URI = Uri.withAppendedPath(CONTENT_URI, "raw_contacts"); + + /** + * The minimum ID for any entity that belongs to the profile. This essentially + * defines an ID-space in which profile data is stored, and is used by the provider + * to determine whether a request via a non-profile-specific URI should be directed + * to the profile data rather than general contacts data, along with all the special + * permission checks that entails. + * + * Callers may use {@link #isProfileId} to check whether a specific ID falls into + * the set of data intended for the profile. + */ + public static final long MIN_ID = Long.MAX_VALUE - (long) Integer.MAX_VALUE; + } + + /** + * This method can be used to identify whether the given ID is associated with profile + * data. It does not necessarily indicate that the ID is tied to valid data, merely + * that accessing data using this ID will result in profile access checks and will only + * return data from the profile. + * + * @param id The ID to check. + * @return Whether the ID is associated with profile data. + */ + public static boolean isProfileId(long id) { + return id >= Profile.MIN_ID; } protected interface RawContactsColumns { @@ -4542,6 +4608,12 @@ public final class ContactsContract { * either. * </p> * <p> + * Inserting or updating a status update for the user's profile requires either using + * the {@link #DATA_ID} to identify the data row to attach the update to, or + * {@link StatusUpdates#PROFILE_CONTENT_URI} to ensure that the change is scoped to the + * profile. + * </p> + * <p> * You cannot use {@link ContentResolver#update} to change a status, but * {@link ContentResolver#insert} will replace the latests status if it already * exists. @@ -4687,6 +4759,12 @@ public final class ContactsContract { public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "status_updates"); /** + * The content:// style URI for this table, specific to the user's profile. + */ + public static final Uri PROFILE_CONTENT_URI = + Uri.withAppendedPath(Profile.CONTENT_URI, "status_updates"); + + /** * Gets the resource ID for the proper presence icon. * * @param status the status to get the icon for diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 4c563ce..39f9367 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -139,6 +139,7 @@ public abstract class WallpaperService extends Service { boolean mSurfaceCreated; boolean mIsCreating; boolean mDrawingAllowed; + boolean mOffsetsChanged; int mWidth; int mHeight; int mFormat; @@ -604,12 +605,15 @@ public abstract class WallpaperService extends Service { if (DEBUG) Log.v(TAG, "Layout: Surface destroyed"); return; } - + + boolean didSurface = false; + try { mSurfaceHolder.ungetCallbacks(); if (surfaceCreating) { mIsCreating = true; + didSurface = true; if (DEBUG) Log.v(TAG, "onSurfaceCreated(" + mSurfaceHolder + "): " + this); onSurfaceCreated(mSurfaceHolder); @@ -637,6 +641,7 @@ public abstract class WallpaperService extends Service { + mSurfaceHolder + ", " + mFormat + ", " + mCurWidth + ", " + mCurHeight + "): " + this); + didSurface = true; onSurfaceChanged(mSurfaceHolder, mFormat, mCurWidth, mCurHeight); SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); @@ -661,6 +666,26 @@ public abstract class WallpaperService extends Service { } } + if (didSurface && !mReportedVisible) { + // This wallpaper is currently invisible, but its + // surface has changed. At this point let's tell it + // again that it is invisible in case the report about + // the surface caused it to start running. We really + // don't want wallpapers running when not visible. + if (mIsCreating) { + // Some wallpapers will ignore this call if they + // had previously been told they were invisble, + // so if we are creating a new surface then toggle + // the state to get them to notice. + if (DEBUG) Log.v(TAG, "onVisibilityChanged(true) at surface: " + + this); + onVisibilityChanged(true); + } + if (DEBUG) Log.v(TAG, "onVisibilityChanged(false) at surface: " + + this); + onVisibilityChanged(false); + } + } finally { mIsCreating = false; mSurfaceCreated = true; @@ -701,6 +726,7 @@ public abstract class WallpaperService extends Service { onCreate(mSurfaceHolder); mInitializing = false; + mReportedVisible = false; updateSurface(false, false, false); } @@ -711,7 +737,7 @@ public abstract class WallpaperService extends Service { mIWallpaperEngine.mReqWidth = desiredWidth; mIWallpaperEngine.mReqHeight = desiredHeight; onDesiredSizeChanged(desiredWidth, desiredHeight); - doOffsetsChanged(); + doOffsetsChanged(true); } } @@ -733,6 +759,7 @@ public abstract class WallpaperService extends Service { // If becoming visible, in preview mode the surface // may have been destroyed so now we need to make // sure it is re-created. + doOffsetsChanged(false); updateSurface(false, false, false); } onVisibilityChanged(visible); @@ -740,11 +767,15 @@ public abstract class WallpaperService extends Service { } } - void doOffsetsChanged() { + void doOffsetsChanged(boolean always) { if (mDestroyed) { return; } - + + if (!always && !mOffsetsChanged) { + return; + } + float xOffset; float yOffset; float xOffsetStep; @@ -759,15 +790,19 @@ public abstract class WallpaperService extends Service { mPendingSync = false; mOffsetMessageEnqueued = false; } - + if (mSurfaceCreated) { - if (DEBUG) Log.v(TAG, "Offsets change in " + this - + ": " + xOffset + "," + yOffset); - final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; - final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; - final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; - final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; - onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); + if (mReportedVisible) { + if (DEBUG) Log.v(TAG, "Offsets change in " + this + + ": " + xOffset + "," + yOffset); + final int availw = mIWallpaperEngine.mReqWidth-mCurWidth; + final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0; + final int availh = mIWallpaperEngine.mReqHeight-mCurHeight; + final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0; + onOffsetsChanged(xOffset, yOffset, xOffsetStep, yOffsetStep, xPixels, yPixels); + } else { + mOffsetsChanged = true; + } } if (sync) { @@ -953,7 +988,7 @@ public abstract class WallpaperService extends Service { mEngine.doVisibilityChanged(message.arg1 != 0); break; case MSG_WALLPAPER_OFFSETS: { - mEngine.doOffsetsChanged(); + mEngine.doOffsetsChanged(true); } break; case MSG_WALLPAPER_COMMAND: { WallpaperCommand cmd = (WallpaperCommand)message.obj; @@ -962,7 +997,7 @@ public abstract class WallpaperService extends Service { case MSG_WINDOW_RESIZED: { final boolean reportDraw = message.arg1 != 0; mEngine.updateSurface(true, false, reportDraw); - mEngine.doOffsetsChanged(); + mEngine.doOffsetsChanged(true); } break; case MSG_TOUCH_EVENT: { boolean skip = false; diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index 89b6f32..75dcd6e 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -390,10 +390,10 @@ class AudioPlaybackHandler { audioTrack.play(); } int count = 0; - while (count < bufferCopy.mLength) { + while (count < bufferCopy.mBytes.length) { // Note that we don't take bufferCopy.mOffset into account because // it is guaranteed to be 0. - int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength); + int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mBytes.length); if (written <= 0) { break; } @@ -415,6 +415,9 @@ class AudioPlaybackHandler { } if (params.mBytesWritten < params.mAudioBufferSize) { + if (DBG) Log.d(TAG, "Stopping audio track to flush audio, state was : " + + audioTrack.getPlayState()); + params.mIsShortUtterance = true; audioTrack.stop(); } @@ -452,10 +455,40 @@ class AudioPlaybackHandler { return; } + if (params.mIsShortUtterance) { + // In this case we would have called AudioTrack#stop() to flush + // buffers to the mixer. This makes the playback head position + // unobservable and notification markers do not work reliably. We + // have no option but to wait until we think the track would finish + // playing and release it after. + // + // This isn't as bad as it looks because (a) We won't end up waiting + // for much longer than we should because even at 4khz mono, a short + // utterance weighs in at about 2 seconds, and (b) such short utterances + // are expected to be relatively infrequent and in a stream of utterances + // this shows up as a slightly longer pause. + blockUntilEstimatedCompletion(params); + } else { + blockUntilCompletion(params); + } + } + + private static void blockUntilEstimatedCompletion(SynthesisMessageParams params) { + final int lengthInFrames = params.mBytesWritten / params.mBytesPerFrame; + final long estimatedTimeMs = (lengthInFrames * 1000 / params.mSampleRateInHz); + + if (DBG) Log.d(TAG, "About to sleep for: " + estimatedTimeMs + "ms for a short utterance"); + + try { + Thread.sleep(estimatedTimeMs); + } catch (InterruptedException ie) { + // Do nothing. + } + } + + private static void blockUntilCompletion(SynthesisMessageParams params) { final AudioTrack audioTrack = params.mAudioTrack; - final int bytesPerFrame = getBytesPerFrame(params.mAudioFormat); - final int lengthInBytes = params.mBytesWritten; - final int lengthInFrames = lengthInBytes / bytesPerFrame; + final int lengthInFrames = params.mBytesWritten / params.mBytesPerFrame; int currentPosition = 0; while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) { @@ -511,16 +544,6 @@ class AudioPlaybackHandler { return 0; } - static int getBytesPerFrame(int audioFormat) { - if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { - return 1; - } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { - return 2; - } - - return -1; - } - private static void setupVolume(AudioTrack audioTrack, float volume, float pan) { float vol = clip(volume, 0.0f, 1.0f); float panning = clip(pan, -1.0f, 1.0f); diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index 7dbf1ac..0cca06a 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -85,6 +85,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // Note that mLogger.mError might be true too at this point. mLogger.onStopped(); + SynthesisMessageParams token = null; synchronized (mStateLock) { if (mStopped) { Log.w(TAG, "stop() called twice"); @@ -97,9 +98,19 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // In all other cases, mAudioTrackHandler.stop() will // result in onComplete being called. mLogger.onWriteData(); + } else { + token = mToken; } mStopped = true; } + + if (token != null) { + // This might result in the synthesis thread being woken up, at which + // point it will write an additional buffer to the token - but we + // won't worry about that because the audio playback queue will be cleared + // soon after (see SynthHandler#stop(String). + token.clearBuffers(); + } } @Override @@ -155,18 +166,22 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { + length + " bytes)"); } + SynthesisMessageParams token = null; synchronized (mStateLock) { if (mToken == null || mStopped) { return TextToSpeech.ERROR; } - - // Sigh, another copy. - final byte[] bufferCopy = new byte[length]; - System.arraycopy(buffer, offset, bufferCopy, 0, length); - mToken.addBuffer(bufferCopy); - mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken); + token = mToken; } + // Sigh, another copy. + final byte[] bufferCopy = new byte[length]; + System.arraycopy(buffer, offset, bufferCopy, 0, length); + // Might block on mToken.this, if there are too many buffers waiting to + // be consumed. + token.addBuffer(bufferCopy); + mAudioTrackHandler.enqueueSynthesisDataAvailable(token); + mLogger.onEngineDataReceived(); return TextToSpeech.SUCCESS; @@ -176,6 +191,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { public int done() { if (DBG) Log.d(TAG, "done()"); + SynthesisMessageParams token = null; synchronized (mStateLock) { if (mDone) { Log.w(TAG, "Duplicate call to done()"); @@ -188,9 +204,12 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { return TextToSpeech.ERROR; } - mAudioTrackHandler.enqueueSynthesisDone(mToken); - mLogger.onEngineComplete(); + token = mToken; } + + mAudioTrackHandler.enqueueSynthesisDone(token); + mLogger.onEngineComplete(); + return TextToSpeech.SUCCESS; } diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 7da5daa..779721e 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -15,6 +15,7 @@ */ package android.speech.tts; +import android.media.AudioFormat; import android.media.AudioTrack; import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher; @@ -24,6 +25,8 @@ import java.util.LinkedList; * Params required to play back a synthesis request. */ final class SynthesisMessageParams extends MessageParams { + private static final long MAX_UNCONSUMED_AUDIO_MS = 500; + final int mStreamType; final int mSampleRateInHz; final int mAudioFormat; @@ -32,10 +35,22 @@ final class SynthesisMessageParams extends MessageParams { final float mPan; final EventLogger mLogger; + final int mBytesPerFrame; + volatile AudioTrack mAudioTrack; - // Not volatile, accessed only from the synthesis thread. - int mBytesWritten; + // Written by the synthesis thread, but read on the audio playback + // thread. + volatile int mBytesWritten; + // A "short utterance" is one that uses less bytes than the audio + // track buffer size (mAudioBufferSize). In this case, we need to call + // AudioTrack#stop() to send pending buffers to the mixer, and slightly + // different logic is required to wait for the track to finish. + // + // Not volatile, accessed only from the audio playback thread. + boolean mIsShortUtterance; int mAudioBufferSize; + // Always synchronized on "this". + int mUnconsumedBytes; private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); @@ -53,6 +68,8 @@ final class SynthesisMessageParams extends MessageParams { mPan = pan; mLogger = logger; + mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount; + // initially null. mAudioTrack = null; mBytesWritten = 0; @@ -64,18 +81,36 @@ final class SynthesisMessageParams extends MessageParams { return TYPE_SYNTHESIS; } - synchronized void addBuffer(byte[] buffer, int offset, int length) { - mDataBufferList.add(new ListEntry(buffer, offset, length)); + synchronized void addBuffer(byte[] buffer) { + long unconsumedAudioMs = 0; + + while ((unconsumedAudioMs = getUnconsumedAudioLengthMs()) > MAX_UNCONSUMED_AUDIO_MS) { + try { + wait(); + } catch (InterruptedException ie) { + return; + } + } + + mDataBufferList.add(new ListEntry(buffer)); + mUnconsumedBytes += buffer.length; } - synchronized void addBuffer(byte[] buffer) { - mDataBufferList.add(new ListEntry(buffer, 0, buffer.length)); + synchronized void clearBuffers() { + mDataBufferList.clear(); + mUnconsumedBytes = 0; + notifyAll(); } synchronized ListEntry getNextBuffer() { - return mDataBufferList.poll(); - } + ListEntry entry = mDataBufferList.poll(); + if (entry != null) { + mUnconsumedBytes -= entry.mBytes.length; + notifyAll(); + } + return entry; + } void setAudioTrack(AudioTrack audioTrack) { mAudioTrack = audioTrack; @@ -85,15 +120,29 @@ final class SynthesisMessageParams extends MessageParams { return mAudioTrack; } + // Must be called synchronized on this. + private long getUnconsumedAudioLengthMs() { + final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; + final long estimatedTimeMs = unconsumedFrames * 1000 / mSampleRateInHz; + + return estimatedTimeMs; + } + + private static int getBytesPerFrame(int audioFormat) { + if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { + return 1; + } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { + return 2; + } + + return -1; + } + static final class ListEntry { final byte[] mBytes; - final int mOffset; - final int mLength; - ListEntry(byte[] bytes, int offset, int length) { + ListEntry(byte[] bytes) { mBytes = bytes; - mOffset = offset; - mLength = length; } } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 788711d..e8b2045 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -729,12 +729,22 @@ public class StaticLayout extends Layout { start - widthStart, end - start); } - // If ellipsize is in marquee mode, do not apply ellipsis on the first line - if (ellipsize != null && (ellipsize != TextUtils.TruncateAt.MARQUEE || j != 0)) { + if (ellipsize != null) { + // If there is only one line, then do any type of ellipsis except when it is MARQUEE + // if there are multiple lines, just allow END ellipsis on the last line + boolean firstLine = (j == 0); + boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount); boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount); - calculateEllipsis(start, end, widths, widthStart, - ellipsisWidth, ellipsize, j, - textWidth, paint, forceEllipsis); + + boolean doEllipsis = (firstLine && !moreChars && + ellipsize != TextUtils.TruncateAt.MARQUEE) || + (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) && + ellipsize == TextUtils.TruncateAt.END); + if (doEllipsis) { + calculateEllipsis(start, end, widths, widthStart, + ellipsisWidth, ellipsize, j, + textWidth, paint, forceEllipsis); + } } mLineCount++; @@ -797,8 +807,8 @@ public class StaticLayout extends Layout { ellipsisStart = i; ellipsisCount = len - i; - if (forceEllipsis && ellipsisCount == 0 && i > 0) { - ellipsisStart = i - 1; + if (forceEllipsis && ellipsisCount == 0 && len > 0) { + ellipsisStart = len - 1; ellipsisCount = 1; } } else { diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index fcc372e..68a6b3e 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -723,7 +723,7 @@ class TextLine { float ret = 0; int contextLen = contextEnd - contextStart; - if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineColor !=0 || runIsRtl))) { + if (needWidth || (c != null && (wp.bgColor != 0 || wp.underlineCount != 0 || runIsRtl))) { int flags = runIsRtl ? Paint.DIRECTION_RTL : Paint.DIRECTION_LTR; if (mCharsValid) { ret = wp.getTextRunAdvances(mChars, start, runLen, @@ -753,21 +753,26 @@ class TextLine { wp.setColor(previousColor); } - if (wp.underlineColor != 0) { + if (wp.underlineCount != 0) { // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h - float middle = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); - // kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h - float halfHeight = wp.underlineThickness * (1.0f / 18.0f / 2.0f) * wp.getTextSize(); + float underlineTop = y + wp.baselineShift + (1.0f / 9.0f) * wp.getTextSize(); int previousColor = wp.getColor(); Paint.Style previousStyle = wp.getStyle(); + boolean previousAntiAlias = wp.isAntiAlias(); - wp.setColor(wp.underlineColor); wp.setStyle(Paint.Style.FILL); - c.drawRect(x, middle - halfHeight, x + ret, middle + halfHeight, wp); + wp.setAntiAlias(true); + + for (int i = 0; i < wp.underlineCount; i++) { + wp.setColor(wp.underlineColors[i]); + c.drawRect(x, underlineTop, x + ret, underlineTop + wp.underlineThicknesses[i], + wp); + } wp.setStyle(previousStyle); wp.setColor(previousColor); + wp.setAntiAlias(previousAntiAlias); } drawTextRun(c, wp, start, end, contextStart, contextEnd, runIsRtl, diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java index de57dfa..625d869 100644 --- a/core/java/android/text/TextPaint.java +++ b/core/java/android/text/TextPaint.java @@ -23,6 +23,9 @@ import android.graphics.Paint; * data used during text measuring and drawing. */ public class TextPaint extends Paint { + + private static final int DEFAULT_UNDERLINE_SIZE = 3; + // Special value 0 means no background paint public int bgColor; public int baselineShift; @@ -33,12 +36,17 @@ public class TextPaint extends Paint { * Special value 0 means no custom underline * @hide */ - public int underlineColor; + public int[] underlineColors; /** * Defined as a multiplier of the default underline thickness. Use 1.0f for default thickness. * @hide */ - public float underlineThickness; + public float[] underlineThicknesses; + /** + * The number of underlines currently stored in the array. If 0, no underline is drawn. + * @hide + */ + public int underlineCount; public TextPaint() { super(); @@ -64,24 +72,43 @@ public class TextPaint extends Paint { linkColor = tp.linkColor; drawableState = tp.drawableState; density = tp.density; - underlineColor = tp.underlineColor; - underlineThickness = tp.underlineThickness; + underlineColors = tp.underlineColors; + underlineThicknesses = tp.underlineThicknesses; + underlineCount = tp.underlineCount; } /** * Defines a custom underline for this Paint. * @param color underline solid color - * @param thickness underline thickness, defined as a multiplier of the default underline - * thickness. + * @param thickness underline thickness * @hide */ - public void setUnderlineText(boolean isUnderlined, int color, float thickness) { - setUnderlineText(false); - if (isUnderlined) { - underlineColor = color; - underlineThickness = thickness; + public void setUnderlineText(int color, float thickness) { + if (color == 0) { + // No underline + return; + } + + if (underlineCount == 0) { + underlineColors = new int[DEFAULT_UNDERLINE_SIZE]; + underlineThicknesses = new float[DEFAULT_UNDERLINE_SIZE]; + underlineColors[underlineCount] = color; + underlineThicknesses[underlineCount] = thickness; + underlineCount++; } else { - underlineColor = 0; + if (underlineCount == underlineColors.length) { + int[] newColors = new int[underlineColors.length + DEFAULT_UNDERLINE_SIZE]; + float[] newThickness = new float[underlineThicknesses.length + + DEFAULT_UNDERLINE_SIZE]; + System.arraycopy(underlineColors, 0, newColors, 0, underlineColors.length); + System.arraycopy( + underlineThicknesses, 0, newThickness, 0, underlineThicknesses.length); + underlineColors = newColors; + underlineThicknesses = newThickness; + } + underlineColors[underlineCount] = color; + underlineThicknesses[underlineCount] = thickness; + underlineCount++; } } } diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index b8b54f4..e914316 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -25,6 +25,7 @@ import android.text.style.BackgroundColorSpan; import android.text.style.BulletSpan; import android.text.style.CharacterStyle; import android.text.style.ForegroundColorSpan; +import android.text.style.EasyEditSpan; import android.text.style.LeadingMarginSpan; import android.text.style.MetricAffectingSpan; import android.text.style.QuoteSpan; @@ -585,6 +586,8 @@ public class TextUtils { public static final int SPELL_CHECK_SPAN = 20; /** @hide */ public static final int SUGGESTION_RANGE_SPAN = 21; + /** @hide */ + public static final int EASY_EDIT_SPAN = 22; /** * Flatten a CharSequence and whatever styles can be copied across processes @@ -748,6 +751,10 @@ public class TextUtils { readSpan(p, sp, new SuggestionRangeSpan()); break; + case EASY_EDIT_SPAN: + readSpan(p, sp, new EasyEditSpan(p)); + break; + default: throw new RuntimeException("bogus span encoding " + kind); } diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java new file mode 100644 index 0000000..e6e4d2c --- /dev/null +++ b/core/java/android/text/style/EasyEditSpan.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2011 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.text.style; + +import android.os.Parcel; +import android.text.ParcelableSpan; +import android.text.TextUtils; +import android.widget.TextView; + +/** + * Provides an easy way to edit a portion of text. + * <p> + * The {@link TextView} uses this span to allow the user to delete a chuck of text in one click. + * the text. {@link TextView} removes this span as soon as the text is edited, or the cursor moves. + */ +public class EasyEditSpan implements ParcelableSpan { + + public EasyEditSpan() { + // Empty + } + + public EasyEditSpan(Parcel src) { + this(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + // Empty + } + + @Override + public int getSpanTypeId() { + return TextUtils.EASY_EDIT_SPAN; + } +} diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index ea57f917..8625257 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -61,12 +61,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { public static final String SUGGESTION_SPAN_PICKED_BEFORE = "before"; public static final String SUGGESTION_SPAN_PICKED_HASHCODE = "hashcode"; - /** - * The default underline thickness as a percentage of the system's default underline thickness - * (i.e., 100 means the default thickness, and 200 is a double thickness). - */ - private static final int DEFAULT_UNDERLINE_PERCENTAGE = 100; - public static final int SUGGESTIONS_MAX_SIZE = 5; /* @@ -82,10 +76,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { private final String mNotificationTargetClassName; private final int mHashCode; - private float mMisspelledUnderlineThickness; - private int mMisspelledUnderlineColor; - private float mEasyCorrectUnderlineThickness; - private int mEasyCorrectUnderlineColor; + private float mUnderlineThickness; + private int mUnderlineColor; /* * TODO: If switching IME is required, needs to add parameters for ids of InputMethodInfo @@ -140,31 +132,26 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } private void initStyle(Context context) { - // Read the colors. We need to store the color and the underline thickness, as the span - // does not have access to the context when it is read from a parcel. - TypedArray typedArray; + int defStyle = 0; + if ((getFlags() & FLAG_MISSPELLED) != 0) { + defStyle = com.android.internal.R.attr.textAppearanceMisspelledSuggestion; + } else if ((getFlags() & FLAG_EASY_CORRECT) != 0) { + defStyle = com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion; + } else { + // No style is applied. + mUnderlineThickness = 0; + mUnderlineColor = 0; + return; + } - typedArray = context.obtainStyledAttributes(null, + TypedArray typedArray = context.obtainStyledAttributes(null, com.android.internal.R.styleable.SuggestionSpan, - com.android.internal.R.attr.textAppearanceEasyCorrectSuggestion, 0); + defStyle, 0); - mEasyCorrectUnderlineThickness = getThicknessPercentage(typedArray, - com.android.internal.R.styleable.SuggestionSpan_textUnderlineThicknessPercentage); - mEasyCorrectUnderlineColor = typedArray.getColor( + mUnderlineThickness = typedArray.getDimension( + com.android.internal.R.styleable.SuggestionSpan_textUnderlineThickness, 0); + mUnderlineColor = typedArray.getColor( com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK); - - typedArray = context.obtainStyledAttributes(null, - com.android.internal.R.styleable.SuggestionSpan, - com.android.internal.R.attr.textAppearanceMisspelledSuggestion, 0); - mMisspelledUnderlineThickness = getThicknessPercentage(typedArray, - com.android.internal.R.styleable.SuggestionSpan_textUnderlineThicknessPercentage); - mMisspelledUnderlineColor = typedArray.getColor( - com.android.internal.R.styleable.SuggestionSpan_textUnderlineColor, Color.BLACK); - } - - private static float getThicknessPercentage(TypedArray typedArray, int index) { - int value = typedArray.getInteger(index, DEFAULT_UNDERLINE_PERCENTAGE); - return value / 100.0f; } public SuggestionSpan(Parcel src) { @@ -173,10 +160,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { mLocaleString = src.readString(); mNotificationTargetClassName = src.readString(); mHashCode = src.readInt(); - mEasyCorrectUnderlineColor = src.readInt(); - mEasyCorrectUnderlineThickness = src.readFloat(); - mMisspelledUnderlineColor = src.readInt(); - mMisspelledUnderlineThickness = src.readFloat(); + mUnderlineColor = src.readInt(); + mUnderlineThickness = src.readFloat(); } /** @@ -226,10 +211,8 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { dest.writeString(mLocaleString); dest.writeString(mNotificationTargetClassName); dest.writeInt(mHashCode); - dest.writeInt(mEasyCorrectUnderlineColor); - dest.writeFloat(mEasyCorrectUnderlineThickness); - dest.writeInt(mMisspelledUnderlineColor); - dest.writeFloat(mMisspelledUnderlineThickness); + dest.writeInt(mUnderlineColor); + dest.writeFloat(mUnderlineThickness); } @Override @@ -271,10 +254,6 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { @Override public void updateDrawState(TextPaint tp) { - if ((getFlags() & FLAG_MISSPELLED) != 0) { - tp.setUnderlineText(true, mMisspelledUnderlineColor, mMisspelledUnderlineThickness); - } else if ((getFlags() & FLAG_EASY_CORRECT) != 0) { - tp.setUnderlineText(true, mEasyCorrectUnderlineColor, mEasyCorrectUnderlineThickness); - } + tp.setUnderlineText(mUnderlineColor, mUnderlineThickness); } } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index ef3d3fa..3880bc4 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -36,10 +36,14 @@ public class Surface implements Parcelable { public static final int ROTATION_270 = 3; /** - * Create Surface from a SurfaceTexture. + * Create Surface from a {@link SurfaceTexture}. * - * @param surfaceTexture The {@link SurfaceTexture} that is updated by this Surface. - * @hide + * Images drawn to the Surface will be made available to the {@link + * SurfaceTexture}, which can attach them an OpenGL ES texture via {@link + * SurfaceTexture#updateTexImage}. + * + * @param surfaceTexture The {@link SurfaceTexture} that is updated by this + * Surface. */ public Surface(SurfaceTexture surfaceTexture) { if (DEBUG_RELEASE) { @@ -442,7 +446,9 @@ public class Surface implements Parcelable { /** @hide */ public native void setLayer(int zorder); /** @hide */ - public native void setPosition(int x, int y); + public void setPosition(int x, int y) { setPosition((float)x, (float)y); } + /** @hide */ + public native void setPosition(float x, float y); /** @hide */ public native void setSize(int w, int h); /** @hide */ diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index da5baf8..302be57 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -1583,6 +1583,14 @@ public final class InputMethodManager { /** * Set additional input method subtypes. Only a process which shares the same uid with the IME * can add additional input method subtypes to the IME. + * Please note that a subtype's status is stored in the system. + * For example, enabled subtypes are remembered by the framework even after they are removed + * by using this method. If you re-add the same subtypes again, + * they will just get enabled. If you want to avoid such conflicts, for instance, you may + * want to create a "different" new subtype even with the same locale and mode, + * by changing its extra value. The different subtype won't get affected by the stored past + * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer + * to the current implementation.) * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to. * @param subtypes subtypes will be added as additional subtypes of the current input method. * @return true if the additional input method subtypes are successfully added. diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java index c85b2d9..01587aa 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -66,7 +66,13 @@ public final class TextServicesManager { /** * Get a spell checker session for the specified spell checker - * @param locale the locale for the spell checker + * @param locale the locale for the spell checker. If {@param locale} is null and + * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be + * returned. If {@param locale} is not null and referToSpellCheckerLanguageSettings is true, + * the locale specified in Settings will be returned only when it is same as {@param locale}. + * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@param locale} is + * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be + * selected. * @param listener a spell checker session lister for getting results from a spell checker. * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled * languages in settings will be returned. @@ -81,6 +87,11 @@ public final class TextServicesManager { throw new IllegalArgumentException("Locale should not be null if you don't refer" + " settings."); } + + if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) { + return null; + } + final SpellCheckerInfo sci; try { sci = sService.getCurrentSpellChecker(null); @@ -108,7 +119,12 @@ public final class TextServicesManager { final String localeStr = locale.toString(); for (int i = 0; i < sci.getSubtypeCount(); ++i) { final SpellCheckerSubtype subtype = sci.getSubtypeAt(i); - if (subtype.getLocale().equals(localeStr)) { + final String tempSubtypeLocale = subtype.getLocale(); + if (tempSubtypeLocale.equals(localeStr)) { + subtypeInUse = subtype; + break; + } else if (localeStr.length() >= 2 && tempSubtypeLocale.length() >= 2 + && localeStr.startsWith(tempSubtypeLocale)) { subtypeInUse = subtype; } } diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java index ef1906c..1b9a25e 100644 --- a/core/java/android/webkit/HTML5VideoInline.java +++ b/core/java/android/webkit/HTML5VideoInline.java @@ -5,6 +5,7 @@ import android.graphics.SurfaceTexture; import android.media.MediaPlayer; import android.webkit.HTML5VideoView; import android.webkit.HTML5VideoViewProxy; +import android.view.Surface; import android.opengl.GLES20; /** @@ -38,7 +39,10 @@ public class HTML5VideoInline extends HTML5VideoView{ @Override public void decideDisplayMode() { - mPlayer.setTexture(getSurfaceTexture(getVideoLayerId())); + SurfaceTexture surfaceTexture = getSurfaceTexture(getVideoLayerId()); + Surface surface = new Surface(surfaceTexture); + mPlayer.setSurface(surface); + surface.release(); } // Normally called immediately after setVideoURI. But for full screen, diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 2aa481c..4748522 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -9124,7 +9124,9 @@ public class WebView extends AbsoluteLayout /** @hide send content invalidate */ protected void contentInvalidateAll() { - mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL); + if (mWebViewCore != null && !mBlockWebkitViewMessages) { + mWebViewCore.sendMessage(EventHub.CONTENT_INVALIDATE_ALL); + } } /** @hide call pageSwapCallback upon next page swap */ diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index ff13dcb..b89b8ec 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -112,6 +112,8 @@ public class SpellChecker implements SpellCheckerSessionListener { private void scheduleSpellCheck() { if (mLength == 0) return; + if (spellCheckerSession == null) return; + if (mChecker != null) { mTextView.removeCallbacks(mChecker); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index e9662ae..4ce2d90 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -64,6 +64,7 @@ import android.text.TextDirectionHeuristics; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.TextUtils.TruncateAt; import android.text.method.AllCapsTransformationMethod; import android.text.method.ArrowKeyMovementMethod; import android.text.method.DateKeyListener; @@ -81,7 +82,9 @@ import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; import android.text.method.TransformationMethod2; import android.text.method.WordIterator; +import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; +import android.text.style.EasyEditSpan; import android.text.style.ParagraphStyle; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionRangeSpan; @@ -364,6 +367,35 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mResolvedDrawables = false; + /** + * On some devices the fading edges add a performance penalty if used + * extensively in the same layout. This mode indicates how the marquee + * is currently being shown, if applicable. (mEllipsize will == MARQUEE) + */ + private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL; + + /** + * When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores + * the layout that should be used when the mode switches. + */ + private Layout mSavedMarqueeModeLayout; + + /** + * Draw marquee text with fading edges as usual + */ + private static final int MARQUEE_FADE_NORMAL = 0; + + /** + * Draw marquee text as ellipsize end while inactive instead of with the fade. + * (Useful for devices where the fade can be expensive if overdone) + */ + private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1; + + /** + * Draw marquee text with fading edges because it is currently active/animating. + */ + private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2; + /* * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -995,8 +1027,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setEllipsize(TextUtils.TruncateAt.END); break; case 4: - setHorizontalFadingEdgeEnabled( - ViewConfiguration.get(context).isFadingMarqueeEnabled()); + if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) { + setHorizontalFadingEdgeEnabled(true); + mMarqueeFadeMode = MARQUEE_FADE_NORMAL; + } else { + setHorizontalFadingEdgeEnabled(false); + mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS; + } setEllipsize(TextUtils.TruncateAt.MARQUEE); break; } @@ -3067,8 +3104,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (text instanceof Spanned && ((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) { - setHorizontalFadingEdgeEnabled( - ViewConfiguration.get(mContext).isFadingMarqueeEnabled()); + if (ViewConfiguration.get(mContext).isFadingMarqueeEnabled()) { + setHorizontalFadingEdgeEnabled(true); + mMarqueeFadeMode = MARQUEE_FADE_NORMAL; + } else { + setHorizontalFadingEdgeEnabled(false); + mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS; + } setEllipsize(TextUtils.TruncateAt.MARQUEE); } @@ -4761,7 +4803,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int layoutDirection = getResolvedLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); - if (mEllipsize == TextUtils.TruncateAt.MARQUEE) { + if (mEllipsize == TextUtils.TruncateAt.MARQUEE && + mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { if (!mSingleLine && getLineCount() == 1 && canMarquee() && (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { canvas.translate(mLayout.getLineRight(0) - (mRight - mLeft - @@ -5945,7 +5988,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mSavedHintLayout = (BoringLayout) mHintLayout; } - mLayout = mHintLayout = null; + mSavedMarqueeModeLayout = mLayout = mHintLayout = null; // Since it depends on the value of mLayout prepareCursorControllers(); @@ -6065,73 +6108,25 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Layout.Alignment alignment = getLayoutAlignment(); boolean shouldEllipsize = mEllipsize != null && mInput == null; + final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE && + mMarqueeFadeMode != MARQUEE_FADE_NORMAL; + TruncateAt effectiveEllipsize = mEllipsize; + if (mEllipsize == TruncateAt.MARQUEE && + mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { + effectiveEllipsize = TruncateAt.END; + } if (mTextDir == null) { resolveTextDirection(); } - if (mText instanceof Spannable) { - mLayout = new DynamicLayout(mText, mTransformed, mTextPaint, w, - alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mInput == null ? mEllipsize : null, - ellipsisWidth); - } else { - if (boring == UNKNOWN_BORING) { - boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); - if (boring != null) { - mBoring = boring; - } - } - - if (boring != null) { - if (boring.width <= w && - (mEllipsize == null || boring.width <= ellipsisWidth)) { - if (mSavedLayout != null) { - mLayout = mSavedLayout. - replaceOrMake(mTransformed, mTextPaint, - w, alignment, mSpacingMult, mSpacingAdd, - boring, mIncludePad); - } else { - mLayout = BoringLayout.make(mTransformed, mTextPaint, - w, alignment, mSpacingMult, mSpacingAdd, - boring, mIncludePad); - } - mSavedLayout = (BoringLayout) mLayout; - } else if (shouldEllipsize && boring.width <= w) { - if (mSavedLayout != null) { - mLayout = mSavedLayout. - replaceOrMake(mTransformed, mTextPaint, - w, alignment, mSpacingMult, mSpacingAdd, - boring, mIncludePad, mEllipsize, - ellipsisWidth); - } else { - mLayout = BoringLayout.make(mTransformed, mTextPaint, - w, alignment, mSpacingMult, mSpacingAdd, - boring, mIncludePad, mEllipsize, - ellipsisWidth); - } - } else if (shouldEllipsize) { - mLayout = new StaticLayout(mTransformed, - 0, mTransformed.length(), - mTextPaint, w, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - mLayout = new StaticLayout(mTransformed, mTextPaint, - w, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); - } - } else if (shouldEllipsize) { - mLayout = new StaticLayout(mTransformed, - 0, mTransformed.length(), - mTextPaint, w, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mEllipsize, - ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); - } else { - mLayout = new StaticLayout(mTransformed, mTextPaint, - w, alignment, mTextDir, mSpacingMult, mSpacingAdd, - mIncludePad); - } + mLayout = makeSingleLayout(w, boring, ellipsisWidth, alignment, shouldEllipsize, + effectiveEllipsize, effectiveEllipsize == mEllipsize); + if (switchEllipsize) { + TruncateAt oppositeEllipsize = effectiveEllipsize == TruncateAt.MARQUEE ? + TruncateAt.END : TruncateAt.MARQUEE; + mSavedMarqueeModeLayout = makeSingleLayout(w, boring, ellipsisWidth, alignment, + shouldEllipsize, oppositeEllipsize, effectiveEllipsize != mEllipsize); } shouldEllipsize = mEllipsize != null; @@ -6222,6 +6217,77 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener prepareCursorControllers(); } + private Layout makeSingleLayout(int w, BoringLayout.Metrics boring, int ellipsisWidth, + Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize, + boolean useSaved) { + Layout result = null; + if (mText instanceof Spannable) { + result = new DynamicLayout(mText, mTransformed, mTextPaint, w, + alignment, mTextDir, mSpacingMult, + mSpacingAdd, mIncludePad, mInput == null ? effectiveEllipsize : null, + ellipsisWidth); + } else { + if (boring == UNKNOWN_BORING) { + boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring); + if (boring != null) { + mBoring = boring; + } + } + + if (boring != null) { + if (boring.width <= w && + (effectiveEllipsize == null || boring.width <= ellipsisWidth)) { + if (useSaved && mSavedLayout != null) { + result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint, + w, alignment, mSpacingMult, mSpacingAdd, + boring, mIncludePad); + } else { + result = BoringLayout.make(mTransformed, mTextPaint, + w, alignment, mSpacingMult, mSpacingAdd, + boring, mIncludePad); + } + + if (useSaved) { + mSavedLayout = (BoringLayout) result; + } + } else if (shouldEllipsize && boring.width <= w) { + if (useSaved && mSavedLayout != null) { + result = mSavedLayout.replaceOrMake(mTransformed, mTextPaint, + w, alignment, mSpacingMult, mSpacingAdd, + boring, mIncludePad, effectiveEllipsize, + ellipsisWidth); + } else { + result = BoringLayout.make(mTransformed, mTextPaint, + w, alignment, mSpacingMult, mSpacingAdd, + boring, mIncludePad, effectiveEllipsize, + ellipsisWidth); + } + } else if (shouldEllipsize) { + result = new StaticLayout(mTransformed, + 0, mTransformed.length(), + mTextPaint, w, alignment, mTextDir, mSpacingMult, + mSpacingAdd, mIncludePad, effectiveEllipsize, + ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); + } else { + result = new StaticLayout(mTransformed, mTextPaint, + w, alignment, mTextDir, mSpacingMult, mSpacingAdd, + mIncludePad); + } + } else if (shouldEllipsize) { + result = new StaticLayout(mTransformed, + 0, mTransformed.length(), + mTextPaint, w, alignment, mTextDir, mSpacingMult, + mSpacingAdd, mIncludePad, effectiveEllipsize, + ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE); + } else { + result = new StaticLayout(mTransformed, mTextPaint, + w, alignment, mTextDir, mSpacingMult, mSpacingAdd, + mIncludePad); + } + } + return result; + } + private boolean compressText(float width) { if (isHardwareAccelerated()) return false; @@ -7177,7 +7243,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean canMarquee() { int width = (mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight()); - return width > 0 && mLayout.getLineWidth(0) > width; + return width > 0 && (mLayout.getLineWidth(0) > width || + (mMarqueeFadeMode != MARQUEE_FADE_NORMAL && mSavedMarqueeModeLayout != null && + mSavedMarqueeModeLayout.getLineWidth(0) > width)); } private void startMarquee() { @@ -7191,6 +7259,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if ((mMarquee == null || mMarquee.isStopped()) && (isFocused() || isSelected()) && getLineCount() == 1 && canMarquee()) { + if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { + mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_FADE; + final Layout tmp = mLayout; + mLayout = mSavedMarqueeModeLayout; + mSavedMarqueeModeLayout = tmp; + setHorizontalFadingEdgeEnabled(true); + requestLayout(); + invalidate(); + } + if (mMarquee == null) mMarquee = new Marquee(this); mMarquee.start(mMarqueeRepeatLimit); } @@ -7200,6 +7278,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mMarquee != null && !mMarquee.isStopped()) { mMarquee.stop(); } + + if (mMarqueeFadeMode == MARQUEE_FADE_SWITCH_SHOW_FADE) { + mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS; + final Layout tmp = mSavedMarqueeModeLayout; + mSavedMarqueeModeLayout = mLayout; + mLayout = tmp; + setHorizontalFadingEdgeEnabled(false); + requestLayout(); + invalidate(); + } } private void startStopMarquee(boolean start) { @@ -7705,10 +7793,148 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** + * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related + * pop-up should be displayed. + */ + private class EditTextShortcutController { + + private EditTextShortcutPopupWindow mPopupWindow; + + private EasyEditSpan mEditTextShortcutSpan; + + private void hide() { + if (mEditTextShortcutSpan != null) { + mPopupWindow.hide(); + if (mText instanceof Spannable) { + ((Spannable) mText).removeSpan(mEditTextShortcutSpan); + } + mEditTextShortcutSpan = null; + } + } + + /** + * Monitors the changes in the text. + * + * <p>{@link ChangeWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used, + * as the notifications are not sent when a spannable (with spans) is inserted. + */ + public void onTextChange(CharSequence buffer) { + if (mEditTextShortcutSpan != null) { + hide(); + } + + if (buffer instanceof Spanned) { + mEditTextShortcutSpan = getSpan((Spanned) buffer); + if (mEditTextShortcutSpan != null) { + if (mPopupWindow == null) { + mPopupWindow = new EditTextShortcutPopupWindow(); + } + mPopupWindow.show(mEditTextShortcutSpan); + } + } + } + + private EasyEditSpan getSpan(Spanned spanned) { + EasyEditSpan[] inputMethodSpans = spanned.getSpans(0, spanned.length(), + EasyEditSpan.class); + + if (inputMethodSpans.length == 0) { + return null; + } else { + return inputMethodSpans[0]; + } + } + } + + /** + * Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled + * by {@link EditTextShortcutController}. + */ + private class EditTextShortcutPopupWindow extends PinnedPopupWindow + implements OnClickListener { + private static final int POPUP_TEXT_LAYOUT = + com.android.internal.R.layout.text_edit_action_popup_text; + private TextView mDeleteTextView; + private EasyEditSpan mEditTextShortcutSpan; + + @Override + protected void createPopupWindow() { + mPopupWindow = new PopupWindow(TextView.this.mContext, null, + com.android.internal.R.attr.textSelectHandleWindowStyle); + mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + mPopupWindow.setClippingEnabled(true); + } + + @Override + protected void initContentView() { + mContentView.setOrientation(LinearLayout.HORIZONTAL); + mContentView.setBackgroundResource( + com.android.internal.R.drawable.text_edit_side_paste_window); + + LayoutInflater inflater = (LayoutInflater)TextView.this.mContext. + getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + LayoutParams wrapContent = new LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + mDeleteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null); + mDeleteTextView.setLayoutParams(wrapContent); + mDeleteTextView.setText(com.android.internal.R.string.delete); + mDeleteTextView.setOnClickListener(this); + mContentView.addView(mDeleteTextView); + } + + public void show(EasyEditSpan inputMethodSpan) { + mEditTextShortcutSpan = inputMethodSpan; + super.show(); + } + + @Override + public void onClick(View view) { + if (view == mDeleteTextView) { + deleteText(); + } + } + + private void deleteText() { + Editable editable = (Editable) mText; + int start = editable.getSpanStart(mEditTextShortcutSpan); + int end = editable.getSpanEnd(mEditTextShortcutSpan); + if (start >= 0 && end >= 0) { + editable.delete(start, end); + } + } + + @Override + protected int getTextOffset() { + // Place the pop-up at the end of the span + Editable editable = (Editable) mText; + return editable.getSpanEnd(mEditTextShortcutSpan); + } + + @Override + protected int getVerticalLocalPosition(int line) { + return mLayout.getLineBottom(line); + } + + @Override + protected int clipVertically(int positionY) { + // As we display the pop-up below the span, no vertical clipping is required. + return positionY; + } + } + private class ChangeWatcher implements TextWatcher, SpanWatcher { private CharSequence mBeforeText; + private EditTextShortcutController mEditTextShortcutController; + + private ChangeWatcher() { + mEditTextShortcutController = new EditTextShortcutController(); + } + public void beforeTextChanged(CharSequence buffer, int start, int before, int after) { if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start @@ -7729,6 +7955,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " before=" + before + " after=" + after + ": " + buffer); TextView.this.handleTextChanged(buffer, start, before, after); + mEditTextShortcutController.onTextChange(buffer); + if (AccessibilityManager.getInstance(mContext).isEnabled() && (isFocused() || isSelected() && isShown())) { sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); @@ -7763,6 +7991,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + " what=" + what + ": " + buf); TextView.this.spanChange(buf, what, s, -1, e, -1); } + + private void hideControllers() { + mEditTextShortcutController.hide(); + } } /** @@ -8261,7 +8493,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected float getLeftFadingEdgeStrength() { if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f; - if (mEllipsize == TextUtils.TruncateAt.MARQUEE) { + if (mEllipsize == TextUtils.TruncateAt.MARQUEE && + mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { if (mMarquee != null && !mMarquee.isStopped()) { final Marquee marquee = mMarquee; if (marquee.shouldDrawLeftFade()) { @@ -8290,7 +8523,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected float getRightFadingEdgeStrength() { if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return 0.0f; - if (mEllipsize == TextUtils.TruncateAt.MARQUEE) { + if (mEllipsize == TextUtils.TruncateAt.MARQUEE && + mMarqueeFadeMode != MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS) { if (mMarquee != null && !mMarquee.isStopped()) { final Marquee marquee = mMarquee; return (marquee.mMaxFadeScroll - marquee.mScroll) / getHorizontalFadingEdgeLength(); @@ -8928,6 +9162,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener stopSelectionActionMode(); } else { selectCurrentWord(); + getSelectionController().show(); } handled = true; } @@ -9183,6 +9418,34 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + private static class SuggestionRangeSpan extends CharacterStyle { + + private final int mTextColor; + private final int mBackgroundColor; + + public SuggestionRangeSpan(Context context) { + TypedArray typedArray = context.obtainStyledAttributes(null, + com.android.internal.R.styleable.SuggestionRangeSpan, + com.android.internal.R.attr.textAppearanceSuggestionRange, 0); + + mTextColor = typedArray.getColor( + com.android.internal.R.styleable.SuggestionRangeSpan_textColor, 0); + mBackgroundColor = typedArray.getColor( + com.android.internal.R.styleable.SuggestionRangeSpan_colorBackground, 0); + } + + @Override + public void updateDrawState(TextPaint tp) { + if (mTextColor != 0) { + tp.setColor(mTextColor); + } + + if (mBackgroundColor != 0) { + tp.bgColor = mBackgroundColor; + } + } + } + private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnClickListener { private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE; private static final int NO_SUGGESTIONS = -1; @@ -9367,7 +9630,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (totalNbSuggestions == 0) return false; - if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan(); + if (mSuggestionRangeSpan == null) { + mSuggestionRangeSpan = new SuggestionRangeSpan(getContext()); + } + + ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = + new SuggestionRangeSpan(getContext()); ((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); @@ -10694,6 +10965,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void hideControllers() { hideInsertionPointCursorController(); stopSelectionActionMode(); + + if (mChangeWatcher != null) { + mChangeWatcher.hideControllers(); + } } /** diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java index 3961de3..1c47ca8 100644 --- a/core/java/com/android/internal/widget/TransportControlView.java +++ b/core/java/com/android/internal/widget/TransportControlView.java @@ -16,88 +16,369 @@ package com.android.internal.widget; -import com.android.internal.R; +import java.lang.ref.WeakReference; + +import com.android.internal.widget.LockScreenWidgetCallback; +import com.android.internal.widget.LockScreenWidgetInterface; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; import android.media.AudioManager; +import android.media.MediaMetadataRetriever; +import android.media.RemoteControlClient; +import android.media.IRemoteControlDisplay; +import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; +import android.text.Spannable; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; +import android.util.Log; +import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; -import android.widget.LinearLayout; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; -/** - * A special widget for displaying audio playback ("transport controls") in LockScreen. - * - */ -public class TransportControlView extends LinearLayout implements LockScreenWidgetInterface, - OnClickListener { - private static final String TAG = "TransportControlView"; - static final int sViewIds[] = { R.id.control_prev, R.id.control_pauseplay, R.id.control_next }; - protected static final int AUDIO_FOCUS_CHANGED = 100; - private LockScreenWidgetCallback mCallback; +import com.android.internal.R; + +public class TransportControlView extends FrameLayout implements OnClickListener, + LockScreenWidgetInterface { + + private static final int MSG_UPDATE_STATE = 100; + private static final int MSG_SET_METADATA = 101; + private static final int MSG_SET_TRANSPORT_CONTROLS = 102; + private static final int MSG_SET_ARTWORK = 103; + private static final int MSG_SET_GENERATION_ID = 104; + private static final int MAXDIM = 512; + protected static final boolean DEBUG = true; + protected static final String TAG = "TransportControlView"; + + private ImageView mAlbumArt; + private TextView mTrackTitle; + private ImageView mBtnPrev; + private ImageView mBtnPlay; + private ImageView mBtnNext; + private int mClientGeneration; + private Metadata mMetadata = new Metadata(); + private boolean mAttached; + private ComponentName mClientName; + private int mTransportControlFlags; + private int mPlayState; + private AudioManager mAudioManager; + private LockScreenWidgetCallback mWidgetCallbacks; + private IRemoteControlDisplayWeak mIRCD; + + /** + * The metadata which should be populated into the view once we've been attached + */ + private Bundle mPopulateMetadataWhenAttached = null; + + // This handler is required to ensure messages from IRCD are handled in sequence and on + // the UI thread. private Handler mHandler = new Handler() { + @Override public void handleMessage(Message msg) { - switch (msg.what){ - case AUDIO_FOCUS_CHANGED: - handleAudioFocusChange(msg.arg1); + switch (msg.what) { + case MSG_UPDATE_STATE: + if (mClientGeneration == msg.arg1) updatePlayPauseState(msg.arg2); + break; + + case MSG_SET_METADATA: + if (mClientGeneration == msg.arg1) updateMetadata((Bundle) msg.obj); + break; + + case MSG_SET_TRANSPORT_CONTROLS: + if (mClientGeneration == msg.arg1) updateTransportControls(msg.arg2); + break; + + case MSG_SET_ARTWORK: + if (mClientGeneration == msg.arg1) { + mMetadata.bitmap = (Bitmap) msg.obj; + mAlbumArt.setImageBitmap(mMetadata.bitmap); + } + break; + + case MSG_SET_GENERATION_ID: + if (mWidgetCallbacks != null) { + boolean clearing = msg.arg2 != 0; + if (DEBUG) Log.v(TAG, "New genId = " + msg.arg1 + ", clearing = " + clearing); + if (!clearing) { + mWidgetCallbacks.requestShow(TransportControlView.this); + } else { + mWidgetCallbacks.requestHide(TransportControlView.this); + } + } + mClientGeneration = msg.arg1; + mClientName = (ComponentName) msg.obj; + break; + } } }; - AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener = - new AudioManager.OnAudioFocusChangeListener() { - public void onAudioFocusChange(final int focusChange) { - mHandler.obtainMessage(AUDIO_FOCUS_CHANGED, focusChange, 0).sendToTarget(); + /** + * This class is required to have weak linkage to the current TransportControlView + * because the remote process can hold a strong reference to this binder object and + * we can't predict when it will be GC'd in the remote process. Without this code, it + * would allow a heavyweight object to be held on this side of the binder when there's + * no requirement to run a GC on the other side. + */ + private static class IRemoteControlDisplayWeak extends IRemoteControlDisplay.Stub { + private WeakReference<Handler> mLocalHandler; + + IRemoteControlDisplayWeak(Handler handler) { + mLocalHandler = new WeakReference<Handler>(handler); + } + + public void setPlaybackState(int generationId, int state) { + Handler handler = mLocalHandler.get(); + if (handler != null) { + handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget(); } - }; + } - public TransportControlView(Context context) { - this(context, null); - } + public void setMetadata(int generationId, Bundle metadata) { + Handler handler = mLocalHandler.get(); + if (handler != null) { + handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget(); + } + } + + public void setTransportControlFlags(int generationId, int flags) { + Handler handler = mLocalHandler.get(); + if (handler != null) { + handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, generationId, flags) + .sendToTarget(); + } + } + + public void setArtwork(int generationId, Bitmap bitmap) { + Handler handler = mLocalHandler.get(); + if (handler != null) { + handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget(); + } + } + + public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) { + Handler handler = mLocalHandler.get(); + if (handler != null) { + handler.obtainMessage(MSG_SET_METADATA, generationId, 0, metadata).sendToTarget(); + handler.obtainMessage(MSG_SET_ARTWORK, generationId, 0, bitmap).sendToTarget(); + } + } + + public void setCurrentClientId(int clientGeneration, ComponentName clientEventReceiver, + boolean clearing) throws RemoteException { + Handler handler = mLocalHandler.get(); + if (handler != null) { + handler.obtainMessage(MSG_SET_GENERATION_ID, + clientGeneration, (clearing ? 1 : 0), clientEventReceiver).sendToTarget(); + } + } + }; public TransportControlView(Context context, AttributeSet attrs) { super(context, attrs); + Log.v(TAG, "Create TCV " + this); + mAudioManager = new AudioManager(mContext); + mIRCD = new IRemoteControlDisplayWeak(mHandler); } - protected void handleAudioFocusChange(int focusChange) { - // TODO + private void updateTransportControls(int transportControlFlags) { + mTransportControlFlags = transportControlFlags; } - public void setCallback(LockScreenWidgetCallback callback) { - mCallback = callback; + @Override + public void onFinishInflate() { + super.onFinishInflate(); + mTrackTitle = (TextView) findViewById(R.id.title); + mTrackTitle.setSelected(true); // enable marquee + mAlbumArt = (ImageView) findViewById(R.id.albumart); + mBtnPrev = (ImageView) findViewById(R.id.btn_prev); + mBtnPlay = (ImageView) findViewById(R.id.btn_play); + mBtnNext = (ImageView) findViewById(R.id.btn_next); + final View buttons[] = { mBtnPrev, mBtnPlay, mBtnNext }; + for (View view : buttons) { + view.setOnClickListener(this); + } } @Override - public void onFinishInflate() { - for (int i = 0; i < sViewIds.length; i++) { - View view = findViewById(sViewIds[i]); - if (view != null) { - view.setOnClickListener(this); + public void onAttachedToWindow() { + super.onAttachedToWindow(); + if (mPopulateMetadataWhenAttached != null) { + updateMetadata(mPopulateMetadataWhenAttached); + mPopulateMetadataWhenAttached = null; + } + if (!mAttached) { + if (DEBUG) Log.v(TAG, "Registering TCV " + this); + mAudioManager.registerRemoteControlDisplay(mIRCD); + } + mAttached = true; + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mAttached) { + if (DEBUG) Log.v(TAG, "Unregistering TCV " + this); + mAudioManager.unregisterRemoteControlDisplay(mIRCD); + } + mAttached = false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int dim = Math.min(MAXDIM, Math.max(getWidth(), getHeight())); +// Log.v(TAG, "setting max bitmap size: " + dim + "x" + dim); +// mAudioManager.remoteControlDisplayUsesBitmapSize(mIRCD, dim, dim); + } + + class Metadata { + private String artist; + private String trackTitle; + private String albumTitle; + private Bitmap bitmap; + + public String toString() { + return "Metadata[artist=" + artist + " trackTitle=" + trackTitle + " albumTitle=" + albumTitle + "]"; + } + } + + private String getMdString(Bundle data, int id) { + return data.getString(Integer.toString(id)); + } + + private void updateMetadata(Bundle data) { + if (mAttached) { + mMetadata.artist = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST); + mMetadata.trackTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_TITLE); + mMetadata.albumTitle = getMdString(data, MediaMetadataRetriever.METADATA_KEY_ALBUM); + populateMetadata(); + } else { + mPopulateMetadataWhenAttached = data; + } + } + + /** + * Populates the given metadata into the view + */ + private void populateMetadata() { + StringBuilder sb = new StringBuilder(); + int trackTitleLength = 0; + if (!TextUtils.isEmpty(mMetadata.trackTitle)) { + sb.append(mMetadata.trackTitle); + trackTitleLength = mMetadata.trackTitle.length(); + } + if (!TextUtils.isEmpty(mMetadata.artist)) { + if (sb.length() != 0) { + sb.append(" - "); + } + sb.append(mMetadata.artist); + } + if (!TextUtils.isEmpty(mMetadata.albumTitle)) { + if (sb.length() != 0) { + sb.append(" - "); } + sb.append(mMetadata.albumTitle); + } + mTrackTitle.setText(sb.toString(), TextView.BufferType.SPANNABLE); + Spannable str = (Spannable) mTrackTitle.getText(); + if (trackTitleLength != 0) { + str.setSpan(new ForegroundColorSpan(0xffffffff), 0, trackTitleLength, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + trackTitleLength++; + } + if (sb.length() > trackTitleLength) { + str.setSpan(new ForegroundColorSpan(0x7fffffff), trackTitleLength, sb.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } + + mAlbumArt.setImageBitmap(mMetadata.bitmap); + final int flags = mTransportControlFlags; + setVisibilityBasedOnFlag(mBtnPrev, flags, RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS); + setVisibilityBasedOnFlag(mBtnNext, flags, RemoteControlClient.FLAG_KEY_MEDIA_NEXT); + setVisibilityBasedOnFlag(mBtnPrev, flags, + RemoteControlClient.FLAG_KEY_MEDIA_PLAY + | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE + | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE + | RemoteControlClient.FLAG_KEY_MEDIA_STOP); + + updatePlayPauseState(mPlayState); } - public void onClick(View v) { - switch (v.getId()) { - case R.id.control_prev: - // TODO + private static void setVisibilityBasedOnFlag(View view, int flags, int flag) { + if ((flags & flag) != 0) { + view.setVisibility(View.VISIBLE); + } else { + view.setVisibility(View.GONE); + } + } + + private void updatePlayPauseState(int state) { + if (DEBUG) Log.v(TAG, + "updatePlayPauseState(), old=" + mPlayState + ", state=" + state); + if (state == mPlayState) { + return; + } + switch (state) { + case RemoteControlClient.PLAYSTATE_PLAYING: + mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_pause); break; - case R.id.control_pauseplay: - // TODO + case RemoteControlClient.PLAYSTATE_BUFFERING: + mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_stop); break; - case R.id.control_next: - // TODO + case RemoteControlClient.PLAYSTATE_PAUSED: + default: + mBtnPlay.setImageResource(com.android.internal.R.drawable.ic_media_play); break; } - // Have any button click extend lockscreen's timeout. - if (mCallback != null) { - mCallback.userActivity(this); + mPlayState = state; + } + + public void onClick(View v) { + int keyCode = -1; + if (v == mBtnPrev) { + keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS; + } else if (v == mBtnNext) { + keyCode = KeyEvent.KEYCODE_MEDIA_NEXT; + } else if (v == mBtnPlay) { + keyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; + + } + if (keyCode != -1) { + sendMediaButtonClick(keyCode); + if (mWidgetCallbacks != null) { + mWidgetCallbacks.userActivity(this); + } } } + private void sendMediaButtonClick(int keyCode) { + // TODO: target to specific player based on mClientName + KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode); + Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); + intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + getContext().sendOrderedBroadcast(intent, null); + + keyEvent = new KeyEvent(KeyEvent.ACTION_UP, keyCode); + intent = new Intent(Intent.ACTION_MEDIA_BUTTON); + intent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); + getContext().sendOrderedBroadcast(intent, null); + } + + public void setCallback(LockScreenWidgetCallback callback) { + mWidgetCallbacks = callback; + } + } diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java index ec926e4..76bc535 100644 --- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java +++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java @@ -16,8 +16,6 @@ package com.android.internal.widget.multiwaveview; -import java.util.ArrayList; - import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; @@ -31,15 +29,20 @@ import android.graphics.Canvas; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Vibrator; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.MotionEvent; import android.view.View; -import android.view.View.MeasureSpec; +import android.view.ViewConfiguration; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import com.android.internal.R; +import java.util.ArrayList; + /** * A special widget containing a center and outer ring. Moving the center ring to the outer ring * causes an event that can be caught by implementing OnTriggerListener. @@ -82,6 +85,8 @@ public class MultiWaveView extends View { private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>(); private ArrayList<Tweener> mChevronAnimations = new ArrayList<Tweener>(); private ArrayList<Tweener> mTargetAnimations = new ArrayList<Tweener>(); + private ArrayList<String> mTargetDescriptions; + private ArrayList<String> mDirectionDescriptions; private Tweener mHandleAnimation; private OnTriggerListener mOnTriggerListener; private TargetDrawable mHandleDrawable; @@ -103,6 +108,9 @@ public class MultiWaveView extends View { private boolean mDragging; private int mNewTargetResources; + private boolean mWaveHovered = false; + private long mLastHoverExitTimeMillis = 0; + private AnimatorListener mResetListener = new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animator) { switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); @@ -128,6 +136,8 @@ public class MultiWaveView extends View { } }; private int mTargetResourceId; + private int mTargetDescriptionsResourceId; + private int mDirectionDescriptionsResourceId; public MultiWaveView(Context context) { this(context, null); @@ -177,6 +187,25 @@ public class MultiWaveView extends View { throw new IllegalStateException("Must specify at least one target drawable"); } + // Read array of target descriptions + if (a.getValue(R.styleable.MultiWaveView_targetDescriptions, outValue)) { + final int resourceId = outValue.resourceId; + if (resourceId == 0) { + throw new IllegalStateException("Must specify target descriptions"); + } + setTargetDescriptionsResourceId(resourceId); + } + + // Read array of direction descriptions + if (a.getValue(R.styleable.MultiWaveView_directionDescriptions, outValue)) { + final int resourceId = outValue.resourceId; + if (resourceId == 0) { + throw new IllegalStateException("Must specify direction descriptions"); + } + setDirectionDescriptionsResourceId(resourceId); + } + + a.recycle(); setVibrateEnabled(mVibrationDuration > 0); } @@ -247,6 +276,9 @@ public class MultiWaveView extends View { showTargets(true); mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE); setGrabbedState(OnTriggerListener.CENTER_HANDLE); + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + announceTargets(); + } break; case STATE_TRACKING: @@ -347,6 +379,13 @@ public class MultiWaveView extends View { } } + private void dispatchGrabbedEvent(int whichHandler) { + vibrate(); + if (mOnTriggerListener != null) { + mOnTriggerListener.onGrabbed(this, whichHandler); + } + } + private void doFinish() { final int activeTarget = mActiveTarget; boolean targetHit = activeTarget != -1; @@ -475,6 +514,7 @@ public class MultiWaveView extends View { Drawable drawable = array.getDrawable(i); targetDrawables.add(new TargetDrawable(res, drawable)); } + array.recycle(); mTargetResourceId = resourceId; mTargetDrawables = targetDrawables; updateTargetPositions(); @@ -499,6 +539,48 @@ public class MultiWaveView extends View { } /** + * Sets the resource id specifying the target descriptions for accessibility. + * + * @param resourceId The resource id. + */ + public void setTargetDescriptionsResourceId(int resourceId) { + mTargetDescriptionsResourceId = resourceId; + if (mTargetDescriptions != null) { + mTargetDescriptions.clear(); + } + } + + /** + * Gets the resource id specifying the target descriptions for accessibility. + * + * @return The resource id. + */ + public int getTargetDescriptionsResourceId() { + return mTargetDescriptionsResourceId; + } + + /** + * Sets the resource id specifying the target direction descriptions for accessibility. + * + * @param resourceId The resource id. + */ + public void setDirectionDescriptionsResourceId(int resourceId) { + mDirectionDescriptionsResourceId = resourceId; + if (mDirectionDescriptions != null) { + mDirectionDescriptions.clear(); + } + } + + /** + * Gets the resource id specifying the target direction descriptions. + * + * @return The resource id. + */ + public int getDirectionDescriptionsResourceId() { + return mDirectionDescriptionsResourceId; + } + + /** * Enable or disable vibrate on touch. * * @param enabled @@ -593,6 +675,43 @@ public class MultiWaveView extends View { } } + @Override + public boolean onHoverEvent(MotionEvent event) { + if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) { + final int action = event.getAction(); + switch (action) { + case MotionEvent.ACTION_HOVER_ENTER: + case MotionEvent.ACTION_HOVER_MOVE: + final float dx = event.getX() - mWaveCenterX; + final float dy = event.getY() - mWaveCenterY; + if (dist2(dx,dy) <= square(mTapRadius)) { + if (!mWaveHovered) { + mWaveHovered = true; + final long timeSinceLastHoverExitMillis = + event.getEventTime() - mLastHoverExitTimeMillis; + final long recurringEventsInterval = + ViewConfiguration.getSendRecurringAccessibilityEventsInterval(); + if (timeSinceLastHoverExitMillis > recurringEventsInterval) { + String text = + mContext.getString(R.string.content_description_sliding_handle); + announceText(text); + } + } + } else { + mWaveHovered = false; + } + break; + case MotionEvent.ACTION_HOVER_EXIT: + mLastHoverExitTimeMillis = event.getEventTime(); + mWaveHovered = false; + break; + default: + mWaveHovered = false; + } + } + return super.onHoverEvent(event); + } + private void handleUp(MotionEvent event) { if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE"); switchToState(STATE_FINISH, event.getX(), event.getY()); @@ -663,7 +782,11 @@ public class MultiWaveView extends View { invalidateGlobalRegion(mHandleDrawable); if (mActiveTarget != activeTarget && activeTarget != -1) { - vibrate(); + dispatchGrabbedEvent(activeTarget); + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + String targetContentDescription = getTargetDescription(activeTarget); + announceText(targetContentDescription); + } } mActiveTarget = activeTarget; } @@ -771,4 +894,62 @@ public class MultiWaveView extends View { return dx*dx + dy*dy; } -}
\ No newline at end of file + private void announceTargets() { + StringBuilder utterance = new StringBuilder(); + final int targetCount = mTargetDrawables.size(); + for (int i = 0; i < targetCount; i++) { + String targetDescription = getTargetDescription(i); + String directionDescription = getDirectionDescription(i); + if (!TextUtils.isEmpty(targetDescription) + && !TextUtils.isEmpty(directionDescription)) { + utterance.append(targetDescription); + utterance.append(" "); + utterance.append(directionDescription); + utterance.append("."); + } + } + announceText(utterance.toString()); + } + + private void announceText(String text) { + setContentDescription(text); + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + setContentDescription(null); + } + + private String getTargetDescription(int index) { + if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) { + mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId); + if (mTargetDrawables.size() != mTargetDescriptions.size()) { + Log.w(TAG, "The number of target drawables must be" + + " euqal to the number of target descriptions."); + return null; + } + } + return mTargetDescriptions.get(index); + } + + private String getDirectionDescription(int index) { + if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) { + mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId); + if (mTargetDrawables.size() != mDirectionDescriptions.size()) { + Log.w(TAG, "The number of target drawables must be" + + " euqal to the number of direction descriptions."); + return null; + } + } + return mDirectionDescriptions.get(index); + } + + private ArrayList<String> loadDescriptions(int resourceId) { + TypedArray array = getContext().getResources().obtainTypedArray(resourceId); + final int count = array.length(); + ArrayList<String> targetContentDescriptions = new ArrayList<String>(count); + for (int i = 0; i < count; i++) { + String contentDescription = array.getString(i); + targetContentDescriptions.add(contentDescription); + } + array.recycle(); + return targetContentDescriptions; + } +} diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 4cf4afa..494a2b3 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -119,6 +119,11 @@ static struct binderproxy_offsets_t } gBinderProxyOffsets; +static struct class_offsets_t +{ + jmethodID mGetName; +} gClassOffsets; + // ---------------------------------------------------------------------------- static struct parcel_offsets_t @@ -452,22 +457,18 @@ public: // Okay, something is wrong -- we have a hard reference to a live death // recipient on the VM side, but the list is being torn down. JNIEnv* env = javavm_to_jnienv(mVM); - ScopedLocalRef<jclass> classRef(env, env->GetObjectClass(mObject)); - jmethodID getnameMethod = env->GetMethodID(classRef.get(), - "getName", "()Ljava/lang/String;"); - if (getnameMethod) { - ScopedLocalRef<jstring> nameRef(env, - (jstring) env->CallObjectMethod(classRef.get(), getnameMethod)); - ScopedUtfChars nameUtf(env, nameRef.get()); - if (nameUtf.c_str() != NULL) { - LOGW("BinderProxy is being destroyed but the application did not call " - "unlinkToDeath to unlink all of its death recipients beforehand. " - "Releasing leaked death recipient: %s", nameUtf.c_str()); - } else { - LOGW("BinderProxy being destroyed; unable to get DR object name"); - env->ExceptionClear(); - } - } else LOGW("BinderProxy being destroyed; unable to find DR class getName"); + ScopedLocalRef<jclass> objClassRef(env, env->GetObjectClass(mObject)); + ScopedLocalRef<jstring> nameRef(env, + (jstring) env->CallObjectMethod(objClassRef.get(), gClassOffsets.mGetName)); + ScopedUtfChars nameUtf(env, nameRef.get()); + if (nameUtf.c_str() != NULL) { + LOGW("BinderProxy is being destroyed but the application did not call " + "unlinkToDeath to unlink all of its death recipients beforehand. " + "Releasing leaked death recipient: %s", nameUtf.c_str()); + } else { + LOGW("BinderProxy being destroyed; unable to get DR object name"); + env->ExceptionClear(); + } } } @@ -1230,6 +1231,11 @@ static int int_register_android_os_BinderProxy(JNIEnv* env) = env->GetFieldID(clazz, "mOrgue", "I"); assert(gBinderProxyOffsets.mOrgue); + clazz = env->FindClass("java/lang/Class"); + LOG_FATAL_IF(clazz == NULL, "Unable to find java.lang.Class"); + gClassOffsets.mGetName = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;"); + assert(gClassOffsets.mGetName); + return AndroidRuntime::registerNativeMethods( env, kBinderProxyPathName, gBinderProxyMethods, NELEM(gBinderProxyMethods)); diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 4c1ca31..49441eb 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -589,7 +589,7 @@ static void Surface_setLayer( } static void Surface_setPosition( - JNIEnv* env, jobject clazz, jint x, jint y) + JNIEnv* env, jobject clazz, jfloat x, jfloat y) { const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz)); if (surface == 0) return; @@ -832,7 +832,7 @@ static JNINativeMethod gSurfaceMethods[] = { {"screenshot", "(II)Landroid/graphics/Bitmap;", (void*)Surface_screenshotAll }, {"screenshot", "(IIII)Landroid/graphics/Bitmap;", (void*)Surface_screenshot }, {"setLayer", "(I)V", (void*)Surface_setLayer }, - {"setPosition", "(II)V",(void*)Surface_setPosition }, + {"setPosition", "(FF)V",(void*)Surface_setPosition }, {"setSize", "(II)V",(void*)Surface_setSize }, {"hide", "()V", (void*)Surface_hide }, {"show", "()V", (void*)Surface_show }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 540c65a..01f2a8f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -93,6 +93,9 @@ <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" /> + <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" /> + <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" /> + <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" /> <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" /> <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" /> diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png Binary files differindex 81829bb..769463b 100644 --- a/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_bottom_solid_dark_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png Binary files differindex 0436801..88f11dc 100644 --- a/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_bottom_solid_inverse_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png Binary files differindex 6574c8c..7305047 100644 --- a/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_bottom_solid_light_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png Binary files differindex 1565ac2..712a551 100644 --- a/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_bottom_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png b/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png Binary files differindex 77d4c4b..bf3b943 100644 --- a/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_bottom_transparent_light_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png Binary files differindex 0fc8632..cbbaec5 100644 --- a/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_solid_dark_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png b/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png Binary files differindex 74540c1..af917e5 100644 --- a/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_solid_light_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png b/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png Binary files differindex 69bcd7a..2d59f35 100644 --- a/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_solid_shadow_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png Binary files differindex 9f8829f..0520e5a 100644 --- a/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_stacked_solid_dark_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png Binary files differindex d63e85e..42528b1 100644 --- a/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_stacked_solid_inverse_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png Binary files differindex 9cff5d8..e3e3f93 100644 --- a/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_stacked_solid_light_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png Binary files differindex 12a6454..1e39572 100644 --- a/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_stacked_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png b/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png Binary files differindex 3355d30..a16db85 100644 --- a/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_stacked_transparent_light_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png b/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png Binary files differindex a5e8570..0eff695 100644 --- a/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png b/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png Binary files differindex c49412a..219b170 100644 --- a/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png +++ b/core/res/res/drawable-hdpi/ab_transparent_light_holo.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png Binary files differindex fabc252..0d165bb 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png Binary files differindex f59ef4e..73c7e25 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_default_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png Binary files differindex c87c5a6..1459eee 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png Binary files differindex 6e4cae2..04de530 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_focused_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png Binary files differindex e6f83cc..bab70fa 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png Binary files differindex 5c97e3f..b46adfa 100644 --- a/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png +++ b/core/res/res/drawable-hdpi/btn_cab_done_pressed_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png Binary files differindex 269def4..1d836f6 100644 --- a/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/cab_background_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png b/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png Binary files differindex 49bcfb4..5818666 100644 --- a/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png +++ b/core/res/res/drawable-hdpi/cab_background_bottom_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png Binary files differindex b7ddc14..564fb34 100644 --- a/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png +++ b/core/res/res/drawable-hdpi/cab_background_top_holo_dark.9.png diff --git a/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png Binary files differindex af80ad5..ae21b76 100644 --- a/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png +++ b/core/res/res/drawable-hdpi/cab_background_top_holo_light.9.png diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png b/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png Binary files differnew file mode 100644 index 0000000..b187358 --- /dev/null +++ b/core/res/res/drawable-hdpi/ic_lockscreen_player_background.9.png diff --git a/core/res/res/drawable-hdpi/ic_media_embed_play.png b/core/res/res/drawable-hdpi/ic_media_embed_play.png Binary files differindex 05778c1..23ac7e4 100644 --- a/core/res/res/drawable-hdpi/ic_media_embed_play.png +++ b/core/res/res/drawable-hdpi/ic_media_embed_play.png diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png Binary files differindex 1e69eac..5f2f604 100644 --- a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png +++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png diff --git a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png Binary files differindex 2f6accc..7f3459c 100644 --- a/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png +++ b/core/res/res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_light.png diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png Binary files differindex b3d51ed..b229367 100644 --- a/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_bottom_solid_dark_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png Binary files differindex abae537..c65f443 100644 --- a/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_bottom_solid_inverse_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png Binary files differindex 4c98afb..0706c8a 100644 --- a/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_bottom_solid_light_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png Binary files differindex de8010a..d814d02 100644 --- a/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_bottom_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png b/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png Binary files differindex ecb2a0e..b139c8e 100644 --- a/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_bottom_transparent_light_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png Binary files differindex 56d27a8..743d00b 100644 --- a/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_solid_dark_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png b/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png Binary files differindex 98b4956..17c1fb9 100644 --- a/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_solid_light_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png b/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png Binary files differindex dcd3703..ddfc8e3 100644 --- a/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_solid_shadow_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png Binary files differindex aa0a3a0..007a4b2 100644 --- a/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_stacked_solid_dark_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png Binary files differindex cf17967..a823841 100644 --- a/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_stacked_solid_inverse_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png Binary files differindex ab0003b..ad6e1a4 100644 --- a/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_stacked_solid_light_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png Binary files differindex 5f1eb1e..0ad6c88 100644 --- a/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_stacked_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png b/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png Binary files differindex 89822b6..19b50ab 100644 --- a/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_stacked_transparent_light_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png b/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png Binary files differindex bd9921f..ad980b1 100644 --- a/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png b/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png Binary files differindex 8d93926..60e6c52 100644 --- a/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png +++ b/core/res/res/drawable-mdpi/ab_transparent_light_holo.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png Binary files differindex d00d235..9e936b3 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png Binary files differindex d7b2049..0360104 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_default_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png Binary files differindex 59c1580..dd947d2 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png Binary files differindex 143564a..51cfca2 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_focused_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png Binary files differindex e5b9cfd..fd6e6c7 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png Binary files differindex ec43f85..5db212c 100644 --- a/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png +++ b/core/res/res/drawable-mdpi/btn_cab_done_pressed_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png Binary files differindex 0e4b28e..d8f1c8b 100644 --- a/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/cab_background_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png b/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png Binary files differindex e449090..31e4989 100644 --- a/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png +++ b/core/res/res/drawable-mdpi/cab_background_bottom_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png Binary files differindex d0ebc1a..7c2cbe5 100644 --- a/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png +++ b/core/res/res/drawable-mdpi/cab_background_top_holo_dark.9.png diff --git a/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png Binary files differindex c2dd608..30cbdc1 100644 --- a/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png +++ b/core/res/res/drawable-mdpi/cab_background_top_holo_light.9.png diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png b/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png Binary files differnew file mode 100644 index 0000000..8cfd1af --- /dev/null +++ b/core/res/res/drawable-mdpi/ic_lockscreen_player_background.9.png diff --git a/core/res/res/drawable-mdpi/ic_media_embed_play.png b/core/res/res/drawable-mdpi/ic_media_embed_play.png Binary files differindex 3576ce5..fc5d8c6 100644 --- a/core/res/res/drawable-mdpi/ic_media_embed_play.png +++ b/core/res/res/drawable-mdpi/ic_media_embed_play.png diff --git a/core/res/res/drawable-mdpi/ic_media_ff.png b/core/res/res/drawable-mdpi/ic_media_ff.png Binary files differindex 170dd2d..892772e 100644 --- a/core/res/res/drawable-mdpi/ic_media_ff.png +++ b/core/res/res/drawable-mdpi/ic_media_ff.png diff --git a/core/res/res/drawable-mdpi/ic_media_fullscreen.png b/core/res/res/drawable-mdpi/ic_media_fullscreen.png Binary files differindex 960aa85..1c60e15 100644 --- a/core/res/res/drawable-mdpi/ic_media_fullscreen.png +++ b/core/res/res/drawable-mdpi/ic_media_fullscreen.png diff --git a/core/res/res/drawable-mdpi/ic_media_next.png b/core/res/res/drawable-mdpi/ic_media_next.png Binary files differindex a6feed0..bbe311b 100644 --- a/core/res/res/drawable-mdpi/ic_media_next.png +++ b/core/res/res/drawable-mdpi/ic_media_next.png diff --git a/core/res/res/drawable-mdpi/ic_media_pause.png b/core/res/res/drawable-mdpi/ic_media_pause.png Binary files differindex 548ba02..e4e8d86 100644 --- a/core/res/res/drawable-mdpi/ic_media_pause.png +++ b/core/res/res/drawable-mdpi/ic_media_pause.png diff --git a/core/res/res/drawable-mdpi/ic_media_play.png b/core/res/res/drawable-mdpi/ic_media_play.png Binary files differindex 0fe6806..8eaf962 100644 --- a/core/res/res/drawable-mdpi/ic_media_play.png +++ b/core/res/res/drawable-mdpi/ic_media_play.png diff --git a/core/res/res/drawable-mdpi/ic_media_previous.png b/core/res/res/drawable-mdpi/ic_media_previous.png Binary files differindex 0163d09..e9abc7f 100644 --- a/core/res/res/drawable-mdpi/ic_media_previous.png +++ b/core/res/res/drawable-mdpi/ic_media_previous.png diff --git a/core/res/res/drawable-mdpi/ic_media_rew.png b/core/res/res/drawable-mdpi/ic_media_rew.png Binary files differindex 5489180..a5eb94a 100644 --- a/core/res/res/drawable-mdpi/ic_media_rew.png +++ b/core/res/res/drawable-mdpi/ic_media_rew.png diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png Binary files differindex 135ca6e..1d2592a 100644 --- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png +++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png diff --git a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png Binary files differindex ccbf143..4060afe 100644 --- a/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png +++ b/core/res/res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_light.png diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png Binary files differindex 506cd68..5753346 100644 --- a/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_bottom_solid_dark_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png Binary files differindex c185112..7e6c047 100644 --- a/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_bottom_solid_inverse_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png Binary files differindex 8ed7f9b..8155fe8 100644 --- a/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_bottom_solid_light_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png Binary files differindex c4694cd..6cee9a1 100644 --- a/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_bottom_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png Binary files differindex 57f5cfa..fa4d76a 100644 --- a/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_bottom_transparent_light_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png Binary files differindex d16e50c..6622cba 100644 --- a/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_solid_dark_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png Binary files differindex 45cc807..c427297 100644 --- a/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_solid_light_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png b/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png Binary files differindex a9e0d4d..d0df29d 100644 --- a/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_solid_shadow_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png Binary files differindex 5ea235d..a0d9c1b 100644 --- a/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_stacked_solid_dark_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png Binary files differindex 0220c8d..16b9bef 100644 --- a/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_stacked_solid_inverse_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png Binary files differindex be13077..d36f99f 100644 --- a/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_stacked_solid_light_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png Binary files differindex ab02e7a..5ad475d 100644 --- a/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_stacked_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png Binary files differindex 0b5a24e..6ade5ee 100644 --- a/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_stacked_transparent_light_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png b/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png Binary files differindex 6d21429..719b923 100644 --- a/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_transparent_dark_holo.9.png diff --git a/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png b/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png Binary files differindex c34c46f..6da264d 100644 --- a/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png +++ b/core/res/res/drawable-xhdpi/ab_transparent_light_holo.9.png diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png Binary files differindex a338924..01efef4 100644 --- a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png Binary files differindex 6f2d3e5..c287605 100644 --- a/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_cab_done_default_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png Binary files differindex faa36b0..9a496e8 100644 --- a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png Binary files differindex c57f3ff..e2a38b4 100644 --- a/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_cab_done_focused_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png Binary files differindex 9d6402f..911722f 100644 --- a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png Binary files differindex bfd966c..da169bf 100644 --- a/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/btn_cab_done_pressed_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png Binary files differindex 2bd8cee..0bd0980 100644 --- a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png Binary files differindex d254e77..43ed26d 100644 --- a/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/cab_background_bottom_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png Binary files differindex 919eac0..6b31579 100644 --- a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png +++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png Binary files differindex 1357105..df0121b 100644 --- a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png +++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png b/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png Binary files differnew file mode 100644 index 0000000..7fb0cbc --- /dev/null +++ b/core/res/res/drawable-xhdpi/ic_lockscreen_player_background.9.png diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png Binary files differindex 81306ca..1ce25e2 100644 --- a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png +++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png diff --git a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png Binary files differindex 1f46e7a..dcd6514 100644 --- a/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png +++ b/core/res/res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_light.png diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml index f3850d5..3a7c1e1 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml @@ -42,11 +42,8 @@ <com.android.internal.widget.DigitalClock android:id="@+id/time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentTop="true" - android:layout_alignParentLeft="true" android:layout_marginTop="8dip" android:layout_marginBottom="8dip" - android:layout_marginLeft="-10dip" > <!-- Because we can't have multi-tone fonts, we render two TextViews, one on diff --git a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml index f4c99f1..c02341e 100644 --- a/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml +++ b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml @@ -45,8 +45,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="8dip" - android:layout_marginBottom="8dip" - android_layout_marginLeft="-10dip"> + android:layout_marginBottom="8dip"> <!-- Because we can't have multi-tone fonts, we render two TextViews, one on top of the other. Hence the redundant layout... --> diff --git a/core/res/res/layout/keyguard_screen_password_landscape.xml b/core/res/res/layout/keyguard_screen_password_landscape.xml index 12df99e..694db50 100644 --- a/core/res/res/layout/keyguard_screen_password_landscape.xml +++ b/core/res/res/layout/keyguard_screen_password_landscape.xml @@ -186,6 +186,8 @@ android:layout_rowSpan="6" android:layout_columnSpan="1" android:layout_gravity="fill" + android:layout_width="0dip" + android:layout_height="0dip" /> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_password_portrait.xml b/core/res/res/layout/keyguard_screen_password_portrait.xml index 6145e47..cf3bd42 100644 --- a/core/res/res/layout/keyguard_screen_password_portrait.xml +++ b/core/res/res/layout/keyguard_screen_password_portrait.xml @@ -174,6 +174,8 @@ android:layout_rowSpan="3" android:layout_columnSpan="1" android:layout_gravity="fill" + android:layout_width="0dip" + android:layout_height="0dip" /> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml index 6016d4e..4c8c0d1 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml @@ -130,6 +130,8 @@ android:layout_alignParentBottom="true" android:targetDrawables="@array/lockscreen_targets_with_camera" + android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera" + android:directionDescriptions="@array/lockscreen_direction_descriptions_with_camera" android:handleDrawable="@drawable/ic_lockscreen_handle" android:waveDrawable="@drawable/ic_lockscreen_outerring" android:outerRadius="@dimen/multiwaveview_target_placement_radius" @@ -188,6 +190,8 @@ android:layout_rowSpan="4" android:layout_columnSpan="1" android:layout_gravity="fill" + android:layout_width="0dip" + android:layout_height="0dip" /> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml index 0568dd9..ba55f0c0 100644 --- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml @@ -135,6 +135,8 @@ android:layout_rowSpan="7" android:targetDrawables="@array/lockscreen_targets_with_camera" + android:targetDescriptions="@array/lockscreen_target_descriptions_with_camera" + android:directionDescriptions="@array/lockscreen_direction_descriptions_with_camera" android:handleDrawable="@drawable/ic_lockscreen_handle" android:waveDrawable="@drawable/ic_lockscreen_outerring" android:outerRadius="@dimen/multiwaveview_target_placement_radius" @@ -155,6 +157,8 @@ android:layout_rowSpan="5" android:layout_columnSpan="1" android:layout_gravity="fill" + android:layout_width="0dip" + android:layout_height="0dip" /> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_landscape.xml b/core/res/res/layout/keyguard_screen_unlock_landscape.xml index 9b28731..d71dbff 100644 --- a/core/res/res/layout/keyguard_screen_unlock_landscape.xml +++ b/core/res/res/layout/keyguard_screen_unlock_landscape.xml @@ -156,6 +156,8 @@ android:layout_rowSpan="5" android:layout_columnSpan="1" android:layout_gravity="fill" + android:layout_width="0dip" + android:layout_height="0dip" /> </GridLayout> diff --git a/core/res/res/layout/keyguard_screen_unlock_portrait.xml b/core/res/res/layout/keyguard_screen_unlock_portrait.xml index 433dda7..64c479f 100644 --- a/core/res/res/layout/keyguard_screen_unlock_portrait.xml +++ b/core/res/res/layout/keyguard_screen_unlock_portrait.xml @@ -167,6 +167,8 @@ android:layout_rowSpan="4" android:layout_columnSpan="1" android:layout_gravity="fill" + android:layout_width="0dip" + android:layout_height="0dip" /> </GridLayout> diff --git a/core/res/res/layout/keyguard_transport_control.xml b/core/res/res/layout/keyguard_transport_control.xml index 6308b02..2ebe5fc 100644 --- a/core/res/res/layout/keyguard_transport_control.xml +++ b/core/res/res/layout/keyguard_transport_control.xml @@ -1,53 +1,102 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2008, 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) 2011 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. --> +<!-- Note: This file is meant to be included in various password unlock screens. As such, + LayoutParams (layout_*) for TransportControlView should *NOT* be specified here, + but rather as include tags for this file or the layout will break. --> <com.android.internal.widget.TransportControlView xmlns:android="http://schemas.android.com/apk/res/android" - android:gravity="bottom" - android:orientation="horizontal" - android:background="#a0808080" - android:visibility="gone"> + android:id="@+id/transport_controls" + android:background="@drawable/ic_lockscreen_player_background"> - <Button android:id="@+id/control_prev" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="#80ff0000" + <ImageView + android:id="@+id/albumart" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="fill" + android:scaleType="centerCrop" + android:adjustViewBounds="false" /> - <Space android:layout_width="0dip" - android:layout_height="0dip" - android:layout_weight="1"/> - - <Button android:id="@+id/control_pauseplay" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="#8000ff00" - /> - - <Space android:layout_width="0dip" - android:layout_height="0dip" - android:layout_weight="1"/> - - <Button android:id="@+id/control_next" - android:layout_width="wrap_content" + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="#800000ff" + android:background="#c8000000" + android:layout_gravity="bottom"> + <TextView + android:id="@+id/title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="8dip" + android:layout_marginLeft="16dip" + android:layout_marginRight="16dip" + android:gravity="center_horizontal" + android:singleLine="true" + android:ellipsize="end" + android:textAppearance="?android:attr/textAppearanceMedium" /> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_marginTop="5dip"> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + <ImageView + android:id="@+id/btn_prev" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:src="@drawable/ic_media_rew" + android:clickable="true" + android:background="?android:attr/selectableItemBackground" + android:padding="10dip"/> + </FrameLayout> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + <ImageView + android:id="@+id/btn_play" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:clickable="true" + android:src="@drawable/ic_media_play" + android:background="?android:attr/selectableItemBackground" + android:padding="10dip"/> + </FrameLayout> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + <ImageView + android:id="@+id/btn_next" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:clickable="true" + android:src="@drawable/ic_media_ff" + android:background="?android:attr/selectableItemBackground" + android:padding="10dip"/> + </FrameLayout> + </LinearLayout> + </LinearLayout> </com.android.internal.widget.TransportControlView> diff --git a/core/res/res/values-land/arrays.xml b/core/res/res/values-land/arrays.xml index fd492ec..57aafc8 100644 --- a/core/res/res/values-land/arrays.xml +++ b/core/res/res/values-land/arrays.xml @@ -27,13 +27,41 @@ <item>@drawable/ic_lockscreen_soundon</item> </array> + <array name="lockscreen_target_descriptions_when_silent"> + <item>@null</item> + <item>@string/description_target_unlock</item> + <item>@null</item> + <item>@string/description_target_soundon</item> + </array> + + <array name="lockscreen_direction_descriptions_when_silent"> + <item>@null</item> + <item>@string/description_direction_up</item> + <item>@null</item> + <item>@string/description_direction_down</item> + </array> + <array name="lockscreen_targets_when_soundon"> - <item>@null</item>" + <item>@null</item> <item>@drawable/ic_lockscreen_unlock</item> <item>@null</item> <item>@drawable/ic_lockscreen_silent</item> </array> + <array name="lockscreen_target_descriptions_when_soundon"> + <item>@null</item> + <item>@string/description_target_unlock</item> + <item>@null</item> + <item>@string/description_target_silent</item> + </array> + + <array name="lockscreen_direction_descriptions_when_soundon"> + <item>@null</item> + <item>@string/description_direction_up</item> + <item>@null</item> + <item>@string/description_direction_down</item> + </array> + <array name="lockscreen_targets_with_camera"> <item>@null</item> <item>@drawable/ic_lockscreen_unlock</item> @@ -41,4 +69,18 @@ <item>@drawable/ic_lockscreen_camera</item> </array> + <array name="lockscreen_target_descriptions_with_camera"> + <item>@null</item> + <item>@string/description_target_unlock</item> + <item>@null</item> + <item>@string/description_target_camera</item> + </array> + + <array name="lockscreen_direction_descriptions_with_camera"> + <item>@null</item> + <item>@string/description_direction_up</item> + <item>@null</item> + <item>@string/description_direction_down</item> + </array> + </resources> diff --git a/core/res/res/values-sw600dp/colors.xml b/core/res/res/values-sw600dp/colors.xml index 6b5a55a..edd2712 100644 --- a/core/res/res/values-sw600dp/colors.xml +++ b/core/res/res/values-sw600dp/colors.xml @@ -21,7 +21,7 @@ <!-- keyguard clock --> <color name="lockscreen_clock_background">#b3ffffff</color> <color name="lockscreen_clock_foreground">#7affffff</color> - <color name="lockscreen_clock_am_pm">#ff9a9a9a</color> + <color name="lockscreen_clock_am_pm">#ffffffff</color> <color name="lockscreen_owner_info">#ff9a9a9a</color> </resources> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 57e9bbf..c9043ba 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -349,18 +349,60 @@ <item>@null</item> </array> + <array name="lockscreen_target_descriptions_when_silent"> + <item>@string/description_target_unlock</item> + <item>@null</item> + <item>@string/description_target_soundon</item> + <item>@null</item> + </array> + + <array name="lockscreen_direction_descriptions_when_silent"> + <item>@string/description_direction_right</item> + <item>@null</item> + <item>@string/description_direction_left</item> + <item>@null</item> + </array> + <array name="lockscreen_targets_when_soundon"> <item>@drawable/ic_lockscreen_unlock</item> <item>@null</item> <item>@drawable/ic_lockscreen_silent</item> - <item>@null</item>" + <item>@null</item> + </array> + + <array name="lockscreen_target_descriptions_when_soundon"> + <item>@string/description_target_unlock</item> + <item>@null</item> + <item>@string/description_target_silent</item> + <item>@null</item> + </array> + + <array name="lockscreen_direction_descriptions_when_soundon"> + <item>@string/description_direction_right</item> + <item>@null</item> + <item>@string/description_direction_left</item> + <item>@null</item> </array> <array name="lockscreen_targets_with_camera"> <item>@drawable/ic_lockscreen_unlock</item> <item>@null</item> <item>@drawable/ic_lockscreen_camera</item> - <item>@null</item>" + <item>@null</item> + </array> + + <array name="lockscreen_target_descriptions_with_camera"> + <item>@string/description_target_unlock</item> + <item>@null</item> + <item>@string/description_target_camera</item> + <item>@null</item> + </array> + + <array name="lockscreen_direction_descriptions_with_camera"> + <item>@string/description_direction_right</item> + <item>@null</item> + <item>@string/description_direction_left</item> + <item>@null</item> </array> </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index dae9f70..fed5651 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -158,11 +158,14 @@ <!-- The underline color and thickness for misspelled suggestion --> <attr name="textAppearanceMisspelledSuggestion" format="reference" /> + <!-- The text color and the background for suggestion range span. This span identifies the + portion of text the suggestions refer to). --> + <attr name="textAppearanceSuggestionRange" format="reference" /> + <!-- The underline color --> <attr name="textUnderlineColor" format="reference|color" /> - <!-- The underline thickness, expressed as a percentage of the default underline thickness - (i.e., 100 means default thickness, and 200 means double thickness). --> - <attr name="textUnderlineThicknessPercentage" format="reference|integer" /> + <!-- The underline thickness --> + <attr name="textUnderlineThickness" format="reference|dimension" /> <!-- EditText text foreground color. --> <attr name="editTextColor" format="reference|color" /> @@ -3149,7 +3152,11 @@ </declare-styleable> <declare-styleable name="SuggestionSpan"> <attr name="textUnderlineColor" /> - <attr name="textUnderlineThicknessPercentage" /> + <attr name="textUnderlineThickness" /> + </declare-styleable> + <declare-styleable name="SuggestionRangeSpan"> + <attr name="textColor" /> + <attr name="colorBackground" /> </declare-styleable> <!-- An <code>input-extras</code> is a container for extra data to supply to an input method. Contains @@ -5169,6 +5176,12 @@ <!-- Reference to an array resource that be shown as targets around a circle. --> <attr name="targetDrawables" format="reference"/> + <!-- Reference to an array resource that be used as description for the targets around the circle. --> + <attr name="targetDescriptions" format="reference"/> + + <!-- Reference to an array resource that be used to announce the directions with targets around the circle. --> + <attr name="directionDescriptions" format="reference"/> + <!-- Sets a drawable as the drag center. --> <attr name="handleDrawable" format="reference" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b155b80..051ed14 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -189,6 +189,9 @@ <!-- Boolean indicating whether the wifi chipset has p2p support --> <bool translatable="false" name="config_wifi_p2p_support">false</bool> + <!-- Device type information conforming to Annex B format in WiFi Direct specification. + The default represents a dual-mode smartphone --> + <string translatable="false" name="config_wifi_p2p_device_type">10-0050F204-5</string> <!-- Boolean indicating whether the wifi chipset supports background scanning mechanism. This mechanism allows the host to remain in suspend state and the dongle to actively diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index fcd3bba..6988f6b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2006,4 +2006,8 @@ <public type="color" name="holo_orange_dark" /> <public type="color" name="holo_purple" /> <public type="color" name="holo_blue_bright" /> + + <public type="attr" name="targetDescriptions" /> + <public type="attr" name="directionDescriptions" /> + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 8e0f300..a9e2971 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1763,7 +1763,7 @@ --> <skip /> <!-- On the keyguard screen, it shows the carrier the phone is connected to. This is displayed if the phone is not connected to a carrier.--> - <string name="lockscreen_carrier_default">(No service)</string> + <string name="lockscreen_carrier_default">No service.</string> <!-- Shown in the lock screen to tell the user that the screen is locked. --> <string name="lockscreen_screen_locked">Screen locked.</string> @@ -2472,6 +2472,9 @@ <!-- Item on EditText context menu. This action is used to replace the current word by other suggested words, suggested by the IME or the spell checker --> <string name="replace">Replace\u2026</string> + <!-- Item on EditText pop-up window. This action is used to delete the text that the user recently added. [CHAR LIMIT=15] --> + <string name="delete">Delete</string> + <!-- Item on EditText context menu. This action is used to copy a URL from the edit field into the clipboard. --> <string name="copyUrl">Copy URL</string> @@ -3129,6 +3132,29 @@ <!-- Description of the Enter button in a KeyboardView. [CHAR LIMIT=NONE] --> <string name="keyboardview_keycode_enter">Enter</string> + <!-- Slide lock screen --> + + <!-- Description of the sliding handle in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="content_description_sliding_handle">"Sliding handle. Tap and hold."</string> + + <!-- Description of the up direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_direction_up">"Up</string> + <!-- Description of the down direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_direction_down">Down</string> + <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_direction_left">"Left</string> + <!-- Description of the right direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_direction_right">Right</string> + + <!-- Description of the unlock target in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_target_unlock">Unlock</string> + <!-- Description of the camera target in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_target_camera">Camera</string> + <!-- Description of the silent target in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_target_silent">Silent</string> + <!-- Description of the sound on target in the Slide unlock screen. [CHAR LIMIT=NONE] --> + <string name="description_target_soundon">Sound on</string> + <!-- Announce that a headset is required to hear keyboard keys while typing a password. [CHAR LIMIT=NONE] --> <string name="keyboard_headset_required_to_hear_password">Key. Headset required to hear keys while typing a password.</string> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 052a040..1a2ad05 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -244,7 +244,7 @@ please see styles_device_defaults.xml. </style> <style name="TextAppearance.Suggestion"> - <item name="android:textUnderlineThicknessPercentage">200</item> + <item name="android:textUnderlineThickness">2dip</item> </style> <style name="TextAppearance.EasyCorrectSuggestion" parent="TextAppearance.Suggestion"> @@ -255,6 +255,11 @@ please see styles_device_defaults.xml. <item name="android:textUnderlineColor">@color/holo_red_light</item> </style> + <style name="TextAppearance.SuggestionRange"> + <item name="android:textColor">@color/white</item> + <item name="android:colorBackground">@color/holo_blue_dark</item> + </style> + <!-- Widget Styles --> <style name="Widget"> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index f434ce8..899c9d5 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -91,6 +91,7 @@ please see themes_device_defaults.xml. <item name="textAppearanceEasyCorrectSuggestion">@android:style/TextAppearance.EasyCorrectSuggestion</item> <item name="textAppearanceMisspelledSuggestion">@android:style/TextAppearance.MisspelledSuggestion</item> + <item name="textAppearanceSuggestionRange">@android:style/TextAppearance.SuggestionRange</item> <item name="textAppearanceButton">@android:style/TextAppearance.Widget.Button</item> diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf Binary files differindex 03ceae5..ba9d76f 100644 --- a/data/fonts/DroidSansFallback.ttf +++ b/data/fonts/DroidSansFallback.ttf diff --git a/data/fonts/DroidSansFallbackFull.ttf b/data/fonts/DroidSansFallbackFull.ttf Binary files differnew file mode 100644 index 0000000..03ceae5 --- /dev/null +++ b/data/fonts/DroidSansFallbackFull.ttf diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index d8c1fa2..cacbdaa 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -15,10 +15,10 @@ # Warning: this is actually a product definition, to be inherited from PRODUCT_COPY_FILES := \ - frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \ - frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \ - frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \ - frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \ + frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \ + frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \ + frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \ + frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \ frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \ frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \ frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \ diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd index 97df84f..f87e1f6 100644 --- a/docs/html/sdk/ndk/index.jd +++ b/docs/html/sdk/ndk/index.jd @@ -1,16 +1,16 @@ ndk=true -ndk.win_download=android-ndk-r6-windows.zip -ndk.win_bytes=67642809 -ndk.win_checksum=9c7d5ccc02151a3e5e950c70dc05ac6d +ndk.win_download=android-ndk-r6b-windows.zip +ndk.win_bytes=67670219 +ndk.win_checksum=f496b48fffb6d341303de170a081b812 -ndk.mac_download=android-ndk-r6-darwin-x86.tar.bz2 -ndk.mac_bytes=52682746 -ndk.mac_checksum=a154905e49a6246abd823b75b6eda738 +ndk.mac_download=android-ndk-r6b-darwin-x86.tar.bz2 +ndk.mac_bytes=52798843 +ndk.mac_checksum=65f2589ac1b08aabe3183f9ed1a8ce8e -ndk.linux_download=android-ndk-r6-linux-x86.tar.bz2 -ndk.linux_bytes=46425290 -ndk.linux_checksum=ff0a43085fe206696d5cdcef3f4f4637 +ndk.linux_download=android-ndk-r6b-linux-x86.tar.bz2 +ndk.linux_bytes=46532436 +ndk.linux_checksum=309f35e49b64313cfb20ac428df4cec2 page.title=Android NDK @jd:body @@ -58,10 +58,42 @@ padding: .25em 1em; } </style> - <div class="toggleable open"> <a href="#" onclick="return toggleDiv(this)"><img src= "{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px"> + Android NDK, Revision 6b</a> <em>(August 2011)</em> + + <div class="toggleme"> + <p>This release of the NDK does not include any new features compared to r6. The r6b release + addresses the following issues in the r6 release:</p> + <dl> + <dt>Important bug fixes</dt> + <dd> + <ul> + <li>Fixed the build when <code>APP_ABI="armeabi x86"</code> is used for + multi-architecture builds.</li> + <li>Fixed the location of prebuilt STLport binaries in the NDK release package. + A bug in the packaging script placed them in the wrong location.</li> + <li>Fixed <code>atexit()</code> usage in shared libraries with the x86standalone + toolchain.</li> + <li>Fixed <code>make-standalone-toolchain.sh --arch=x86</code>. It used to fail + to copy the proper GNU libstdc++ binaries to the right location.</li> + <li>Fixed the standalone toolchain linker warnings about missing the definition and + size for the <code>__dso_handle</code> symbol (ARM only).</li> + <li>Fixed the inclusion order of <code>$(SYSROOT)/usr/include</code> for x86 builds. + See the <a href="http://code.google.com/p/android/issues/detail?id=18540">bug</a> for + more information.</li> + <li>Fixed the definitions of <code>ptrdiff_t</code> and <code>size_t</code> in + x86-specific systems when they are used with the x86 standalone toolchain.</li> + </ul> + </dd> + </dl> + </div> +</div> + +<div class="toggleable closed"> + <a href="#" onclick="return toggleDiv(this)"><img src= + "{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px"> Android NDK, Revision 6</a> <em>(July 2011)</em> <div class="toggleme"> diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 1b1fc8d..a00ca12 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -183,7 +183,7 @@ r3</a> <span class="new">new!</span></li> <span style="display:none" class="zh-TW"></span> </h2> <ul> - <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r6</a> + <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r6b</a> <span class="new">new!</span> </li> <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li> diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index b4d94f3..896f81e 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1349,7 +1349,7 @@ public class Paint { if (text == null) { throw new IllegalArgumentException("text cannot be null"); } - if ((index | count) < 0 || index + count > text.length) { + if (index < 0 || text.length - index < Math.abs(count)) { throw new ArrayIndexOutOfBoundsException(); } diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java index 94a8488..7c4e147 100644 --- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java @@ -55,7 +55,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac private void init() { final AnimatedRotateState state = mState; - mIncrement = 360.0f / (float) state.mFramesCount; + mIncrement = 360.0f / state.mFramesCount; final Drawable drawable = state.mDrawable; if (drawable != null) { drawable.setFilterBitmap(true); @@ -65,6 +65,7 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } } + @Override public void draw(Canvas canvas) { int saveCount = canvas.save(); @@ -146,14 +147,17 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac | mState.mDrawable.getChangingConfigurations(); } + @Override public void setAlpha(int alpha) { mState.mDrawable.setAlpha(alpha); } + @Override public void setColorFilter(ColorFilter cf) { mState.mDrawable.setColorFilter(cf); } + @Override public int getOpacity() { return mState.mDrawable.getOpacity(); } @@ -228,10 +232,10 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac tv = a.peekValue(R.styleable.AnimatedRotateDrawable_pivotY); final boolean pivotYRel = tv.type == TypedValue.TYPE_FRACTION; final float pivotY = pivotYRel ? tv.getFraction(1.0f, 1.0f) : tv.getFloat(); - - final int framesCount = a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12); - final int frameDuration = a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150); - + + setFramesCount(a.getInt(R.styleable.AnimatedRotateDrawable_framesCount, 12)); + setFramesDuration(a.getInt(R.styleable.AnimatedRotateDrawable_frameDuration, 150)); + final int res = a.getResourceId(R.styleable.AnimatedRotateDrawable_drawable, 0); Drawable drawable = null; if (res > 0) { @@ -265,8 +269,6 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac rotateState.mPivotX = pivotX; rotateState.mPivotYRel = pivotYRel; rotateState.mPivotY = pivotY; - rotateState.mFramesCount = framesCount; - rotateState.mFrameDuration = frameDuration; init(); @@ -275,6 +277,15 @@ public class AnimatedRotateDrawable extends Drawable implements Drawable.Callbac } } + public void setFramesCount(int framesCount) { + mState.mFramesCount = framesCount; + mIncrement = 360.0f / mState.mFramesCount; + } + + public void setFramesDuration(int framesDuration) { + mState.mFrameDuration = framesDuration; + } + @Override public Drawable mutate() { if (!mMutated && super.mutate() == this) { diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index e0d7898..6a15f6e 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -185,6 +185,10 @@ public: static status_t unregisterEffect(int id); static status_t setEffectEnabled(int id, bool enabled); + // clear stream to output mapping cache (gStreamOutputMap) + // and output configuration cache (gOutputs) + static void clearAudioConfigCache(); + static const sp<IAudioPolicyService>& get_audio_policy_service(); // ---------------------------------------------------------------------------- @@ -236,7 +240,8 @@ private: // mapping between stream types and outputs static DefaultKeyedVector<int, audio_io_handle_t> gStreamOutputMap; - // list of output descritor containing cached parameters (sampling rate, framecount, channel count...) + // list of output descriptors containing cached parameters + // (sampling rate, framecount, channel count...) static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs; }; diff --git a/include/private/surfaceflinger/LayerState.h b/include/private/surfaceflinger/LayerState.h index d2fed41..3eb5c99 100644 --- a/include/private/surfaceflinger/LayerState.h +++ b/include/private/surfaceflinger/LayerState.h @@ -54,8 +54,8 @@ struct layer_state_t { }; SurfaceID surface; uint32_t what; - int32_t x; - int32_t y; + float x; + float y; uint32_t z; uint32_t w; uint32_t h; diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h index 7fbbfb24..ace0735 100644 --- a/include/surfaceflinger/SurfaceComposerClient.h +++ b/include/surfaceflinger/SurfaceComposerClient.h @@ -145,7 +145,7 @@ public: status_t setAlpha(SurfaceID id, float alpha=1.0f); status_t setFreezeTint(SurfaceID id, uint32_t tint); status_t setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy); - status_t setPosition(SurfaceID id, int32_t x, int32_t y); + status_t setPosition(SurfaceID id, float x, float y); status_t setSize(SurfaceID id, uint32_t w, uint32_t h); status_t destroySurface(SurfaceID sid); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3b0ffea..00a4bf6 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -102,7 +102,7 @@ class Composer : public Singleton<Composer> public: status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id, - int32_t x, int32_t y); + float x, float y); status_t setSize(const sp<SurfaceComposerClient>& client, SurfaceID id, uint32_t w, uint32_t h); status_t setLayer(const sp<SurfaceComposerClient>& client, SurfaceID id, @@ -161,7 +161,7 @@ layer_state_t* Composer::getLayerStateLocked( } status_t Composer::setPosition(const sp<SurfaceComposerClient>& client, - SurfaceID id, int32_t x, int32_t y) { + SurfaceID id, float x, float y) { Mutex::Autolock _l(mLock); layer_state_t* s = getLayerStateLocked(client, id); if (!s) @@ -372,7 +372,7 @@ status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { return getComposer().setFreezeTint(this, id, tint); } -status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { +status_t SurfaceComposerClient::setPosition(SurfaceID id, float x, float y) { return getComposer().setPosition(this, id, x, y); } diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk index 0308af3..55ac133 100644 --- a/libs/gui/tests/Android.mk +++ b/libs/gui/tests/Android.mk @@ -1,4 +1,4 @@ -# Build the unit tests. +# Build the unit tests, LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) @@ -22,17 +22,15 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libutils \ -LOCAL_STATIC_LIBRARIES := \ - libgtest \ - libgtest_main \ - LOCAL_C_INCLUDES := \ bionic \ bionic/libstdc++/include \ external/gtest/include \ external/stlport/stlport \ -include $(BUILD_EXECUTABLE) +# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) +# to integrate with auto-test framework. +include $(BUILD_NATIVE_TEST) # Include subdirectory makefiles # ============================================================ diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 9acf99b..dd05e61 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -394,14 +394,14 @@ void FontRenderer::flushAllAndInvalidate() { bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { // If the glyph is too tall, don't cache it - if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) { // Default cache not large enough for large glyphs - resize cache to // max size and try again flushAllAndInvalidate(); initTextTexture(true); } - if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { LOGE("Font size to large to fit in cache. width, height = %i, %i", (int) glyph.fWidth, (int) glyph.fHeight); return false; diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index dbe4115..bfc6b5d 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -2637,7 +2637,7 @@ public class AudioService extends IAudioService.Stub { notifyTopOfAudioFocusStack(); // there's a new top of the stack, let the remote control know synchronized(mRCStack) { - checkUpdateRemoteControlDisplay(RC_INFO_ALL); + checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } } } else { @@ -2680,7 +2680,7 @@ public class AudioService extends IAudioService.Stub { notifyTopOfAudioFocusStack(); // there's a new top of the stack, let the remote control know synchronized(mRCStack) { - checkUpdateRemoteControlDisplay(RC_INFO_ALL); + checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } } } @@ -2784,7 +2784,7 @@ public class AudioService extends IAudioService.Stub { // there's a new top of the stack, let the remote control know synchronized(mRCStack) { - checkUpdateRemoteControlDisplay(RC_INFO_ALL); + checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } }//synchronized(mAudioFocusLock) @@ -3182,7 +3182,7 @@ public class AudioService extends IAudioService.Stub { * Helper function: * Called synchronized on mRCStack */ - private void clearRemoteControlDisplay() { + private void clearRemoteControlDisplay_syncRcs() { synchronized(mCurrentRcLock) { mCurrentRcClient = null; } @@ -3195,14 +3195,14 @@ public class AudioService extends IAudioService.Stub { * Called synchronized on mRCStack * mRCStack.empty() is false */ - private void updateRemoteControlDisplay(int infoChangedFlags) { + private void updateRemoteControlDisplay_syncRcs(int infoChangedFlags) { RemoteControlStackEntry rcse = mRCStack.peek(); int infoFlagsAboutToBeUsed = infoChangedFlags; // this is where we enforce opt-in for information display on the remote controls // with the new AudioManager.registerRemoteControlClient() API if (rcse.mRcClient == null) { //Log.w(TAG, "Can't update remote control display with null remote control client"); - clearRemoteControlDisplay(); + clearRemoteControlDisplay_syncRcs(); return; } synchronized(mCurrentRcLock) { @@ -3225,11 +3225,11 @@ public class AudioService extends IAudioService.Stub { * that has changed, if applicable (checking for the update conditions might trigger a * clear, rather than an update event). */ - private void checkUpdateRemoteControlDisplay(int infoChangedFlags) { + private void checkUpdateRemoteControlDisplay_syncRcs(int infoChangedFlags) { // determine whether the remote control display should be refreshed // if either stack is empty, there is a mismatch, so clear the RC display if (mRCStack.isEmpty() || mFocusStack.isEmpty()) { - clearRemoteControlDisplay(); + clearRemoteControlDisplay_syncRcs(); return; } // if the top of the two stacks belong to different packages, there is a mismatch, clear @@ -3237,17 +3237,17 @@ public class AudioService extends IAudioService.Stub { && (mFocusStack.peek().mPackageName != null) && !(mRCStack.peek().mCallingPackageName.compareTo( mFocusStack.peek().mPackageName) == 0)) { - clearRemoteControlDisplay(); + clearRemoteControlDisplay_syncRcs(); return; } // if the audio focus didn't originate from the same Uid as the one in which the remote // control information will be retrieved, clear if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) { - clearRemoteControlDisplay(); + clearRemoteControlDisplay_syncRcs(); return; } // refresh conditions were verified: update the remote controls - updateRemoteControlDisplay(infoChangedFlags); + updateRemoteControlDisplay_syncRcs(infoChangedFlags); } /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */ @@ -3258,7 +3258,7 @@ public class AudioService extends IAudioService.Stub { synchronized(mRCStack) { pushMediaButtonReceiver(eventReceiver); // new RC client, assume every type of information shall be queried - checkUpdateRemoteControlDisplay(RC_INFO_ALL); + checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } } } @@ -3273,7 +3273,7 @@ public class AudioService extends IAudioService.Stub { removeMediaButtonReceiver(eventReceiver); if (topOfStackWillChange) { // current RC client will change, assume every type of info needs to be queried - checkUpdateRemoteControlDisplay(RC_INFO_ALL); + checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } } } @@ -3329,7 +3329,7 @@ public class AudioService extends IAudioService.Stub { // if the eventReceiver is at the top of the stack // then check for potential refresh of the remote controls if (isCurrentRcController(eventReceiver)) { - checkUpdateRemoteControlDisplay(RC_INFO_ALL); + checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } } } @@ -3426,7 +3426,9 @@ public class AudioService extends IAudioService.Stub { } /** - * Register an IRemoteControlDisplay and notify all IRemoteControlClient of the new display. + * Register an IRemoteControlDisplay. + * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient + * at the top of the stack to update the new display with its information. * Since only one IRemoteControlDisplay is supported, this will unregister the previous display. * @param rcd the IRemoteControlDisplay to register. No effect if null. */ @@ -3458,20 +3460,8 @@ public class AudioService extends IAudioService.Stub { } } - // we have a new display, tell the current client that it needs to send info - // (following lock order: mRCStack then mCurrentRcLock) - synchronized(mCurrentRcLock) { - if (mCurrentRcClient != null) { - // tell the current client that it needs to send info - try { - mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, - RC_INFO_ALL, mArtworkExpectedWidth, mArtworkExpectedHeight); - } catch (RemoteException e) { - Log.e(TAG, "Current valid remote client is dead: "+e); - mCurrentRcClient = null; - } - } - } + // we have a new display, of which all the clients are now aware: have it be updated + updateRemoteControlDisplay_syncRcs(RC_INFO_ALL); } } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 66bd56a..1ee9a1f 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -381,7 +381,7 @@ import java.lang.ref.WeakReference; * <td>{} </p></td> * <td>This method can be called in any state and calling it does not change * the object state. </p></td></tr> - * <tr><td>setTexture </p></td> + * <tr><td>setSurface </p></td> * <td>any </p></td> * <td>{} </p></td> * <td>This method can be called in any state and calling it does not change @@ -608,7 +608,7 @@ public class MediaPlayer * portion of the media. * * Either a surface holder or surface must be set if a display or video sink - * is needed. Not calling this method or {@link #setTexture(SurfaceTexture)} + * is needed. Not calling this method or {@link #setSurface(Surface)} * when playing back a video will result in only the audio track being played. * A null surface holder or surface will result in only the audio track being * played. @@ -629,14 +629,21 @@ public class MediaPlayer /** * Sets the {@link Surface} to be used as the sink for the video portion of - * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but does not - * support {@link #setScreenOnWhilePlaying(boolean)} or {@link #updateSurfaceScreenOn()}. - * Setting a Surface will un-set any Surface or SurfaceHolder that was previously set. + * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but + * does not support {@link #setScreenOnWhilePlaying(boolean)}. Setting a + * Surface will un-set any Surface or SurfaceHolder that was previously set. * A null surface will result in only the audio track being played. * - * @param surface The {@link Surface} to be used for the video portion of the media. + * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps + * returned from {@link SurfaceTexture#getTimestamp()} will have an + * unspecified zero point. These timestamps cannot be directly compared + * between different media sources, different instances of the same media + * source, or multiple runs of the same program. The timestamp is normally + * monotonically increasing and is unaffected by time-of-day adjustments, + * but it is reset when the position is set. * - * @hide Pending review by API council. + * @param surface The {@link Surface} to be used for the video portion of + * the media. */ public void setSurface(Surface surface) { if (mScreenOnWhilePlaying && surface != null) { diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java index d59bc2b..d7b85e4 100644 --- a/media/java/android/media/RemoteControlClient.java +++ b/media/java/android/media/RemoteControlClient.java @@ -22,6 +22,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; +import android.media.MediaMetadataRetriever; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -29,6 +30,7 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; +import java.lang.IllegalArgumentException; import java.util.HashMap; /** @@ -236,6 +238,23 @@ public class RemoteControlClient mEventHandler = new EventHandler(this, looper); } + private static final int[] METADATA_KEYS_TYPE_STRING = { + MediaMetadataRetriever.METADATA_KEY_ALBUM, + MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, + MediaMetadataRetriever.METADATA_KEY_TITLE, + MediaMetadataRetriever.METADATA_KEY_ARTIST, + MediaMetadataRetriever.METADATA_KEY_AUTHOR, + MediaMetadataRetriever.METADATA_KEY_COMPILATION, + MediaMetadataRetriever.METADATA_KEY_COMPOSER, + MediaMetadataRetriever.METADATA_KEY_DATE, + MediaMetadataRetriever.METADATA_KEY_GENRE, + MediaMetadataRetriever.METADATA_KEY_TITLE, + MediaMetadataRetriever.METADATA_KEY_WRITER }; + private static final int[] METADATA_KEYS_TYPE_LONG = { + MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, + MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, + MediaMetadataRetriever.METADATA_KEY_DURATION }; + /** * Class used to modify metadata in a {@link RemoteControlClient} object. */ @@ -256,6 +275,11 @@ public class RemoteControlClient } /** + * The metadata key for the content artwork / album art. + */ + public final static int METADATA_KEY_ARTWORK = 100; + + /** * Adds textual information to be displayed. * Note that none of the information added after {@link #apply()} has been called, * will be displayed. @@ -265,49 +289,73 @@ public class RemoteControlClient * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}, - * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. - * @param value the text for the given key, or null to signify there is no valid + * . + * @param value the text for the given key, or {@code null} to signify there is no valid * information for the field. * @return FIXME description */ - public synchronized MetadataEditor putString(int key, String value) { + public synchronized MetadataEditor putString(int key, String value) + throws IllegalArgumentException { if (mApplied) { Log.e(TAG, "Can't edit a previously applied MetadataEditor"); return this; } + if (!validTypeForKey(key, METADATA_KEYS_TYPE_STRING)) { + throw(new IllegalArgumentException("Invalid type 'String' for key "+ key)); + } mEditorMetadata.putString(String.valueOf(key), value); mMetadataChanged = true; return this; } /** - * The metadata key for the content artwork / album art. + * FIXME javadoc + * @param key the identifier of a the metadata field to set. Valid values are + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value + * expressed in milliseconds), + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. + * @param value FIXME javadoc + * @return FIXME javadoc + * @throws IllegalArgumentException */ - public final int METADATA_KEY_ARTWORK = 100; + public synchronized MetadataEditor putLong(int key, long value) + throws IllegalArgumentException { + if (mApplied) { + Log.e(TAG, "Can't edit a previously applied MetadataEditor"); + return this; + } + if (!validTypeForKey(key, METADATA_KEYS_TYPE_LONG)) { + throw(new IllegalArgumentException("Invalid type 'long' for key "+ key)); + } + mEditorMetadata.putLong(String.valueOf(key), value); + mMetadataChanged = true; + return this; + } /** * Sets the album / artwork picture to be displayed on the remote control. * @param key FIXME description * @param bitmap the bitmap for the artwork, or null if there isn't any. * @return FIXME description + * @throws IllegalArgumentException * @see android.graphics.Bitmap */ - public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) { + public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) + throws IllegalArgumentException { if (mApplied) { Log.e(TAG, "Can't edit a previously applied MetadataEditor"); return this; } if (key != METADATA_KEY_ARTWORK) { - return this; + throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key)); } if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) { mEditorArtwork = scaleBitmapIfTooBig(bitmap, @@ -740,6 +788,24 @@ public class RemoteControlClient } } return bitmap; + } + /** + * Fast routine to go through an array of allowed keys and return whether the key is part + * of that array + * @param key the key value + * @param validKeys the array of valid keys for a given type + * @return true if the key is part of the array, false otherwise + */ + private static boolean validTypeForKey(int key, int[] validKeys) { + try { + for (int i = 0 ; ; i++) { + if (key == validKeys[i]) { + return true; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + return false; + } } } diff --git a/media/java/android/media/videoeditor/VideoEditorProfile.java b/media/java/android/media/videoeditor/VideoEditorProfile.java index ecdcdfb..202a2df 100755 --- a/media/java/android/media/videoeditor/VideoEditorProfile.java +++ b/media/java/android/media/videoeditor/VideoEditorProfile.java @@ -91,7 +91,7 @@ public class VideoEditorProfile case MediaProperties.VCODEC_H263: case MediaProperties.VCODEC_H264: case MediaProperties.VCODEC_MPEG4: - level = native_get_videoeditor_export_profile(vidCodec); + level = native_get_videoeditor_export_level(vidCodec); break; default : throw new IllegalArgumentException("Unsupported video codec" + vidCodec); diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index bb91fa9..853a5f6 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -727,6 +727,14 @@ status_t AudioSystem::isStreamActive(int stream, bool* state, uint32_t inPastMs) } +void AudioSystem::clearAudioConfigCache() +{ + Mutex::Autolock _l(gLock); + LOGV("clearAudioConfigCache()"); + gStreamOutputMap.clear(); + gOutputs.clear(); +} + // --------------------------------------------------------------------------- void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) { diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index cecedb5..3b6c64d 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -1164,6 +1164,10 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart) cblk->cv.broadcast(); cblk->lock.unlock(); + // refresh the audio configuration cache in this process to make sure we get new + // output parameters in getOutput_l() and createTrack_l() + AudioSystem::clearAudioConfigCache(); + // if the new IAudioTrack is created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index a6a3a18..f41e9d2 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -42,7 +42,7 @@ NuPlayer::StreamingSource::~StreamingSource() { void NuPlayer::StreamingSource::start() { mStreamListener = new NuPlayerStreamListener(mSource, 0); - mTSParser = new ATSParser; + mTSParser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE); mStreamListener->start(); } diff --git a/media/libstagefright/AACExtractor.cpp b/media/libstagefright/AACExtractor.cpp index a5a6b64..52b1200 100644 --- a/media/libstagefright/AACExtractor.cpp +++ b/media/libstagefright/AACExtractor.cpp @@ -33,8 +33,6 @@ namespace android { -#define ADTS_HEADER_LENGTH 7 - class AACSource : public MediaSource { public: AACSource(const sp<DataSource> &source, @@ -88,7 +86,16 @@ uint32_t get_sample_rate(const uint8_t sf_index) return 0; } -static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) { +// Returns the frame length in bytes as described in an ADTS header starting at the given offset, +// or 0 if the size can't be read due to an error in the header or a read failure. +// The returned value is the AAC frame size with the ADTS header length (regardless of +// the presence of the CRC). +// If headerSize is non-NULL, it will be used to return the size of the header of this ADTS frame. +static size_t getAdtsFrameLength(const sp<DataSource> &source, off64_t offset, size_t* headerSize) { + + const size_t kAdtsHeaderLengthNoCrc = 7; + const size_t kAdtsHeaderLengthWithCrc = 9; + size_t frameSize = 0; uint8_t syncword[2]; @@ -111,7 +118,15 @@ static size_t getFrameSize(const sp<DataSource> &source, off64_t offset) { } frameSize = (header[0] & 0x3) << 11 | header[1] << 3 | header[2] >> 5; - frameSize += ADTS_HEADER_LENGTH + protectionAbsent ? 0 : 2; + + // protectionAbsent is 0 if there is CRC + size_t headSize = protectionAbsent ? kAdtsHeaderLengthNoCrc : kAdtsHeaderLengthWithCrc; + if (headSize > frameSize) { + return 0; + } + if (headerSize != NULL) { + *headerSize = headSize; + } return frameSize; } @@ -148,7 +163,7 @@ AACExtractor::AACExtractor(const sp<DataSource> &source) if (mDataSource->getSize(&streamSize) == OK) { while (offset < streamSize) { - if ((frameSize = getFrameSize(source, offset)) == 0) { + if ((frameSize = getAdtsFrameLength(source, offset, NULL)) == 0) { return; } @@ -268,8 +283,8 @@ status_t AACSource::read( } } - size_t frameSize, frameSizeWithoutHeader; - if ((frameSize = getFrameSize(mDataSource, mOffset)) == 0) { + size_t frameSize, frameSizeWithoutHeader, headerSize; + if ((frameSize = getAdtsFrameLength(mDataSource, mOffset, &headerSize)) == 0) { return ERROR_END_OF_STREAM; } @@ -279,8 +294,8 @@ status_t AACSource::read( return err; } - frameSizeWithoutHeader = frameSize - ADTS_HEADER_LENGTH; - if (mDataSource->readAt(mOffset + ADTS_HEADER_LENGTH, buffer->data(), + frameSizeWithoutHeader = frameSize - headerSize; + if (mDataSource->readAt(mOffset + headerSize, buffer->data(), frameSizeWithoutHeader) != (ssize_t)frameSizeWithoutHeader) { buffer->release(); buffer = NULL; diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 142dda0..f98b0de 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -513,7 +513,8 @@ void AwesomePlayer::reset_l() { // If we did this later, audio would continue playing while we // shutdown the video-related resources and the player appear to // not be as responsive to a reset request. - if (mAudioPlayer == NULL && mAudioSource != NULL) { + if ((mAudioPlayer == NULL || !(mFlags & AUDIOPLAYER_STARTED)) + && mAudioSource != NULL) { // If we had an audio player, it would have effectively // taken possession of the audio source and stopped it when // _it_ is stopped. Otherwise this is still our responsibility. diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 5bbc2b4..74a3b32 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -325,14 +325,16 @@ sp<MediaSource> ATSParser::Program::getSource(SourceType type) { } int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) { - if (!mFirstPTSValid) { - mFirstPTSValid = true; - mFirstPTS = PTS; - PTS = 0; - } else if (PTS < mFirstPTS) { - PTS = 0; - } else { - PTS -= mFirstPTS; + if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) { + if (!mFirstPTSValid) { + mFirstPTSValid = true; + mFirstPTS = PTS; + PTS = 0; + } else if (PTS < mFirstPTS) { + PTS = 0; + } else { + PTS -= mFirstPTS; + } } return (PTS * 100) / 9; @@ -734,7 +736,8 @@ sp<MediaSource> ATSParser::Stream::getSource(SourceType type) { //////////////////////////////////////////////////////////////////////////////// -ATSParser::ATSParser() { +ATSParser::ATSParser(uint32_t flags) + : mFlags(flags) { } ATSParser::~ATSParser() { diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 1e6451d..d12d998 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -38,7 +38,16 @@ struct ATSParser : public RefBase { DISCONTINUITY_FORMATCHANGE }; - ATSParser(); + enum Flags { + // The 90kHz clock (PTS/DTS) is absolute, i.e. PTS=0 corresponds to + // a media time of 0. + // If this flag is _not_ specified, the first PTS encountered in a + // program of this stream will be assumed to correspond to media time 0 + // instead. + TS_TIMESTAMPS_ARE_ABSOLUTE = 1 + }; + + ATSParser(uint32_t flags = 0); void feedTSPacket(const void *data, size_t size); @@ -73,6 +82,7 @@ private: struct Program; struct Stream; + uint32_t mFlags; Vector<sp<Program> > mPrograms; void parseProgramAssociationTable(ABitReader *br); diff --git a/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java b/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java index 809ee82..f76cf37 100644 --- a/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java +++ b/media/tests/MediaDump/src/com/android/mediadump/VideoDumpView.java @@ -49,6 +49,7 @@ import android.opengl.Matrix; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; import android.widget.MediaController; @@ -569,7 +570,9 @@ class VideoDumpView extends GLSurfaceView implements MediaPlayerControl { mSurface = new SurfaceTexture(mTextureID); mSurface.setOnFrameAvailableListener(this); - mMediaPlayer.setTexture(mSurface); + Surface surface = new Surface(mSurface); + mMediaPlayer.setSurface(surface); + surface.release(); try { mMediaPlayer.prepare(); diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index ed13ace..f3d0bee 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -71,6 +71,7 @@ </LinearLayout> <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon" android:layout_width="wrap_content" android:layout_height="match_parent" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 6e6567b..e3ea0de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1060,6 +1060,12 @@ public class PhoneStatusBar extends StatusBar { */ } + public void showClock(boolean show) { + View clock = mStatusBarView.findViewById(R.id.clock); + if (clock != null) { + clock.setVisibility(show ? View.VISIBLE : View.GONE); + } + } /** * State is one or more of the DISABLE constants from StatusBarManager. @@ -1074,6 +1080,10 @@ public class PhoneStatusBar extends StatusBar { old, state, diff)); } + if ((diff & StatusBarManager.DISABLE_CLOCK) != 0) { + boolean show = (state & StatusBarManager.DISABLE_CLOCK) == 0; + showClock(show); + } if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { Slog.d(TAG, "DISABLE_EXPAND: yes"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java index 1e417ac..5911378 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java @@ -428,7 +428,7 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel, private CharSequence getIMIName(InputMethodInfo imi) { if (imi == null) return null; - return mPackageManager.getApplicationLabel(imi.getServiceInfo().applicationInfo); + return imi.loadLabel(mPackageManager); } private CharSequence getSubtypeName(InputMethodInfo imi, InputMethodSubtype subtype) { diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java index 9629702..40cc7d8 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java @@ -20,6 +20,7 @@ import com.android.internal.R; import com.android.internal.telephony.IccCard; import com.android.internal.telephony.IccCard.State; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.TransportControlView; import com.android.internal.policy.impl.KeyguardUpdateMonitor.SimStateCallback; import java.util.ArrayList; @@ -52,8 +53,6 @@ class KeyguardStatusViewManager implements OnClickListener { public static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery; private static final long INSTRUCTION_RESET_DELAY = 2000; // time until instruction text resets - private static final int SHOW_WIDGET = 8; - private static final int HIDE_WIDGET = 9; private static final int INSTRUCTION_TEXT = 10; private static final int CARRIER_TEXT = 11; private static final int CARRIER_HELP_TEXT = 12; @@ -71,7 +70,7 @@ class KeyguardStatusViewManager implements OnClickListener { private TextView mStatus1View; private TextView mOwnerInfoView; private TextView mAlarmStatusView; - private View mTransportView; + private TransportControlView mTransportView; // Top-level container view for above views private View mContainer; @@ -162,7 +161,7 @@ class KeyguardStatusViewManager implements OnClickListener { mStatus1View = (TextView) findViewById(R.id.status1); mAlarmStatusView = (TextView) findViewById(R.id.alarm_status); mOwnerInfoView = (TextView) findViewById(R.id.propertyOf); - mTransportView = findViewById(R.id.transport); + mTransportView = (TransportControlView) findViewById(R.id.transport); mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton); if (mEmergencyCallButton != null) { mEmergencyCallButton.setText(R.string.lockscreen_emergency_call); @@ -192,20 +191,6 @@ class KeyguardStatusViewManager implements OnClickListener { mUpdateMonitor.getTelephonyPlmn(), mUpdateMonitor.getTelephonySpn())); } - public void enterWidgetMode() { - if (mTransportView != null) { - mTransportView.setVisibility(View.VISIBLE); - update(SHOW_WIDGET, null); - } - } - - public void leaveWidgetMode() { - if (mTransportView != null) { - mTransportView.setVisibility(View.GONE); - update(HIDE_WIDGET, null); - } - } - private boolean inWidgetMode() { return mTransportView != null && mTransportView.getVisibility() == View.VISIBLE; } @@ -248,7 +233,8 @@ class KeyguardStatusViewManager implements OnClickListener { * @param lockIcon */ public void setHelpMessage(int textResId, int lockIcon) { - mHelpMessageText = getText(textResId).toString(); + final CharSequence tmp = getText(textResId); + mHelpMessageText = tmp == null ? null : tmp.toString(); update(HELP_MESSAGE_TEXT, mHelpMessageText); } @@ -603,15 +589,6 @@ class KeyguardStatusViewManager implements OnClickListener { public void onPhoneStateChanged(String newState) { updateEmergencyCallButtonState(); } - - public void onTransportControlStateChanged(int state) { - // TODO: define what state means - if (state == 0) { - leaveWidgetMode(); - } else { - enterWidgetMode(); - } - } }; private SimStateCallback mSimStateCallback = new SimStateCallback() { diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java index 77f1932..2955de3 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java @@ -25,6 +25,7 @@ import static android.os.BatteryManager.BATTERY_STATUS_CHARGING; import static android.os.BatteryManager.BATTERY_STATUS_FULL; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import android.media.AudioManager; +import android.media.IRemoteControlClient; import android.os.BatteryManager; import android.os.Handler; import android.os.Message; @@ -93,8 +94,6 @@ public class KeyguardUpdateMonitor { private static final int MSG_SIM_STATE_CHANGE = 304; private static final int MSG_RINGER_MODE_CHANGED = 305; private static final int MSG_PHONE_STATE_CHANGED = 306; - private static final int MSG_TRANSPORT_CONTROL_STATE_CHANGED = 307; - /** * When we receive a @@ -171,9 +170,6 @@ public class KeyguardUpdateMonitor { case MSG_PHONE_STATE_CHANGED: handlePhoneStateChanged((String)msg.obj); break; - case MSG_TRANSPORT_CONTROL_STATE_CHANGED: - handleTransportControlStateChanged(msg.arg1); - break; } } }; @@ -263,23 +259,10 @@ public class KeyguardUpdateMonitor { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); } - // TODO - else if ("android.media.TRANSPORT_CONTROL_CHANGED".equals(action)) { - int state = intent.getIntExtra("state", 0); - mHandler.sendMessage(mHandler.obtainMessage(MSG_TRANSPORT_CONTROL_STATE_CHANGED, - state)); - } } }, filter); } - protected void handleTransportControlStateChanged(int state) { - if (DEBUG) Log.d(TAG, "handleTransportControlStateChanged()"); - for (int i = 0; i < mInfoCallbacks.size(); i++) { - mInfoCallbacks.get(i).onTransportControlStateChanged(state); - } - } - protected void handlePhoneStateChanged(String newState) { if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")"); for (int i = 0; i < mInfoCallbacks.size(); i++) { @@ -465,11 +448,6 @@ public class KeyguardUpdateMonitor { */ void onPhoneStateChanged(String newState); - /** - * Called when AudioService informs us of a change to the transport control client. - * - */ - void onTransportControlStateChanged(int state); } /** @@ -567,4 +545,5 @@ public class KeyguardUpdateMonitor { public void reportFailedAttempt() { mFailedAttempts++; } + } diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 5661116..162381d 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -145,10 +145,10 @@ public class KeyguardViewMediator implements KeyguardViewCallback, private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000; /** - * Allow the user to operate the status bar when the keyguard is engaged (without a pattern or - * password). + * Allow the user to expand the status bar when the keyguard is engaged + * (without a pattern or password). */ - private static final boolean ENABLE_STATUS_BAR_IN_KEYGUARD = true; + private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true; private Context mContext; private AlarmManager mAlarmManager; @@ -1184,19 +1184,25 @@ public class KeyguardViewMediator implements KeyguardViewCallback, } } - // if the keyguard is shown, allow the status bar to open only if the keyguard is - // insecure and (is covered by another window OR this feature is enabled in general) - boolean enable = !mShowing - || ((ENABLE_STATUS_BAR_IN_KEYGUARD || mHidden) && !isSecure()); + int flags = StatusBarManager.DISABLE_NONE; + if (mShowing && !mHidden) { + // showing lockscreen exclusively; disable various extra + // statusbar components. + flags |= StatusBarManager.DISABLE_NAVIGATION; + flags |= StatusBarManager.DISABLE_CLOCK; + } + if (mShowing && (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND)) { + // showing secure lockscreen; disable expanding. + flags |= StatusBarManager.DISABLE_EXPAND; + } + if (DEBUG) { - Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden - + " isSecure=" + isSecure() + " --> enable=" + enable); + Log.d(TAG, + "adjustStatusBarLocked: mShowing=" + mShowing + " mHidden=" + mHidden + + " isSecure=" + isSecure() + " --> flags=" + flags); } - mStatusBarManager.disable(enable ? - StatusBarManager.DISABLE_NONE : - ( StatusBarManager.DISABLE_EXPAND - | StatusBarManager.DISABLE_NAVIGATION - | StatusBarManager.DISABLE_CLOCK)); + + mStatusBarManager.disable(flags); } } diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index 5c85903..9c14734 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -20,6 +20,8 @@ import com.android.internal.R; import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode; import com.android.internal.telephony.IccCard; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockScreenWidgetCallback; +import com.android.internal.widget.TransportControlView; import android.accounts.Account; import android.accounts.AccountManager; @@ -66,6 +68,8 @@ import java.io.IOException; */ public class LockPatternKeyguardView extends KeyguardViewBase { + private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000; + static final boolean DEBUG_CONFIGURATION = false; // time after launching EmergencyDialer before the screen goes blank. @@ -179,6 +183,22 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } }; + private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() { + public void userActivity(View self) { + mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT); + } + + public void requestShow(View view) { + if (DEBUG) Log.v(TAG, "View " + view + " requested show transports"); + view.setVisibility(View.VISIBLE); + } + + public void requestHide(View view) { + if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports"); + view.setVisibility(View.GONE); + } + }; + /** * @return Whether we are stuck on the lock screen because the sim is * missing. @@ -490,6 +510,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase { super.onDetachedFromWindow(); } + protected void onConfigurationChanged(Configuration newConfig) { + Resources resources = getResources(); + mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen); + mConfiguration = newConfig; + if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed"); + updateScreen(mMode, true /* force */); + } + @Override protected boolean dispatchHoverEvent(MotionEvent event) { // Do not let the screen to get locked while the user is disabled and touch @@ -502,14 +530,6 @@ public class LockPatternKeyguardView extends KeyguardViewBase { return super.dispatchHoverEvent(event); } - protected void onConfigurationChanged(Configuration newConfig) { - Resources resources = getResources(); - mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen); - mConfiguration = newConfig; - if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed"); - updateScreen(mMode, true /* force */); - } - @Override public void wakeWhenReadyTq(int keyCode) { if (DEBUG) Log.d(TAG, "onWakeKey"); @@ -639,12 +659,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } View createLockScreen() { - return new LockScreen( + View lockView = new LockScreen( mContext, mConfiguration, mLockPatternUtils, mUpdateMonitor, mKeyguardScreenCallback); + initializeTransportControlView(lockView); + return lockView; } View createUnlockScreenFor(UnlockMode unlockMode) { @@ -710,10 +732,22 @@ public class LockPatternKeyguardView extends KeyguardViewBase { } else { throw new IllegalArgumentException("unknown unlock mode " + unlockMode); } + initializeTransportControlView(unlockView); mUnlockScreenMode = unlockMode; return unlockView; } + private void initializeTransportControlView(View view) { + com.android.internal.widget.TransportControlView tcv = + (TransportControlView) view.findViewById(R.id.transport); + if (tcv == null) { + if (DEBUG) Log.w(TAG, "Couldn't find transport control widget"); + } else { + tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it. + tcv.setCallback(mWidgetCallback); + } + } + /** * Given the current state of things, what should be the initial mode of * the lock screen (lock or unlock). diff --git a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java index ce3bc74..2f2d3b7 100644 --- a/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java @@ -347,7 +347,8 @@ public class PasswordUnlockScreen extends LinearLayout implements KeyguardScreen public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { // Check if this was the result of hitting the enter key - if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE) { + if (actionId == EditorInfo.IME_NULL || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT) { verifyPasswordAndUnlock(); return true; } diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index d617af8..94efa74 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1362,6 +1362,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream); mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream); + mStreamTypes[stream].valid = true; } } @@ -1530,6 +1531,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type())); chain->incTrackCnt(); } + + // invalidate track immediately if the stream type was moved to another thread since + // createTrack() was called by the client process. + if (!mStreamTypes[streamType].valid) { + LOGW("createTrack_l() on thread %p: invalidating track on stream %d", + this, streamType); + android_atomic_or(CBLK_INVALID_ON, &track->mCblk->flags); + } } lStatus = NO_ERROR; @@ -2219,6 +2228,14 @@ void AudioFlinger::MixerThread::invalidateTracks(int streamType) } } +void AudioFlinger::PlaybackThread::setStreamValid(int streamType, bool valid) +{ + LOGV ("PlaybackThread::setStreamValid() thread %p, streamType %d, valid %d", + this, streamType, valid); + Mutex::Autolock _l(mLock); + + mStreamTypes[streamType].valid = valid; +} // getTrackName_l() must be called with ThreadBase::mLock held int AudioFlinger::MixerThread::getTrackName_l() @@ -5074,11 +5091,14 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output) LOGV("setStreamOutput() stream %d to output %d", stream, output); audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream); + dstThread->setStreamValid(stream, true); + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); if (thread != dstThread && thread->type() != ThreadBase::DIRECT) { MixerThread *srcThread = (MixerThread *)thread; + srcThread->setStreamValid(stream, false); srcThread->invalidateTracks(stream); } } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 1ceb0ec..2e05593 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -751,14 +751,18 @@ private: virtual uint32_t hasAudioSession(int sessionId); virtual uint32_t getStrategyForSession_l(int sessionId); + void setStreamValid(int streamType, bool valid); + struct stream_type_t { stream_type_t() : volume(1.0f), - mute(false) + mute(false), + valid(true) { } float volume; bool mute; + bool valid; }; protected: diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 1d7cc19..06dea36 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -1094,9 +1094,8 @@ void EventHub::clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboar bool EventHub::isExternalDeviceLocked(Device* device) { if (device->configuration) { bool value; - if (device->configuration->tryGetProperty(String8("device.internal"), value) - && value) { - return false; + if (device->configuration->tryGetProperty(String8("device.internal"), value)) { + return !value; } } return device->identifier.bus == BUS_USB || device->identifier.bus == BUS_BLUETOOTH; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index b3309e5..997318a 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -197,14 +197,14 @@ class BackupManagerService extends IBackupManager.Stub { = new SparseArray<HashSet<ApplicationInfo>>(); // set of backup services that have pending changes class BackupRequest { - public ApplicationInfo appInfo; + public String packageName; - BackupRequest(ApplicationInfo app) { - appInfo = app; + BackupRequest(String pkgName) { + packageName = pkgName; } public String toString() { - return "BackupRequest{app=" + appInfo + "}"; + return "BackupRequest{pkg=" + packageName + "}"; } } // Backups that we haven't started yet. Keys are package names. @@ -877,6 +877,7 @@ class BackupManagerService extends IBackupManager.Stub { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_REPLACED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); // Register for events related to sdcard installation. @@ -1174,7 +1175,8 @@ class BackupManagerService extends IBackupManager.Stub { Bundle extras = intent.getExtras(); String pkgList[] = null; if (Intent.ACTION_PACKAGE_ADDED.equals(action) || - Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + Intent.ACTION_PACKAGE_REMOVED.equals(action) || + Intent.ACTION_PACKAGE_REPLACED.equals(action)) { Uri uri = intent.getData(); if (uri == null) { return; @@ -1183,8 +1185,14 @@ class BackupManagerService extends IBackupManager.Stub { if (pkgName != null) { pkgList = new String[] { pkgName }; } - added = Intent.ACTION_PACKAGE_ADDED.equals(action); - replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); + if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) { + // use the existing "add with replacement" logic + if (MORE_DEBUG) Slog.d(TAG, "PACKAGE_REPLACED, updating package " + pkgName); + added = replacing = true; + } else { + added = Intent.ACTION_PACKAGE_ADDED.equals(action); + replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false); + } } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) { added = true; pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); @@ -1192,6 +1200,7 @@ class BackupManagerService extends IBackupManager.Stub { added = false; pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); } + if (pkgList == null || pkgList.length == 0) { return; } @@ -1665,9 +1674,7 @@ class BackupManagerService extends IBackupManager.Stub { if (status == BackupConstants.TRANSPORT_OK) { PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( mPackageManager, allAgentPackages()); - BackupRequest pmRequest = new BackupRequest(new ApplicationInfo()); - pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; - status = processOneBackup(pmRequest, + status = processOneBackup(PACKAGE_MANAGER_SENTINEL, IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport); } @@ -1716,7 +1723,7 @@ class BackupManagerService extends IBackupManager.Stub { if (status != BackupConstants.TRANSPORT_OK) { Slog.w(TAG, "Backup pass unsuccessful, restaging"); for (BackupRequest req : mQueue) { - dataChangedImpl(req.appInfo.packageName); + dataChangedImpl(req.packageName); } // We also want to reset the backup schedule based on whatever @@ -1750,9 +1757,11 @@ class BackupManagerService extends IBackupManager.Stub { // Verify that the requested app exists; it might be something that // requested a backup but was then uninstalled. The request was // journalled and rather than tamper with the journal it's safer - // to sanity-check here. + // to sanity-check here. This also gives us the classname of the + // package's backup agent. + PackageInfo pkg; try { - mPackageManager.getPackageInfo(request.appInfo.packageName, 0); + pkg = mPackageManager.getPackageInfo(request.packageName, 0); } catch (NameNotFoundException e) { Slog.d(TAG, "Package does not exist; skipping"); continue; @@ -1760,11 +1769,11 @@ class BackupManagerService extends IBackupManager.Stub { IBackupAgent agent = null; try { - mWakelock.setWorkSource(new WorkSource(request.appInfo.uid)); - agent = bindToAgentSynchronous(request.appInfo, + mWakelock.setWorkSource(new WorkSource(pkg.applicationInfo.uid)); + agent = bindToAgentSynchronous(pkg.applicationInfo, IApplicationThread.BACKUP_MODE_INCREMENTAL); if (agent != null) { - int result = processOneBackup(request, agent, transport); + int result = processOneBackup(request.packageName, agent, transport); if (result != BackupConstants.TRANSPORT_OK) return result; } } catch (SecurityException ex) { @@ -1772,7 +1781,7 @@ class BackupManagerService extends IBackupManager.Stub { Slog.d(TAG, "error in bind/backup", ex); } finally { try { // unbind even on timeout, just in case - mActivityManager.unbindBackupAgent(request.appInfo); + mActivityManager.unbindBackupAgent(pkg.applicationInfo); } catch (RemoteException e) {} } } @@ -1782,9 +1791,8 @@ class BackupManagerService extends IBackupManager.Stub { return BackupConstants.TRANSPORT_OK; } - private int processOneBackup(BackupRequest request, IBackupAgent agent, + private int processOneBackup(String packageName, IBackupAgent agent, IBackupTransport transport) { - final String packageName = request.appInfo.packageName; if (DEBUG) Slog.d(TAG, "processOneBackup doBackup() on " + packageName); File savedStateName = new File(mStateDir, packageName); @@ -4207,7 +4215,7 @@ class BackupManagerService extends IBackupManager.Stub { if (app.packageName.equals(packageName)) { // Add the caller to the set of pending backups. If there is // one already there, then overwrite it, but no harm done. - BackupRequest req = new BackupRequest(app); + BackupRequest req = new BackupRequest(packageName); if (mPendingBackups.put(app.packageName, req) == null) { // Journal this request in case of crash. The put() // operation returned null when this package was not already @@ -4218,7 +4226,7 @@ class BackupManagerService extends IBackupManager.Stub { int numKeys = mPendingBackups.size(); Slog.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); for (BackupRequest b : mPendingBackups.values()) { - Slog.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); + Slog.d(TAG, " + " + b); } } } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index bfca851..3815c3b 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -17,6 +17,8 @@ package com.android.server; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.ConnectivityManager.isNetworkTypeValid; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; @@ -1418,6 +1420,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { // do this before we broadcast the change handleConnectivityChange(prevNetType, doReset); + final Intent immediateIntent = new Intent(intent); + immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); + sendStickyBroadcast(immediateIntent); sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay()); /* * If the failover network is already connected, then immediately send @@ -1476,11 +1481,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } private void sendConnectedBroadcast(NetworkInfo info) { - sendGeneralBroadcast(info, ConnectivityManager.CONNECTIVITY_ACTION); + sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE); + sendGeneralBroadcast(info, CONNECTIVITY_ACTION); } private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) { - sendGeneralBroadcastDelayed(info, ConnectivityManager.CONNECTIVITY_ACTION, delayMs); + sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE); + sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs); } private void sendInetConditionBroadcast(NetworkInfo info) { @@ -1559,6 +1566,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { } intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished); + + final Intent immediateIntent = new Intent(intent); + immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE); + sendStickyBroadcast(immediateIntent); sendStickyBroadcast(intent); /* * If the failover network is already connected, then immediately send @@ -1576,8 +1587,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { } intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); if (DBG) { - log("sendStickyBroadcast: NetworkInfo=" + - intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)); + log("sendStickyBroadcast: action=" + intent.getAction()); } mContext.sendStickyBroadcast(intent); @@ -1588,7 +1598,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (delayMs <= 0) { sendStickyBroadcast(intent); } else { - if (DBG) log("sendStickyBroadcastDelayed: delayMs=" + delayMs + " intent=" + intent); + if (DBG) { + log("sendStickyBroadcastDelayed: delayMs=" + delayMs + ", action=" + + intent.getAction()); + } mHandler.sendMessageDelayed(mHandler.obtainMessage( EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs); } @@ -2281,7 +2294,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { case EVENT_SEND_STICKY_BROADCAST_INTENT: { Intent intent = (Intent)msg.obj; - log("EVENT_SEND_STICKY_BROADCAST_INTENT: sendStickyBroadcast intent=" + intent); sendStickyBroadcast(intent); break; } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 38bcebc..bb831f5 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1682,7 +1682,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub for (int i = 0; i < packageNum; ++i) { if (packageInfos[i].equals(imi.getPackageName())) { mFileManager.addInputMethodSubtypes(imi, subtypes); - buildInputMethodListLocked(mMethodList, mMethodMap); + final long ident = Binder.clearCallingIdentity(); + try { + buildInputMethodListLocked(mMethodList, mMethodMap); + } finally { + Binder.restoreCallingIdentity(ident); + } return true; } } @@ -1707,7 +1712,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return; } - long ident = Binder.clearCallingIdentity(); + final long ident = Binder.clearCallingIdentity(); try { setInputMethodLocked(id, subtypeId); } finally { @@ -3065,17 +3070,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void addInputMethodSubtypes( InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) { synchronized (mMethodMap) { - final HashSet<InputMethodSubtype> existingSubtypes = - new HashSet<InputMethodSubtype>(); - for (int i = 0; i < imi.getSubtypeCount(); ++i) { - existingSubtypes.add(imi.getSubtypeAt(i)); - } - final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); final int N = additionalSubtypes.length; for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = additionalSubtypes[i]; - if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) { + if (!subtypes.contains(subtype)) { subtypes.add(subtype); } } diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 7a3a344..6ee20bb 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -101,6 +101,7 @@ public class NotificationManagerService extends INotificationManager.Stub private Vibrator mVibrator = new Vibrator(); // for enabling and disabling notification pulse behavior + private boolean mScreenOn = true; private boolean mInCall = false; private boolean mNotificationPulseEnabled; @@ -344,9 +345,19 @@ public class NotificationManagerService extends INotificationManager.Stub cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart); } } + } else if (action.equals(Intent.ACTION_SCREEN_ON)) { + // Keep track of screen on/off state, but do not turn off the notification light + // until user passes through the lock screen or views the notification. + mScreenOn = true; + } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { + mScreenOn = false; } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { - mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK)); + mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals( + TelephonyManager.EXTRA_STATE_OFFHOOK)); updateNotificationPulse(); + } else if (action.equals(Intent.ACTION_USER_PRESENT)) { + // turn off LED when user passes through lock screen + mNotificationLight.turnOff(); } } }; @@ -417,6 +428,7 @@ public class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); + filter.addAction(Intent.ACTION_USER_PRESENT); mContext.registerReceiver(mIntentReceiver, filter); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -1057,8 +1069,8 @@ public class NotificationManagerService extends INotificationManager.Stub } } - // Don't flash while we are in a call - if (mLedNotification == null || mInCall) { + // Don't flash while we are in a call or screen is on + if (mLedNotification == null || mInCall || mScreenOn) { mNotificationLight.turnOff(); } else { int ledARGB = mLedNotification.notification.ledARGB; diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java index f6c369e..c792b33 100644 --- a/services/java/com/android/server/TextServicesManagerService.java +++ b/services/java/com/android/server/TextServicesManagerService.java @@ -201,7 +201,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE); if (DBG) { - Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr); + Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCodeStr); } final SpellCheckerInfo sci = getCurrentSpellChecker(null); if (sci == null || sci.getSubtypeCount() == 0) { @@ -509,7 +509,8 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { listener.mScLocale, listener.mScListener, listener.mBundle); listener.mTsListener.onServiceConnected(session); } catch (RemoteException e) { - Slog.e(TAG, "Exception in getting the spell checker session: " + e); + Slog.e(TAG, "Exception in getting the spell checker session." + + "Reconnect to the spellchecker. ", e); removeAll(); return; } @@ -579,8 +580,12 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { Slog.d(TAG, "cleanLocked"); } if (mListeners.isEmpty()) { - if (mSpellCheckerBindGroups.containsKey(this)) { - mSpellCheckerBindGroups.remove(this); + final String sciId = mInternalConnection.mSciId; + if (mSpellCheckerBindGroups.containsKey(sciId)) { + if (DBG) { + Slog.d(TAG, "Remove bind group."); + } + mSpellCheckerBindGroups.remove(sciId); } // Unbind service when there is no active clients. mContext.unbindService(mInternalConnection); diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 77f53c2..8af90ff 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -26,7 +26,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; @@ -51,6 +51,7 @@ import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readBooleanAttribute; import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readIntAttribute; import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readLongAttribute; @@ -60,7 +61,6 @@ import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeL import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; -import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; import android.app.IActivityManager; import android.app.INotificationManager; @@ -321,7 +321,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.registerReceiver(mScreenReceiver, screenFilter, null, mHandler); // watch for network interfaces to be claimed - final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); + final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE); mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler); // listen for package/uid changes to update policy diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index bb0a0d1..e0dc96f 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -24,7 +24,7 @@ import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; @@ -239,7 +239,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } // watch for network interfaces to be claimed - final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); + final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION_IMMEDIATE); mContext.registerReceiver(mConnReceiver, connFilter, CONNECTIVITY_INTERNAL, mHandler); // listen for periodic polling events diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 192d32b..1a4caa7 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -2533,6 +2533,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean wallpaperMayMove = win.mViewVisibility != viewVisibility && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0; + wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0; win.mRelayoutCalled = true; final int oldVisibility = win.mViewVisibility; @@ -7925,11 +7926,26 @@ public class WindowManagerService extends IWindowManager.Stub } if (windowAnimationBackgroundColor != 0) { + // If this window that wants black is the current wallpaper + // target, then the black goes *below* the wallpaper so we + // don't cause the wallpaper to suddenly disappear. + WindowState target = windowAnimationBackground; + if (mWallpaperTarget == windowAnimationBackground + || mLowerWallpaperTarget == windowAnimationBackground + || mUpperWallpaperTarget == windowAnimationBackground) { + for (i=0; i<mWindows.size(); i++) { + WindowState w = mWindows.get(i); + if (w.mIsWallpaper) { + target = w; + break; + } + } + } if (mWindowAnimationBackgroundSurface == null) { mWindowAnimationBackgroundSurface = new DimSurface(mFxSession); } mWindowAnimationBackgroundSurface.show(dw, dh, - windowAnimationBackground.mAnimLayer - LAYER_OFFSET_DIM, + target.mAnimLayer - LAYER_OFFSET_DIM, windowAnimationBackgroundColor); } else if (mWindowAnimationBackgroundSurface != null) { mWindowAnimationBackgroundSurface.hide(); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index f8925b8..edbc7b0 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -212,19 +212,6 @@ void Layer::setGeometry(hwc_layer_t* hwcl) } else { hwcl->transform = finalTransform; } -} - -void Layer::setPerFrameData(hwc_layer_t* hwcl) { - const sp<GraphicBuffer>& buffer(mActiveBuffer); - if (buffer == NULL) { - // this can happen if the client never drew into this layer yet, - // or if we ran out of memory. In that case, don't let - // HWC handle it. - hwcl->flags |= HWC_SKIP_LAYER; - hwcl->handle = NULL; - } else { - hwcl->handle = buffer->handle; - } if (isCropped()) { hwcl->sourceCrop.left = mCurrentCrop.left; @@ -232,6 +219,7 @@ void Layer::setPerFrameData(hwc_layer_t* hwcl) { hwcl->sourceCrop.right = mCurrentCrop.right; hwcl->sourceCrop.bottom = mCurrentCrop.bottom; } else { + const sp<GraphicBuffer>& buffer(mActiveBuffer); hwcl->sourceCrop.left = 0; hwcl->sourceCrop.top = 0; if (buffer != NULL) { @@ -244,6 +232,19 @@ void Layer::setPerFrameData(hwc_layer_t* hwcl) { } } +void Layer::setPerFrameData(hwc_layer_t* hwcl) { + const sp<GraphicBuffer>& buffer(mActiveBuffer); + if (buffer == NULL) { + // this can happen if the client never drew into this layer yet, + // or if we ran out of memory. In that case, don't let + // HWC handle it. + hwcl->flags |= HWC_SKIP_LAYER; + hwcl->handle = NULL; + } else { + hwcl->handle = buffer->handle; + } +} + void Layer::onDraw(const Region& clip) const { if (CC_UNLIKELY(mActiveBuffer == 0)) { @@ -416,8 +417,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) return; } - mActiveBuffer = mSurfaceTexture->getCurrentBuffer(); - mSurfaceTexture->getTransformMatrix(mTextureMatrix); + sp<GraphicBuffer> newFrontBuffer(mSurfaceTexture->getCurrentBuffer()); const Rect crop(mSurfaceTexture->getCurrentCrop()); const uint32_t transform(mSurfaceTexture->getCurrentTransform()); @@ -432,7 +432,23 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) mFlinger->invalidateHwcGeometry(); } - mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format); + GLfloat textureMatrix[16]; + mSurfaceTexture->getTransformMatrix(textureMatrix); + if (memcmp(textureMatrix, mTextureMatrix, sizeof(textureMatrix))) { + memcpy(mTextureMatrix, textureMatrix, sizeof(textureMatrix)); + mFlinger->invalidateHwcGeometry(); + } + + uint32_t bufWidth = newFrontBuffer->getWidth(); + uint32_t bufHeight = newFrontBuffer->getHeight(); + if (mActiveBuffer != NULL) { + if (bufWidth != uint32_t(mActiveBuffer->width) || + bufHeight != uint32_t(mActiveBuffer->height)) { + mFlinger->invalidateHwcGeometry(); + } + } + + mCurrentOpacity = getOpacityForFormat(newFrontBuffer->format); if (oldOpacity != isOpaque()) { recomputeVisibleRegions = true; } @@ -446,15 +462,14 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // FIXME: mPostedDirtyRegion = dirty & bounds mPostedDirtyRegion.set(front.w, front.h); + // update active buffer + mActiveBuffer = newFrontBuffer; if ((front.w != front.requested_w) || (front.h != front.requested_h)) { // check that we received a buffer of the right size // (Take the buffer's orientation into account) - sp<GraphicBuffer> newFrontBuffer(mActiveBuffer); - uint32_t bufWidth = newFrontBuffer->getWidth(); - uint32_t bufHeight = newFrontBuffer->getHeight(); if (mCurrentTransform & Transform::ROT_90) { swap(bufWidth, bufHeight); } diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp index 4cc245a..603fb60 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -45,7 +45,6 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) mFlinger(flinger), mFiltering(false), mNeedsFiltering(false), mOrientation(0), - mLeft(0), mTop(0), mTransactionFlags(0), mPremultipliedAlpha(true), mName("unnamed"), mDebug(false), mInvalidate(0) @@ -119,7 +118,7 @@ uint32_t LayerBase::setTransactionFlags(uint32_t flags) { return android_atomic_or(flags, &mTransactionFlags); } -bool LayerBase::setPosition(int32_t x, int32_t y) { +bool LayerBase::setPosition(float x, float y) { if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y) return false; mCurrentState.sequence++; @@ -259,8 +258,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform) mOrientation = tr.getOrientation(); mTransform = tr; mTransformedBounds = tr.makeBounds(w, h); - mLeft = tr.tx(); - mTop = tr.ty(); } void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) @@ -335,17 +332,18 @@ void LayerBase::setGeometry(hwc_layer_t* hwcl) reinterpret_cast<hwc_rect_t const *>( visibleRegionScreen.getArray( &hwcl->visibleRegionScreen.numRects)); -} -void LayerBase::setPerFrameData(hwc_layer_t* hwcl) { - hwcl->compositionType = HWC_FRAMEBUFFER; - hwcl->handle = NULL; hwcl->sourceCrop.left = 0; hwcl->sourceCrop.top = 0; hwcl->sourceCrop.right = mTransformedBounds.width(); hwcl->sourceCrop.bottom = mTransformedBounds.height(); } +void LayerBase::setPerFrameData(hwc_layer_t* hwcl) { + hwcl->compositionType = HWC_FRAMEBUFFER; + hwcl->handle = NULL; +} + void LayerBase::setFiltering(bool filtering) { mFiltering = filtering; @@ -476,10 +474,10 @@ void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const snprintf(buffer, SIZE, "+ %s %p\n" " " - "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " + "z=%9d, pos=(%g,%g), size=(%4d,%4d), " "isOpaque=%1d, needsDithering=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - getTypeId(), this, s.z, tx(), ty(), s.w, s.h, + getTypeId(), this, s.z, s.transform.tx(), s.transform.ty(), s.w, s.h, isOpaque(), needsDithering(), contentDirty, s.alpha, s.flags, s.transform[0][0], s.transform[0][1], diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h index 2cd3a94..d20f06a 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -84,7 +84,7 @@ public: String8 getName() const; // modify current state - bool setPosition(int32_t x, int32_t y); + bool setPosition(float x, float y); bool setLayer(uint32_t z); bool setSize(uint32_t w, uint32_t h); bool setAlpha(uint8_t alpha); @@ -217,8 +217,6 @@ public: inline State& currentState() { return mCurrentState; } int32_t getOrientation() const { return mOrientation; } - int tx() const { return mLeft; } - int ty() const { return mTop; } protected: const GraphicPlane& graphicPlane(int dpy) const; @@ -250,8 +248,6 @@ protected: Transform mTransform; GLfloat mVertices[4][2]; Rect mTransformedBounds; - int mLeft; - int mTop; // these are protected by an external lock State mCurrentState; diff --git a/services/surfaceflinger/Transform.cpp b/services/surfaceflinger/Transform.cpp index 05b7527..ba345ce 100644 --- a/services/surfaceflinger/Transform.cpp +++ b/services/surfaceflinger/Transform.cpp @@ -91,12 +91,12 @@ bool Transform::transformed() const { return type() > TRANSLATE; } -int Transform::tx() const { - return floorf(mMatrix[2][0] + 0.5f); +float Transform::tx() const { + return mMatrix[2][0]; } -int Transform::ty() const { - return floorf(mMatrix[2][1] + 0.5f); +float Transform::ty() const { + return mMatrix[2][1]; } void Transform::reset() { @@ -239,7 +239,9 @@ Region Transform::transform(const Region& reg) const out.set(transform(reg.bounds())); } } else { - out = reg.translate(tx(), ty()); + int xpos = floorf(tx() + 0.5f); + int ypos = floorf(ty() + 0.5f); + out = reg.translate(xpos, ypos); } return out; } diff --git a/services/surfaceflinger/Transform.h b/services/surfaceflinger/Transform.h index 8fa5b86..ec74243 100644 --- a/services/surfaceflinger/Transform.h +++ b/services/surfaceflinger/Transform.h @@ -64,8 +64,8 @@ public: uint32_t getOrientation() const; float const* operator [] (int i) const; // returns column i - int tx() const; - int ty() const; + float tx() const; + float ty() const; // modify the transform void reset(); diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index f67d251..e892b5e 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -18,7 +18,7 @@ package com.android.server; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; @@ -511,7 +511,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { future = expectMeteredIfacesChanged(); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); future.get(); verifyAndReset(); @@ -615,7 +615,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { future = expectMeteredIfacesChanged(TEST_IFACE); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); future.get(); verifyAndReset(); } diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 6dd8cd6..54f3bb0 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -18,7 +18,7 @@ package com.android.server; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.EXTRA_UID; -import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIMAX; @@ -180,7 +180,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -232,7 +232,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -322,7 +322,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); verifyAndReset(); // modify some number on wifi, and trigger poll event @@ -372,7 +372,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); verifyAndReset(); // create some traffic on first network @@ -410,7 +410,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsPoll(); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); verifyAndReset(); @@ -452,7 +452,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); verifyAndReset(); // create some traffic @@ -509,7 +509,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); verifyAndReset(); // create some traffic @@ -541,7 +541,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsPoll(); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); verifyAndReset(); @@ -575,7 +575,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); verifyAndReset(); // create some traffic for two apps @@ -637,7 +637,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectNetworkStatsSummary(buildEmptyStats()); replay(); - mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION_IMMEDIATE)); verifyAndReset(); // create some initial traffic diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 1954172..c59dd3c 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -60,12 +60,13 @@ public class TelephonyManager { /** @hide */ public TelephonyManager(Context context) { + context = context.getApplicationContext(); if (sContext == null) { - sContext = context.getApplicationContext(); + sContext = context; sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( "telephony.registry")); - } else { + } else if (sContext != context) { Log.e(TAG, "Hidden constructor called more than once per process!"); Log.e(TAG, "Original: " + sContext.getPackageName() + ", new: " + context.getPackageName()); diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java index bce9991..955849d 100644 --- a/telephony/java/com/android/internal/telephony/IccCard.java +++ b/telephony/java/com/android/internal/telephony/IccCard.java @@ -470,19 +470,6 @@ public abstract class IccCard { } else if (isIccCardAdded) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null)); } - - - - /* - * TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH? - */ - if (oldState != State.READY && newState == State.READY && - mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) { - if (mPhone.mIccRecords instanceof SIMRecords) { - ((SIMRecords)mPhone.mIccRecords).onSimReady(); - } - } - } private void onIccSwap(boolean isAdded) { @@ -720,17 +707,14 @@ public abstract class IccCard { currentRadioState == RadioState.SIM_NOT_READY || currentRadioState == RadioState.RUIM_NOT_READY || currentRadioState == RadioState.NV_NOT_READY || - (currentRadioState == RadioState.NV_READY && - (mPhone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE))) { + currentRadioState == RadioState.NV_READY) { return IccCard.State.NOT_READY; } if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT || currentRadioState == RadioState.SIM_READY || currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT || - currentRadioState == RadioState.RUIM_READY || - (currentRadioState == RadioState.NV_READY && - (mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE))) { + currentRadioState == RadioState.RUIM_READY) { State csimState = getAppState(mIccCardStatus.getCdmaSubscriptionAppIndex()); diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index e17d98d..6743da0 100755 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -36,6 +36,7 @@ import com.android.internal.util.BitwiseOutputStream; import android.content.res.Resources; +import java.util.TimeZone; /** @@ -231,7 +232,7 @@ public final class BearerData { public static class TimeStamp extends Time { public TimeStamp() { - super(Time.TIMEZONE_UTC); + super(TimeZone.getDefault().getID()); // 3GPP2 timestamps use the local timezone } public static TimeStamp fromByteArray(byte[] data) { diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index 28034cc..d84715e 100755 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -181,11 +181,7 @@ public class SIMRecords extends IccRecords { // recordsToLoad is set to 0 because no requests are made yet recordsToLoad = 0; - // SIMRecord is used by CDMA+LTE mode, and SIM_READY event - // will be subscribed by CdmaLteServiceStateTracker. - if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) { - p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null); - } + p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null); p.mCM.registerForOffOrNotAvailable( this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null); @@ -199,9 +195,7 @@ public class SIMRecords extends IccRecords { @Override public void dispose() { //Unregister for all events - if (phone.getLteOnCdmaMode() != Phone.LTE_ON_CDMA_TRUE) { - phone.mCM.unregisterForSIMReady(this); - } + phone.mCM.unregisterForSIMReady(this); phone.mCM.unregisterForOffOrNotAvailable( this); phone.mCM.unregisterForIccRefresh(this); } diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java index 643f709..1fa278a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java @@ -49,7 +49,6 @@ public final class SimCard extends IccCard { updateStateProperty(); if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) { - mPhone.mCM.registerForNVReady(mHandler, EVENT_ICC_READY, null); mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null); } } @@ -63,7 +62,6 @@ public final class SimCard extends IccCard { mPhone.mCM.unregisterForSIMReady(mHandler); if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) { - mPhone.mCM.unregisterForNVReady(mHandler); mPhone.mCM.unregisterForIccStatusChanged(mHandler); } } diff --git a/tests/BandwidthTests/Android.mk b/tests/BandwidthTests/Android.mk new file mode 100644 index 0000000..7bc5f857 --- /dev/null +++ b/tests/BandwidthTests/Android.mk @@ -0,0 +1,24 @@ +# +# Copyright (C) 2011 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. +# +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_PACKAGE_NAME := BandwidthEnforcementTest +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +include $(BUILD_PACKAGE) diff --git a/tests/BandwidthTests/AndroidManifest.xml b/tests/BandwidthTests/AndroidManifest.xml new file mode 100644 index 0000000..19f38ca --- /dev/null +++ b/tests/BandwidthTests/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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.tests.bandwidthenforcement"> + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + <application> + <activity android:name=".BandwidthEnforcementTestActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <!-- adb shell am startservice -n com.android.tests.bandwidthenforcement/.BandwidthEnforcementTestService --> + <service android:name=".BandwidthEnforcementTestService" android:exported="true" /> + </application> +</manifest> diff --git a/tests/BandwidthTests/res/layout/main.xml b/tests/BandwidthTests/res/layout/main.xml new file mode 100644 index 0000000..3392b21 --- /dev/null +++ b/tests/BandwidthTests/res/layout/main.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/app_name" /> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/app_desc" /> +</LinearLayout> diff --git a/tests/BandwidthTests/res/values/strings.xml b/tests/BandwidthTests/res/values/strings.xml new file mode 100644 index 0000000..a4a78c2 --- /dev/null +++ b/tests/BandwidthTests/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_name">BandwidthEnforcementTest</string> + <string name="app_desc">Tries several tricks to get Internet access.</string> + <string name="start">Start</string> + <string name="stop">Stop</string> +</resources> diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestActivity.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestActivity.java new file mode 100644 index 0000000..f0e43ac --- /dev/null +++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestActivity.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 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.tests.bandwidthenforcement; + +import android.app.Activity; +import android.os.Bundle; + +public class BandwidthEnforcementTestActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +} diff --git a/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java new file mode 100644 index 0000000..a2427f5 --- /dev/null +++ b/tests/BandwidthTests/src/com/android/tests/bandwidthenforcement/BandwidthEnforcementTestService.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2011 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.tests.bandwidthenforcement; + +import android.app.IntentService; +import android.content.Intent; +import android.net.SntpClient; +import android.os.Environment; +import android.util.Log; + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.URL; +import java.util.Random; + +import libcore.io.Streams; + +/* + * Test Service that tries to connect to the web via different methods and outputs the results to + * the log and a output file. + */ +public class BandwidthEnforcementTestService extends IntentService { + private static final String TAG = "BandwidthEnforcementTestService"; + private static final String OUTPUT_FILE = "BandwidthEnforcementTestServiceOutputFile"; + + public BandwidthEnforcementTestService() { + super(TAG); + } + + @Override + protected void onHandleIntent(Intent intent) { + Log.d(TAG, "Trying to establish a connection."); + // Read output file path from intent. + String outputFile = intent.getStringExtra(OUTPUT_FILE); + dumpResult("testUrlConnection", testUrlConnection(), outputFile); + dumpResult("testUrlConnectionv6", testUrlConnectionv6(), outputFile); + dumpResult("testSntp", testSntp(), outputFile); + dumpResult("testDns", testDns(), outputFile); + } + + public static void dumpResult(String tag, boolean result, String outputFile) { + Log.d(TAG, "Test output file: " + outputFile); + try { + if (outputFile != null){ + File extStorage = Environment.getExternalStorageDirectory(); + File outFile = new File(extStorage, outputFile); + FileWriter writer = new FileWriter(outFile, true); + BufferedWriter out = new BufferedWriter(writer); + if (result) { + out.append(tag + ":fail\n"); + } else { + out.append(tag + ":pass\n"); + } + out.close(); + } + if (result) { + Log.e(TAG, tag + " FAILURE ===================="); + Log.e(TAG, tag + " FAILURE was able to use data"); + Log.e(TAG, tag + " FAILURE ===================="); + } else { + Log.d(TAG, tag + " success; unable to use data"); + } + } catch (IOException e) { + Log.e(TAG, "Could not write file " + e.getMessage()); + } + } + + /** + * Tests a normal http url connection. + * @return true if it was able to connect, false otherwise. + */ + public static boolean testUrlConnection() { + try { + final HttpURLConnection conn = (HttpURLConnection) new URL("http://www.google.com/") + .openConnection(); + try { + conn.connect(); + final String content = Streams.readFully( + new InputStreamReader(conn.getInputStream())); + if (content.contains("Google")) { + return true; + } + } finally { + conn.disconnect(); + } + } catch (IOException e) { + Log.d(TAG, "error: " + e); + } + return false; + } + + /** + * Tests a ipv6 http url connection. + * @return true if it was able to connect, false otherwise. + */ + public static boolean testUrlConnectionv6() { + try { + final HttpURLConnection conn = (HttpURLConnection) new URL("http://ipv6.google.com/") + .openConnection(); + try { + conn.connect(); + final String content = Streams.readFully( + new InputStreamReader(conn.getInputStream())); + if (content.contains("Google")) { + return true; + } + } finally { + conn.disconnect(); + } + } catch (IOException e) { + Log.d(TAG, "error: " + e); + } + return false; + } + + /** + * Tests to connect via sntp. + * @return true if it was able to connect, false otherwise. + */ + public static boolean testSntp() { + final SntpClient client = new SntpClient(); + if (client.requestTime("0.pool.ntp.org", 10000)) { + return true; + } + return false; + } + + /** + * Tests dns query. + * @return true if it was able to connect, false otherwise. + */ + public static boolean testDns() { + try { + final DatagramSocket socket = new DatagramSocket(); + try { + socket.setSoTimeout(10000); + + final byte[] query = buildDnsQuery("www", "android", "com"); + final DatagramPacket queryPacket = new DatagramPacket( + query, query.length, InetAddress.parseNumericAddress("8.8.8.8"), 53); + socket.send(queryPacket); + + final byte[] reply = new byte[query.length]; + final DatagramPacket replyPacket = new DatagramPacket(reply, reply.length); + socket.receive(replyPacket); + return true; + + } finally { + socket.close(); + } + } catch (IOException e) { + Log.d(TAG, "error: " + e); + } + return false; + } + + /** + * Helper method to build a dns query + * @param query the dns strings of the server + * @return the byte array of the dns query to send + * @throws IOException + */ + private static byte[] buildDnsQuery(String... query) throws IOException { + final Random random = new Random(); + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + + final byte[] id = new byte[2]; + random.nextBytes(id); + + out.write(id); + out.write(new byte[] { 0x01, 0x00 }); + out.write(new byte[] { 0x00, 0x01 }); + out.write(new byte[] { 0x00, 0x00 }); + out.write(new byte[] { 0x00, 0x00 }); + out.write(new byte[] { 0x00, 0x00 }); + + for (String phrase : query) { + final byte[] bytes = phrase.getBytes("US-ASCII"); + out.write(bytes.length); + out.write(bytes); + } + out.write(0x00); + + out.write(new byte[] { 0x00, 0x01 }); + out.write(new byte[] { 0x00, 0x01 }); + + return out.toByteArray(); + } +} diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java index 3b3cbf3..49effa8 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -72,6 +72,7 @@ import javax.sip.TransactionUnavailableException; import javax.sip.address.Address; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; +import javax.sip.header.ContactHeader; import javax.sip.header.ExpiresHeader; import javax.sip.header.FromHeader; import javax.sip.header.HeaderAddress; @@ -873,16 +874,21 @@ class SipSessionGroup implements SipListener { } private int getExpiryTime(Response response) { - int expires = EXPIRY_TIME; - ExpiresHeader expiresHeader = (ExpiresHeader) - response.getHeader(ExpiresHeader.NAME); - if (expiresHeader != null) expires = expiresHeader.getExpires(); - expiresHeader = (ExpiresHeader) - response.getHeader(MinExpiresHeader.NAME); - if (expiresHeader != null) { - expires = Math.max(expires, expiresHeader.getExpires()); - } - return expires; + int time = -1; + ContactHeader contact = (ContactHeader) response.getHeader(ContactHeader.NAME); + if (contact != null) { + time = contact.getExpires(); + } + ExpiresHeader expires = (ExpiresHeader) response.getHeader(ExpiresHeader.NAME); + if (expires != null && (time < 0 || time > expires.getExpires())) { + time = expires.getExpires(); + } + expires = (ExpiresHeader) response.getHeader(MinExpiresHeader.NAME); + if (expires != null && time < expires.getExpires()) { + time = expires.getExpires(); + } + Log.v(TAG, "Expiry time = " + time); + return (time > 0) ? time : EXPIRY_TIME; } private boolean registeringToReady(EventObject evt) diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index 8d327d0..2cd54d9 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -217,6 +217,19 @@ public class WifiNative { return doBooleanCommand("WPS_PIN any " + pin); } + public static boolean setPersistentReconnect(boolean enabled) { + int value = (enabled == true) ? 1 : 0; + return WifiNative.doBooleanCommand("SET persistent_reconnect " + value); + } + + public static boolean setDeviceName(String name) { + return WifiNative.doBooleanCommand("SET device_name " + name); + } + + public static boolean setDeviceType(String type) { + return WifiNative.doBooleanCommand("SET device_type " + type); + } + public static boolean p2pFind() { return doBooleanCommand("P2P_FIND"); } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index bdf0120..f67680d 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -50,6 +50,7 @@ import android.os.Message; import android.os.Messenger; import android.os.ServiceManager; import android.os.SystemProperties; +import android.provider.Settings; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; @@ -126,6 +127,8 @@ public class WifiP2pService extends IWifiP2pManager.Stub { private static final int WIFI_DISABLE_USER_ACCEPT = BASE + 11; private final boolean mP2pSupported; + private final String mDeviceType; + private String mDeviceName; private NetworkInfo mNetworkInfo; @@ -142,6 +145,9 @@ public class WifiP2pService extends IWifiP2pManager.Stub { mP2pSupported = mContext.getResources().getBoolean( com.android.internal.R.bool.config_wifi_p2p_support); + mDeviceType = mContext.getResources().getString( + com.android.internal.R.string.config_wifi_p2p_device_type); + mDeviceName = getDefaultDeviceName(); mP2pStateMachine = new P2pStateMachine(TAG, mP2pSupported); mP2pStateMachine.start(); @@ -182,6 +188,14 @@ public class WifiP2pService extends IWifiP2pManager.Stub { "WifiP2pService"); } + /* We use the 4 digits of the ANDROID_ID to have a friendly + * default that has low likelihood of collision with a peer */ + private String getDefaultDeviceName() { + String id = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.ANDROID_ID); + return "Android_" + id.substring(0,4); + } + /** * Get a reference to handler. This is used by a client to establish * an AsyncChannel communication with WifiP2pService @@ -577,6 +591,7 @@ public class WifiP2pService extends IWifiP2pManager.Stub { mNetworkInfo.setIsAvailable(true); //Start listening for new connections WifiNative.p2pListen(); + initializeP2pSettings(); } @Override @@ -637,6 +652,22 @@ public class WifiP2pService extends IWifiP2pManager.Stub { if (mPeers.clear()) sendP2pPeersChangedBroadcast(); transitionTo(mP2pDisabledState); sendMessageDelayed(WifiP2pManager.ENABLE_P2P, P2P_RESTART_INTERVAL_MSECS); + break; + case WifiMonitor.P2P_GROUP_STARTED_EVENT: + mGroup = (WifiP2pGroup) message.obj; + if (DBG) logd(getName() + " group started"); + if (mGroup.isGroupOwner()) { + startDhcpServer(mGroup.getInterface()); + } else { + mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext, + P2pStateMachine.this, mGroup.getInterface()); + mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); + WifiP2pDevice groupOwner = mGroup.getOwner(); + updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED); + sendP2pPeersChangedBroadcast(); + } + transitionTo(mGroupCreatedState); + break; default: return NOT_HANDLED; } @@ -708,21 +739,6 @@ public class WifiP2pService extends IWifiP2pManager.Stub { sendP2pPeersChangedBroadcast(); transitionTo(mInactiveState); break; - case WifiMonitor.P2P_GROUP_STARTED_EVENT: - mGroup = (WifiP2pGroup) message.obj; - if (DBG) logd(getName() + " group started"); - if (mGroup.isGroupOwner()) { - startDhcpServer(mGroup.getInterface()); - } else { - mDhcpStateMachine = DhcpStateMachine.makeDhcpStateMachine(mContext, - P2pStateMachine.this, mGroup.getInterface()); - mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP); - WifiP2pDevice groupOwner = mGroup.getOwner(); - updateDeviceStatus(groupOwner.deviceAddress, Status.CONNECTED); - sendP2pPeersChangedBroadcast(); - } - transitionTo(mGroupCreatedState); - break; case GROUP_NEGOTIATION_TIMED_OUT: if (mGroupNegotiationTimeoutIndex == message.arg1) { if (DBG) logd("Group negotiation timed out"); @@ -760,25 +776,33 @@ public class WifiP2pService extends IWifiP2pManager.Stub { //After a GO setup, STA connected event comes with interface address String interfaceAddress = (String) message.obj; String deviceAddress = getDeviceAddress(interfaceAddress); - mGroup.addClient(deviceAddress); - updateDeviceStatus(deviceAddress, Status.CONNECTED); - if (DBG) logd(getName() + " ap sta connected"); - sendP2pPeersChangedBroadcast(); + if (deviceAddress != null) { + mGroup.addClient(deviceAddress); + updateDeviceStatus(deviceAddress, Status.CONNECTED); + if (DBG) logd(getName() + " ap sta connected"); + sendP2pPeersChangedBroadcast(); + } else { + loge("Connect on unknown device address : " + interfaceAddress); + } break; case WifiMonitor.AP_STA_DISCONNECTED_EVENT: interfaceAddress = (String) message.obj; deviceAddress = getDeviceAddress(interfaceAddress); - updateDeviceStatus(deviceAddress, Status.AVAILABLE); - if (mGroup.removeClient(deviceAddress)) { - if (DBG) logd("Removed client " + deviceAddress); - sendP2pPeersChangedBroadcast(); - } else { - if (DBG) logd("Failed to remove client " + deviceAddress); - for (WifiP2pDevice c : mGroup.getClientList()) { - if (DBG) logd("client " + c.deviceAddress); + if (deviceAddress != null) { + updateDeviceStatus(deviceAddress, Status.AVAILABLE); + if (mGroup.removeClient(deviceAddress)) { + if (DBG) logd("Removed client " + deviceAddress); + sendP2pPeersChangedBroadcast(); + } else { + if (DBG) logd("Failed to remove client " + deviceAddress); + for (WifiP2pDevice c : mGroup.getClientList()) { + if (DBG) logd("client " + c.deviceAddress); + } } + if (DBG) loge(getName() + " ap sta disconnected"); + } else { + loge("Disconnect on unknown device address : " + interfaceAddress); } - if (DBG) loge(getName() + " ap sta disconnected"); break; case DhcpStateMachine.CMD_POST_DHCP_ACTION: DhcpInfoInternal dhcpInfo = (DhcpInfoInternal) message.obj; @@ -857,6 +881,9 @@ public class WifiP2pService extends IWifiP2pManager.Stub { case WifiMonitor.P2P_PROV_DISC_ENTER_PIN_EVENT: notifyP2pProvDiscPinRequest((WifiP2pDevice) message.obj); break; + case WifiMonitor.P2P_GROUP_STARTED_EVENT: + Slog.e(TAG, "Duplicate group creation event notice, ignore"); + break; case WifiP2pManager.WPS_PBC: WifiNative.wpsPbc(); break; @@ -1117,6 +1144,12 @@ public class WifiP2pService extends IWifiP2pManager.Stub { return null; } + private void initializeP2pSettings() { + WifiNative.setPersistentReconnect(true); + WifiNative.setDeviceName(mDeviceName); + WifiNative.setDeviceType(mDeviceType); + } + //State machine initiated requests can have replyTo set to null indicating //there are no recepients, we ignore those reply actions private void replyToMessage(Message msg, int what) { |