diff options
192 files changed, 4638 insertions, 1205 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index e25f1d7804ce..2178c38efd44 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1609,6 +1609,53 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGE_PERMISSIONS"; /** + * Activity action: Launch UI to review permissions for an app. + * The system uses this intent if permission review for apps not + * supporting the new runtime permissions model is enabled. In + * this mode a permission review is required before any of the + * app components can run. + * <p> + * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose + * permissions will be reviewed (mandatory). + * </p> + * <p> + * Input: {@link #EXTRA_INTENT} specifies a pending intent to + * be fired after the permission review (optional). + * </p> + * <p> + * Input: {@link #EXTRA_REMOTE_CALLBACK} specifies a callback to + * be invoked after the permission review (optional). + * </p> + * <p> + * Input: {@link #EXTRA_RESULT_NEEDED} specifies whether the intent + * passed via {@link #EXTRA_INTENT} needs a result (optional). + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @see #EXTRA_PACKAGE_NAME + * @see #EXTRA_INTENT + * @see #EXTRA_REMOTE_CALLBACK + * @see #EXTRA_RESULT_NEEDED + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REVIEW_PERMISSIONS = + "android.intent.action.REVIEW_PERMISSIONS"; + + /** + * Intent extra: A callback for reporting remote result as a bundle. + * <p> + * Type: IRemoteCallback + * </p> + * + * @hide + */ + public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; + + /** * Intent extra: An app package name. * <p> * Type: String @@ -1620,6 +1667,16 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; /** + * Intent extra: An extra for specifying whether a result is needed. + * <p> + * Type: boolean + * </p> + * + * @hide + */ + public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; + + /** * Broadcast action that requests current permission granted information. It will respond * to the request by sending a broadcast with action defined by * {@link #EXTRA_GET_PERMISSIONS_RESPONSE_INTENT}. The response will contain diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0c28008b1b87..aa960a400ebb 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2018,7 +2018,6 @@ public abstract class PackageManager { */ public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4; - /** * Permission flag: The permission is granted by default because it * enables app functionality that is expected to work out-of-the-box @@ -2030,6 +2029,14 @@ public abstract class PackageManager { public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5; /** + * Permission flag: The permission has to be reviewed before any of + * the app components can run. + * + * @hide + */ + public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 1 << 6; + + /** * Mask for all permission flags. * * @hide @@ -4808,6 +4815,7 @@ public abstract class PackageManager { case FLAG_PERMISSION_USER_SET: return "USER_SET"; case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE"; case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED"; + case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED"; default: return Integer.toString(flag); } } diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 905ac5e093d8..8bf20bfa5207 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -122,4 +122,13 @@ public abstract class PackageManagerInternal { * @param packageList List of package names to keep cached. */ public abstract void setKeepUninstalledPackages(List<String> packageList); + + /** + * Gets whether some of the permissions used by this package require a user + * review before any of the app components can run. + * @param packageName The package name for which to check. + * @param userId The user under which to check. + * @return True a permissions review is required. + */ + public abstract boolean isPermissionsReviewRequired(String packageName, int userId); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index f7c8662515fc..de8b6905c954 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -788,6 +788,18 @@ public class Build { SystemProperties.getInt("ro.debuggable", 0) == 1; /** + * Specifies whether the permissions needed by a legacy app should be + * reviewed before any of its components can run. A legacy app is one + * with targetSdkVersion < 23, i.e apps using the old permission model. + * If review is not required, permissions are reviewed before the app + * is installed. + * + * @hide + */ + public static final boolean PERMISSIONS_REVIEW_REQUIRED = + SystemProperties.getInt("ro.permission_review_required", 0) == 1; + + /** * Returns the version string for the radio firmware. May return * null (if, for instance, the radio is not currently on). */ diff --git a/core/java/android/os/IProcessInfoService.aidl b/core/java/android/os/IProcessInfoService.aidl index c98daa282ec4..62237f567841 100644 --- a/core/java/android/os/IProcessInfoService.aidl +++ b/core/java/android/os/IProcessInfoService.aidl @@ -25,5 +25,12 @@ interface IProcessInfoService * to indicate that no process with the given PID exists. */ void getProcessStatesFromPids(in int[] pids, out int[] states); + + /** + * For each PID in the given input array, write the current process state and OOM score + * for that process into the output arrays, or ActivityManager.PROCESS_STATE_NONEXISTENT + * in the states array to indicate that no process with the given PID exists. + */ + void getProcessStatesAndOomScoresFromPids(in int[] pids, out int[] states, out int[] scores); } diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java index ca95bdf529cd..89e30a96f65d 100644 --- a/core/java/android/os/RemoteCallback.java +++ b/core/java/android/os/RemoteCallback.java @@ -16,88 +16,84 @@ package android.os; +import android.annotation.NonNull; +import android.annotation.Nullable; + /** - * TODO: Make this a public API? Let's see how it goes with a few use - * cases first. * @hide */ -public abstract class RemoteCallback implements Parcelable { - final Handler mHandler; - final IRemoteCallback mTarget; - - class DeliverResult implements Runnable { - final Bundle mResult; - - DeliverResult(Bundle result) { - mResult = result; - } - - public void run() { - onResult(mResult); - } +public final class RemoteCallback implements Parcelable { + + public interface OnResultListener { + public void onResult(Bundle result); } - - class LocalCallback extends IRemoteCallback.Stub { - public void sendResult(Bundle bundle) { - mHandler.post(new DeliverResult(bundle)); - } + + private final OnResultListener mListener; + private final Handler mHandler; + private final IRemoteCallback mCallback; + + public RemoteCallback(OnResultListener listener) { + this(listener, null); } - - static class RemoteCallbackProxy extends RemoteCallback { - RemoteCallbackProxy(IRemoteCallback target) { - super(target); - } - - protected void onResult(Bundle bundle) { + + public RemoteCallback(@NonNull OnResultListener listener, @Nullable Handler handler) { + if (listener == null) { + throw new NullPointerException("listener cannot be null"); } - } - - public RemoteCallback(Handler handler) { + mListener = listener; mHandler = handler; - mTarget = new LocalCallback(); + mCallback = new IRemoteCallback.Stub() { + @Override + public void sendResult(Bundle data) { + RemoteCallback.this.sendResult(data); + } + }; } - - RemoteCallback(IRemoteCallback target) { + + RemoteCallback(Parcel parcel) { + mListener = null; mHandler = null; - mTarget = target; - } - - public void sendResult(Bundle bundle) throws RemoteException { - mTarget.sendResult(bundle); + mCallback = IRemoteCallback.Stub.asInterface( + parcel.readStrongBinder()); } - - protected abstract void onResult(Bundle bundle); - - public boolean equals(Object otherObj) { - if (otherObj == null) { - return false; - } - try { - return mTarget.asBinder().equals(((RemoteCallback)otherObj) - .mTarget.asBinder()); - } catch (ClassCastException e) { + + public void sendResult(@Nullable final Bundle result) { + // Do local dispatch + if (mListener != null) { + if (mHandler != null) { + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onResult(result); + } + }); + } else { + mListener.onResult(result); + } + // Do remote dispatch + } else { + try { + mCallback.sendResult(result); + } catch (RemoteException e) { + /* ignore */ + } } - return false; } - - public int hashCode() { - return mTarget.asBinder().hashCode(); - } - + + @Override public int describeContents() { return 0; } - public void writeToParcel(Parcel out, int flags) { - out.writeStrongBinder(mTarget.asBinder()); + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeStrongBinder(mCallback.asBinder()); } public static final Parcelable.Creator<RemoteCallback> CREATOR = new Parcelable.Creator<RemoteCallback>() { - public RemoteCallback createFromParcel(Parcel in) { - IBinder target = in.readStrongBinder(); - return target != null ? new RemoteCallbackProxy( - IRemoteCallback.Stub.asInterface(target)) : null; + public RemoteCallback createFromParcel(Parcel parcel) { + return new RemoteCallback(parcel); } public RemoteCallback[] newArray(int size) { diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 8ae899f4d453..d53bb0d2aa18 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -67,7 +67,7 @@ import java.util.List; * @see DocumentsProvider */ public final class DocumentsContract { - private static final String TAG = "Documents"; + private static final String TAG = "DocumentsContract"; // content://com.example/root/ // content://com.example/root/sdcard/ @@ -591,6 +591,12 @@ public final class DocumentsContract { */ public static final String EXTRA_ERROR = "error"; + /** + * Optional result (I'm thinking boolean) answer to a question. + * {@hide} + */ + public static final String EXTRA_RESULT = "result"; + /** {@hide} */ public static final String METHOD_CREATE_DOCUMENT = "android:createDocument"; /** {@hide} */ @@ -601,6 +607,8 @@ public final class DocumentsContract { public static final String METHOD_COPY_DOCUMENT = "android:copyDocument"; /** {@hide} */ public static final String METHOD_MOVE_DOCUMENT = "android:moveDocument"; + /** {@hide} */ + public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument"; /** {@hide} */ public static final String EXTRA_URI = "uri"; @@ -1025,6 +1033,24 @@ public final class DocumentsContract { return out.getParcelable(DocumentsContract.EXTRA_URI); } + /** {@hide} */ + public static boolean isChildDocument(ContentProviderClient client, Uri parentDocumentUri, + Uri childDocumentUri) throws RemoteException { + + final Bundle in = new Bundle(); + in.putParcelable(DocumentsContract.EXTRA_URI, parentDocumentUri); + in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, childDocumentUri); + + final Bundle out = client.call(METHOD_IS_CHILD_DOCUMENT, null, in); + if (out == null) { + throw new RemoteException("Failed to get a reponse from isChildDocument query."); + } + if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) { + throw new RemoteException("Response did not include result field.."); + } + return out.getBoolean(DocumentsContract.EXTRA_RESULT); + } + /** * Change the display name of an existing document. * <p> diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index f01073bbd43d..e25ba35c8bb6 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -16,11 +16,12 @@ package android.provider; +import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT; import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT; import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT; -import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT; -import static android.provider.DocumentsContract.METHOD_COPY_DOCUMENT; +import static android.provider.DocumentsContract.METHOD_IS_CHILD_DOCUMENT; import static android.provider.DocumentsContract.METHOD_MOVE_DOCUMENT; +import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT; import static android.provider.DocumentsContract.buildDocumentUri; import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree; import static android.provider.DocumentsContract.buildTreeDocumentUri; @@ -688,6 +689,16 @@ public abstract class DocumentsProvider extends ContentProvider { return super.call(method, arg, extras); } + try { + return callUnchecked(method, arg, extras); + } catch (FileNotFoundException e) { + throw new IllegalStateException("Failed call " + method, e); + } + } + + private Bundle callUnchecked(String method, String arg, Bundle extras) + throws FileNotFoundException { + final Context context = getContext(); final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI); final String authority = documentUri.getAuthority(); @@ -697,109 +708,120 @@ public abstract class DocumentsProvider extends ContentProvider { throw new SecurityException( "Requested authority " + authority + " doesn't match provider " + mAuthority); } - enforceTree(documentUri); final Bundle out = new Bundle(); - try { - if (METHOD_CREATE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); - final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE); - final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); - final String newDocumentId = createDocument(documentId, mimeType, displayName); + // If the URI is a tree URI performs some validation. + enforceTree(documentUri); - // No need to issue new grants here, since caller either has - // manage permission or a prefix grant. We might generate a - // tree style URI if that's how they called us. - final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, - newDocumentId); - out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); + if (METHOD_IS_CHILD_DOCUMENT.equals(method)) { + enforceReadPermissionInner(documentUri, getCallingPackage(), null); - } else if (METHOD_RENAME_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); + final Uri childUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); + final String childAuthority = childUri.getAuthority(); + final String childId = DocumentsContract.getDocumentId(childUri); - final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); - final String newDocumentId = renameDocument(documentId, displayName); + out.putBoolean( + DocumentsContract.EXTRA_RESULT, + mAuthority.equals(childAuthority) + && isChildDocument(documentId, childId)); - if (newDocumentId != null) { - final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, - newDocumentId); + } else if (METHOD_CREATE_DOCUMENT.equals(method)) { + enforceWritePermissionInner(documentUri, getCallingPackage(), null); - // If caller came in with a narrow grant, issue them a - // narrow grant for the newly renamed document. - if (!isTreeUri(newDocumentUri)) { - final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, - documentUri); - context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); - } + final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE); + final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); + final String newDocumentId = createDocument(documentId, mimeType, displayName); + + // No need to issue new grants here, since caller either has + // manage permission or a prefix grant. We might generate a + // tree style URI if that's how they called us. + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, + newDocumentId); + out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); + + } else if (METHOD_RENAME_DOCUMENT.equals(method)) { + enforceWritePermissionInner(documentUri, getCallingPackage(), null); - out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); + final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); + final String newDocumentId = renameDocument(documentId, displayName); + + if (newDocumentId != null) { + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, + newDocumentId); - // Original document no longer exists, clean up any grants - revokeDocumentPermission(documentId); + // If caller came in with a narrow grant, issue them a + // narrow grant for the newly renamed document. + if (!isTreeUri(newDocumentUri)) { + final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, + documentUri); + context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); } - } else if (METHOD_DELETE_DOCUMENT.equals(method)) { - enforceWritePermissionInner(documentUri, getCallingPackage(), null); - deleteDocument(documentId); + out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); - // Document no longer exists, clean up any grants + // Original document no longer exists, clean up any grants revokeDocumentPermission(documentId); + } - } else if (METHOD_COPY_DOCUMENT.equals(method)) { - final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); - final String targetId = DocumentsContract.getDocumentId(targetUri); + } else if (METHOD_DELETE_DOCUMENT.equals(method)) { + enforceWritePermissionInner(documentUri, getCallingPackage(), null); + deleteDocument(documentId); - enforceReadPermissionInner(documentUri, getCallingPackage(), null); - enforceWritePermissionInner(targetUri, getCallingPackage(), null); + // Document no longer exists, clean up any grants + revokeDocumentPermission(documentId); - final String newDocumentId = copyDocument(documentId, targetId); + } else if (METHOD_COPY_DOCUMENT.equals(method)) { + final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); + final String targetId = DocumentsContract.getDocumentId(targetUri); - if (newDocumentId != null) { - final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, - newDocumentId); + enforceReadPermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(targetUri, getCallingPackage(), null); - if (!isTreeUri(newDocumentUri)) { - final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, - documentUri); - context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); - } + final String newDocumentId = copyDocument(documentId, targetId); + + if (newDocumentId != null) { + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, + newDocumentId); - out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); + if (!isTreeUri(newDocumentUri)) { + final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, + documentUri); + context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); } - } else if (METHOD_MOVE_DOCUMENT.equals(method)) { - final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); - final String targetId = DocumentsContract.getDocumentId(targetUri); + out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); + } - enforceReadPermissionInner(documentUri, getCallingPackage(), null); - enforceWritePermissionInner(targetUri, getCallingPackage(), null); + } else if (METHOD_MOVE_DOCUMENT.equals(method)) { + final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI); + final String targetId = DocumentsContract.getDocumentId(targetUri); - final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME); - final String newDocumentId = moveDocument(documentId, targetId); + enforceReadPermissionInner(documentUri, getCallingPackage(), null); + enforceWritePermissionInner(targetUri, getCallingPackage(), null); - if (newDocumentId != null) { - final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, - newDocumentId); + final String newDocumentId = moveDocument(documentId, targetId); - if (!isTreeUri(newDocumentUri)) { - final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, - documentUri); - context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); - } + if (newDocumentId != null) { + final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri, + newDocumentId); - out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); + if (!isTreeUri(newDocumentUri)) { + final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, + documentUri); + context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags); } - // Original document no longer exists, clean up any grants - revokeDocumentPermission(documentId); - - } else { - throw new UnsupportedOperationException("Method not supported " + method); + out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri); } - } catch (FileNotFoundException e) { - throw new IllegalStateException("Failed call " + method, e); + + // Original document no longer exists, clean up any grants + revokeDocumentPermission(documentId); + + } else { + throw new UnsupportedOperationException("Method not supported " + method); } + return out; } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 7bab446d4705..3a004690d2cb 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.ComponentCallbacks; import android.content.Context; import android.content.res.Configuration; @@ -28,7 +29,9 @@ import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; +import android.graphics.drawable.AnimatedVectorDrawable; import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.Size; import android.view.ContextThemeWrapper; @@ -36,6 +39,7 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.View.MeasureSpec; import android.view.ViewGroup; @@ -45,6 +49,8 @@ import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.Transformation; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; @@ -277,70 +283,56 @@ public final class FloatingToolbar { * A popup window used by the floating toolbar. * * This class is responsible for the rendering/animation of the floating toolbar. - * It can hold one of 2 panels (i.e. main panel and overflow panel) at a time. - * It delegates specific panel functionality to the appropriate panel. + * It holds 2 panels (i.e. main panel and overflow panel) and an overflow button + * to transition between panels. */ private static final class FloatingToolbarPopup { - public static final int OVERFLOW_DIRECTION_UP = 0; - public static final int OVERFLOW_DIRECTION_DOWN = 1; + /* Minimum and maximum number of items allowed in the overflow. */ + private static final int MIN_OVERFLOW_SIZE = 2; + private static final int MAX_OVERFLOW_SIZE = 4; + + /* The duration of the overflow button vector animation duration. */ + private static final int OVERFLOW_BUTTON_ANIMATION_DELAY = 400; private final Context mContext; - private final View mParent; + private final View mParent; // Parent for the popup window. private final PopupWindow mPopupWindow; - private final ViewGroup mContentContainer; + + /* Margins between the popup window and it's content. */ private final int mMarginHorizontal; private final int mMarginVertical; - private final Animation.AnimationListener mOnOverflowOpened = - new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) {} - - @Override - public void onAnimationEnd(Animation animation) { - setOverflowPanelAsContent(); - mOverflowPanel.fadeIn(true); - } - - @Override - public void onAnimationRepeat(Animation animation) {} - }; - private final Animation.AnimationListener mOnOverflowClosed = - new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) {} - - @Override - public void onAnimationEnd(Animation animation) { - setMainPanelAsContent(); - mMainPanel.fadeIn(true); - } - - @Override - public void onAnimationRepeat(Animation animation) { - } - }; + /* View components */ + private final ViewGroup mContentContainer; // holds all contents. + private final ViewGroup mMainPanel; // holds menu items that are initially displayed. + private final ListView mOverflowPanel; // holds menu items hidden in the overflow. + private final ImageButton mOverflowButton; // opens/closes the overflow. + /* overflow button drawables. */ + private final Drawable mArrow; + private final Drawable mOverflow; + private final AnimatedVectorDrawable mToArrow; + private final AnimatedVectorDrawable mToOverflow; + + private final OverflowPanelViewHelper mOverflowPanelViewHelper; + + /* Animation interpolators. */ + private final Interpolator mLogAccelerateInterpolator; + private final Interpolator mFastOutSlowInInterpolator; + private final Interpolator mLinearOutSlowInInterpolator; + private final Interpolator mFastOutLinearInInterpolator; + + /* Animations. */ + private final AnimatorSet mShowAnimation; private final AnimatorSet mDismissAnimation; private final AnimatorSet mHideAnimation; - private final AnimationSet mOpenOverflowAnimation = new AnimationSet(true); - private final AnimationSet mCloseOverflowAnimation = new AnimationSet(true); - - private final Runnable mOpenOverflow = new Runnable() { - @Override - public void run() { - openOverflow(); - } - }; - private final Runnable mCloseOverflow = new Runnable() { - @Override - public void run() { - closeOverflow(); - } - }; + private final AnimationSet mOpenOverflowAnimation; + private final AnimationSet mCloseOverflowAnimation; + private final Animation.AnimationListener mOverflowAnimationListener; - private final Rect mViewPortOnScreen = new Rect(); - private final Point mCoordsOnWindow = new Point(); + private final Rect mViewPortOnScreen = new Rect(); // portion of screen we can draw in. + private final Point mCoordsOnWindow = new Point(); // popup window coordinates. + /* Temporary data holders. Reset values before using. */ private final int[] mTmpCoords = new int[2]; private final Rect mTmpRect = new Rect(); @@ -357,12 +349,56 @@ public final class FloatingToolbar { } }; + /** + * @see OverflowPanelViewHelper#preparePopupContent(). + */ + private final Runnable mPreparePopupContentRTLHelper = new Runnable() { + @Override + public void run() { + setPanelsStatesAtRestingPosition(); + setContentAreaAsTouchableSurface(); + mContentContainer.setAlpha(1); + } + }; + + /* Runnable to reset the overflow button's drawable after an overflow transition. */ + private final Runnable mResetOverflowButtonDrawable = new Runnable() { + @Override + public void run() { + if (mIsOverflowOpen) { + mOverflowButton.setImageDrawable(mArrow); + } else { + mOverflowButton.setImageDrawable(mOverflow); + } + } + }; + private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing. private boolean mHidden; // tracks whether this popup is hidden or hiding. - private FloatingToolbarOverflowPanel mOverflowPanel; - private FloatingToolbarMainPanel mMainPanel; - private int mOverflowDirection; + /* Calculated sizes for panels and overflow button. */ + private final Size mOverflowButtonSize; + private Size mOverflowPanelSize; // Should be null when there is no overflow. + private Size mMainPanelSize; + + /* Item click listeners */ + private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener; + private final View.OnClickListener mMenuItemButtonOnClickListener = + new View.OnClickListener() { + @Override + public void onClick(View v) { + if (v.getTag() instanceof MenuItem) { + if (mOnMenuItemClickListener != null) { + mOnMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag()); + } + } + } + }; + + private boolean mOpenOverflowUpwards; // Whether the overflow opens upwards or downwards. + private boolean mIsOverflowOpen; + + private int mTransitionDurationScale; // Used to scale the toolbar transition duration. /** * Initializes a new floating toolbar popup. @@ -375,6 +411,48 @@ public final class FloatingToolbar { mContext = Preconditions.checkNotNull(context); mContentContainer = createContentContainer(context); mPopupWindow = createPopupWindow(mContentContainer); + mMarginHorizontal = parent.getResources() + .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin); + mMarginVertical = parent.getResources() + .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin); + + // Interpolators + mLogAccelerateInterpolator = new LogAccelerateInterpolator(); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.fast_out_slow_in); + mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.linear_out_slow_in); + mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.fast_out_linear_in); + + // Drawables. Needed for views. + mArrow = mContext.getResources() + .getDrawable(R.drawable.ft_avd_tooverflow, mContext.getTheme()); + mArrow.setAutoMirrored(true); + mOverflow = mContext.getResources() + .getDrawable(R.drawable.ft_avd_toarrow, mContext.getTheme()); + mOverflow.setAutoMirrored(true); + mToArrow = (AnimatedVectorDrawable) mContext.getResources() + .getDrawable(R.drawable.ft_avd_toarrow_animation, mContext.getTheme()); + mToArrow.setAutoMirrored(true); + mToOverflow = (AnimatedVectorDrawable) mContext.getResources() + .getDrawable(R.drawable.ft_avd_tooverflow_animation, mContext.getTheme()); + mToOverflow.setAutoMirrored(true); + + // Views + mOverflowButton = createOverflowButton(); + mOverflowButtonSize = measure(mOverflowButton); + mMainPanel = createMainPanel(); + mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext); + mOverflowPanel = createOverflowPanel(); + + // Animation. Need views. + mOverflowAnimationListener = createOverflowAnimationListener(); + mOpenOverflowAnimation = new AnimationSet(true); + mOpenOverflowAnimation.setAnimationListener(mOverflowAnimationListener); + mCloseOverflowAnimation = new AnimationSet(true); + mCloseOverflowAnimation.setAnimationListener(mOverflowAnimationListener); + mShowAnimation = createEnterAnimation(mContentContainer); mDismissAnimation = createExitAnimation( mContentContainer, 150, // startDelay @@ -394,35 +472,23 @@ public final class FloatingToolbar { mPopupWindow.dismiss(); } }); - mMarginHorizontal = parent.getResources() - .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin); - mMarginVertical = parent.getResources() - .getDimensionPixelSize(R.dimen.floating_toolbar_vertical_margin); } /** * Lays out buttons for the specified menu items. + * Requires a subsequent call to {@link #show()} to show the items. */ public void layoutMenuItems( List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener, int suggestedWidth) { - Preconditions.checkNotNull(menuItems); - - mContentContainer.removeAllViews(); - if (mMainPanel == null) { - mMainPanel = new FloatingToolbarMainPanel(mContext, mOpenOverflow); - } - List<MenuItem> overflowMenuItems = - mMainPanel.layoutMenuItems(menuItems, getToolbarWidth(suggestedWidth)); - mMainPanel.setOnMenuItemClickListener(menuItemClickListener); - if (!overflowMenuItems.isEmpty()) { - if (mOverflowPanel == null) { - mOverflowPanel = - new FloatingToolbarOverflowPanel(mContext, mCloseOverflow); - } - mOverflowPanel.setMenuItems(overflowMenuItems); - mOverflowPanel.setOnMenuItemClickListener(menuItemClickListener); + mOnMenuItemClickListener = menuItemClickListener; + cancelOverflowAnimations(); + clearPanels(); + menuItems = layoutMainPanelItems(menuItems, getAdjustedToolbarWidth(suggestedWidth)); + if (!menuItems.isEmpty()) { + // Add remaining items to the overflow. + layoutOverflowPanelItems(menuItems); } updatePopupSize(); } @@ -443,20 +509,13 @@ public final class FloatingToolbar { cancelDismissAndHideAnimations(); cancelOverflowAnimations(); - // Make sure a panel is set as the content. - if (mContentContainer.getChildCount() == 0) { - setMainPanelAsContent(); - // If we're yet to show the popup, set the container visibility to zero. - // The "show" animation will make this visible. - mContentContainer.setAlpha(0); - } refreshCoordinatesAndOverflowDirection(contentRectOnScreen); preparePopupContent(); // We need to specify the position in window coordinates. // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can - // specify the popup poision in screen coordinates. - mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, - mCoordsOnWindow.y); + // specify the popup position in screen coordinates. + mPopupWindow.showAtLocation( + mParent, Gravity.NO_GRAVITY, mCoordsOnWindow.x, mCoordsOnWindow.y); setTouchableSurfaceInsetsComputer(); runShowAnimation(); } @@ -472,6 +531,7 @@ public final class FloatingToolbar { mHidden = false; mDismissed = true; mHideAnimation.cancel(); + runDismissAnimation(); setZeroTouchableSurface(); } @@ -521,104 +581,90 @@ public final class FloatingToolbar { preparePopupContent(); // We need to specify the position in window coordinates. // TODO: Consider to use PopupWindow.setLayoutInScreenEnabled(true) so that we can - // specify the popup poision in screen coordinates. - mPopupWindow.update(mCoordsOnWindow.x, mCoordsOnWindow.y, getWidth(), getHeight()); - } - - /** - * Returns the width of this popup. - */ - public int getWidth() { - return mPopupWindow.getWidth(); - } - - /** - * Returns the height of this popup. - */ - public int getHeight() { - return mPopupWindow.getHeight(); - } - - /** - * Returns the context this popup is running in. - */ - public Context getContext() { - return mContext; + // specify the popup position in screen coordinates. + mPopupWindow.update( + mCoordsOnWindow.x, mCoordsOnWindow.y, + mPopupWindow.getWidth(), mPopupWindow.getHeight()); } private void refreshCoordinatesAndOverflowDirection(Rect contentRectOnScreen) { refreshViewPort(); - int x = contentRectOnScreen.centerX() - getWidth() / 2; + int x = contentRectOnScreen.centerX() - mPopupWindow.getWidth() / 2; // Update x so that the toolbar isn't rendered behind the nav bar in landscape. - x = Math.max(0, Math.min(x, mViewPortOnScreen.right - getWidth())); + x = Math.max(0, Math.min(x, mViewPortOnScreen.right - mPopupWindow.getWidth())); + + final int y; - int y; + final int availableHeightAboveContent = + contentRectOnScreen.top - mViewPortOnScreen.top; + final int availableHeightBelowContent = + mViewPortOnScreen.bottom - contentRectOnScreen.bottom; - int availableHeightAboveContent = contentRectOnScreen.top - mViewPortOnScreen.top; - int availableHeightBelowContent = mViewPortOnScreen.bottom - contentRectOnScreen.bottom; + final int margin = 2 * mMarginVertical; + final int toolbarHeightWithVerticalMargin = getLineHeight(mContext) + margin; - if (mOverflowPanel == null) { // There is no overflow. - if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin()) { + if (!hasOverflow()) { + if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin) { // There is enough space at the top of the content. - y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin(); - } else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin()) { + y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin; + } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin) { // There is enough space at the bottom of the content. y = contentRectOnScreen.bottom; - } else if (availableHeightBelowContent >= getEstimatedToolbarHeight(mContext)) { + } else if (availableHeightBelowContent >= getLineHeight(mContext)) { // Just enough space to fit the toolbar with no vertical margins. y = contentRectOnScreen.bottom - mMarginVertical; } else { // Not enough space. Prefer to position as high as possible. y = Math.max( mViewPortOnScreen.top, - contentRectOnScreen.top - getToolbarHeightWithVerticalMargin()); + contentRectOnScreen.top - toolbarHeightWithVerticalMargin); } - } else { // There is an overflow. - int margin = 2 * mMarginVertical; - int minimumOverflowHeightWithMargin = mOverflowPanel.getMinimumHeight() + margin; - int availableHeightThroughContentDown = mViewPortOnScreen.bottom - - contentRectOnScreen.top + getToolbarHeightWithVerticalMargin(); - int availableHeightThroughContentUp = contentRectOnScreen.bottom - - mViewPortOnScreen.top + getToolbarHeightWithVerticalMargin(); + } else { + // Has an overflow. + final int minimumOverflowHeightWithMargin = + calculateOverflowHeight(MIN_OVERFLOW_SIZE) + margin; + final int availableHeightThroughContentDown = mViewPortOnScreen.bottom - + contentRectOnScreen.top + toolbarHeightWithVerticalMargin; + final int availableHeightThroughContentUp = contentRectOnScreen.bottom - + mViewPortOnScreen.top + toolbarHeightWithVerticalMargin; if (availableHeightAboveContent >= minimumOverflowHeightWithMargin) { // There is enough space at the top of the content rect for the overflow. // Position above and open upwards. updateOverflowHeight(availableHeightAboveContent - margin); - y = contentRectOnScreen.top - getHeight(); - mOverflowDirection = OVERFLOW_DIRECTION_UP; - } else if (availableHeightAboveContent >= getToolbarHeightWithVerticalMargin() + y = contentRectOnScreen.top - mPopupWindow.getHeight(); + mOpenOverflowUpwards = true; + } else if (availableHeightAboveContent >= toolbarHeightWithVerticalMargin && availableHeightThroughContentDown >= minimumOverflowHeightWithMargin) { // There is enough space at the top of the content rect for the main panel // but not the overflow. // Position above but open downwards. updateOverflowHeight(availableHeightThroughContentDown - margin); - y = contentRectOnScreen.top - getToolbarHeightWithVerticalMargin(); - mOverflowDirection = OVERFLOW_DIRECTION_DOWN; + y = contentRectOnScreen.top - toolbarHeightWithVerticalMargin; + mOpenOverflowUpwards = false; } else if (availableHeightBelowContent >= minimumOverflowHeightWithMargin) { // There is enough space at the bottom of the content rect for the overflow. // Position below and open downwards. updateOverflowHeight(availableHeightBelowContent - margin); y = contentRectOnScreen.bottom; - mOverflowDirection = OVERFLOW_DIRECTION_DOWN; - } else if (availableHeightBelowContent >= getToolbarHeightWithVerticalMargin() + mOpenOverflowUpwards = false; + } else if (availableHeightBelowContent >= toolbarHeightWithVerticalMargin && mViewPortOnScreen.height() >= minimumOverflowHeightWithMargin) { // There is enough space at the bottom of the content rect for the main panel // but not the overflow. // Position below but open upwards. updateOverflowHeight(availableHeightThroughContentUp - margin); - y = contentRectOnScreen.bottom + getToolbarHeightWithVerticalMargin() - - getHeight(); - mOverflowDirection = OVERFLOW_DIRECTION_UP; + y = contentRectOnScreen.bottom + toolbarHeightWithVerticalMargin - + mPopupWindow.getHeight(); + mOpenOverflowUpwards = true; } else { // Not enough space. // Position at the top of the view port and open downwards. updateOverflowHeight(mViewPortOnScreen.height() - margin); y = mViewPortOnScreen.top; - mOverflowDirection = OVERFLOW_DIRECTION_DOWN; + mOpenOverflowUpwards = false; } - mOverflowPanel.setOverflowDirection(mOverflowDirection); } // We later specify the location of PopupWindow relative to the attached window. @@ -639,15 +685,11 @@ public final class FloatingToolbar { mCoordsOnWindow.set(x - windowLeftOnScreen, y - windowTopOnScreen); } - private int getToolbarHeightWithVerticalMargin() { - return getEstimatedToolbarHeight(mContext) + mMarginVertical * 2; - } - /** * Performs the "show" animation on the floating popup. */ private void runShowAnimation() { - createEnterAnimation(mContentContainer).start(); + mShowAnimation.start(); } /** @@ -670,42 +712,16 @@ public final class FloatingToolbar { } private void cancelOverflowAnimations() { - if (mOpenOverflowAnimation.hasStarted() - && !mOpenOverflowAnimation.hasEnded()) { - // Remove the animation listener, stop the animation, - // then trigger the lister explicitly so it is not posted - // to the message queue. - mOpenOverflowAnimation.setAnimationListener(null); - mContentContainer.clearAnimation(); - mOnOverflowOpened.onAnimationEnd(null); - } - if (mCloseOverflowAnimation.hasStarted() - && !mCloseOverflowAnimation.hasEnded()) { - // Remove the animation listener, stop the animation, - // then trigger the lister explicitly so it is not posted - // to the message queue. - mCloseOverflowAnimation.setAnimationListener(null); - mContentContainer.clearAnimation(); - mOnOverflowClosed.onAnimationEnd(null); - } + mContentContainer.clearAnimation(); + mMainPanel.animate().cancel(); + mOverflowPanel.animate().cancel(); + mToArrow.stop(); + mToOverflow.stop(); } - /** - * Opens the floating toolbar overflow. - * This method should not be called if menu items have not been laid out with - * {@link #layoutMenuItems(java.util.List, MenuItem.OnMenuItemClickListener, int)}. - * - * @throws IllegalStateException if called when menu items have not been laid out. - */ private void openOverflow() { - Preconditions.checkState(mMainPanel != null); - Preconditions.checkState(mOverflowPanel != null); - - mMainPanel.fadeOut(true); - Size overflowPanelSize = mOverflowPanel.measure(); - final int targetWidth = overflowPanelSize.getWidth(); - final int targetHeight = overflowPanelSize.getHeight(); - final boolean morphUpwards = (mOverflowDirection == OVERFLOW_DIRECTION_UP); + final int targetWidth = mOverflowPanelSize.getWidth(); + final int targetHeight = mOverflowPanelSize.getHeight(); final int startWidth = mContentContainer.getWidth(); final int startHeight = mContentContainer.getHeight(); final float startY = mContentContainer.getY(); @@ -714,230 +730,281 @@ public final class FloatingToolbar { Animation widthAnimation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { - ViewGroup.LayoutParams params = mContentContainer.getLayoutParams(); int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth)); - params.width = startWidth + deltaWidth; - mContentContainer.setLayoutParams(params); + setWidth(mContentContainer, startWidth + deltaWidth); if (isRTL()) { mContentContainer.setX(left); + + // Lock the panels in place. + mMainPanel.setX(0); + mOverflowPanel.setX(0); } else { mContentContainer.setX(right - mContentContainer.getWidth()); + + // Offset the panels' positions so they look like they're locked in place + // on the screen. + mMainPanel.setX(mContentContainer.getWidth() - startWidth); + mOverflowPanel.setX(mContentContainer.getWidth() - targetWidth); } } }; Animation heightAnimation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { - ViewGroup.LayoutParams params = mContentContainer.getLayoutParams(); int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight)); - params.height = startHeight + deltaHeight; - mContentContainer.setLayoutParams(params); - if (morphUpwards) { - float y = startY - (mContentContainer.getHeight() - startHeight); - mContentContainer.setY(y); + setHeight(mContentContainer, startHeight + deltaHeight); + if (mOpenOverflowUpwards) { + mContentContainer.setY( + startY - (mContentContainer.getHeight() - startHeight)); + positionContentYCoordinatesIfOpeningOverflowUpwards(); } } }; - widthAnimation.setDuration(240); - heightAnimation.setDuration(180); - heightAnimation.setStartOffset(60); + final float overflowButtonStartX = mOverflowButton.getX(); + final float overflowButtonTargetX = isRTL() ? + overflowButtonStartX + targetWidth - mOverflowButton.getWidth() : + overflowButtonStartX - targetWidth + mOverflowButton.getWidth(); + Animation overflowButtonAnimation = new Animation() { + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float overflowButtonX = overflowButtonStartX + + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX); + float deltaContainerWidth = isRTL() ? + 0 : + mContentContainer.getWidth() - startWidth; + float actualOverflowButtonX = overflowButtonX + deltaContainerWidth; + mOverflowButton.setX(actualOverflowButtonX); + } + }; + widthAnimation.setInterpolator(mLogAccelerateInterpolator); + widthAnimation.setDuration(getAdjustedDuration(250)); + heightAnimation.setInterpolator(mFastOutSlowInInterpolator); + heightAnimation.setDuration(getAdjustedDuration(250)); + overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator); + overflowButtonAnimation.setDuration(getAdjustedDuration(250)); + mOpenOverflowAnimation.getAnimations().clear(); mOpenOverflowAnimation.getAnimations().clear(); - mOpenOverflowAnimation.setAnimationListener(mOnOverflowOpened); mOpenOverflowAnimation.addAnimation(widthAnimation); mOpenOverflowAnimation.addAnimation(heightAnimation); + mOpenOverflowAnimation.addAnimation(overflowButtonAnimation); mContentContainer.startAnimation(mOpenOverflowAnimation); + mIsOverflowOpen = true; + mMainPanel.animate() + .alpha(0).withLayer() + .setInterpolator(mLinearOutSlowInInterpolator) + .setDuration(250) + .start(); + mOverflowPanel.setAlpha(1); // fadeIn in 0ms. } - /** - * Opens the floating toolbar overflow. - * This method should not be called if menu items have not been laid out with - * {@link #layoutMenuItems(java.util.List, MenuItem.OnMenuItemClickListener, int)}. - * - * @throws IllegalStateException if called when menu items have not been laid out. - */ private void closeOverflow() { - Preconditions.checkState(mMainPanel != null); - Preconditions.checkState(mOverflowPanel != null); - - mOverflowPanel.fadeOut(true); - Size mainPanelSize = mMainPanel.measure(); - final int targetWidth = mainPanelSize.getWidth(); - final int targetHeight = mainPanelSize.getHeight(); + final int targetWidth = mMainPanelSize.getWidth(); final int startWidth = mContentContainer.getWidth(); - final int startHeight = mContentContainer.getHeight(); - final float bottom = mContentContainer.getY() + mContentContainer.getHeight(); - final boolean morphedUpwards = (mOverflowDirection == OVERFLOW_DIRECTION_UP); final float left = mContentContainer.getX(); final float right = left + mContentContainer.getWidth(); Animation widthAnimation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { - ViewGroup.LayoutParams params = mContentContainer.getLayoutParams(); int deltaWidth = (int) (interpolatedTime * (targetWidth - startWidth)); - params.width = startWidth + deltaWidth; - mContentContainer.setLayoutParams(params); + setWidth(mContentContainer, startWidth + deltaWidth); if (isRTL()) { mContentContainer.setX(left); + + // Lock the panels in place. + mMainPanel.setX(0); + mOverflowPanel.setX(0); } else { mContentContainer.setX(right - mContentContainer.getWidth()); + + // Offset the panels' positions so they look like they're locked in place + // on the screen. + mMainPanel.setX(mContentContainer.getWidth() - targetWidth); + mOverflowPanel.setX(mContentContainer.getWidth() - startWidth); } } }; + final int targetHeight = mMainPanelSize.getHeight(); + final int startHeight = mContentContainer.getHeight(); + final float bottom = mContentContainer.getY() + mContentContainer.getHeight(); Animation heightAnimation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { - ViewGroup.LayoutParams params = mContentContainer.getLayoutParams(); int deltaHeight = (int) (interpolatedTime * (targetHeight - startHeight)); - params.height = startHeight + deltaHeight; - mContentContainer.setLayoutParams(params); - if (morphedUpwards) { + setHeight(mContentContainer, startHeight + deltaHeight); + if (mOpenOverflowUpwards) { mContentContainer.setY(bottom - mContentContainer.getHeight()); + positionContentYCoordinatesIfOpeningOverflowUpwards(); } } }; - widthAnimation.setDuration(150); - widthAnimation.setStartOffset(150); - heightAnimation.setDuration(210); + final float overflowButtonStartX = mOverflowButton.getX(); + final float overflowButtonTargetX = isRTL() ? + overflowButtonStartX - startWidth + mOverflowButton.getWidth() : + overflowButtonStartX + startWidth - mOverflowButton.getWidth(); + Animation overflowButtonAnimation = new Animation() { + @Override + protected void applyTransformation(float interpolatedTime, Transformation t) { + float overflowButtonX = overflowButtonStartX + + interpolatedTime * (overflowButtonTargetX - overflowButtonStartX); + float deltaContainerWidth = isRTL() ? + 0 : + mContentContainer.getWidth() - startWidth; + float actualOverflowButtonX = overflowButtonX + deltaContainerWidth; + mOverflowButton.setX(actualOverflowButtonX); + } + }; + widthAnimation.setInterpolator(mFastOutSlowInInterpolator); + widthAnimation.setDuration(getAdjustedDuration(250)); + heightAnimation.setInterpolator(mLogAccelerateInterpolator); + heightAnimation.setDuration(getAdjustedDuration(250)); + overflowButtonAnimation.setInterpolator(mFastOutSlowInInterpolator); + overflowButtonAnimation.setDuration(getAdjustedDuration(250)); mCloseOverflowAnimation.getAnimations().clear(); - mCloseOverflowAnimation.setAnimationListener(mOnOverflowClosed); mCloseOverflowAnimation.addAnimation(widthAnimation); mCloseOverflowAnimation.addAnimation(heightAnimation); + mCloseOverflowAnimation.addAnimation(overflowButtonAnimation); mContentContainer.startAnimation(mCloseOverflowAnimation); - } - - /** - * Prepares the content container for show and update calls. - */ - private void preparePopupContent() { - // Reset visibility. - if (mMainPanel != null) { - mMainPanel.fadeIn(false); - } - if (mOverflowPanel != null) { - mOverflowPanel.fadeIn(false); - } - - // Reset position. - if (isMainPanelContent()) { - positionMainPanel(); - } - if (isOverflowPanelContent()) { - positionOverflowPanel(); - } - } - - private boolean isMainPanelContent() { - return mMainPanel != null - && mContentContainer.getChildAt(0) == mMainPanel.getView(); - } - - private boolean isOverflowPanelContent() { - return mOverflowPanel != null - && mContentContainer.getChildAt(0) == mOverflowPanel.getView(); - } - - /** - * Sets the current content to be the main view panel. - */ - private void setMainPanelAsContent() { - // This should never be called if the main panel has not been initialized. - Preconditions.checkNotNull(mMainPanel); - mContentContainer.removeAllViews(); - Size mainPanelSize = mMainPanel.measure(); - ViewGroup.LayoutParams params = mContentContainer.getLayoutParams(); - params.width = mainPanelSize.getWidth(); - params.height = mainPanelSize.getHeight(); - mContentContainer.setLayoutParams(params); - mContentContainer.addView(mMainPanel.getView()); - setContentAreaAsTouchableSurface(); - } - - /** - * Sets the current content to be the overflow view panel. - */ - private void setOverflowPanelAsContent() { - // This should never be called if the overflow panel has not been initialized. - Preconditions.checkNotNull(mOverflowPanel); - mContentContainer.removeAllViews(); - Size overflowPanelSize = mOverflowPanel.measure(); - ViewGroup.LayoutParams params = mContentContainer.getLayoutParams(); - params.width = overflowPanelSize.getWidth(); - params.height = overflowPanelSize.getHeight(); - mContentContainer.setLayoutParams(params); - mContentContainer.addView(mOverflowPanel.getView()); - setContentAreaAsTouchableSurface(); - } - - /** - * Places the main view panel at the appropriate resting coordinates. - */ - private void positionMainPanel() { - Preconditions.checkNotNull(mMainPanel); - mContentContainer.setX(mMarginHorizontal); - - float y = mMarginVertical; - if (mOverflowDirection == OVERFLOW_DIRECTION_UP) { - y = getHeight() - - (mMainPanel.getView().getMeasuredHeight() + mMarginVertical); - } - mContentContainer.setY(y); - setContentAreaAsTouchableSurface(); - } + mIsOverflowOpen = false; + mMainPanel.animate() + .alpha(1).withLayer() + .setInterpolator(mFastOutLinearInInterpolator) + .setDuration(100) + .start(); + mOverflowPanel.animate() + .alpha(0).withLayer() + .setInterpolator(mLinearOutSlowInInterpolator) + .setDuration(150) + .start(); + } + + private void setPanelsStatesAtRestingPosition() { + mOverflowButton.setEnabled(true); + + if (mIsOverflowOpen) { + // Set open state. + final Size containerSize = mOverflowPanelSize; + setSize(mContentContainer, containerSize); + mMainPanel.setAlpha(0); + mOverflowPanel.setAlpha(1); + mOverflowButton.setImageDrawable(mArrow); + + // Update x-coordinates depending on RTL state. + if (isRTL()) { + mContentContainer.setX(mMarginHorizontal); // align left + mMainPanel.setX(0); // align left + mOverflowButton.setX( // align right + containerSize.getWidth() - mOverflowButtonSize.getWidth()); + mOverflowPanel.setX(0); // align left + } else { + mContentContainer.setX( // align right + mMarginHorizontal + + mMainPanelSize.getWidth() - containerSize.getWidth()); + mMainPanel.setX(-mContentContainer.getX()); // align right + mOverflowButton.setX(0); // align left + mOverflowPanel.setX(0); // align left + } - /** - * Places the main view panel at the appropriate resting coordinates. - */ - private void positionOverflowPanel() { - Preconditions.checkNotNull(mOverflowPanel); - float x; - if (isRTL()) { - x = mMarginHorizontal; + // Update y-coordinates depending on overflow's open direction. + if (mOpenOverflowUpwards) { + mContentContainer.setY(mMarginVertical); // align top + mMainPanel.setY( // align bottom + containerSize.getHeight() - mContentContainer.getHeight()); + mOverflowButton.setY( // align bottom + containerSize.getHeight() - mOverflowButtonSize.getHeight()); + mOverflowPanel.setY(0); // align top + } else { + // opens downwards. + mContentContainer.setY(mMarginVertical); // align top + mMainPanel.setY(0); // align top + mOverflowButton.setY(0); // align top + mOverflowPanel.setY(mOverflowButtonSize.getHeight()); // align bottom + } } else { - x = mPopupWindow.getWidth() - - (mOverflowPanel.getView().getMeasuredWidth() + mMarginHorizontal); + if (hasOverflow()) { + // overflow not open. Set closed state. + final Size containerSize = mMainPanelSize; + setSize(mContentContainer, containerSize); + mMainPanel.setAlpha(1); + mOverflowPanel.setAlpha(0); + mOverflowButton.setImageDrawable(mOverflow); + + // Update x-coordinates depending on RTL state. + if (isRTL()) { + mContentContainer.setX(mMarginHorizontal); // align left + mMainPanel.setX(0); // align left + mOverflowButton.setX(0); // align left + mOverflowPanel.setX(0); // align left + } else { + mContentContainer.setX(mMarginHorizontal); // align left + mMainPanel.setX(0); // align left + mOverflowButton.setX( // align right + containerSize.getWidth() - mOverflowButtonSize.getWidth()); + mOverflowPanel.setX( // align right + containerSize.getWidth() - mOverflowPanelSize.getWidth()); + } + + // Update y-coordinates depending on overflow's open direction. + if (mOpenOverflowUpwards) { + mContentContainer.setY( // align bottom + mMarginVertical + + mOverflowPanelSize.getHeight() - containerSize.getHeight()); + mMainPanel.setY(0); // align top + mOverflowButton.setY(0); // align top + mOverflowPanel.setY( // align bottom + containerSize.getHeight() - mOverflowPanelSize.getHeight()); + } else { + // opens downwards. + mContentContainer.setY(mMarginVertical); // align top + mMainPanel.setY(0); // align top + mOverflowButton.setY(0); // align top + mOverflowPanel.setY(mOverflowButtonSize.getHeight()); // align bottom + } + } else { + mContentContainer.setX(mMarginHorizontal); + mContentContainer.setY(mMarginVertical); + } } - mContentContainer.setX(x); - mContentContainer.setY(mMarginVertical); - setContentAreaAsTouchableSurface(); } - private void updateOverflowHeight(int height) { - if (mOverflowPanel != null) { - mOverflowPanel.setSuggestedHeight(height); - - // Re-measure the popup and it's contents. - boolean mainPanelContent = isMainPanelContent(); - boolean overflowPanelContent = isOverflowPanelContent(); - mContentContainer.removeAllViews(); // required to update popup size. - updatePopupSize(); - // Reset the appropriate content. - if (mainPanelContent) { - setMainPanelAsContent(); + private void updateOverflowHeight(int suggestedHeight) { + if (hasOverflow()) { + final int maxItemSize = (suggestedHeight - mOverflowButtonSize.getHeight()) / + getLineHeight(mContext); + final int newHeight = calculateOverflowHeight(maxItemSize); + if (mOverflowPanelSize.getHeight() != newHeight) { + mOverflowPanelSize = new Size(mOverflowPanelSize.getWidth(), newHeight); } - if (overflowPanelContent) { - setOverflowPanelAsContent(); + setSize(mOverflowPanel, mOverflowPanelSize); + if (mIsOverflowOpen) { + setSize(mContentContainer, mOverflowPanelSize); + if (mOpenOverflowUpwards) { + final int deltaHeight = mOverflowPanelSize.getHeight() - newHeight; + mContentContainer.setY(mContentContainer.getY() + deltaHeight); + mOverflowButton.setY(mOverflowButton.getY() - deltaHeight); + } + } else { + setSize(mContentContainer, mMainPanelSize); } + updatePopupSize(); } } private void updatePopupSize() { int width = 0; int height = 0; - if (mMainPanel != null) { - Size mainPanelSize = mMainPanel.measure(); - width = mainPanelSize.getWidth(); - height = mainPanelSize.getHeight(); + if (mMainPanelSize != null) { + width = Math.max(width, mMainPanelSize.getWidth()); + height = Math.max(height, mMainPanelSize.getHeight()); } - if (mOverflowPanel != null) { - Size overflowPanelSize = mOverflowPanel.measure(); - width = Math.max(width, overflowPanelSize.getWidth()); - height = Math.max(height, overflowPanelSize.getHeight()); + if (mOverflowPanelSize != null) { + width = Math.max(width, mOverflowPanelSize.getWidth()); + height = Math.max(height, mOverflowPanelSize.getHeight()); } mPopupWindow.setWidth(width + mMarginHorizontal * 2); mPopupWindow.setHeight(height + mMarginVertical * 2); + maybeComputeTransitionDurationScale(); } - private void refreshViewPort() { mParent.getWindowVisibleDisplayFrame(mViewPortOnScreen); } @@ -947,7 +1014,7 @@ public final class FloatingToolbar { return !mTmpRect.equals(mViewPortOnScreen); } - private int getToolbarWidth(int suggestedWidth) { + private int getAdjustedToolbarWidth(int suggestedWidth) { int width = suggestedWidth; refreshViewPort(); int maximumWidth = mViewPortOnScreen.width() - 2 * mParent.getResources() @@ -971,11 +1038,17 @@ public final class FloatingToolbar { * Sets the touchable region of this popup to be the area occupied by its content. */ private void setContentAreaAsTouchableSurface() { - if (!mPopupWindow.isShowing()) { - mContentContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + Preconditions.checkNotNull(mMainPanelSize); + final int width; + final int height; + if (mIsOverflowOpen) { + Preconditions.checkNotNull(mOverflowPanelSize); + width = mOverflowPanelSize.getWidth(); + height = mOverflowPanelSize.getHeight(); + } else { + width = mMainPanelSize.getWidth(); + height = mMainPanelSize.getHeight(); } - int width = mContentContainer.getMeasuredWidth(); - int height = mContentContainer.getMeasuredHeight(); mTouchableRegion.set( (int) mContentContainer.getX(), (int) mContentContainer.getY(), @@ -997,45 +1070,12 @@ public final class FloatingToolbar { } private boolean isRTL() { - return mContentContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; + return mContext.getResources().getConfiguration().getLayoutDirection() + == View.LAYOUT_DIRECTION_RTL; } - } - - /** - * A widget that holds the primary menu items in the floating toolbar. - */ - private static final class FloatingToolbarMainPanel { - - private final Context mContext; - private final ViewGroup mContentView; - private final View.OnClickListener mMenuItemButtonOnClickListener = - new View.OnClickListener() { - @Override - public void onClick(View v) { - if (v.getTag() instanceof MenuItem) { - if (mOnMenuItemClickListener != null) { - mOnMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag()); - } - } - } - }; - private final ViewFader viewFader; - private final Runnable mOpenOverflow; - - private View mOpenOverflowButton; - private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener; - /** - * Initializes a floating toolbar popup main view panel. - * - * @param context - * @param openOverflow The code that opens the toolbar popup overflow. - */ - public FloatingToolbarMainPanel(Context context, Runnable openOverflow) { - mContext = Preconditions.checkNotNull(context); - mContentView = new LinearLayout(context); - viewFader = new ViewFader(mContentView); - mOpenOverflow = Preconditions.checkNotNull(openOverflow); + private boolean hasOverflow() { + return mOverflowPanelSize != null; } /** @@ -1044,16 +1084,14 @@ public final class FloatingToolbar { * * @return The menu items that are not included in this main panel. */ - public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int width) { + public List<MenuItem> layoutMainPanelItems( + List<MenuItem> menuItems, final int toolbarWidth) { Preconditions.checkNotNull(menuItems); - // Reserve space for the "open overflow" button. - final int toolbarWidth = width - getEstimatedOpenOverflowButtonWidth(mContext); - int availableWidth = toolbarWidth; final LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems); - mContentView.removeAllViews(); + mMainPanel.removeAllViews(); boolean isFirstItem = true; while (!remainingMenuItems.isEmpty()) { @@ -1081,59 +1119,119 @@ public final class FloatingToolbar { menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int menuItemButtonWidth = Math.min(menuItemButton.getMeasuredWidth(), toolbarWidth); - if (menuItemButtonWidth <= availableWidth) { + // Check if we can fit an item while reserving space for the overflowButton. + boolean canFitWithOverflow = + menuItemButtonWidth <= availableWidth - mOverflowButtonSize.getWidth(); + boolean canFitNoOverflow = + remainingMenuItems.size() == 1 && menuItemButtonWidth <= availableWidth; + if (canFitWithOverflow || canFitNoOverflow) { setButtonTagAndClickListener(menuItemButton, menuItem); - mContentView.addView(menuItemButton); + mMainPanel.addView(menuItemButton); ViewGroup.LayoutParams params = menuItemButton.getLayoutParams(); params.width = menuItemButtonWidth; menuItemButton.setLayoutParams(params); availableWidth -= menuItemButtonWidth; remainingMenuItems.pop(); } else { - if (mOpenOverflowButton == null) { - mOpenOverflowButton = LayoutInflater.from(mContext) - .inflate(R.layout.floating_popup_open_overflow_button, null); - mOpenOverflowButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mOpenOverflowButton != null) { - mOpenOverflow.run(); - } - } - }); - } - mContentView.addView(mOpenOverflowButton); + // Reserve space for overflowButton. + mMainPanel.setPaddingRelative(0, 0, mOverflowButtonSize.getWidth(), 0); break; } } + mMainPanelSize = measure(mMainPanel); return remainingMenuItems; } - public void setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener listener) { - mOnMenuItemClickListener = listener; - } + private void layoutOverflowPanelItems(List<MenuItem> menuItems) { + ArrayAdapter<MenuItem> overflowPanelAdapter = + (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter(); + overflowPanelAdapter.clear(); + final int size = menuItems.size(); + for (int i = 0; i < size; i++) { + overflowPanelAdapter.add(menuItems.get(i)); + } + mOverflowPanel.setAdapter(overflowPanelAdapter); + if (mOpenOverflowUpwards) { + mOverflowPanel.setY(0); + } else { + mOverflowPanel.setY(mOverflowButtonSize.getHeight()); + } - public View getView() { - return mContentView; + int width = Math.max(getOverflowWidth(), mOverflowButtonSize.getWidth()); + int height = calculateOverflowHeight(MAX_OVERFLOW_SIZE); + mOverflowPanelSize = new Size(width, height); + setSize(mOverflowPanel, mOverflowPanelSize); } - public void fadeIn(boolean animate) { - viewFader.fadeIn(animate); - } + /** + * Resets the content container and appropriately position it's panels. + */ + private void preparePopupContent() { + mContentContainer.removeAllViews(); + + // Add views in the specified order so they stack up as expected. + // Order: overflowPanel, mainPanel, overflowButton. + if (hasOverflow()) { + mContentContainer.addView(mOverflowPanel); + } + mContentContainer.addView(mMainPanel); + if (hasOverflow()) { + mContentContainer.addView(mOverflowButton); + } + setPanelsStatesAtRestingPosition(); + setContentAreaAsTouchableSurface(); - public void fadeOut(boolean animate) { - viewFader.fadeOut(animate); + // The positioning of contents in RTL is wrong when the view is first rendered. + // Hide the view and post a runnable to recalculate positions and render the view. + // TODO: Investigate why this happens and fix. + if (isRTL()) { + mContentContainer.setAlpha(0); + mContentContainer.post(mPreparePopupContentRTLHelper); + } } /** - * Returns how big this panel's view should be. - * This method should only be called when the view has not been attached to a parent - * otherwise it will throw an illegal state. + * Clears out the panels and their container. Resets their calculated sizes. */ - public Size measure() throws IllegalStateException { - Preconditions.checkState(mContentView.getParent() == null); - mContentView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - return new Size(mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight()); + private void clearPanels() { + mOverflowPanelSize = null; + mMainPanelSize = null; + mIsOverflowOpen = false; + mMainPanel.removeAllViews(); + ArrayAdapter<MenuItem> overflowPanelAdapter = + (ArrayAdapter<MenuItem>) mOverflowPanel.getAdapter(); + overflowPanelAdapter.clear(); + mOverflowPanel.setAdapter(overflowPanelAdapter); + mContentContainer.removeAllViews(); + } + + private void positionContentYCoordinatesIfOpeningOverflowUpwards() { + if (mOpenOverflowUpwards) { + mMainPanel.setY(mContentContainer.getHeight() - mMainPanelSize.getHeight()); + mOverflowButton.setY(mContentContainer.getHeight() - mOverflowButton.getHeight()); + mOverflowPanel.setY(mContentContainer.getHeight() - mOverflowPanelSize.getHeight()); + } + } + + private int getOverflowWidth() { + int overflowWidth = 0; + final int count = mOverflowPanel.getAdapter().getCount(); + for (int i = 0; i < count; i++) { + MenuItem menuItem = (MenuItem) mOverflowPanel.getAdapter().getItem(i); + overflowWidth = + Math.max(mOverflowPanelViewHelper.calculateWidth(menuItem), overflowWidth); + } + return overflowWidth; + } + + private int calculateOverflowHeight(int maxItemSize) { + // Maximum of 4 items, minimum of 2 if the overflow has to scroll. + int actualSize = Math.min( + MAX_OVERFLOW_SIZE, + Math.min( + Math.max(MIN_OVERFLOW_SIZE, maxItemSize), + mOverflowPanel.getCount())); + return actualSize * getLineHeight(mContext) + mOverflowButtonSize.getHeight(); } private void setButtonTagAndClickListener(View menuItemButton, MenuItem menuItem) { @@ -1144,281 +1242,326 @@ public final class FloatingToolbar { button.setTag(menuItem); button.setOnClickListener(mMenuItemButtonOnClickListener); } - } + /** + * NOTE: Use only in android.view.animation.* animations. Do not use in android.animation.* + * animations. See comment about this in the code. + */ + private int getAdjustedDuration(int originalDuration) { + if (mTransitionDurationScale < 150) { + // For smaller transition, decrease the time. + return Math.max(originalDuration - 50, 0); + } else if (mTransitionDurationScale > 300) { + // For bigger transition, increase the time. + return originalDuration + 50; + } - /** - * A widget that holds the overflow items in the floating toolbar. - */ - private static final class FloatingToolbarOverflowPanel { - - private final LinearLayout mContentView; - private final ViewGroup mBackButtonContainer; - private final View mBackButton; - private final ListView mListView; - private final TextView mListViewItemWidthCalculator; - private final ViewFader mViewFader; - private final Runnable mCloseOverflow; + // Scale the animation duration with getDurationScale(). This allows + // android.view.animation.* animations to scale just like android.animation.* animations + // when animator duration scale is adjusted in "Developer Options". + // For this reason, do not use this method for android.animation.* animations. + return (int) (originalDuration * ValueAnimator.getDurationScale()); + } - private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener; - private int mOverflowWidth; - private int mSuggestedHeight; + private void maybeComputeTransitionDurationScale() { + if (mMainPanelSize == null || mOverflowPanel == null) { + int w = mMainPanelSize.getWidth() - mOverflowPanelSize.getWidth(); + int h = mOverflowPanelSize.getHeight() - mMainPanelSize.getHeight(); + mTransitionDurationScale = (int) (Math.sqrt(w * w + h * h) / + mContentContainer.getContext().getResources().getDisplayMetrics().density); + } + } - /** - * Initializes a floating toolbar popup overflow view panel. - * - * @param context - * @param closeOverflow The code that closes the toolbar popup's overflow. - */ - public FloatingToolbarOverflowPanel(Context context, Runnable closeOverflow) { - mCloseOverflow = Preconditions.checkNotNull(closeOverflow); + private ViewGroup createMainPanel() { + ViewGroup mainPanel = new LinearLayout(mContext) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (isOverflowAnimating()) { + // Update widthMeasureSpec to make sure that this view is not clipped + // as we offset it's coordinates with respect to it's parent. + widthMeasureSpec = MeasureSpec.makeMeasureSpec( + mMainPanelSize.getWidth(), + MeasureSpec.EXACTLY); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } - mContentView = new LinearLayout(context); - mContentView.setOrientation(LinearLayout.VERTICAL); - mViewFader = new ViewFader(mContentView); + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + // Intercept the touch event while the overflow is animating. + return isOverflowAnimating(); + } + }; + return mainPanel; + } - mBackButton = LayoutInflater.from(context) - .inflate(R.layout.floating_popup_close_overflow_button, null); - mBackButton.setOnClickListener(new View.OnClickListener() { + private ImageButton createOverflowButton() { + final ImageButton overflowButton = (ImageButton) LayoutInflater.from(mContext) + .inflate(R.layout.floating_popup_overflow_button, null); + overflowButton.setImageDrawable(mOverflow); + overflowButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mCloseOverflow.run(); + final Drawable drawable = overflowButton.getDrawable(); + if (mIsOverflowOpen) { + overflowButton.setImageDrawable(mToOverflow); + mToOverflow.start(); + closeOverflow(); + } else { + overflowButton.setImageDrawable(mToArrow); + mToArrow.start(); + openOverflow(); + } + overflowButton.postDelayed( + mResetOverflowButtonDrawable, OVERFLOW_BUTTON_ANIMATION_DELAY); } }); - mBackButtonContainer = new LinearLayout(context); - mBackButtonContainer.addView(mBackButton); + return overflowButton; + } + + private ListView createOverflowPanel() { + final ListView overflowPanel = new ListView(FloatingToolbarPopup.this.mContext) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Update heightMeasureSpec to make sure that this view is not clipped + // as we offset it's coordinates with respect to it's parent. + heightMeasureSpec = MeasureSpec.makeMeasureSpec( + mOverflowPanelSize.getHeight() - mOverflowButtonSize.getHeight(), + MeasureSpec.EXACTLY); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (isOverflowAnimating()) { + // Eat the touch event. + return true; + } + return super.dispatchTouchEvent(ev); + } + }; + overflowPanel.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + overflowPanel.setDivider(null); + overflowPanel.setDividerHeight(0); + + final ArrayAdapter adapter = + new ArrayAdapter<MenuItem>(mContext, 0) { + @Override + public int getViewTypeCount() { + return mOverflowPanelViewHelper.getViewTypeCount(); + } + + @Override + public int getItemViewType(int position) { + return mOverflowPanelViewHelper.getItemViewType(getItem(position)); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return mOverflowPanelViewHelper.getView( + getItem(position), mOverflowPanelSize.getWidth(), convertView); + } + }; + overflowPanel.setAdapter(adapter); - mListView = createOverflowListView(); - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + overflowPanel.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - MenuItem menuItem = (MenuItem) mListView.getAdapter().getItem(position); + MenuItem menuItem = (MenuItem) overflowPanel.getAdapter().getItem(position); if (mOnMenuItemClickListener != null) { mOnMenuItemClickListener.onMenuItemClick(menuItem); } } }); - mContentView.addView(mListView); - mContentView.addView(mBackButtonContainer); - - mListViewItemWidthCalculator = createOverflowMenuItemButton(context); - mListViewItemWidthCalculator.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + return overflowPanel; } - /** - * Sets the menu items to be displayed in the overflow. - */ - public void setMenuItems(List<MenuItem> menuItems) { - ArrayAdapter overflowListViewAdapter = (ArrayAdapter) mListView.getAdapter(); - overflowListViewAdapter.clear(); - overflowListViewAdapter.addAll(menuItems); - setListViewHeight(); - setOverflowWidth(); + private boolean isOverflowAnimating() { + final boolean overflowOpening = mOpenOverflowAnimation.hasStarted() + && !mOpenOverflowAnimation.hasEnded(); + final boolean overflowClosing = mCloseOverflowAnimation.hasStarted() + && !mCloseOverflowAnimation.hasEnded(); + return overflowOpening || overflowClosing; } - public void setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener listener) { - mOnMenuItemClickListener = listener; + private Animation.AnimationListener createOverflowAnimationListener() { + Animation.AnimationListener listener = new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { + // Disable the overflow button while it's animating. + // It will be re-enabled when the animation stops. + mOverflowButton.setEnabled(false); + } + + @Override + public void onAnimationEnd(Animation animation) { + // Posting this because it seems like this is called before the animation + // actually ends. + mContentContainer.post(new Runnable() { + @Override + public void run() { + setPanelsStatesAtRestingPosition(); + setContentAreaAsTouchableSurface(); + } + }); + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + }; + return listener; } - /** - * Notifies the overflow of the current direction in which the overflow will be opened. - * - * @param overflowDirection {@link FloatingToolbarPopup#OVERFLOW_DIRECTION_UP} - * or {@link FloatingToolbarPopup#OVERFLOW_DIRECTION_DOWN}. - */ - public void setOverflowDirection(int overflowDirection) { - mContentView.removeView(mBackButtonContainer); - int index = (overflowDirection == FloatingToolbarPopup.OVERFLOW_DIRECTION_UP)? 1 : 0; - mContentView.addView(mBackButtonContainer, index); + private static Size measure(View view) { + Preconditions.checkState(view.getParent() == null); + view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); + return new Size(view.getMeasuredWidth(), view.getMeasuredHeight()); } - public void setSuggestedHeight(int height) { - mSuggestedHeight = height; - setListViewHeight(); + private static void setSize(View view, int width, int height) { + view.setMinimumWidth(width); + view.setMinimumHeight(height); + ViewGroup.LayoutParams params = view.getLayoutParams(); + params = (params == null) ? new ViewGroup.LayoutParams(0, 0) : params; + params.width = width; + params.height = height; + view.setLayoutParams(params); } - public int getMinimumHeight() { - return mContentView.getContext().getResources(). - getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height) - + getEstimatedToolbarHeight(mContentView.getContext()); + private static void setSize(View view, Size size) { + setSize(view, size.getWidth(), size.getHeight()); } - /** - * Returns the content view of the overflow. - */ - public View getView() { - return mContentView; + private static void setWidth(View view, int width) { + ViewGroup.LayoutParams params = view.getLayoutParams(); + setSize(view, width, params.height); } - public void fadeIn(boolean animate) { - mViewFader.fadeIn(animate); + private static void setHeight(View view, int height) { + ViewGroup.LayoutParams params = view.getLayoutParams(); + setSize(view, params.width, height); } - public void fadeOut(boolean animate) { - mViewFader.fadeOut(animate); + private static int getLineHeight(Context context) { + return context.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_height); } /** - * Returns how big this panel's view should be. - * This method should only be called when the view has not been attached to a parent. - * - * @throws IllegalStateException + * A custom interpolator used for various floating toolbar animations. */ - public Size measure() { - Preconditions.checkState(mContentView.getParent() == null); - mContentView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - return new Size(mContentView.getMeasuredWidth(), mContentView.getMeasuredHeight()); - } + private static final class LogAccelerateInterpolator implements Interpolator { - private void setListViewHeight() { - int itemHeight = getEstimatedToolbarHeight(mContentView.getContext()); - int height = mListView.getAdapter().getCount() * itemHeight; - int maxHeight = mContentView.getContext().getResources(). - getDimensionPixelSize(R.dimen.floating_toolbar_maximum_overflow_height); - int minHeight = mContentView.getContext().getResources(). - getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height); - int suggestedListViewHeight = mSuggestedHeight - (mSuggestedHeight % itemHeight) - - itemHeight; // reserve space for the back button. - ViewGroup.LayoutParams params = mListView.getLayoutParams(); - if (suggestedListViewHeight <= 0) { - // Invalid height. Use the maximum height available. - params.height = Math.min(maxHeight, height); - } else if (suggestedListViewHeight < minHeight) { - // Height is smaller than minimum allowed. Use minimum height. - params.height = minHeight; - } else { - // Use the suggested height. Cap it at the maximum available height. - params.height = Math.min(Math.min(suggestedListViewHeight, maxHeight), height); + private static final int BASE = 100; + private static final float LOGS_SCALE = 1f / computeLog(1, BASE); + + private static float computeLog(float t, int base) { + return (float) (1 - Math.pow(base, -t)); } - mListView.setLayoutParams(params); - } - private void setOverflowWidth() { - mOverflowWidth = 0; - for (int i = 0; i < mListView.getAdapter().getCount(); i++) { - MenuItem menuItem = (MenuItem) mListView.getAdapter().getItem(i); - Preconditions.checkNotNull(menuItem); - mListViewItemWidthCalculator.setText(menuItem.getTitle()); - mListViewItemWidthCalculator.measure( - MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); - mOverflowWidth = Math.max( - mListViewItemWidthCalculator.getMeasuredWidth(), mOverflowWidth); + @Override + public float getInterpolation(float t) { + return 1 - computeLog(1 - t, BASE) * LOGS_SCALE; } } - private ListView createOverflowListView() { - final Context context = mContentView.getContext(); - final ListView overflowListView = new ListView(context); - overflowListView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - overflowListView.setDivider(null); - overflowListView.setDividerHeight(0); - - final int viewTypeCount = 2; - final int stringLabelViewType = 0; - final int iconOnlyViewType = 1; - final ArrayAdapter overflowListViewAdapter = - new ArrayAdapter<MenuItem>(context, 0) { - @Override - public int getViewTypeCount() { - return viewTypeCount; - } - - @Override - public int getItemViewType(int position) { - if (isIconOnlyMenuItem(getItem(position))) { - return iconOnlyViewType; - } - return stringLabelViewType; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (getItemViewType(position) == iconOnlyViewType) { - return getIconOnlyView(position, convertView); - } - return getStringTitleView(position, convertView); - } + /** + * A helper for generating views for the overflow panel. + */ + private static final class OverflowPanelViewHelper { - private View getStringTitleView(int position, View convertView) { - TextView menuButton; - if (convertView != null) { - menuButton = (TextView) convertView; - } else { - menuButton = createOverflowMenuItemButton(context); - } - MenuItem menuItem = getItem(position); - menuButton.setText(menuItem.getTitle()); - menuButton.setContentDescription(menuItem.getTitle()); - menuButton.setMinimumWidth(mOverflowWidth); - return menuButton; - } + private static final int NUM_OF_VIEW_TYPES = 2; + private static final int VIEW_TYPE_STRING_TITLE = 0; + private static final int VIEW_TYPE_ICON_ONLY = 1; - private View getIconOnlyView(int position, View convertView) { - View menuButton; - if (convertView != null) { - menuButton = convertView; - } else { - menuButton = LayoutInflater.from(context).inflate( - R.layout.floating_popup_overflow_image_list_item, null); - } - MenuItem menuItem = getItem(position); - ((ImageView) menuButton - .findViewById(R.id.floating_toolbar_menu_item_image_button)) - .setImageDrawable(menuItem.getIcon()); - menuButton.setMinimumWidth(mOverflowWidth); - return menuButton; - } - }; - overflowListView.setAdapter(overflowListViewAdapter); - return overflowListView; - } - } + private final TextView mStringTitleViewCalculator; + private final View mIconOnlyViewCalculator; + private final Context mContext; - /** - * A helper for fading in or out a view. - */ - private static final class ViewFader { + public OverflowPanelViewHelper(Context context) { + mContext = Preconditions.checkNotNull(context); + mStringTitleViewCalculator = getStringTitleView(null, 0, null); + mIconOnlyViewCalculator = getIconOnlyView(null, 0, null); + } - private static final int FADE_OUT_DURATION = 250; - private static final int FADE_IN_DURATION = 150; + public int getViewTypeCount() { + return NUM_OF_VIEW_TYPES; + } - private final View mView; - private final ObjectAnimator mFadeOutAnimation; - private final ObjectAnimator mFadeInAnimation; + public View getView(MenuItem menuItem, int minimumWidth, View convertView) { + Preconditions.checkNotNull(menuItem); + if (getItemViewType(menuItem) == VIEW_TYPE_ICON_ONLY) { + return getIconOnlyView(menuItem, minimumWidth, convertView); + } + return getStringTitleView(menuItem, minimumWidth, convertView); + } - private ViewFader(View view) { - mView = Preconditions.checkNotNull(view); - mFadeOutAnimation = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0) - .setDuration(FADE_OUT_DURATION); - mFadeInAnimation = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1) - .setDuration(FADE_IN_DURATION); - } + public int getItemViewType(MenuItem menuItem) { + Preconditions.checkNotNull(menuItem); + if (isIconOnlyMenuItem(menuItem)) { + return VIEW_TYPE_ICON_ONLY; + } + return VIEW_TYPE_STRING_TITLE; + } - public void fadeIn(boolean animate) { - cancelFadeAnimations(); - if (animate) { - mFadeInAnimation.start(); - } else { - mView.setAlpha(1); + public int calculateWidth(MenuItem menuItem) { + final View calculator; + if (isIconOnlyMenuItem(menuItem)) { + ((ImageView) mIconOnlyViewCalculator + .findViewById(R.id.floating_toolbar_menu_item_image_button)) + .setImageDrawable(menuItem.getIcon()); + calculator = mIconOnlyViewCalculator; + } else { + mStringTitleViewCalculator.setText(menuItem.getTitle()); + calculator = mStringTitleViewCalculator; + } + calculator.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + return calculator.getMeasuredWidth(); } - } - public void fadeOut(boolean animate) { - cancelFadeAnimations(); - if (animate) { - mFadeOutAnimation.start(); - } else { - mView.setAlpha(0); + private TextView getStringTitleView( + MenuItem menuItem, int minimumWidth, View convertView) { + TextView menuButton; + if (convertView != null) { + menuButton = (TextView) convertView; + } else { + menuButton = (TextView) LayoutInflater.from(mContext) + .inflate(R.layout.floating_popup_overflow_list_item, null); + menuButton.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + } + if (menuItem != null) { + menuButton.setText(menuItem.getTitle()); + menuButton.setContentDescription(menuItem.getTitle()); + menuButton.setMinimumWidth(minimumWidth); + } + return menuButton; } - } - private void cancelFadeAnimations() { - mFadeInAnimation.cancel(); - mFadeOutAnimation.cancel(); + private View getIconOnlyView( + MenuItem menuItem, int minimumWidth, View convertView) { + View menuButton; + if (convertView != null) { + menuButton = convertView; + } else { + menuButton = LayoutInflater.from(mContext).inflate( + R.layout.floating_popup_overflow_image_list_item, null); + menuButton.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + } + if (menuItem != null) { + ((ImageView) menuButton + .findViewById(R.id.floating_toolbar_menu_item_image_button)) + .setImageDrawable(menuItem.getIcon()); + menuButton.setMinimumWidth(minimumWidth); + } + return menuButton; + } } } @@ -1453,14 +1596,6 @@ public final class FloatingToolbar { return menuItemButton; } - /** - * Creates and returns a styled floating toolbar overflow list view item. - */ - private static TextView createOverflowMenuItemButton(Context context) { - return (TextView) LayoutInflater.from(context) - .inflate(R.layout.floating_popup_overflow_list_item, null); - } - private static ViewGroup createContentContainer(Context context) { ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context) .inflate(R.layout.floating_popup_container, null); @@ -1468,7 +1603,7 @@ public final class FloatingToolbar { return contentContainer; } - private static PopupWindow createPopupWindow(View content) { + private static PopupWindow createPopupWindow(ViewGroup content) { ViewGroup popupContentHolder = new LinearLayout(content.getContext()); PopupWindow popupWindow = new PopupWindow(popupContentHolder); // TODO: Use .setLayoutInScreenEnabled(true) instead of .setClippingEnabled(false) @@ -1490,11 +1625,9 @@ public final class FloatingToolbar { * @param view The view to animate */ private static AnimatorSet createEnterAnimation(View view) { - AnimatorSet animation = new AnimatorSet(); + AnimatorSet animation = new AnimatorSet(); animation.playTogether( - ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150), - // Make sure that view.x is always fixed throughout the duration of this animation. - ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX())); + ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150)); return animation; } @@ -1525,13 +1658,4 @@ public final class FloatingToolbar { a.recycle(); return new ContextThemeWrapper(originalContext, themeId); } - - private static int getEstimatedToolbarHeight(Context context) { - return context.getResources().getDimensionPixelSize(R.dimen.floating_toolbar_height); - } - - private static int getEstimatedOpenOverflowButtonWidth(Context context) { - return context.getResources() - .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_minimum_width); - } -} +}
\ No newline at end of file diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_1_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_1_animation.xml new file mode 100644 index 000000000000..e6a28bb4a2b2 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_1_animation.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="600" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -6.5,0.0 c 1.08333,-1.0 5.41667,-5.0 6.5,-6.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_4" /> + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="300" + android:propertyName="rotation" + android:valueFrom="-45.0" + android:valueTo="-45.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="150" + android:propertyName="rotation" + android:valueFrom="-45.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_0_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_0_animation.xml new file mode 100644 index 000000000000..c2414de11917 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_0_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="200" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0.0,0.0 c 0.75,0.0 3.75,0.0 4.5,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_5" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_animation.xml new file mode 100644 index 000000000000..593a0eaf0c9b --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_1_pivot_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="450" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_0" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_2_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_2_animation.xml new file mode 100644 index 000000000000..5b09a41780af --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_2_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="600" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -8.0,0.0 c 1.33333,0.0 6.66667,0.0 8.0,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_3" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_0_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_0_animation.xml new file mode 100644 index 000000000000..dccbc67de2c5 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_0_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="250" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0.0,0.0 c 1.66667,0.0 8.5,0.0 10.0,0.0 c 0.13052,0.0 -0.83333,0.0 -1.0,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_2" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_animation.xml new file mode 100644 index 000000000000..c1c0c1c3aa51 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_2_pivot_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="516" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 9.0,0.0 c -1.5,0.0 -7.5,0.0 -9.0,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_6" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_3_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_3_animation.xml new file mode 100644 index 000000000000..df69bf75a279 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_3_animation.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="600" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -6.5,0.0 c 1.08333,1.0 5.41667,5.0 6.5,6.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_4" /> + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="300" + android:propertyName="rotation" + android:valueFrom="45.0" + android:valueTo="45.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="150" + android:propertyName="rotation" + android:valueFrom="45.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_0_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_0_animation.xml new file mode 100644 index 000000000000..c2414de11917 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_0_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="200" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0.0,0.0 c 0.75,0.0 3.75,0.0 4.5,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_5" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_animation.xml new file mode 100644 index 000000000000..593a0eaf0c9b --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_3_pivot_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="450" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_0" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_4_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_4_animation.xml new file mode 100644 index 000000000000..a3753302e588 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_4_animation.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="300" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0.0,6.0 c -1.08333,-1.0 -5.41667,-5.0 -6.5,-6.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="400" + android:propertyName="rotation" + android:valueFrom="0.0" + android:valueTo="45.0" + android:valueType="floatType" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_1" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_5_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_5_animation.xml new file mode 100644 index 000000000000..ae7ee47f9656 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_5_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="250" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0.0,0.0 c -1.33333,0.0 -6.66667,0.0 -8.0,0.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_6_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_6_animation.xml new file mode 100644 index 000000000000..3b872af636f8 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_6_animation.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="300" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 0.0,-6.0 c -1.08333,1.0 -5.41667,5.0 -6.5,6.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="400" + android:propertyName="rotation" + android:valueFrom="0.0" + android:valueTo="-45.0" + android:valueType="floatType" + android:interpolator="@interpolator/ft_avd_toarrow_animation_interpolator_1" /> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_1_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_1_animation.xml new file mode 100644 index 000000000000..94e04b4ebf33 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_1_animation.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="300" + android:propertyName="pathData" + android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z" + android:valueTo="M -3.54375000003,-1.21249999999 l 7.08750000005,0.0 c 0.669645259129,0.0 1.21249999999,0.542854740865 1.21249999999,1.21249999999 l 0.0,0.0 c 0.0,0.669645259129 -0.542854740865,1.21249999999 -1.21249999999,1.21249999999 l -7.08750000005,0.0 c -0.669645259129,0.0 -1.21249999999,-0.542854740865 -1.21249999999,-1.21249999999 l 0.0,0.0 c 0.0,-0.669645259129 0.542854740865,-1.21249999999 1.21249999999,-1.21249999999 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -3.54375000003,-1.21249999999 l 7.08750000005,0.0 c 0.669645259129,0.0 1.21249999999,0.542854740865 1.21249999999,1.21249999999 l 0.0,0.0 c 0.0,0.669645259129 -0.542854740865,1.21249999999 -1.21249999999,1.21249999999 l -7.08750000005,0.0 c -0.669645259129,0.0 -1.21249999999,-0.542854740865 -1.21249999999,-1.21249999999 l 0.0,0.0 c 0.0,-0.669645259129 0.542854740865,-1.21249999999 1.21249999999,-1.21249999999 Z" + android:valueTo="M -1.68627963028,-1.62527119327 l 3.37255926056,0.0 c 0.897612494333,0.0 1.62527119327,0.727658698938 1.62527119327,1.62527119327 l 0.0,0.0 c 0.0,0.897612494333 -0.727658698938,1.62527119327 -1.62527119327,1.62527119327 l -3.37255926056,0.0 c -0.897612494333,0.0 -1.62527119327,-0.727658698938 -1.62527119327,-1.62527119327 l 0.0,0.0 c 0.0,-0.897612494333 0.727658698938,-1.62527119327 1.62527119327,-1.62527119327 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.68627963028,-1.62527119327 l 3.37255926056,0.0 c 0.897612494333,0.0 1.62527119327,0.727658698938 1.62527119327,1.62527119327 l 0.0,0.0 c 0.0,0.897612494333 -0.727658698938,1.62527119327 -1.62527119327,1.62527119327 l -3.37255926056,0.0 c -0.897612494333,0.0 -1.62527119327,-0.727658698938 -1.62527119327,-1.62527119327 l 0.0,0.0 c 0.0,-0.897612494333 0.727658698938,-1.62527119327 1.62527119327,-1.62527119327 Z" + android:valueTo="M -0.866611597071,-1.8074196451 l 1.73322319414,0.0 c 0.998210306475,0.0 1.8074196451,0.80920933862 1.8074196451,1.8074196451 l 0.0,0.0 c 0.0,0.998210306475 -0.80920933862,1.8074196451 -1.8074196451,1.8074196451 l -1.73322319414,0.0 c -0.998210306475,0.0 -1.8074196451,-0.80920933862 -1.8074196451,-1.8074196451 l 0.0,0.0 c 0.0,-0.998210306475 0.80920933862,-1.8074196451 1.8074196451,-1.8074196451 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.866611597071,-1.8074196451 l 1.73322319414,0.0 c 0.998210306475,0.0 1.8074196451,0.80920933862 1.8074196451,1.8074196451 l 0.0,0.0 c 0.0,0.998210306475 -0.80920933862,1.8074196451 -1.8074196451,1.8074196451 l -1.73322319414,0.0 c -0.998210306475,0.0 -1.8074196451,-0.80920933862 -1.8074196451,-1.8074196451 l 0.0,0.0 c 0.0,-0.998210306475 0.80920933862,-1.8074196451 1.8074196451,-1.8074196451 Z" + android:valueTo="M -0.416762258226,-1.90738616484 l 0.833524516453,0.0 c 1.05342029082,0.0 1.90738616484,0.853965874019 1.90738616484,1.90738616484 l 0.0,0.0 c 0.0,1.05342029082 -0.853965874019,1.90738616484 -1.90738616484,1.90738616484 l -0.833524516453,0.0 c -1.05342029082,0.0 -1.90738616484,-0.853965874019 -1.90738616484,-1.90738616484 l 0.0,0.0 c 0.0,-1.05342029082 0.853965874019,-1.90738616484 1.90738616484,-1.90738616484 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.416762258226,-1.90738616484 l 0.833524516453,0.0 c 1.05342029082,0.0 1.90738616484,0.853965874019 1.90738616484,1.90738616484 l 0.0,0.0 c 0.0,1.05342029082 -0.853965874019,1.90738616484 -1.90738616484,1.90738616484 l -0.833524516453,0.0 c -1.05342029082,0.0 -1.90738616484,-0.853965874019 -1.90738616484,-1.90738616484 l 0.0,0.0 c 0.0,-1.05342029082 0.853965874019,-1.90738616484 1.90738616484,-1.90738616484 Z" + android:valueTo="M -0.163643891635,-1.96363469075 l 0.32728778327,0.0 c 1.08448549388,0.0 1.96363469075,0.87914919687 1.96363469075,1.96363469075 l 0.0,0.0 c 0.0,1.08448549388 -0.87914919687,1.96363469075 -1.96363469075,1.96363469075 l -0.32728778327,0.0 c -1.08448549388,0.0 -1.96363469075,-0.87914919687 -1.96363469075,-1.96363469075 l 0.0,0.0 c 0.0,-1.08448549388 0.87914919687,-1.96363469075 1.96363469075,-1.96363469075 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.163643891635,-1.96363469075 l 0.32728778327,0.0 c 1.08448549388,0.0 1.96363469075,0.87914919687 1.96363469075,1.96363469075 l 0.0,0.0 c 0.0,1.08448549388 -0.87914919687,1.96363469075 -1.96363469075,1.96363469075 l -0.32728778327,0.0 c -1.08448549388,0.0 -1.96363469075,-0.87914919687 -1.96363469075,-1.96363469075 l 0.0,0.0 c 0.0,-1.08448549388 0.87914919687,-1.96363469075 1.96363469075,-1.96363469075 Z" + android:valueTo="M -0.0368976091105,-1.99180053131 l 0.073795218221,0.0 c 1.10004105809,0.0 1.99180053131,0.891759473223 1.99180053131,1.99180053131 l 0.0,0.0 c 0.0,1.10004105809 -0.891759473223,1.99180053131 -1.99180053131,1.99180053131 l -0.073795218221,0.0 c -1.10004105809,0.0 -1.99180053131,-0.891759473223 -1.99180053131,-1.99180053131 l 0.0,0.0 c 0.0,-1.10004105809 0.891759473223,-1.99180053131 1.99180053131,-1.99180053131 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.0368976091105,-1.99180053131 l 0.073795218221,0.0 c 1.10004105809,0.0 1.99180053131,0.891759473223 1.99180053131,1.99180053131 l 0.0,0.0 c 0.0,1.10004105809 -0.891759473223,1.99180053131 -1.99180053131,1.99180053131 l -0.073795218221,0.0 c -1.10004105809,0.0 -1.99180053131,-0.891759473223 -1.99180053131,-1.99180053131 l 0.0,0.0 c 0.0,-1.10004105809 0.891759473223,-1.99180053131 1.99180053131,-1.99180053131 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_2_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_2_animation.xml new file mode 100644 index 000000000000..423f8d73f900 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_2_animation.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="300" + android:propertyName="pathData" + android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -4.54266574364,-1.0 l 9.08533148727,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -9.08533148727,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z" + android:valueTo="M -2.49262086719,-1.44608425174 l 4.98524173437,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -4.98524173437,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -2.49262086719,-1.44608425174 l 4.98524173437,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -4.98524173437,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z" + android:valueTo="M -1.39285008918,-1.69047775796 l 2.78570017836,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -2.78570017836,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.39285008918,-1.69047775796 l 2.78570017836,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -2.78570017836,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z" + android:valueTo="M -0.77117021921,-1.82862884018 l 1.54234043842,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -1.54234043842,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.77117021921,-1.82862884018 l 1.54234043842,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -1.54234043842,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z" + android:valueTo="M -0.387649645988,-1.91385563422 l 0.775299291975,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -0.775299291975,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.387649645988,-1.91385563422 l 0.775299291975,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -0.775299291975,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z" + android:valueTo="M -0.156963485208,-1.96511922551 l 0.313926970417,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.313926970417,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.156963485208,-1.96511922551 l 0.313926970417,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.313926970417,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z" + android:valueTo="M -0.0362224951386,-1.99195055664 l 0.0724449902773,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0724449902773,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.0362224951386,-1.99195055664 l 0.0724449902773,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0724449902773,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_3_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_3_animation.xml new file mode 100644 index 000000000000..444f6b65c724 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_3_animation.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="300" + android:propertyName="pathData" + android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -6.04266574364,-1.0 l 12.0853314873,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -12.0853314873,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -6.04266574364,-1.0 l 12.0853314873,0.0 c 0.528721110251,0.0 0.957334256364,0.428613146113 0.957334256364,0.957334256364 l 0.0,0.0853314872718 c 0.0,0.528721110251 -0.428613146113,0.957334256364 -0.957334256364,0.957334256364 l -12.0853314873,0.0 c -0.528721110251,0.0 -0.957334256364,-0.428613146113 -0.957334256364,-0.957334256364 l 0.0,-0.0853314872718 c 0.0,-0.528721110251 0.428613146113,-0.957334256364 0.957334256364,-0.957334256364 Z" + android:valueTo="M -3.32349448958,-1.44608425174 l 6.64698897916,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -6.64698897916,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -3.32349448958,-1.44608425174 l 6.64698897916,0.0 c 0.79865027916,0.0 1.44608425174,0.647433972576 1.44608425174,1.44608425174 l 0.0,0.0 c 0.0,0.79865027916 -0.647433972576,1.44608425174 -1.44608425174,1.44608425174 l -6.64698897916,0.0 c -0.79865027916,0.0 -1.44608425174,-0.647433972576 -1.44608425174,-1.44608425174 l 0.0,0.0 c 0.0,-0.79865027916 0.647433972576,-1.44608425174 1.44608425174,-1.44608425174 Z" + android:valueTo="M -1.85713345224,-1.69047775796 l 3.71426690449,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -3.71426690449,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.85713345224,-1.69047775796 l 3.71426690449,0.0 c 0.933625085597,0.0 1.69047775796,0.756852672362 1.69047775796,1.69047775796 l 0.0,0.0 c 0.0,0.933625085597 -0.756852672362,1.69047775796 -1.69047775796,1.69047775796 l -3.71426690449,0.0 c -0.933625085597,0.0 -1.69047775796,-0.756852672362 -1.69047775796,-1.69047775796 l 0.0,0.0 c 0.0,-0.933625085597 0.756852672362,-1.69047775796 1.69047775796,-1.69047775796 Z" + android:valueTo="M -1.02822695895,-1.82862884018 l 2.05645391789,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -2.05645391789,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.02822695895,-1.82862884018 l 2.05645391789,0.0 c 1.00992382147,0.0 1.82862884018,0.818705018702 1.82862884018,1.82862884018 l 0.0,0.0 c 0.0,1.00992382147 -0.818705018702,1.82862884018 -1.82862884018,1.82862884018 l -2.05645391789,0.0 c -1.00992382147,0.0 -1.82862884018,-0.818705018702 -1.82862884018,-1.82862884018 l 0.0,0.0 c 0.0,-1.00992382147 0.818705018702,-1.82862884018 1.82862884018,-1.82862884018 Z" + android:valueTo="M -0.51686619465,-1.91385563422 l 1.0337323893,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -1.0337323893,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.51686619465,-1.91385563422 l 1.0337323893,0.0 c 1.0569932801,0.0 1.91385563422,0.856862354124 1.91385563422,1.91385563422 l 0.0,0.0 c 0.0,1.0569932801 -0.856862354124,1.91385563422 -1.91385563422,1.91385563422 l -1.0337323893,0.0 c -1.0569932801,0.0 -1.91385563422,-0.856862354124 -1.91385563422,-1.91385563422 l 0.0,0.0 c 0.0,-1.0569932801 0.856862354124,-1.91385563422 1.91385563422,-1.91385563422 Z" + android:valueTo="M -0.209284646944,-1.96511922551 l 0.418569293889,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.418569293889,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.209284646944,-1.96511922551 l 0.418569293889,0.0 c 1.08530537979,0.0 1.96511922551,0.879813845722 1.96511922551,1.96511922551 l 0.0,0.0 c 0.0,1.08530537979 -0.879813845722,1.96511922551 -1.96511922551,1.96511922551 l -0.418569293889,0.0 c -1.08530537979,0.0 -1.96511922551,-0.879813845722 -1.96511922551,-1.96511922551 l 0.0,0.0 c 0.0,-1.08530537979 0.879813845722,-1.96511922551 1.96511922551,-1.96511922551 Z" + android:valueTo="M -0.0482966601849,-1.99195055664 l 0.0965933203697,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0965933203697,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.0482966601849,-1.99195055664 l 0.0965933203697,0.0 c 1.10012391479,0.0 1.99195055664,0.89182664185 1.99195055664,1.99195055664 l 0.0,0.0 c 0.0,1.10012391479 -0.89182664185,1.99195055664 -1.99195055664,1.99195055664 l -0.0965933203697,0.0 c -1.10012391479,0.0 -1.99195055664,-0.89182664185 -1.99195055664,-1.99195055664 l 0.0,0.0 c 0.0,-1.10012391479 0.89182664185,-1.99195055664 1.99195055664,-1.99195055664 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_4_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_4_animation.xml new file mode 100644 index 000000000000..db294c7c1f6b --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_4_animation.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="100" + android:propertyName="pathData" + android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="49" + android:propertyName="pathData" + android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueTo="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="50" + android:propertyName="pathData" + android:valueFrom="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z" + android:valueTo="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="49" + android:propertyName="pathData" + android:valueFrom="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="49" + android:propertyName="pathData" + android:valueFrom="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_5_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_5_animation.xml new file mode 100644 index 000000000000..86e4dd638a88 --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_5_animation.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="50" + android:propertyName="pathData" + android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueTo="M -0.510644950991,-1.91489250817 l 1.02128990198,0.0 c 1.05756592977,0.0 1.91489250817,0.857326578401 1.91489250817,1.91489250817 l 0.0,0.0 c 0.0,1.05756592977 -0.857326578401,1.91489250817 -1.91489250817,1.91489250817 l -1.02128990198,0.0 c -1.05756592977,0.0 -1.91489250817,-0.857326578401 -1.91489250817,-1.91489250817 l 0.0,0.0 c 0.0,-1.05756592977 0.857326578401,-1.91489250817 1.91489250817,-1.91489250817 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:valueFrom="M -0.510644950991,-1.91489250817 l 1.02128990198,0.0 c 1.05756592977,0.0 1.91489250817,0.857326578401 1.91489250817,1.91489250817 l 0.0,0.0 c 0.0,1.05756592977 -0.857326578401,1.91489250817 -1.91489250817,1.91489250817 l -1.02128990198,0.0 c -1.05756592977,0.0 -1.91489250817,-0.857326578401 -1.91489250817,-1.91489250817 l 0.0,0.0 c 0.0,-1.05756592977 0.857326578401,-1.91489250817 1.91489250817,-1.91489250817 Z" + android:valueTo="M -3.66172292328,-1.54166666667 l 7.32344584656,0.0 c 0.347908322704,0.0 0.629943743386,0.282035420682 0.629943743386,0.629943743386 l 0.0,1.82344584656 c 0.0,0.347908322704 -0.282035420682,0.629943743386 -0.629943743386,0.629943743386 l -7.32344584656,0.0 c -0.347908322704,0.0 -0.629943743386,-0.282035420682 -0.629943743386,-0.629943743386 l 0.0,-1.82344584656 c 0.0,-0.347908322704 0.282035420682,-0.629943743386 0.629943743386,-0.629943743386 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:valueFrom="M -3.66172292328,-1.54166666667 l 7.32344584656,0.0 c 0.347908322704,0.0 0.629943743386,0.282035420682 0.629943743386,0.629943743386 l 0.0,1.82344584656 c 0.0,0.347908322704 -0.282035420682,0.629943743386 -0.629943743386,0.629943743386 l -7.32344584656,0.0 c -0.347908322704,0.0 -0.629943743386,-0.282035420682 -0.629943743386,-0.629943743386 l 0.0,-1.82344584656 c 0.0,-0.347908322704 0.282035420682,-0.629943743386 0.629943743386,-0.629943743386 Z" + android:valueTo="M -5.80605656225,-1.22447422869 l 11.6121131245,0.0 c 0.0395282866537,0.0 0.0715722943065,0.0320440076528 0.0715722943065,0.0715722943065 l 0.0,2.30580386877 c 0.0,0.0395282866537 -0.0320440076528,0.0715722943065 -0.0715722943065,0.0715722943065 l -11.6121131245,0.0 c -0.0395282866537,0.0 -0.0715722943065,-0.0320440076528 -0.0715722943065,-0.0715722943065 l 0.0,-2.30580386877 c 0.0,-0.0395282866537 0.0320440076528,-0.0715722943065 0.0715722943065,-0.0715722943065 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:valueFrom="M -5.80605656225,-1.22447422869 l 11.6121131245,0.0 c 0.0395282866537,0.0 0.0715722943065,0.0320440076528 0.0715722943065,0.0715722943065 l 0.0,2.30580386877 c 0.0,0.0395282866537 -0.0320440076528,0.0715722943065 -0.0715722943065,0.0715722943065 l -11.6121131245,0.0 c -0.0395282866537,0.0 -0.0715722943065,-0.0320440076528 -0.0715722943065,-0.0715722943065 l 0.0,-2.30580386877 c 0.0,-0.0395282866537 0.0320440076528,-0.0715722943065 0.0715722943065,-0.0715722943065 Z" + android:valueTo="M -6.60386380145,-1.07922723971 l 13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:valueFrom="M -6.60386380145,-1.07922723971 l 13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.2077276029,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.15845447942 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -6.91679014084,-1.01664197183 l 13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:valueFrom="M -6.91679014084,-1.01664197183 l 13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 l -13.8335802817,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.03328394367 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_toarrow_rectangle_path_6_animation.xml b/core/res/res/anim/ft_avd_toarrow_rectangle_path_6_animation.xml new file mode 100644 index 000000000000..db294c7c1f6b --- /dev/null +++ b/core/res/res/anim/ft_avd_toarrow_rectangle_path_6_animation.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="100" + android:propertyName="pathData" + android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="49" + android:propertyName="pathData" + android:valueFrom="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueTo="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="50" + android:propertyName="pathData" + android:valueFrom="M -4.0950630677,-1.30952224204 l 8.1901261354,0.0 c 0.177619793131,0.0 0.32160908516,0.143989292029 0.32160908516,0.32160908516 l 0.0,1.97582631376 c 0.0,0.177619793131 -0.143989292029,0.32160908516 -0.32160908516,0.32160908516 l -8.1901261354,0.0 c -0.177619793131,0.0 -0.32160908516,-0.143989292029 -0.32160908516,-0.32160908516 l 0.0,-1.97582631376 c 0.0,-0.177619793131 0.143989292029,-0.32160908516 0.32160908516,-0.32160908516 Z" + android:valueTo="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="49" + android:propertyName="pathData" + android:valueFrom="M -5.11454284324,-1.11013061622 l 10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.2290856865,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.22026123243 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="49" + android:propertyName="pathData" + android:valueFrom="M -5.41755510258,-1.02355568498 l 10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.8351102052,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.04711136995 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_1_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_animation.xml new file mode 100644 index 000000000000..3869ced8afe0 --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_animation.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="400" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -6.5,0.0 c 1.08333,-1.0 5.41667,-5.0 6.5,-6.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="400" + android:propertyName="rotation" + android:valueFrom="-45.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_1_pivot_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_pivot_animation.xml new file mode 100644 index 000000000000..90010a75dcfe --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_1_pivot_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="300" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_2_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_animation.xml new file mode 100644 index 000000000000..b5e031e4e8b6 --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="300" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -8.0,0.0 c 1.33333,0.0 6.66667,0.0 8.0,0.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_2_pivot_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_pivot_animation.xml new file mode 100644 index 000000000000..55c72c6e852c --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_2_pivot_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="216" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 9.0,0.0 c -1.5,0.0 -7.5,0.0 -9.0,0.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_3_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_animation.xml new file mode 100644 index 000000000000..0524f2a21ed0 --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_animation.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="400" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M -6.5,0.0 c 1.08333,1.0 5.41667,5.0 6.5,6.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> + <objectAnimator + android:duration="400" + android:propertyName="rotation" + android:valueFrom="45.0" + android:valueTo="0.0" + android:valueType="floatType" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_3_pivot_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_pivot_animation.xml new file mode 100644 index 000000000000..90010a75dcfe --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_3_pivot_animation.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <objectAnimator + android:duration="300" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:pathData="M 4.5,0.0 c -0.75,0.0 -3.75,0.0 -4.5,0.0" + android:interpolator="@android:interpolator/fast_out_slow_in" /> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_path_1_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_1_animation.xml new file mode 100644 index 000000000000..ced8cf50d33b --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_1_animation.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z" + android:valueTo="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z" + android:valueTo="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z" + android:valueTo="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z" + android:valueTo="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z" + android:valueTo="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_path_2_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_2_animation.xml new file mode 100644 index 000000000000..ced8cf50d33b --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_2_animation.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -5.349609375,-1.04296875 l 10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 l -10.69921875,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0859375 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -4.57327108594,-1.25 l 9.14654217189,0.0 c 0.0285690903566,0.0 0.0517289140556,0.023159823699 0.0517289140556,0.0517289140556 l 0.0,2.39654217189 c 0.0,0.0285690903566 -0.023159823699,0.0517289140556 -0.0517289140556,0.0517289140556 l -9.14654217189,0.0 c -0.0285690903566,0.0 -0.0517289140556,-0.023159823699 -0.0517289140556,-0.0517289140556 l 0.0,-2.39654217189 c 0.0,-0.0285690903566 0.023159823699,-0.0517289140556 0.0517289140556,-0.0517289140556 Z" + android:valueTo="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -3.04137381938,-1.55960748018 l 6.08274763876,0.0 c 0.2761423749,0.0 0.5,0.2238576251 0.5,0.5 l 0.0,2.11921496035 c 0.0,0.2761423749 -0.2238576251,0.5 -0.5,0.5 l -6.08274763876,0.0 c -0.2761423749,0.0 -0.5,-0.2238576251 -0.5,-0.5 l 0.0,-2.11921496035 c 0.0,-0.2761423749 0.2238576251,-0.5 0.5,-0.5 Z" + android:valueTo="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.55858989728,-1.77552577131 l 3.11717979456,0.0 c 0.677691994437,0.0 1.22706990313,0.549377908693 1.22706990313,1.22706990313 l 0.0,1.09691173636 c 0.0,0.677691994437 -0.549377908693,1.22706990313 -1.22706990313,1.22706990313 l -3.11717979456,0.0 c -0.677691994437,0.0 -1.22706990313,-0.549377908693 -1.22706990313,-1.22706990313 l 0.0,-1.09691173636 c 0.0,-0.677691994437 0.549377908693,-1.22706990313 1.22706990313,-1.22706990313 Z" + android:valueTo="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.706008791281,-1.89447268498 l 1.41201758256,0.0 c 0.918635554655,0.0 1.66333681129,0.744701256633 1.66333681129,1.66333681129 l 0.0,0.462271747384 c 0.0,0.918635554655 -0.744701256633,1.66333681129 -1.66333681129,1.66333681129 l -1.41201758256,0.0 c -0.918635554655,0.0 -1.66333681129,-0.744701256633 -1.66333681129,-1.66333681129 l 0.0,-0.462271747384 c 0.0,-0.918635554655 0.744701256633,-1.66333681129 1.66333681129,-1.66333681129 Z" + android:valueTo="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.265730251554,-1.95936709392 l 0.531460503108,0.0 c 1.03635400439,0.0 1.87648491973,0.840130915331 1.87648491973,1.87648491973 l 0.0,0.165764348389 c 0.0,1.03635400439 -0.840130915331,1.87648491973 -1.87648491973,1.87648491973 l -0.531460503108,0.0 c -1.03635400439,0.0 -1.87648491973,-0.840130915331 -1.87648491973,-1.87648491973 l 0.0,-0.165764348389 c 0.0,-1.03635400439 0.840130915331,-1.87648491973 1.87648491973,-1.87648491973 Z" + android:valueTo="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.0581061000545,-1.99098433926 l 0.116212200109,0.0 c 1.08990562844,0.0 1.97344871252,0.883543084083 1.97344871252,1.97344871252 l 0.0,0.0350712534878 c 0.0,1.08990562844 -0.883543084083,1.97344871252 -1.97344871252,1.97344871252 l -0.116212200109,0.0 c -1.08990562844,0.0 -1.97344871252,-0.883543084083 -1.97344871252,-1.97344871252 l 0.0,-0.0350712534878 c 0.0,-1.08990562844 0.883543084083,-1.97344871252 1.97344871252,-1.97344871252 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/anim/ft_avd_tooverflow_rectangle_path_3_animation.xml b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_3_animation.xml new file mode 100644 index 000000000000..cb294100b48d --- /dev/null +++ b/core/res/res/anim/ft_avd_tooverflow_rectangle_path_3_animation.xml @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<set + xmlns:android="http://schemas.android.com/apk/res/android" > + <set + android:ordering="sequentially" > + <objectAnimator + android:duration="50" + android:propertyName="pathData" + android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" + android:valueTo="M -4.36843359242,-1.49992262412 l 8.73686718484,0.0 c 0.0728757880921,0.0 0.131953286993,0.0590774989007 0.131953286993,0.131953286993 l 0.0,2.73593867425 c 0.0,0.0728757880921 -0.0590774989007,0.131953286993 -0.131953286993,0.131953286993 l -8.73686718484,0.0 c -0.0728757880921,0.0 -0.131953286993,-0.0590774989007 -0.131953286993,-0.131953286993 l 0.0,-2.73593867425 c 0.0,-0.0728757880921 0.0590774989007,-0.131953286993 0.131953286993,-0.131953286993 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -4.36843359242,-1.49992262412 l 8.73686718484,0.0 c 0.0728757880921,0.0 0.131953286993,0.0590774989007 0.131953286993,0.131953286993 l 0.0,2.73593867425 c 0.0,0.0728757880921 -0.0590774989007,0.131953286993 -0.131953286993,0.131953286993 l -8.73686718484,0.0 c -0.0728757880921,0.0 -0.131953286993,-0.0590774989007 -0.131953286993,-0.131953286993 l 0.0,-2.73593867425 c 0.0,-0.0728757880921 0.0590774989007,-0.131953286993 0.131953286993,-0.131953286993 Z" + android:valueTo="M -2.7976112102,-1.69047775796 l 5.59522242041,0.0 c 0.41421356235,0.0 0.75,0.33578643765 0.75,0.75 l 0.0,1.88095551592 c 0.0,0.41421356235 -0.33578643765,0.75 -0.75,0.75 l -5.59522242041,0.0 c -0.41421356235,0.0 -0.75,-0.33578643765 -0.75,-0.75 l 0.0,-1.88095551592 c 0.0,-0.41421356235 0.33578643765,-0.75 0.75,-0.75 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -2.7976112102,-1.69047775796 l 5.59522242041,0.0 c 0.41421356235,0.0 0.75,0.33578643765 0.75,0.75 l 0.0,1.88095551592 c 0.0,0.41421356235 -0.33578643765,0.75 -0.75,0.75 l -5.59522242041,0.0 c -0.41421356235,0.0 -0.75,-0.33578643765 -0.75,-0.75 l 0.0,-1.88095551592 c 0.0,-0.41421356235 0.33578643765,-0.75 0.75,-0.75 Z" + android:valueTo="M -1.5412962309,-1.81003891076 l 3.08259246181,0.0 c 0.777898159561,0.0 1.4085092153,0.630611055735 1.4085092153,1.4085092153 l 0.0,0.803059390927 c 0.0,0.777898159561 -0.630611055735,1.4085092153 -1.4085092153,1.4085092153 l -3.08259246181,0.0 c -0.777898159561,0.0 -1.4085092153,-0.630611055735 -1.4085092153,-1.4085092153 l 0.0,-0.803059390927 c 0.0,-0.777898159561 0.630611055735,-1.4085092153 1.4085092153,-1.4085092153 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -1.5412962309,-1.81003891076 l 3.08259246181,0.0 c 0.777898159561,0.0 1.4085092153,0.630611055735 1.4085092153,1.4085092153 l 0.0,0.803059390927 c 0.0,0.777898159561 -0.630611055735,1.4085092153 -1.4085092153,1.4085092153 l -3.08259246181,0.0 c -0.777898159561,0.0 -1.4085092153,-0.630611055735 -1.4085092153,-1.4085092153 l 0.0,-0.803059390927 c 0.0,-0.777898159561 0.630611055735,-1.4085092153 1.4085092153,-1.4085092153 Z" + android:valueTo="M -0.798718330914,-1.88987363368 l 1.59743666183,0.0 c 0.967555109393,0.0 1.75191350068,0.784358391285 1.75191350068,1.75191350068 l 0.0,0.275920266008 c 0.0,0.967555109393 -0.784358391285,1.75191350068 -1.75191350068,1.75191350068 l -1.59743666183,0.0 c -0.967555109393,0.0 -1.75191350068,-0.784358391285 -1.75191350068,-1.75191350068 l 0.0,-0.275920266008 c 0.0,-0.967555109393 0.784358391285,-1.75191350068 1.75191350068,-1.75191350068 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.798718330914,-1.88987363368 l 1.59743666183,0.0 c 0.967555109393,0.0 1.75191350068,0.784358391285 1.75191350068,1.75191350068 l 0.0,0.275920266008 c 0.0,0.967555109393 -0.784358391285,1.75191350068 -1.75191350068,1.75191350068 l -1.59743666183,0.0 c -0.967555109393,0.0 -1.75191350068,-0.784358391285 -1.75191350068,-1.75191350068 l 0.0,-0.275920266008 c 0.0,-0.967555109393 0.784358391285,-1.75191350068 1.75191350068,-1.75191350068 Z" + android:valueTo="M -0.366220962052,-1.94300934217 l 0.732441924103,0.0 c 1.05968660322,0.0 1.91873232712,0.859045723904 1.91873232712,1.91873232712 l 0.0,0.0485540300878 c 0.0,1.05968660322 -0.859045723904,1.91873232712 -1.91873232712,1.91873232712 l -0.732441924103,0.0 c -1.05968660322,0.0 -1.91873232712,-0.859045723904 -1.91873232712,-1.91873232712 l 0.0,-0.0485540300878 c 0.0,-1.05968660322 0.859045723904,-1.91873232712 1.91873232712,-1.91873232712 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.366220962052,-1.94300934217 l 0.732441924103,0.0 c 1.05968660322,0.0 1.91873232712,0.859045723904 1.91873232712,1.91873232712 l 0.0,0.0485540300878 c 0.0,1.05968660322 -0.859045723904,1.91873232712 -1.91873232712,1.91873232712 l -0.732441924103,0.0 c -1.05968660322,0.0 -1.91873232712,-0.859045723904 -1.91873232712,-1.91873232712 l 0.0,-0.0485540300878 c 0.0,-1.05968660322 0.859045723904,-1.91873232712 1.91873232712,-1.91873232712 Z" + android:valueTo="M -0.141334109858,-1.97644431502 l 0.282668219716,0.0 c 1.09156005402,0.0 1.97644431502,0.884884261007 1.97644431502,1.97644431502 l 0.0,0.0 c 0.0,1.09156005402 -0.884884261007,1.97644431502 -1.97644431502,1.97644431502 l -0.282668219716,0.0 c -1.09156005402,0.0 -1.97644431502,-0.884884261007 -1.97644431502,-1.97644431502 l 0.0,0.0 c 0.0,-1.09156005402 0.884884261007,-1.97644431502 1.97644431502,-1.97644431502 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.141334109858,-1.97644431502 l 0.282668219716,0.0 c 1.09156005402,0.0 1.97644431502,0.884884261007 1.97644431502,1.97644431502 l 0.0,0.0 c 0.0,1.09156005402 -0.884884261007,1.97644431502 -1.97644431502,1.97644431502 l -0.282668219716,0.0 c -1.09156005402,0.0 -1.97644431502,-0.884884261007 -1.97644431502,-1.97644431502 l 0.0,0.0 c 0.0,-1.09156005402 0.884884261007,-1.97644431502 1.97644431502,-1.97644431502 Z" + android:valueTo="M -0.0331287849506,-1.99447853584 l 0.0662575699012,0.0 c 1.10152007915,0.0 1.99447853584,0.892958456693 1.99447853584,1.99447853584 l 0.0,0.0 c 0.0,1.10152007915 -0.892958456693,1.99447853584 -1.99447853584,1.99447853584 l -0.0662575699012,0.0 c -1.10152007915,0.0 -1.99447853584,-0.892958456693 -1.99447853584,-1.99447853584 l 0.0,0.0 c 0.0,-1.10152007915 0.892958456693,-1.99447853584 1.99447853584,-1.99447853584 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + <objectAnimator + android:duration="37" + android:propertyName="pathData" + android:valueFrom="M -0.0331287849506,-1.99447853584 l 0.0662575699012,0.0 c 1.10152007915,0.0 1.99447853584,0.892958456693 1.99447853584,1.99447853584 l 0.0,0.0 c 0.0,1.10152007915 -0.892958456693,1.99447853584 -1.99447853584,1.99447853584 l -0.0662575699012,0.0 c -1.10152007915,0.0 -1.99447853584,-0.892958456693 -1.99447853584,-1.99447853584 l 0.0,0.0 c 0.0,-1.10152007915 0.892958456693,-1.99447853584 1.99447853584,-1.99447853584 Z" + android:valueTo="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" + android:valueType="pathType" + android:interpolator="@android:interpolator/linear" /> + </set> +</set> diff --git a/core/res/res/drawable/ft_avd_toarrow.xml b/core/res/res/drawable/ft_avd_toarrow.xml new file mode 100644 index 000000000000..087ea74fc60f --- /dev/null +++ b/core/res/res/drawable/ft_avd_toarrow.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:name="ft_avd_toarrow" + android:width="24dp" + android:viewportWidth="24" + android:height="24dp" + android:viewportHeight="24" > + <group + android:name="carrot_3" + android:translateX="12" + android:translateY="12" > + <group + android:name="rectangle_4" + android:translateY="6" > + <group + android:name="rectangle_3_pivot_0" > + <path + android:name="rectangle_path_4" + android:fillColor="#FF000000" + android:pathData="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" /> + </group> + </group> + <group + android:name="rectangle_5" > + <group + android:name="rectangle_2_pivot_0" > + <path + android:name="rectangle_path_5" + android:fillColor="#FF000000" + android:pathData="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" /> + </group> + </group> + <group + android:name="rectangle_6" + android:translateY="-6" > + <group + android:name="rectangle_1_pivot_0" > + <path + android:name="rectangle_path_6" + android:fillColor="#FF000000" + android:pathData="M 0.0,-2.0 l 0.0,0.0 c 1.1045694996,0.0 2.0,0.8954305004 2.0,2.0 l 0.0,0.0 c 0.0,1.1045694996 -0.8954305004,2.0 -2.0,2.0 l 0.0,0.0 c -1.1045694996,0.0 -2.0,-0.8954305004 -2.0,-2.0 l 0.0,0.0 c 0.0,-1.1045694996 0.8954305004,-2.0 2.0,-2.0 Z" /> + </group> + </group> + </group> +</vector> diff --git a/core/res/res/drawable/ft_avd_toarrow_animation.xml b/core/res/res/drawable/ft_avd_toarrow_animation.xml new file mode 100644 index 000000000000..e31067d56893 --- /dev/null +++ b/core/res/res/drawable/ft_avd_toarrow_animation.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<animated-vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ft_avd_toarrow" > + <target + android:name="rectangle_4" + android:animation="@anim/ft_avd_toarrow_rectangle_4_animation" /> + <target + android:name="rectangle_3_pivot_0" + android:animation="@anim/ft_avd_toarrow_rectangle_3_pivot_0_animation" /> + <target + android:name="rectangle_path_4" + android:animation="@anim/ft_avd_toarrow_rectangle_path_4_animation" /> + <target + android:name="rectangle_5" + android:animation="@anim/ft_avd_toarrow_rectangle_5_animation" /> + <target + android:name="rectangle_2_pivot_0" + android:animation="@anim/ft_avd_toarrow_rectangle_2_pivot_0_animation" /> + <target + android:name="rectangle_path_5" + android:animation="@anim/ft_avd_toarrow_rectangle_path_5_animation" /> + <target + android:name="rectangle_6" + android:animation="@anim/ft_avd_toarrow_rectangle_6_animation" /> + <target + android:name="rectangle_1_pivot_0" + android:animation="@anim/ft_avd_toarrow_rectangle_1_pivot_0_animation" /> + <target + android:name="rectangle_path_6" + android:animation="@anim/ft_avd_toarrow_rectangle_path_6_animation" /> +</animated-vector> diff --git a/core/res/res/drawable/ft_avd_tooverflow.xml b/core/res/res/drawable/ft_avd_tooverflow.xml new file mode 100644 index 000000000000..a2b9cec56fa7 --- /dev/null +++ b/core/res/res/drawable/ft_avd_tooverflow.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:name="ft_avd_tooverflow" + android:width="24dp" + android:viewportWidth="24" + android:height="24dp" + android:viewportHeight="24" > + <group + android:name="carrot_5" + android:translateX="12" + android:translateY="12" > + <group + android:name="rectangle_3" + android:translateX="-6.5" + android:rotation="45" > + <group + android:name="rectangle_3_pivot" + android:translateX="4.5" > + <path + android:name="rectangle_path_2" + android:fillColor="#FF000000" + android:pathData="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + <group + android:name="rectangle_2" + android:translateX="-8" > + <group + android:name="rectangle_2_pivot" + android:translateX="9" > + <path + android:name="rectangle_path_3" + android:fillColor="#FF000000" + android:pathData="M -7.0,-1.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + <group + android:name="rectangle_1" + android:translateX="-6.5" + android:rotation="-45" > + <group + android:name="rectangle_1_pivot" + android:translateX="4.5" > + <path + android:name="rectangle_path_1" + android:fillColor="#FF000000" + android:pathData="M -5.5,-1.0 l 11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -11.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + </group> +</vector> diff --git a/core/res/res/drawable/ft_avd_tooverflow_animation.xml b/core/res/res/drawable/ft_avd_tooverflow_animation.xml new file mode 100644 index 000000000000..ed9975bceb0d --- /dev/null +++ b/core/res/res/drawable/ft_avd_tooverflow_animation.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<animated-vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:drawable="@drawable/ft_avd_tooverflow" > + <target + android:name="rectangle_3" + android:animation="@anim/ft_avd_tooverflow_rectangle_3_animation" /> + <target + android:name="rectangle_3_pivot" + android:animation="@anim/ft_avd_tooverflow_rectangle_3_pivot_animation" /> + <target + android:name="rectangle_path_2" + android:animation="@anim/ft_avd_tooverflow_rectangle_path_2_animation" /> + <target + android:name="rectangle_2" + android:animation="@anim/ft_avd_tooverflow_rectangle_2_animation" /> + <target + android:name="rectangle_2_pivot" + android:animation="@anim/ft_avd_tooverflow_rectangle_2_pivot_animation" /> + <target + android:name="rectangle_path_3" + android:animation="@anim/ft_avd_tooverflow_rectangle_path_3_animation" /> + <target + android:name="rectangle_1" + android:animation="@anim/ft_avd_tooverflow_rectangle_1_animation" /> + <target + android:name="rectangle_1_pivot" + android:animation="@anim/ft_avd_tooverflow_rectangle_1_pivot_animation" /> + <target + android:name="rectangle_path_1" + android:animation="@anim/ft_avd_tooverflow_rectangle_path_1_animation" /> +</animated-vector> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_0.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_0.xml new file mode 100644 index 000000000000..c6db901fdd22 --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_0.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 l 0.668316831683,0.0 c 0.00003,0.0 0.0663366336634,1.0 0.331683168317,1.0 L 1.0,1.0" /> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_1.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_1.xml new file mode 100644 index 000000000000..584385da8c20 --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_1.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" /> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_2.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_2.xml new file mode 100644 index 000000000000..334dee7a2b6c --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_2.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 c 0.161290322581,0.0 0.0806451612903,0.909090909091 0.403225806452,0.909090909091 c 0.238709677419,0.0 0.11935483871,0.0909090909091 0.596774193548,0.0909090909091 L 1.0,1.0" /> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_3.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_3.xml new file mode 100644 index 000000000000..67891fc15281 --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_3.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 l 0.5,0.0 c 0.2,0.0 0.1,1.0 0.5,1.0 L 1.0,1.0" /> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_4.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_4.xml new file mode 100644 index 000000000000..756a9e1f1c64 --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_4.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 l 0.5,0.0 c 0.00005,0.0 0.1,1.0 0.5,1.0 L 1.0,1.0" /> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_5.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_5.xml new file mode 100644 index 000000000000..584385da8c20 --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_5.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" /> diff --git a/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_6.xml b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_6.xml new file mode 100644 index 000000000000..bed54d419dbb --- /dev/null +++ b/core/res/res/interpolator/ft_avd_toarrow_animation_interpolator_6.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<pathInterpolator + xmlns:android="http://schemas.android.com/apk/res/android" + android:pathData="M 0.0,0.0 l 0.582706766917,0.0 c 0.166917293233,0.0 0.0834586466165,1.0 0.417293233083,1.0 L 1.0,1.0" /> diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml index 63dae4470cbe..dd161e38486e 100644 --- a/core/res/res/layout/floating_popup_container.xml +++ b/core/res/res/layout/floating_popup_container.xml @@ -15,12 +15,11 @@ ** limitations under the License. */ --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="@dimen/floating_toolbar_height" + android:layout_height="wrap_content" android:padding="0dp" - android:layout_margin="0dp" + android:layout_margin="20dp" android:elevation="2dp" android:focusable="true" android:focusableInTouchMode="true" diff --git a/core/res/res/layout/floating_popup_overflow_button.xml b/core/res/res/layout/floating_popup_overflow_button.xml new file mode 100644 index 000000000000..7053f3ec07fe --- /dev/null +++ b/core/res/res/layout/floating_popup_overflow_button.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<ImageButton xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/overflow" + android:layout_width="@dimen/floating_toolbar_menu_image_button_width" + android:layout_height="@dimen/floating_toolbar_height" + android:paddingStart="@dimen/floating_toolbar_menu_button_side_padding" + android:paddingTop="@dimen/floating_toolbar_menu_image_button_vertical_padding" + android:paddingEnd="@dimen/floating_toolbar_menu_button_side_padding" + android:paddingBottom="@dimen/floating_toolbar_menu_image_button_vertical_padding" + android:scaleType="centerInside" + android:background="?attr/selectableItemBackgroundBorderless" + android:tint="?attr/floatingToolbarForegroundColor" /> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9dbdaaa2941a..3860f68f1292 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2268,6 +2268,7 @@ <java-symbol type="layout" name="floating_popup_menu_image_button" /> <java-symbol type="layout" name="floating_popup_overflow_list_item" /> <java-symbol type="layout" name="floating_popup_overflow_image_list_item" /> + <java-symbol type="layout" name="floating_popup_overflow_button" /> <java-symbol type="dimen" name="floating_toolbar_height" /> <java-symbol type="dimen" name="floating_toolbar_menu_button_side_padding" /> <java-symbol type="dimen" name="floating_toolbar_overflow_side_padding" /> @@ -2279,6 +2280,10 @@ <java-symbol type="dimen" name="floating_toolbar_horizontal_margin" /> <java-symbol type="dimen" name="floating_toolbar_vertical_margin" /> <java-symbol type="dimen" name="content_rect_bottom_clip_allowance" /> + <java-symbol type="drawable" name="ft_avd_tooverflow" /> + <java-symbol type="drawable" name="ft_avd_toarrow" /> + <java-symbol type="drawable" name="ft_avd_toarrow_animation" /> + <java-symbol type="drawable" name="ft_avd_tooverflow_animation" /> <java-symbol type="string" name="date_picker_prev_month_button" /> <java-symbol type="string" name="date_picker_next_month_button" /> diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java index c5e2ae67affc..ddbdc87a4972 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java @@ -16,7 +16,11 @@ package android.widget; +import static android.widget.espresso.TextViewActions.mouseDoubleClickOnTextAtIndex; +import static android.widget.espresso.TextViewActions.mouseLongClickOnTextAtIndex; +import static android.widget.espresso.TextViewActions.mouseDoubleClickAndDragOnText; import static android.widget.espresso.TextViewActions.mouseDragOnText; +import static android.widget.espresso.TextViewActions.mouseLongClickAndDragOnText; import static android.widget.espresso.TextViewAssertions.hasSelection; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; @@ -62,4 +66,104 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< onView(withId(R.id.textview)).check(hasSelection("llo wor")); } + + @SmallTest + public void testSelectTextByLongClick() throws Exception { + getActivity(); + + final String helloWorld = "Hello world!"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); + + onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex(0)); + onView(withId(R.id.textview)).check(hasSelection("Hello")); + + onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex( + helloWorld.indexOf("world"))); + onView(withId(R.id.textview)).check(hasSelection("world")); + + onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex( + helloWorld.indexOf("llo"))); + onView(withId(R.id.textview)).check(hasSelection("Hello")); + + onView(withId(R.id.textview)).perform(mouseLongClickOnTextAtIndex( + helloWorld.indexOf("rld"))); + onView(withId(R.id.textview)).check(hasSelection("world")); + } + + @SmallTest + public void testSelectTextByDoubleClick() throws Exception { + getActivity(); + + final String helloWorld = "Hello world!"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); + + onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex(0)); + onView(withId(R.id.textview)).check(hasSelection("Hello")); + + onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex( + helloWorld.indexOf("world"))); + onView(withId(R.id.textview)).check(hasSelection("world")); + + onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex( + helloWorld.indexOf("llo"))); + onView(withId(R.id.textview)).check(hasSelection("Hello")); + + onView(withId(R.id.textview)).perform(mouseDoubleClickOnTextAtIndex( + helloWorld.indexOf("rld"))); + onView(withId(R.id.textview)).check(hasSelection("world")); + } + + @SmallTest + public void testSelectTextByDoubleClickAndDrag() throws Exception { + getActivity(); + + final String text = "abcd efg hijk lmn"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); + + onView(withId(R.id.textview)).perform( + mouseDoubleClickAndDragOnText(text.indexOf("f"), text.indexOf("j"))); + onView(withId(R.id.textview)).check(hasSelection("efg hijk")); + } + + @SmallTest + public void testSelectTextByDoubleClickAndDrag_reverse() throws Exception { + getActivity(); + + final String text = "abcd efg hijk lmn"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); + + onView(withId(R.id.textview)).perform( + mouseDoubleClickAndDragOnText(text.indexOf("j"), text.indexOf("f"))); + onView(withId(R.id.textview)).check(hasSelection("efg hijk")); + } + + @SmallTest + public void testSelectTextByLongPressAndDrag() throws Exception { + getActivity(); + + final String text = "abcd efg hijk lmn"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); + + onView(withId(R.id.textview)).perform( + mouseLongClickAndDragOnText(text.indexOf("f"), text.indexOf("j"))); + onView(withId(R.id.textview)).check(hasSelection("efg hijk")); + } + + @SmallTest + public void testSelectTextByLongPressAndDrag_reverse() throws Exception { + getActivity(); + + final String text = "abcd efg hijk lmn"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); + + onView(withId(R.id.textview)).perform( + mouseLongClickAndDragOnText(text.indexOf("j"), text.indexOf("f"))); + onView(withId(R.id.textview)).check(hasSelection("efg hijk")); + } } diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java index 1132ce0c52ac..ce975688a894 100644 --- a/core/tests/coretests/src/android/widget/espresso/DragAction.java +++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java @@ -91,6 +91,72 @@ public final class DragAction implements ViewAction { }, /** + * Starts a drag with a mouse double click. + */ + MOUSE_DOUBLE_CLICK { + private DownMotionPerformer downMotion = new DownMotionPerformer() { + @Override + @Nullable + public MotionEvent perform( + UiController uiController, float[] coordinates, float[] precision) { + return performDoubleTap(uiController, coordinates, precision); + } + }; + + @Override + public Status sendSwipe( + UiController uiController, + float[] startCoordinates, float[] endCoordinates, float[] precision) { + return sendLinearDrag( + uiController, downMotion, startCoordinates, endCoordinates, precision); + } + + @Override + public String toString() { + return "mouse double click and drag to select"; + } + + @Override + public UiController wrapUiController(UiController uiController) { + return new MouseUiController(uiController); + } + }, + + /** + * Starts a drag with a mouse long click. + */ + MOUSE_LONG_CLICK { + private DownMotionPerformer downMotion = new DownMotionPerformer() { + @Override + public MotionEvent perform( + UiController uiController, float[] coordinates, float[] precision) { + MotionEvent downEvent = MotionEvents.sendDown( + uiController, coordinates, precision) + .down; + return performLongPress(uiController, coordinates, precision); + } + }; + + @Override + public Status sendSwipe( + UiController uiController, + float[] startCoordinates, float[] endCoordinates, float[] precision) { + return sendLinearDrag( + uiController, downMotion, startCoordinates, endCoordinates, precision); + } + + @Override + public String toString() { + return "mouse long click and drag to select"; + } + + @Override + public UiController wrapUiController(UiController uiController) { + return new MouseUiController(uiController); + } + }, + + /** * Starts a drag with a tap. */ TAP { @@ -127,15 +193,7 @@ public final class DragAction implements ViewAction { @Override public MotionEvent perform( UiController uiController, float[] coordinates, float[] precision) { - MotionEvent downEvent = MotionEvents.sendDown( - uiController, coordinates, precision) - .down; - // Duration before a press turns into a long press. - // Factor 1.5 is needed, otherwise a long press is not safely detected. - // See android.test.TouchUtils longClickView - long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); - uiController.loopMainThreadForAtLeast(longPressTimeout); - return downEvent; + return performLongPress(uiController, coordinates, precision); } }; @@ -162,25 +220,7 @@ public final class DragAction implements ViewAction { @Nullable public MotionEvent perform( UiController uiController, float[] coordinates, float[] precision) { - MotionEvent downEvent = MotionEvents.sendDown( - uiController, coordinates, precision) - .down; - try { - if (!MotionEvents.sendUp(uiController, downEvent)) { - String logMessage = "Injection of up event as part of the double tap " + - "failed. Sending cancel event."; - Log.d(TAG, logMessage); - MotionEvents.sendCancel(uiController, downEvent); - return null; - } - - long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime(); - uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout); - - return MotionEvents.sendDown(uiController, coordinates, precision).down; - } finally { - downEvent.recycle(); - } + return performDoubleTap(uiController, coordinates, precision); } }; @@ -267,6 +307,43 @@ public final class DragAction implements ViewAction { return res; } + private static MotionEvent performLongPress( + UiController uiController, float[] coordinates, float[] precision) { + MotionEvent downEvent = MotionEvents.sendDown( + uiController, coordinates, precision) + .down; + // Duration before a press turns into a long press. + // Factor 1.5 is needed, otherwise a long press is not safely detected. + // See android.test.TouchUtils longClickView + long longPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f); + uiController.loopMainThreadForAtLeast(longPressTimeout); + return downEvent; + } + + @Nullable + private static MotionEvent performDoubleTap( + UiController uiController, float[] coordinates, float[] precision) { + MotionEvent downEvent = MotionEvents.sendDown( + uiController, coordinates, precision) + .down; + try { + if (!MotionEvents.sendUp(uiController, downEvent)) { + String logMessage = "Injection of up event as part of the double tap " + + "failed. Sending cancel event."; + Log.d(TAG, logMessage); + MotionEvents.sendCancel(uiController, downEvent); + return null; + } + + long doubleTapMinimumTimeout = ViewConfiguration.getDoubleTapMinTime(); + uiController.loopMainThreadForAtLeast(doubleTapMinimumTimeout); + + return MotionEvents.sendDown(uiController, coordinates, precision).down; + } finally { + downEvent.recycle(); + } + } + @Override public UiController wrapUiController(UiController uiController) { return uiController; diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java new file mode 100644 index 000000000000..de640ca1053d --- /dev/null +++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.widget.espresso; + +import org.hamcrest.Matcher; + +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.support.test.espresso.action.CoordinatesProvider; +import android.support.test.espresso.action.GeneralClickAction; +import android.support.test.espresso.action.PrecisionDescriber; +import android.support.test.espresso.action.Tapper; +import android.view.View; + +/** + * ViewAction for performing an click on View by a mouse. + */ +public final class MouseClickAction implements ViewAction { + private final GeneralClickAction mGeneralClickAction; + + public MouseClickAction(Tapper tapper, CoordinatesProvider coordinatesProvider, + PrecisionDescriber precisionDescriber) { + mGeneralClickAction = new GeneralClickAction(tapper, coordinatesProvider, + precisionDescriber); + } + + @Override + public Matcher<View> getConstraints() { + return mGeneralClickAction.getConstraints(); + } + + @Override + public String getDescription() { + return mGeneralClickAction.getDescription(); + } + + @Override + public void perform(UiController uiController, View view) { + mGeneralClickAction.perform(new MouseUiController(uiController), view); + } +} diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java index 32abc861587d..63e310e8af5c 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java @@ -53,6 +53,21 @@ public final class TextViewActions { } /** + * Returns an action that clicks by mouse on text at an index on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param index The index of the TextView's text to click on. + */ + public static ViewAction mouseClickOnTextAtIndex(int index) { + return actionWithAssertions( + new MouseClickAction(Tap.SINGLE, new TextCoordinates(index), Press.PINPOINT)); + } + + /** * Returns an action that double-clicks on text at an index on the TextView.<br> * <br> * View constraints: @@ -68,6 +83,21 @@ public final class TextViewActions { } /** + * Returns an action that double-clicks by mouse on text at an index on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param index The index of the TextView's text to double-click on. + */ + public static ViewAction mouseDoubleClickOnTextAtIndex(int index) { + return actionWithAssertions( + new MouseClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.PINPOINT)); + } + + /** * Returns an action that long presses on text at an index on the TextView.<br> * <br> * View constraints: @@ -83,6 +113,21 @@ public final class TextViewActions { } /** + * Returns an action that long click by mouse on text at an index on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param index The index of the TextView's text to long click on. + */ + public static ViewAction mouseLongClickOnTextAtIndex(int index) { + return actionWithAssertions( + new MouseClickAction(Tap.LONG, new TextCoordinates(index), Press.PINPOINT)); + } + + /** * Returns an action that long presses then drags on text from startIndex to endIndex on the * TextView.<br> * <br> @@ -148,6 +193,50 @@ public final class TextViewActions { TextView.class)); } + /** + * Returns an action that double click then drags by mouse on text from startIndex to endIndex + * on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param startIndex The index of the TextView's text to start a drag from + * @param endIndex The index of the TextView's text to end the drag at + */ + public static ViewAction mouseDoubleClickAndDragOnText(int startIndex, int endIndex) { + return actionWithAssertions( + new DragAction( + DragAction.Drag.MOUSE_DOUBLE_CLICK, + new TextCoordinates(startIndex), + new TextCoordinates(endIndex), + Press.PINPOINT, + TextView.class)); + } + + /** + * Returns an action that long click then drags by mouse on text from startIndex to endIndex + * on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param startIndex The index of the TextView's text to start a drag from + * @param endIndex The index of the TextView's text to end the drag at + */ + public static ViewAction mouseLongClickAndDragOnText(int startIndex, int endIndex) { + return actionWithAssertions( + new DragAction( + DragAction.Drag.MOUSE_LONG_CLICK, + new TextCoordinates(startIndex), + new TextCoordinates(endIndex), + Press.PINPOINT, + TextView.class)); + } + public enum Handle { SELECTION_START, SELECTION_END, diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 95ae72e0c6f8..5acc1a3e09dd 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -484,7 +484,7 @@ public class Canvas { * * @param bounds The maximum size the offscreen bitmap needs to be * (in local coordinates) - * @param alpha The alpha to apply to the offscreen when when it is + * @param alpha The alpha to apply to the offscreen when it is drawn during restore() * @param saveFlags see _SAVE_FLAG constants, generally {@link #ALL_SAVE_FLAG} is recommended * for performance reasons. diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 1c2c9406621d..f5b7a2e44d71 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -36,6 +36,7 @@ import android.os.Process; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemProperties; import android.provider.Settings; import android.system.ErrnoException; import android.system.OsConstants; @@ -1260,6 +1261,13 @@ public class MediaPlayer implements SubtitleController.Listener */ public void setWakeMode(Context context, int mode) { boolean washeld = false; + + /* Disable persistant wakelocks in media player based on property */ + if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) { + Log.w(TAG, "IGNORING setWakeMode " + mode); + return; + } + if (mWakeLock != null) { if (mWakeLock.isHeld()) { washeld = true; diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index 41b8ab2d3c40..be9fb474db00 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -240,7 +240,7 @@ public final class MediaBrowser { /** * Gets the root id. * <p> - * Note that the root id may become invalid or change when when the + * Note that the root id may become invalid or change when the * browser is disconnected. * </p> * @@ -270,7 +270,7 @@ public final class MediaBrowser { /** * Gets the media session token associated with the media browser. * <p> - * Note that the session token may become invalid or change when when the + * Note that the session token may become invalid or change when the * browser is disconnected. * </p> * diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java index 55e2f441a882..b99c8063843c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java @@ -39,13 +39,15 @@ import android.os.RemoteException; import android.os.SystemClock; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; +import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; import android.support.design.widget.Snackbar; import android.text.format.DateUtils; import android.util.Log; -import android.widget.Toast; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; +import com.android.documentsui.model.RootInfo; import libcore.io.IoUtils; @@ -72,6 +74,12 @@ public class CopyService extends IntentService { // TODO: Move it to a shared file when more operations are implemented. public static final int FAILURE_COPY = 1; + // Parameters of the copy job. Requests to an IntentService are serialized so this code only + // needs to deal with one job at a time. + // NOTE: This must be declared by concrete type as the concrete type + // is required by putParcelableArrayListExtra. + private final ArrayList<DocumentInfo> mFailedFiles = new ArrayList<>(); + private PowerManager mPowerManager; private NotificationManager mNotificationManager; @@ -80,9 +88,6 @@ public class CopyService extends IntentService { // Jobs are serialized but a job ID is used, to avoid mixing up cancellation requests. private String mJobId; private volatile boolean mIsCancelled; - // Parameters of the copy job. Requests to an IntentService are serialized so this code only - // needs to deal with one job at a time. - private final ArrayList<DocumentInfo> mFailedFiles; private long mBatchSize; private long mBytesCopied; private long mStartTime; @@ -97,10 +102,11 @@ public class CopyService extends IntentService { private ContentProviderClient mSrcClient; private ContentProviderClient mDstClient; + // For testing only. + @Nullable private TestOnlyListener mJobFinishedListener; + public CopyService() { super("CopyService"); - - mFailedFiles = new ArrayList<DocumentInfo>(); } /** @@ -115,7 +121,11 @@ public class CopyService extends IntentService { final Resources res = activity.getResources(); final Intent copyIntent = new Intent(activity, CopyService.class); copyIntent.putParcelableArrayListExtra( - EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs)); + EXTRA_SRC_LIST, + // Don't create a copy unless absolutely necessary :) + srcDocs instanceof ArrayList + ? (ArrayList<DocumentInfo>) srcDocs + : new ArrayList<DocumentInfo>(srcDocs)); copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack); copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode); @@ -198,6 +208,11 @@ public class CopyService extends IntentService { .setAutoCancel(true); mNotificationManager.notify(mJobId, 0, errorBuilder.build()); } + + if (mJobFinishedListener != null) { + mJobFinishedListener.onFinished(mFailedFiles); + } + if (DEBUG) Log.d(TAG, "Done cleaning up"); } } @@ -269,6 +284,26 @@ public class CopyService extends IntentService { } /** + * Sets a callback to be run when the next run job is finished. + * This is test ONLY instrumentation. The alternative is for us to add + * broadcast intents SOLELY for the purpose of testing. + * @param listener + */ + @VisibleForTesting + void addFinishedListener(TestOnlyListener listener) { + this.mJobFinishedListener = listener; + + } + + /** + * Only used for testing. Is that obvious enough? + */ + @VisibleForTesting + interface TestOnlyListener { + void onFinished(List<DocumentInfo> failed); + } + + /** * Calculates the cumulative size of all the documents in the list. Directories are recursed * into and totaled up. * @@ -279,7 +314,7 @@ public class CopyService extends IntentService { private long calculateFileSizes(List<DocumentInfo> srcs) throws RemoteException { long result = 0; for (DocumentInfo src : srcs) { - if (Document.MIME_TYPE_DIR.equals(src.mimeType)) { + if (src.isDirectory()) { // Directories need to be recursed into. result += calculateFileSizesHelper(src.derivedUri); } else { @@ -412,8 +447,21 @@ public class CopyService extends IntentService { */ private void copy(DocumentInfo srcInfo, DocumentInfo dstDirInfo, int mode) throws RemoteException { - if (DEBUG) Log.d(TAG, "Copying " + srcInfo.displayName + " (" + srcInfo.derivedUri + ")" + - " to " + dstDirInfo.displayName + " (" + dstDirInfo.derivedUri + ")"); + + String opDesc = mode == TRANSFER_MODE_COPY ? "copy" : "move"; + + // Guard unsupported recursive operation. + if (dstDirInfo.equals(srcInfo) || isDescendentOf(srcInfo, dstDirInfo)) { + if (DEBUG) Log.d(TAG, + "Skipping recursive " + opDesc + " of directory " + dstDirInfo.derivedUri); + mFailedFiles.add(srcInfo); + return; + } + + if (DEBUG) Log.d(TAG, + "Performing " + opDesc + " of " + srcInfo.displayName + + " (" + srcInfo.derivedUri + ")" + " to " + dstDirInfo.displayName + + " (" + dstDirInfo.derivedUri + ")"); // When copying within the same provider, try to use optimized copying and moving. // If not supported, then fallback to byte-by-byte copy/move. @@ -450,7 +498,7 @@ public class CopyService extends IntentService { return; } - if (Document.MIME_TYPE_DIR.equals(srcInfo.mimeType)) { + if (srcInfo.isDirectory()) { copyDirectoryHelper(srcInfo.derivedUri, dstUri, mode); } else { copyFileHelper(srcInfo.derivedUri, dstUri, mode); @@ -458,6 +506,17 @@ public class CopyService extends IntentService { } /** + * Returns true if {@code doc} is a descendant of {@code parentDoc}. + */ + boolean isDescendentOf(DocumentInfo doc, DocumentInfo parentDoc) throws RemoteException { + if (parentDoc.isDirectory() && doc.authority.equals(parentDoc.authority)) { + return DocumentsContract.isChildDocument( + mDstClient, doc.derivedUri, parentDoc.derivedUri); + } + return false; + } + + /** * Handles recursion into a directory and copying its contents. Note that in linux terms, this * does the equivalent of "cp src/* dst", not "cp -r src dst". * diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index c6425a6e41f2..f3c3f2fcabf0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -32,6 +32,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; +import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.util.Log; import android.view.KeyEvent; @@ -83,8 +84,10 @@ public class CreateDirectoryFragment extends DialogFragment { editText.setOnEditorActionListener( new OnEditorActionListener() { @Override - public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_ENTER + public boolean onEditorAction( + TextView view, int actionId, @Nullable KeyEvent event) { + if (event != null + && event.getKeyCode() == KeyEvent.KEYCODE_ENTER && event.hasNoModifiers()) { createDirectory(editText.getText().toString()); dialog.dismiss(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java index 120f6106a7a0..23074f072c99 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java @@ -37,7 +37,6 @@ public class FailureDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { private static final String TAG = "FailureDialogFragment"; - private int mFailure; private int mTransferMode; private ArrayList<DocumentInfo> mFailedSrcList; @@ -75,7 +74,6 @@ public class FailureDialogFragment extends DialogFragment public Dialog onCreateDialog(Bundle inState) { super.onCreate(inState); - mFailure = getArguments().getInt(CopyService.EXTRA_FAILURE); mTransferMode = getArguments().getInt(CopyService.EXTRA_TRANSFER_MODE); mFailedSrcList = getArguments().getParcelableArrayList(CopyService.EXTRA_SRC_LIST); diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java index cc981e1eaaad..dfdc705a16c8 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java @@ -25,6 +25,7 @@ import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsProvider; +import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import com.android.documentsui.DocumentsApplication; @@ -204,13 +205,18 @@ public class DocumentInfo implements Durable, Parcelable { } } - private void deriveFields() { + @VisibleForTesting + void deriveFields() { derivedUri = DocumentsContract.buildDocumentUri(authority, documentId); } @Override public String toString() { - return "Document{docId=" + documentId + ", name=" + displayName + "}"; + return "Document{" + + "docId=" + documentId + + ", name=" + displayName + + ", isDirectory=" + isDirectory() + + "}"; } public boolean isCreateSupported() { @@ -237,6 +243,22 @@ public class DocumentInfo implements Durable, Parcelable { return (flags & Document.FLAG_DIR_HIDE_GRID_TITLES) != 0; } + public int hashCode() { + return derivedUri.hashCode() + mimeType.hashCode(); + } + + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (!(other instanceof DocumentInfo)) { + return false; + } + + DocumentInfo that = (DocumentInfo) other; + // Uri + mime type should be totally unique. + return derivedUri.equals(that.derivedUri) && mimeType.equals(that.mimeType); + } + public static String getCursorString(Cursor cursor, String columnName) { final int index = cursor.getColumnIndex(columnName); return (index != -1) ? cursor.getString(index) : null; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java index 369ab7dc59ea..079d59914de6 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyServiceTest.java @@ -28,12 +28,10 @@ import android.net.Uri; import android.os.Parcelable; import android.os.RemoteException; import android.provider.DocumentsContract; -import android.provider.DocumentsContract.Document; import android.test.MoreAsserts; import android.test.ServiceTestCase; import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import com.android.documentsui.model.DocumentInfo; @@ -48,6 +46,7 @@ import libcore.io.Streams; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -55,9 +54,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @MediumTest -public class CopyTest extends ServiceTestCase<CopyService> { +public class CopyServiceTest extends ServiceTestCase<CopyService> { - public CopyTest() { + public CopyServiceTest() { super(CopyService.class); } @@ -72,11 +71,13 @@ public class CopyTest extends ServiceTestCase<CopyService> { private DocumentsProviderHelper mDocHelper; private StubProvider mStorage; private Context mSystemContext; + private CopyJobListener mListener; @Override protected void setUp() throws Exception { super.setUp(); + mListener = new CopyJobListener(); setupTestContext(); mClient = mResolver.acquireContentProviderClient(AUTHORITY); @@ -84,6 +85,8 @@ public class CopyTest extends ServiceTestCase<CopyService> { mStorage.clearCacheAndBuildRoots(); mDocHelper = new DocumentsProviderHelper(AUTHORITY, mClient); + + assertDestFileCount(0); } @Override @@ -97,15 +100,13 @@ public class CopyTest extends ServiceTestCase<CopyService> { Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", "The five boxing wizards jump quickly".getBytes()); - assertDstFileCountEquals(0); - startService(createCopyIntent(Lists.newArrayList(testFile))); // 2 operations: file creation, then writing data. mResolver.waitForChanges(2); // Verify that one file was copied; check file contents. - assertDstFileCountEquals(1); + assertDestFileCount(1); assertCopied(srcPath); } @@ -114,8 +115,6 @@ public class CopyTest extends ServiceTestCase<CopyService> { String testContent = "The five boxing wizards jump quickly"; Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", testContent.getBytes()); - assertDstFileCountEquals(0); - Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile)); moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE); startService(moveIntent); @@ -124,7 +123,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { mResolver.waitForChanges(3); // Verify that one file was moved; check file contents. - assertDstFileCountEquals(1); + assertDestFileCount(1); assertDoesNotExist(SRC_ROOT, srcPath); byte[] dstContent = readFile(DST_ROOT, srcPath); @@ -147,15 +146,13 @@ public class CopyTest extends ServiceTestCase<CopyService> { mStorage.createFile(SRC_ROOT, srcPaths[1], "text/plain", testContent[1].getBytes()), mStorage.createFile(SRC_ROOT, srcPaths[2], "text/plain", testContent[2].getBytes())); - assertDstFileCountEquals(0); - // Copy all the test files. startService(createCopyIntent(testFiles)); // 3 file creations, 3 file writes. mResolver.waitForChanges(6); - assertDstFileCountEquals(3); + assertDestFileCount(3); for (String path : srcPaths) { assertCopied(path); } @@ -163,29 +160,54 @@ public class CopyTest extends ServiceTestCase<CopyService> { public void testCopyEmptyDir() throws Exception { String srcPath = "/emptyDir"; - Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR, - null); - - assertDstFileCountEquals(0); + Uri testDir = createTestDirectory(srcPath); startService(createCopyIntent(Lists.newArrayList(testDir))); // Just 1 operation: Directory creation. mResolver.waitForChanges(1); - assertDstFileCountEquals(1); + assertDestFileCount(1); // Verify that the dst exists and is a directory. File dst = mStorage.getFile(DST_ROOT, srcPath); assertTrue(dst.isDirectory()); } + public void testNoCopyDirToSelf() throws Exception { + Uri testDir = createTestDirectory("/someDir"); + + Intent intent = createCopyIntent(Lists.newArrayList(testDir), testDir); + startService(intent); + + getService().addFinishedListener(mListener); + + mListener.waitForFinished(); + mListener.assertFailedCount(1); + mListener.assertFileFailed("someDir"); + + assertDestFileCount(0); + } + + public void testNoCopyDirToDescendent() throws Exception { + Uri testDir = createTestDirectory("/someDir"); + Uri descDir = createTestDirectory("/someDir/theDescendent"); + + Intent intent = createCopyIntent(Lists.newArrayList(testDir), descDir); + startService(intent); + + getService().addFinishedListener(mListener); + + mListener.waitForFinished(); + mListener.assertFailedCount(1); + mListener.assertFileFailed("someDir"); + + assertDestFileCount(0); + } + public void testMoveEmptyDir() throws Exception { String srcPath = "/emptyDir"; - Uri testDir = mStorage.createFile(SRC_ROOT, srcPath, DocumentsContract.Document.MIME_TYPE_DIR, - null); - - assertDstFileCountEquals(0); + Uri testDir = createTestDirectory(srcPath); Intent moveIntent = createCopyIntent(Lists.newArrayList(testDir)); moveIntent.putExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_MOVE); @@ -194,7 +216,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { // 2 operations: Directory creation, and removal of the original. mResolver.waitForChanges(2); - assertDstFileCountEquals(1); + assertDestFileCount(1); // Verify that the dst exists and is a directory. File dst = mStorage.getFile(DST_ROOT, srcPath); @@ -217,8 +239,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { srcDir + "/test2.txt" }; // Create test dir; put some files in it. - Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR, - null); + Uri testDir = createTestDirectory(srcDir); mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes()); mStorage.createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes()); mStorage.createFile(SRC_ROOT, srcFiles[2], "text/plain", testContent[2].getBytes()); @@ -252,8 +273,6 @@ public class CopyTest extends ServiceTestCase<CopyService> { Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", "The five boxing wizards jump quickly".getBytes()); - assertDstFileCountEquals(0); - mStorage.simulateReadErrorsForFile(testFile); startService(createCopyIntent(Lists.newArrayList(testFile))); @@ -262,7 +281,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { mResolver.waitForChanges(3); // Verify that the failed copy was cleaned up. - assertDstFileCountEquals(0); + assertDestFileCount(0); } public void testMoveFileWithReadErrors() throws Exception { @@ -270,8 +289,6 @@ public class CopyTest extends ServiceTestCase<CopyService> { Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", "The five boxing wizards jump quickly".getBytes()); - assertDstFileCountEquals(0); - mStorage.simulateReadErrorsForFile(testFile); Intent moveIntent = createCopyIntent(Lists.newArrayList(testFile)); @@ -288,7 +305,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { return; } finally { // Verify that the failed copy was cleaned up, and the src file wasn't removed. - assertDstFileCountEquals(0); + assertDestFileCount(0); assertExists(SRC_ROOT, srcPath); } // The asserts above didn't fail, but the CopyService did something unexpected. @@ -308,8 +325,7 @@ public class CopyTest extends ServiceTestCase<CopyService> { srcDir + "/test2.txt" }; // Create test dir; put some files in it. - Uri testDir = mStorage.createFile(SRC_ROOT, srcDir, DocumentsContract.Document.MIME_TYPE_DIR, - null); + Uri testDir = createTestDirectory(srcDir); mStorage.createFile(SRC_ROOT, srcFiles[0], "text/plain", testContent[0].getBytes()); Uri errFile = mStorage .createFile(SRC_ROOT, srcFiles[1], "text/plain", testContent[1].getBytes()); @@ -346,33 +362,37 @@ public class CopyTest extends ServiceTestCase<CopyService> { assertExists(SRC_ROOT, srcFiles[1]); } - /** - * Copies the given files to a pre-determined destination. - * - * @throws FileNotFoundException - */ + private Uri createTestDirectory(String dir) throws IOException { + return mStorage.createFile( + SRC_ROOT, dir, DocumentsContract.Document.MIME_TYPE_DIR, null); + } + private Intent createCopyIntent(List<Uri> srcs) throws Exception { + RootInfo root = mDocHelper.getRoot(DST_ROOT); + final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, root.documentId); + + return createCopyIntent(srcs, dst); + } + + private Intent createCopyIntent(List<Uri> srcs, Uri dst) throws Exception { final ArrayList<DocumentInfo> srcDocs = Lists.newArrayList(); for (Uri src : srcs) { srcDocs.add(DocumentInfo.fromUri(mResolver, src)); } - RootInfo root = mDocHelper.getRoot(DST_ROOT); - final Uri dst = DocumentsContract.buildDocumentUri(AUTHORITY, root.documentId); DocumentStack stack = new DocumentStack(); stack.push(DocumentInfo.fromUri(mResolver, dst)); final Intent copyIntent = new Intent(mContext, CopyService.class); copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs); copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack); - // startService(copyIntent); return copyIntent; } /** * Returns a count of the files in the given directory. */ - private void assertDstFileCountEquals(int expected) throws RemoteException { + private void assertDestFileCount(int expected) throws RemoteException { RootInfo dest = mDocHelper.getRoot(DST_ROOT); final Uri queryUri = DocumentsContract.buildChildDocumentsUri(AUTHORITY, dest.documentId); @@ -449,6 +469,34 @@ public class CopyTest extends ServiceTestCase<CopyService> { mResolver.addProvider(AUTHORITY, mStorage); } + private final class CopyJobListener implements CopyService.TestOnlyListener { + + final CountDownLatch latch = new CountDownLatch(1); + final List<DocumentInfo> failedDocs = new ArrayList<>(); + @Override + public void onFinished(List<DocumentInfo> failed) { + failedDocs.addAll(failed); + latch.countDown(); + } + + public void assertFileFailed(String expectedName) { + for (DocumentInfo failed : failedDocs) { + if (expectedName.equals(failed.displayName)) { + return; + } + } + fail("Couldn't find failed file: " + expectedName); + } + + public void waitForFinished() throws InterruptedException { + latch.await(500, TimeUnit.MILLISECONDS); + } + + public void assertFailedCount(int expected) { + assertEquals(expected, failedDocs.size()); + } + } + /** * A test resolver that enables this test suite to listen for notifications that mark when copy * operations are done. diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java index 2d42ddc4e6b5..d23cdeb18269 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java @@ -531,6 +531,16 @@ public class StubProvider extends DocumentsProvider { this.rootInfo = rootInfo; mStorage.put(this.documentId, this); } + @Override + public String toString() { + return "StubDocument{" + + "path:" + file.getPath() + + ", mimeType:" + mimeType + + ", rootInfo:" + rootInfo + + ", documentId:" + documentId + + ", parentId:" + parentId + + "}"; + } } private static String getDocumentIdForFile(File file) { diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java new file mode 100644 index 000000000000..a6aba7b06816 --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/model/DocumentInfoTest.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui.model; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +@SmallTest +public class DocumentInfoTest extends AndroidTestCase { + + public void testEquals() throws Exception { + DocumentInfo doc = createDocInfo("authority.a", "doc.1", "text/plain"); + assertEquals(doc, doc); + } + + public void testNotEquals_differentAuthority() throws Exception { + DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain"); + DocumentInfo docB = createDocInfo("authority.b", "doc.1", "text/plain"); + assertFalse(docA.equals(docB)); + } + + public void testNotEquals_differentDocId() throws Exception { + DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain"); + DocumentInfo docB = createDocInfo("authority.a", "doc.2", "text/plain"); + assertFalse(docA.equals(docB)); + } + + public void testNotEquals_differentMimetype() throws Exception { + DocumentInfo docA = createDocInfo("authority.a", "doc.1", "text/plain"); + DocumentInfo docB = createDocInfo("authority.a", "doc.1", "image/png"); + assertFalse(docA.equals(docB)); + } + + private DocumentInfo createDocInfo(String authority, String docId, String mimeType) { + DocumentInfo doc = new DocumentInfo(); + doc.authority = authority; + doc.documentId = docId; + doc.mimeType = mimeType; + doc.deriveFields(); + return doc; + } +} diff --git a/packages/Keyguard/res/values/config.xml b/packages/Keyguard/res/values/config.xml index b398ab2320b0..bde6ed531353 100644 --- a/packages/Keyguard/res/values/config.xml +++ b/packages/Keyguard/res/values/config.xml @@ -23,9 +23,9 @@ <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] --> <bool name="config_disableMenuKeyInLockScreen">false</bool> - <!-- Threshold in micro amperes below which a charger is rated as "slow" --> - <integer name="config_chargingSlowlyThreshold">1000000</integer> + <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V --> + <integer name="config_chargingSlowlyThreshold">5000000</integer> - <!-- Threshold in micro amperes above which a charger is rated as "fast" --> - <integer name="config_chargingFastThreshold">1500000</integer> + <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V --> + <integer name="config_chargingFastThreshold">7500000</integer> </resources> diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 3d78028a72f8..8102c34755a8 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -75,6 +75,7 @@ import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import static android.os.BatteryManager.EXTRA_HEALTH; import static android.os.BatteryManager.EXTRA_LEVEL; import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; +import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.EXTRA_STATUS; @@ -155,6 +156,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { */ private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3; + private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; + private static KeyguardUpdateMonitor sInstance; private final Context mContext; @@ -617,10 +620,25 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0); final int level = intent.getIntExtra(EXTRA_LEVEL, 0); final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); - final int maxChargingCurrent = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); + + final int maxChargingMicroAmp = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); + int maxChargingMicroVolt = intent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); + final int maxChargingMicroWatt; + + if (maxChargingMicroVolt <= 0) { + maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; + } + if (maxChargingMicroAmp > 0) { + // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor + // to maintain precision equally on both factors. + maxChargingMicroWatt = (maxChargingMicroAmp / 1000) + * (maxChargingMicroVolt / 1000); + } else { + maxChargingMicroWatt = -1; + } final Message msg = mHandler.obtainMessage( MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health, - maxChargingCurrent)); + maxChargingMicroWatt)); mHandler.sendMessage(msg); } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { SimData args = SimData.fromIntent(intent); @@ -806,13 +824,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { public final int level; public final int plugged; public final int health; - public final int maxChargingCurrent; - public BatteryStatus(int status, int level, int plugged, int health, int maxChargingCurrent) { + public final int maxChargingWattage; + public BatteryStatus(int status, int level, int plugged, int health, + int maxChargingWattage) { this.status = status; this.level = level; this.plugged = plugged; this.health = health; - this.maxChargingCurrent = maxChargingCurrent; + this.maxChargingWattage = maxChargingWattage; } /** @@ -844,9 +863,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } public final int getChargingSpeed(int slowThreshold, int fastThreshold) { - return maxChargingCurrent <= 0 ? CHARGING_UNKNOWN : - maxChargingCurrent < slowThreshold ? CHARGING_SLOWLY : - maxChargingCurrent > fastThreshold ? CHARGING_FAST : + return maxChargingWattage <= 0 ? CHARGING_UNKNOWN : + maxChargingWattage < slowThreshold ? CHARGING_SLOWLY : + maxChargingWattage > fastThreshold ? CHARGING_FAST : CHARGING_REGULAR; } } @@ -1422,7 +1441,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } // change in charging current while plugged in - if (nowPluggedIn && current.maxChargingCurrent != old.maxChargingCurrent) { + if (nowPluggedIn && current.maxChargingWattage != old.maxChargingWattage) { return true; } diff --git a/packages/MtpDocumentsProvider/res/drawable-hdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-hdpi/ic_root_mtp.png Binary files differnew file mode 100644 index 000000000000..7691433a58eb --- /dev/null +++ b/packages/MtpDocumentsProvider/res/drawable-hdpi/ic_root_mtp.png diff --git a/packages/MtpDocumentsProvider/res/drawable-mdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-mdpi/ic_root_mtp.png Binary files differnew file mode 100644 index 000000000000..1cf7b3ac0480 --- /dev/null +++ b/packages/MtpDocumentsProvider/res/drawable-mdpi/ic_root_mtp.png diff --git a/packages/MtpDocumentsProvider/res/drawable-xhdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-xhdpi/ic_root_mtp.png Binary files differnew file mode 100644 index 000000000000..27e35421a957 --- /dev/null +++ b/packages/MtpDocumentsProvider/res/drawable-xhdpi/ic_root_mtp.png diff --git a/packages/MtpDocumentsProvider/res/drawable-xxhdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-xxhdpi/ic_root_mtp.png Binary files differnew file mode 100644 index 000000000000..3df2578b263f --- /dev/null +++ b/packages/MtpDocumentsProvider/res/drawable-xxhdpi/ic_root_mtp.png diff --git a/packages/MtpDocumentsProvider/res/drawable-xxxhdpi/ic_root_mtp.png b/packages/MtpDocumentsProvider/res/drawable-xxxhdpi/ic_root_mtp.png Binary files differnew file mode 100644 index 000000000000..fd2b79566983 --- /dev/null +++ b/packages/MtpDocumentsProvider/res/drawable-xxxhdpi/ic_root_mtp.png diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java index 00fe7a7e3deb..51c0281c51b5 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java @@ -401,7 +401,7 @@ class MtpDatabase { values.put(Document.COLUMN_DISPLAY_NAME, root.getRootName(resources)); values.putNull(Document.COLUMN_SUMMARY); values.putNull(Document.COLUMN_LAST_MODIFIED); - values.putNull(Document.COLUMN_ICON); + values.put(Document.COLUMN_ICON, R.drawable.ic_root_mtp); values.put(Document.COLUMN_FLAGS, 0); values.put(Document.COLUMN_SIZE, (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE)); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java index 6d9193d7ed7b..67b06729d389 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java @@ -80,7 +80,7 @@ public class MtpDatabaseTest extends AndroidTestCase { assertEquals("displayName", "Device Storage", cursor.getString(5)); assertTrue("summary", cursor.isNull(6)); assertTrue("lastModified", cursor.isNull(7)); - assertTrue("icon", cursor.isNull(8)); + assertEquals("icon", R.drawable.ic_root_mtp, cursor.getInt(8)); assertEquals("flag", 0, cursor.getInt(9)); assertEquals("size", 1000, cursor.getInt(10)); @@ -111,7 +111,7 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.moveToNext(); assertEquals(1, cursor.getInt(0)); assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); - assertTrue(cursor.isNull(2)); + assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device Storage", cursor.getString(3)); assertTrue(cursor.isNull(4)); assertEquals(1, cursor.getInt(5)); @@ -121,7 +121,7 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.moveToNext(); assertEquals(2, cursor.getInt(0)); assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); - assertTrue(cursor.isNull(2)); + assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device Storage", cursor.getString(3)); assertTrue(cursor.isNull(4)); assertEquals(2, cursor.getInt(5)); @@ -131,7 +131,7 @@ public class MtpDatabaseTest extends AndroidTestCase { cursor.moveToNext(); assertEquals(3, cursor.getInt(0)); assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); - assertTrue(cursor.isNull(2)); + assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device /@#%&<>Storage", cursor.getString(3)); assertTrue(cursor.isNull(4)); assertEquals(3, cursor.getInt(5)); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index b20b3bb134d5..dc6f79e37e62 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -140,8 +140,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("1", cursor.getString(0)); assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); - // TODO: Add storage icon for MTP devices. - assertTrue(cursor.isNull(2) /* icon */); + assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device A Storage A", cursor.getString(3)); assertEquals("1", cursor.getString(4)); assertEquals(1024, cursor.getInt(5)); @@ -156,8 +155,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("2", cursor.getString(0)); assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); - // TODO: Add storage icon for MTP devices. - assertTrue(cursor.isNull(2) /* icon */); + assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("2", cursor.getString(4)); assertEquals(2048, cursor.getInt(5)); @@ -189,8 +187,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { cursor.moveToNext(); assertEquals("1", cursor.getString(0)); assertEquals(Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE, cursor.getInt(1)); - // TODO: Add storage icon for MTP devices. - assertTrue(cursor.isNull(2) /* icon */); + assertEquals(R.drawable.ic_root_mtp, cursor.getInt(2)); assertEquals("Device B Storage B", cursor.getString(3)); assertEquals("1", cursor.getString(4)); assertEquals(2048, cursor.getInt(5)); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java index ed6ee7eadd08..a045d06facf0 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java @@ -16,26 +16,18 @@ package com.android.mtp; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.os.CancellationSignal; import android.os.OperationCanceledException; import android.test.InstrumentationTestCase; import java.io.IOException; -import java.util.HashMap; -import java.util.concurrent.CountDownLatch; @RealDeviceTest public class MtpManagerTest extends InstrumentationTestCase { - private static final String ACTION_USB_PERMISSION = - "com.android.mtp.USB_PERMISSION"; + private static final int TIMEOUT_MS = 1000; UsbManager mUsbManager; MtpManager mManager; @@ -45,20 +37,8 @@ public class MtpManagerTest extends InstrumentationTestCase { @Override public void setUp() throws Exception { mUsbManager = getContext().getSystemService(UsbManager.class); - for (int i = 0; i < 2; i++) { - mUsbDevice = findDevice(); - mManager = new MtpManager(getContext()); - mManager.openDevice(mUsbDevice.getDeviceId()); - try { - waitForStorages(mManager, mUsbDevice.getDeviceId()); - return; - } catch (IOException exp) { - // When the MTP device is Android, and it changes the USB device type from - // "Charging" to "MTP", the device ID will be updated. We need to find a device - // again. - continue; - } - } + mManager = new MtpManager(getContext()); + mUsbDevice = TestUtil.setupMtpDevice(getInstrumentation(), mUsbManager, mManager); } @Override @@ -66,6 +46,11 @@ public class MtpManagerTest extends InstrumentationTestCase { mManager.closeDevice(mUsbDevice.getDeviceId()); } + @Override + public TestResultInstrumentation getInstrumentation() { + return (TestResultInstrumentation) super.getInstrumentation(); + } + public void testCancelEvent() throws Exception { final CancellationSignal signal = new CancellationSignal(); final Thread thread = new Thread() { @@ -74,7 +59,7 @@ public class MtpManagerTest extends InstrumentationTestCase { try { mManager.readEvent(mUsbDevice.getDeviceId(), signal); } catch (OperationCanceledException | IOException e) { - show(e.getMessage()); + getInstrumentation().show(e.getMessage()); } } }; @@ -84,72 +69,6 @@ public class MtpManagerTest extends InstrumentationTestCase { thread.join(TIMEOUT_MS); } - private void requestPermission(UsbDevice device) throws InterruptedException { - if (mUsbManager.hasPermission(device)) { - return; - } - final CountDownLatch latch = new CountDownLatch(1); - final BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - latch.countDown(); - getInstrumentation().getTargetContext().unregisterReceiver(this); - } - }; - getInstrumentation().getTargetContext().registerReceiver( - receiver, new IntentFilter(ACTION_USB_PERMISSION)); - mUsbManager.requestPermission(device, PendingIntent.getBroadcast( - getInstrumentation().getTargetContext(), - 0 /* requstCode */, - new Intent(ACTION_USB_PERMISSION), - 0 /* flags */)); - latch.await(); - assertTrue(mUsbManager.hasPermission(device)); - } - - private UsbDevice findDevice() throws InterruptedException { - while (true) { - final HashMap<String,UsbDevice> devices = mUsbManager.getDeviceList(); - if (devices.size() == 0) { - show("Wait for devices."); - Thread.sleep(1000); - continue; - } - final UsbDevice device = devices.values().iterator().next(); - requestPermission(device); - final UsbDeviceConnection connection = mUsbManager.openDevice(device); - if (connection == null) { - fail("Cannot open USB connection."); - } - for (int i = 0; i < device.getInterfaceCount(); i++) { - // Since the test runs real environment, we need to call claim interface with - // force = true to rob interfaces from other applications. - connection.claimInterface(device.getInterface(i), true); - connection.releaseInterface(device.getInterface(i)); - } - connection.close(); - return device; - } - } - - private void waitForStorages(MtpManager manager, int deviceId) throws Exception { - while (true) { - if (manager.getRoots(deviceId).length == 0) { - show("Wait for storages."); - Thread.sleep(1000); - continue; - } - return; - } - } - - private void show(String message) { - if (!(getInstrumentation() instanceof TestResultInstrumentation)) { - return; - } - ((TestResultInstrumentation) getInstrumentation()).show(message); - } - private Context getContext() { return getInstrumentation().getContext(); } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java index 0fb0f34eec65..3e64f9a2917a 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.mtp; import android.os.Bundle; diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java new file mode 100644 index 000000000000..e6c12cb9e10b --- /dev/null +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestUtil.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.mtp; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; + +import java.io.IOException; +import java.util.HashMap; +import java.util.concurrent.CountDownLatch; +import junit.framework.Assert; + +/** + * Static utility methods for testing. + */ +class TestUtil { + private static final String ACTION_USB_PERMISSION = + "com.android.mtp.USB_PERMISSION"; + + private TestUtil() {} + + /** + * Requests permission for a MTP device and returns the first MTP device that has at least one + * storage. + * @throws Exception + */ + static UsbDevice setupMtpDevice( + TestResultInstrumentation instrumentation, + UsbManager usbManager, + MtpManager manager) throws Exception { + for (int i = 0; i < 2; i++) { + final UsbDevice device = findMtpDevice(instrumentation, usbManager); + manager.openDevice(device.getDeviceId()); + try { + waitForStorages(instrumentation, manager, device.getDeviceId()); + return device; + } catch (IOException exp) { + // When the MTP device is Android, and it changes the USB device type from + // "Charging" to "MTP", the device ID will be updated. We need to find a device + // again. + continue; + } + } + throw new IOException("Failed to obtain MTP devices"); + } + + private static UsbDevice findMtpDevice( + TestResultInstrumentation instrumentation, + UsbManager usbManager) throws InterruptedException { + while (true) { + final HashMap<String,UsbDevice> devices = usbManager.getDeviceList(); + if (devices.size() == 0) { + instrumentation.show("Wait for devices."); + Thread.sleep(1000); + continue; + } + final UsbDevice device = devices.values().iterator().next(); + requestPermission(instrumentation, usbManager, device); + final UsbDeviceConnection connection = usbManager.openDevice(device); + if (connection == null) { + Assert.fail("Cannot open USB connection."); + return null; + } + for (int i = 0; i < device.getInterfaceCount(); i++) { + // Since the test runs real environment, we need to call claim interface with + // force = true to rob interfaces from other applications. + connection.claimInterface(device.getInterface(i), true); + connection.releaseInterface(device.getInterface(i)); + } + connection.close(); + return device; + } + } + + private static void requestPermission( + final TestResultInstrumentation instrumentation, + UsbManager usbManager, + UsbDevice device) throws InterruptedException { + if (usbManager.hasPermission(device)) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + latch.countDown(); + instrumentation.getTargetContext().unregisterReceiver(this); + } + }; + instrumentation.getTargetContext().registerReceiver( + receiver, new IntentFilter(ACTION_USB_PERMISSION)); + usbManager.requestPermission(device, PendingIntent.getBroadcast( + instrumentation.getTargetContext(), + 0 /* requstCode */, + new Intent(ACTION_USB_PERMISSION), + 0 /* flags */)); + latch.await(); + Assert.assertTrue(usbManager.hasPermission(device)); + } + + private static void waitForStorages( + TestResultInstrumentation instrumentation, + MtpManager manager, + int deviceId) throws Exception { + while (true) { + if (manager.getRoots(deviceId).length == 0) { + instrumentation.show("Wait for storages."); + Thread.sleep(1000); + continue; + } + return; + } + } +} diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml index d1998bdd39f4..8430bf0d4e91 100644 --- a/packages/Shell/res/values-af/strings.xml +++ b/packages/Shell/res/values-af/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Tuisskerm"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Besig met foutverslag"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Foutverslag vasgevang"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swiep na links om jou foutverslag te deel"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak om jou foutverslag te deel"</string> diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml index 6f905a90043b..9400f370139c 100644 --- a/packages/Shell/res/values-am/strings.xml +++ b/packages/Shell/res/values-am/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"ቀፎ"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"የሳንካ ሪፓርት በሂደት ላይ"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"የሳንካ ሪፖርት ተይዟል"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"የሳንካ ሪፖርትዎን ለማጋራት ወደ ግራ ያንሸራትቱ"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"የሳንካ ሪፖርትዎን ለማጋራት ይንክኩ"</string> diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml index 76100b5684c3..2161a7610420 100644 --- a/packages/Shell/res/values-ar/strings.xml +++ b/packages/Shell/res/values-ar/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"تقرير الخطأ قيد التقدم"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"تم الحصول على تقرير الأخطاء"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"مرر بسرعة لليمين لمشاركة تقرير الخطأ"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"المس لمشاركة تقرير الأخطاء"</string> diff --git a/packages/Shell/res/values-az-rAZ/strings.xml b/packages/Shell/res/values-az-rAZ/strings.xml index d8176f5bcbf4..76ee4bf43cca 100644 --- a/packages/Shell/res/values-az-rAZ/strings.xml +++ b/packages/Shell/res/values-az-rAZ/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Baq hesabatı davam edir"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Baq raport alındı"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Baq raportunu paylaşmaq üçün sola sürüşdürün"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Xətanı şikayətini paylaşmaq üçün toxunun"</string> diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml index fc2dad03e601..bbfae69ab928 100644 --- a/packages/Shell/res/values-bg/strings.xml +++ b/packages/Shell/res/values-bg/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Команден ред"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Отчетът за програмни грешки е записан"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Прекарайте пръст наляво, за да споделите сигнала си за програмна грешка"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Докоснете, за да споделите отчета си за програмни грешки"</string> diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml index 5aa3e9d4a0d1..5ca583594c40 100644 --- a/packages/Shell/res/values-bn-rBD/strings.xml +++ b/packages/Shell/res/values-bn-rBD/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"শেল"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ত্রুটির প্রতিবেদন করা হচ্ছে"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"ত্রুটির প্রতিবেদন নেওয়া হয়েছে"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"আপনার বাগ রিপোর্ট শেয়ার করতে বামে সোয়াইপ করুন"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"আপনার ত্রুটির প্রতিবেদন ভাগ করতে স্পর্শ করুন"</string> diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml index bef1a674b2f0..1e6ec539783b 100644 --- a/packages/Shell/res/values-ca/strings.xml +++ b/packages/Shell/res/values-ca/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Protecció"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe d\'errors en curs"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"S\'ha registrat l\'informe d\'error"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Llisca cap a l\'esquerra per compartir l\'informe d\'errors."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí per compartir el teu informe d\'error."</string> diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml index 46c8f4e5c680..336c21f49a17 100644 --- a/packages/Shell/res/values-cs/strings.xml +++ b/packages/Shell/res/values-cs/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Probíhá zpracování zprávy o chybě"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Bylo vytvořeno chybové hlášení"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Chcete-li hlášení chyby sdílet, přejeďte doleva."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chybové hlášení můžete sdílet klepnutím."</string> diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml index 7a87b9b405c8..6f658944865a 100644 --- a/packages/Shell/res/values-da/strings.xml +++ b/packages/Shell/res/values-da/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Fejlrapporten er under udførelse"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Fejlrapporten er registreret"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Stryg til venstre for at dele din fejlrapport"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryk for at dele din fejlrapport"</string> diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml index 7a25c4da835d..03c616637bf0 100644 --- a/packages/Shell/res/values-de/strings.xml +++ b/packages/Shell/res/values-de/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Fehlerbericht in Bearbeitung"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Fehlerbericht erfasst"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Wischen Sie nach links, um Ihren Fehlerbericht zu teilen."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tippen, um Fehlerbericht zu teilen"</string> diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml index 02e37f761e2d..ceec189a3148 100644 --- a/packages/Shell/res/values-el/strings.xml +++ b/packages/Shell/res/values-el/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Κέλυφος"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Αναφορά σφάλματος σε εξέλιξη"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Η λήψη της αναφοράς ήταν επιτυχής"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Σύρετε προς τα αριστερά για κοινή χρήση της αναφοράς σφαλμάτων"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string> diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml index 1b55115bf7c4..99c9f4f654a3 100644 --- a/packages/Shell/res/values-en-rAU/strings.xml +++ b/packages/Shell/res/values-en-rAU/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bug report in progress"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string> diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml index 1b55115bf7c4..99c9f4f654a3 100644 --- a/packages/Shell/res/values-en-rGB/strings.xml +++ b/packages/Shell/res/values-en-rGB/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bug report in progress"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string> diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml index 1b55115bf7c4..99c9f4f654a3 100644 --- a/packages/Shell/res/values-en-rIN/strings.xml +++ b/packages/Shell/res/values-en-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bug report in progress"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Bug report captured"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swipe left to share your bug report"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string> diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml index 1937349355cc..263459e3f460 100644 --- a/packages/Shell/res/values-es-rUS/strings.xml +++ b/packages/Shell/res/values-es-rUS/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe de errores en progreso"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de errores capturado"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de errores."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de errores."</string> diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml index 002bea9a1d30..3b37d4046321 100644 --- a/packages/Shell/res/values-es/strings.xml +++ b/packages/Shell/res/values-es/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe de errores en curso"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de error registrado"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Desliza el dedo hacia la izquierda para compartir el informe de error"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de error"</string> diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml index a16875c3afc8..652de64e1254 100644 --- a/packages/Shell/res/values-et-rEE/strings.xml +++ b/packages/Shell/res/values-et-rEE/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Kest"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Veaaruande töötlemine on pooleli"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Veaaruanne jäädvustati"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veaaruande jagamiseks pühkige vasakule"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Veaaruande jagamiseks puudutage"</string> diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml index e7f1766a73e4..3234786e91b1 100644 --- a/packages/Shell/res/values-eu-rES/strings.xml +++ b/packages/Shell/res/values-eu-rES/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell-interfazea"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Abian da akatsen txostena egiteko prozesua"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Akatsen txostena jaso da"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Programa-akatsen txostena partekatzeko, pasatu hatza ezkerrera"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Akatsen txostena partekatzeko, ukitu"</string> diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml index 9138b28c2cc7..ff58c25e9d03 100644 --- a/packages/Shell/res/values-fa/strings.xml +++ b/packages/Shell/res/values-fa/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"گزارش اشکال در حال انجام است"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"گزارش اشکال دریافت شد"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"برای اشتراکگذاری گزارش اشکال، به تندی آن را به چپ بکشید"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"جهت اشتراکگذاری گزارش اشکال خود لمس کنید"</string> diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml index 079433dbd4c5..df6851c8f470 100644 --- a/packages/Shell/res/values-fi/strings.xml +++ b/packages/Shell/res/values-fi/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Komentotulkki"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Virheraportti käynnissä"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Virheraportti tallennettu"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Jaa virheraportti pyyhkäisemällä vasemmalle"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Jaa virheraportti koskettamalla tätä"</string> diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml index 4ead085ac32d..5f581644c0bf 100644 --- a/packages/Shell/res/values-fr-rCA/strings.xml +++ b/packages/Shell/res/values-fr-rCA/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Création du rapport de bogue en cours..."</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bogue enregistré"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport de bogue."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bogue"</string> diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml index 14c1d3b5f263..9562c6cf3cb7 100644 --- a/packages/Shell/res/values-fr/strings.xml +++ b/packages/Shell/res/values-fr/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Rapport de bug en cours"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Rapport de bug enregistré"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Faites glisser le doigt vers la gauche pour partager votre rapport d\'erreur."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyez ici pour partager le rapport de bug"</string> diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml index 61b0335bbfc5..1f8da5d245c1 100644 --- a/packages/Shell/res/values-gl-rES/strings.xml +++ b/packages/Shell/res/values-gl-rES/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Informe de erro en curso"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Informe de erros rexistrado"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Pasa o dedo á esquerda para compartir o teu informe de erros"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí para compartir o teu informe de erros"</string> diff --git a/packages/Shell/res/values-gu-rIN/strings.xml b/packages/Shell/res/values-gu-rIN/strings.xml index 746ac4560ef9..7a94dd75d10b 100644 --- a/packages/Shell/res/values-gu-rIN/strings.xml +++ b/packages/Shell/res/values-gu-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"શેલ"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"બગ રિપોર્ટ ચાલુ છે"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"બગ રિપોર્ટ કેપ્ચર કરી"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"તમારી બગ રિપોર્ટ શેર કરવા માટે ડાબે સ્વાઇપ કરો"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"તમારી બગ રિપોર્ટ શેર કરવા માટે ટચ કરો"</string> diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml index 97db6078c38f..273b4841b5d6 100644 --- a/packages/Shell/res/values-hi/strings.xml +++ b/packages/Shell/res/values-hi/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"शेल"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"बग रिपोर्ट प्रगति में है"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"बग रिपोर्ट कैप्चर कर ली गई"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"अपनी बग रिपोर्ट साझा करने के लिए बाएं स्वाइप करें"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"अपनी बग रिपोर्ट साझा करने के लिए स्पर्श करें"</string> diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml index b1fad848870c..3836edb70537 100644 --- a/packages/Shell/res/values-hr/strings.xml +++ b/packages/Shell/res/values-hr/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Ljuska"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Izvješće o programskoj pogrešci u tijeku"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Prijava programske pogreške snimljena je"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Prijeđite prstom ulijevo da biste poslali izvješće o programskim pogreškama"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dodirnite za dijeljenje prijave programske pogreške"</string> diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml index 7dae6c1c38a0..9191e02bc553 100644 --- a/packages/Shell/res/values-hu/strings.xml +++ b/packages/Shell/res/values-hu/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Héj"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Hibajelentés folyamatban"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Programhiba-jelentés rögzítve"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Húzza ujját balra a hibajelentés megosztásához"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Érintse meg a programhiba-jelentés megosztásához"</string> diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml index 149b677a222b..bfa9b049531a 100644 --- a/packages/Shell/res/values-hy-rAM/strings.xml +++ b/packages/Shell/res/values-hy-rAM/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Խեցի"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Վրիպակի զեկույց է ստացվել"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Սահեցրեք ձախ՝ սխալի հաշվետվությունը համօգտագործելու համար"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Հպեք` ձեր վրիպակի մասին զեկույցը տարածելու համար"</string> diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml index 534badcae0b9..9a13d8bc1968 100644 --- a/packages/Shell/res/values-in/strings.xml +++ b/packages/Shell/res/values-in/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Kerangka"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Laporan bug sedang berlangsung"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Laporan bug tercatat"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Gesek ke kiri untuk membagikan laporan bug Anda"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk membagikan laporan bug Anda"</string> diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml index ce742f6ab795..4304c8e47d45 100644 --- a/packages/Shell/res/values-is-rIS/strings.xml +++ b/packages/Shell/res/values-is-rIS/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Skipanalína"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Verið er að útbúa villutilkynningu"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Villutilkynning útbúin"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Strjúktu til vinstri til að deila villuskýrslunni"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Snertu til að deila villutilkynningunni"</string> diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml index 38e360f00b83..63d39d0d4bf0 100644 --- a/packages/Shell/res/values-it/strings.xml +++ b/packages/Shell/res/values-it/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Segnalazione di bug in corso"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Segnalazione di bug acquisita"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Scorri verso sinistra per condividere il rapporto sui bug"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tocca per condividere la segnalazione di bug"</string> diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml index ab9aecf19045..94a167f236d6 100644 --- a/packages/Shell/res/values-iw/strings.xml +++ b/packages/Shell/res/values-iw/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"מעטפת"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"הדוח על הבאג מתבצע כעת"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"דוח הבאגים צולם"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"החלק שמאלה כדי לשתף את דוח הבאגים"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"גע כדי לשתף את דוח הבאגים שלך"</string> diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml index 619ee4f3259a..77adb37f548f 100644 --- a/packages/Shell/res/values-ja/strings.xml +++ b/packages/Shell/res/values-ja/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"シェル"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"バグレポートを処理しています"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"バグレポートが記録されました"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"バグレポートを共有するには左にスワイプ"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"バグレポートを共有するにはタップします"</string> diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml index f98045f242a8..2521d3ee365e 100644 --- a/packages/Shell/res/values-ka-rGE/strings.xml +++ b/packages/Shell/res/values-ka-rGE/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"გარეკანი"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"ანგარიში ხარვეზების შესახებ შექმნილია"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"გაასრიალეთ მარცხნივ თქვენი ხარვეზის შეტყობინების გასაზიარებლად"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"შეეხეთ თქვენი ხარვეზების ანგარიშის გასაზიარებლად"</string> diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml index a24f2ccf64db..5ce7815a992f 100644 --- a/packages/Shell/res/values-kk-rKZ/strings.xml +++ b/packages/Shell/res/values-kk-rKZ/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Қабыршық"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Вирус туралы баянат қабылданды"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Қате туралы есепті бөлісу үшін солға жанаңыз"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Бөліс үшін, вирус туралы баянатты түртіңіз."</string> diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml index 2439248a5a7c..2814a73fdf83 100644 --- a/packages/Shell/res/values-km-rKH/strings.xml +++ b/packages/Shell/res/values-km-rKH/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"សែល"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"កំពុងដំណើរការរបាយការណ៍កំហុស"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"បានចាប់យករបាយការណ៍កំហុស"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"អូសទៅឆ្វេង ដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នក"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ប៉ះ ដើម្បីចែករំលែករបាយការណ៍កំហុសរបស់អ្នក"</string> diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml index bca9c45db051..be34c8515179 100644 --- a/packages/Shell/res/values-kn-rIN/strings.xml +++ b/packages/Shell/res/values-kn-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"ಶೆಲ್"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ದೋಷ ವರದಿ ಪ್ರಗತಿಯಲ್ಲಿದೆ"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"ದೋಷದ ವರದಿಯನ್ನು ಸೆರೆಹಿಡಿಯಲಾಗಿದೆ"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ನಿಮ್ಮ ದೋಷ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಎಡಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ನಿಮ್ಮ ದೋಷದ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ"</string> diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml index a3a0bf39d16b..46c0daa8367d 100644 --- a/packages/Shell/res/values-ko/strings.xml +++ b/packages/Shell/res/values-ko/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"셸"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"버그 신고서 캡처됨"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"왼쪽으로 스와이프하여 버그 신고서를 공유하세요."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"버그 신고서를 공유하려면 터치하세요."</string> diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml index 622920afca8e..aa8c5385a4bc 100644 --- a/packages/Shell/res/values-ky-rKG/strings.xml +++ b/packages/Shell/res/values-ky-rKG/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Командалык кабык"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Ката тууралуу билдирүү түзүлдү"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ката жөнүндө кабар менен бөлүшүү үчүн солго серпип коюңуз"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Ката тууралуу билдирүүңүздү жөнөтүш үчүн, тийиңиз"</string> diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml index 537f1971ab14..caf11c4ae40c 100644 --- a/packages/Shell/res/values-lo-rLA/strings.xml +++ b/packages/Shell/res/values-lo-rLA/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ລາຍງານບັນຫາພວມດຳເນີນຢູ່"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"ລາຍງານຈຸດບົກພ່ອງຖືກເກັບກຳແລ້ວ"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ປັດໄປຊ້າຍເພື່ອສົ່ງລາຍງານຂໍ້ຜິດພາດຂອງທ່ານ"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ແຕະເພື່ອສົ່ງການລາຍງານປັນຫາຂອງທ່ານ"</string> diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml index 6965e4c786d9..e70cf9f7fa6f 100644 --- a/packages/Shell/res/values-lt/strings.xml +++ b/packages/Shell/res/values-lt/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Apvalkalas"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Riktų ataskaita užfiksuota"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Perbraukite kairėn, kad bendrintumėte rikto ataskaitą"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Palieskite, kad bendrintumėte riktų ataskaitą"</string> diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml index efc40f351bcf..c5d10bba3bc8 100644 --- a/packages/Shell/res/values-lv/strings.xml +++ b/packages/Shell/res/values-lv/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Aizsargs"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Izveidots kļūdu pārskats"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Velciet pa kreisi, lai kopīgotu savu kļūdu ziņojumu."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pieskarieties, lai kopīgotu kļūdu pārskatu."</string> diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml index 7571c2cd5be1..4baa86a5c04b 100644 --- a/packages/Shell/res/values-mk-rMK/strings.xml +++ b/packages/Shell/res/values-mk-rMK/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Обвивка"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Извештајот за грешка е во тек"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Извештајот за грешка е снимен"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Повлечете налево за да споделите пријава за грешка"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Допри да се сподели твојот извештај за грешка"</string> diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml index 4779e4d71952..5dd7763a4cf6 100644 --- a/packages/Shell/res/values-ml-rIN/strings.xml +++ b/packages/Shell/res/values-ml-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"ഷെൽ"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ബഗ് റിപ്പോർട്ട് പുരോഗതിയിലാണ്"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"ബഗ് റിപ്പോർട്ട് ക്യാപ്ചർ ചെയ്തു"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടുന്നതിന് ഇടത്തേയ്ക്ക് സ്വൈപ്പുചെയ്യുക"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ സ്പർശിക്കുക"</string> diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml index 71ad7f6c8dbb..f7a9688fddda 100644 --- a/packages/Shell/res/values-mn-rMN/strings.xml +++ b/packages/Shell/res/values-mn-rMN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Шел"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Алдааны тайлан үргэлжилж байна"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Алдааны мэдээлэл хүлээн авав"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Өөрийн согог репортыг хуваалцахын тулд зүүн шудрана уу"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Та алдааны мэдэгдлийг хуваалцах бол хүрнэ үү"</string> diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml index c32bb5be7319..e0db42b76082 100644 --- a/packages/Shell/res/values-mr-rIN/strings.xml +++ b/packages/Shell/res/values-mr-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"शेल"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"दोष अहवाल प्रगतीपथावर आहे"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"दोष अहवाल कॅप्चर केला"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"आपला दोष अहवाल सामायिक करण्यासाठी डावीकडे स्वाइप करा"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"आपला दोष अहवाल सामायिक करण्यासाठी स्पर्श करा"</string> diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml index 852a9e44fad6..bbbb42a5c973 100644 --- a/packages/Shell/res/values-ms-rMY/strings.xml +++ b/packages/Shell/res/values-ms-rMY/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"Laporan pepijat telah ditangkap"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Leret ke kiri untuk berkongsi laporan pepijat anda"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk berkongsi laporan pepijat anda"</string> diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml index 4ba9037ce2a2..d49dc1519898 100644 --- a/packages/Shell/res/values-my-rMM/strings.xml +++ b/packages/Shell/res/values-my-rMM/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"အခွံ"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ချွတ်ယွင်းချက် အစီရင်ခံစာ ပြုလုပ်ဆဲ"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"အမှားအယွင်းမှတ်တမ်းကို အောင်မြင်စွာ သိမ်းဆည်းပြီး"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"သင်၏ ဘာဂ် အစီရင်ခံစာကို မျှပေးရန် ဘယ်ဘက်သို့ ပွတ်ဆွဲရန်"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"အမှားအယွင်း မှတ်တမ်းကို မျှဝေရန် ထိလိုက်ပါ"</string> diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml index c543e49cbb9f..d3aafdd62a61 100644 --- a/packages/Shell/res/values-nb/strings.xml +++ b/packages/Shell/res/values-nb/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Kommandoliste"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Feilrapport pågår"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Feilrapporten er lagret"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Sveip til venstre for å dele feilrapporten din"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Trykk for å dele feilrapporten din"</string> diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml index f57112ac329a..fb81e558f558 100644 --- a/packages/Shell/res/values-ne-rNP/strings.xml +++ b/packages/Shell/res/values-ne-rNP/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"सेल"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"बग प्रतिवेदन समातियो"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"तपाईँको बग रिपोर्ट साझेदारी गर्न बायाँ स्वाइप गर्नुहोस्"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"तपाईंको बग रिपोर्ट साझेदारी गर्न छुनुहोस्"</string> diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml index 1519454c2b48..a097feabfa70 100644 --- a/packages/Shell/res/values-nl/strings.xml +++ b/packages/Shell/res/values-nl/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Bugrapport wordt uitgevoerd"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Foutenrapport vastgelegd"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Veeg naar links om je bugmelding te delen"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak aan om je foutenrapport te delen"</string> diff --git a/packages/Shell/res/values-pa-rIN/strings.xml b/packages/Shell/res/values-pa-rIN/strings.xml index 3f59bb9ea90a..54e30d6f58cb 100644 --- a/packages/Shell/res/values-pa-rIN/strings.xml +++ b/packages/Shell/res/values-pa-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"ਸ਼ੈਲ"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"ਬੱਗ ਰਿਪੋਰਟ \'ਤੇ ਕਾਰਵਾਈ ਹੋ ਰਹੀ ਹੈ"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"ਬਗ ਰਿਪੋਰਟ ਕੈਪਚਰ ਕੀਤੀ"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ਤੁਹਾਡੀ ਬਗ ਰਿਪੋਰਟ ਸ਼ੇਅਰ ਕਰਨ ਲਈ ਖੱਬੇ ਪਾਸੇ ਸਵਾਈਪ ਕਰੋ"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ਆਪਣੀ ਬਗ ਰਿਪੋਰਟ ਸ਼ੇਅਰ ਕਰਨ ਲਈ ਛੋਹਵੋ"</string> diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml index accc92e83634..599f8c19e3e6 100644 --- a/packages/Shell/res/values-pl/strings.xml +++ b/packages/Shell/res/values-pl/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Powłoka"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Trwa zgłaszanie błędu"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Raport o błędach został zapisany"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Przesuń palcem w lewo, by udostępnić swoje zgłoszenie błędu"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Kliknij, by udostępnić raport o błędach"</string> diff --git a/packages/Shell/res/values-pt-rBR/strings.xml b/packages/Shell/res/values-pt-rBR/strings.xml index 7d4b0d310a9e..a1a9ad1fe690 100644 --- a/packages/Shell/res/values-pt-rBR/strings.xml +++ b/packages/Shell/res/values-pt-rBR/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Relatório de bugs em andamento"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de bugs capturado"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string> diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml index 007d6834f3ce..2e25f8c98ab4 100644 --- a/packages/Shell/res/values-pt-rPT/strings.xml +++ b/packages/Shell/res/values-pt-rPT/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Relatório de erro em curso"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de erros capturado"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslizar rapidamente para a esquerda para partilhar o seu relatório de erros"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para partilhar o relatório de erros"</string> diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml index 7d4b0d310a9e..a1a9ad1fe690 100644 --- a/packages/Shell/res/values-pt/strings.xml +++ b/packages/Shell/res/values-pt/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Relatório de bugs em andamento"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Relatório de bugs capturado"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Deslize para a esquerda para compartilhar seu relatório de bugs"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string> diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml index e7395e1fc4f1..d8a3b92a2b1d 100644 --- a/packages/Shell/res/values-ro/strings.xml +++ b/packages/Shell/res/values-ro/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Raportul de eroare se creează"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Raportul despre erori a fost creat"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Glisați la stânga pentru a trimite raportul de erori"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Atingeți pentru a permite accesul la raportul despre erori"</string> diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml index c9276cf10483..436a5905f66e 100644 --- a/packages/Shell/res/values-ru/strings.xml +++ b/packages/Shell/res/values-ru/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Оболочка"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Отправка сообщения об ошибке..."</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Отчет об ошибке сохранен"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Проведите влево, чтобы отправить отчет"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Нажмите, чтобы отправить отчет об ошибках"</string> diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml index 937c1bc8014b..a0bc5f68bf98 100644 --- a/packages/Shell/res/values-si-rLK/strings.xml +++ b/packages/Shell/res/values-si-rLK/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"ෂෙල්"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"දෝෂය වාර්තා කිරීම සිදු කරමින් පවතී"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"දෝෂ වාර්තාව ලබාගන්නා ලදි"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"ඔබගේ දෝෂ වාර්තාව බෙදාගැනීමට වමට ස්වයිප් කරන්න"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට ස්පර්ශ කරන්න"</string> diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml index cd52efc0e1ef..e568946b0dc9 100644 --- a/packages/Shell/res/values-sk/strings.xml +++ b/packages/Shell/res/values-sk/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Prostredie"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Prebieha hlásenie chyby"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Hlásenie o chybách bolo vytvorené"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Ak chcete hlásenie o chybe zdieľať, prejdite prstom doľava."</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hlásenie o chybách môžete zdielať klepnutím"</string> diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml index 25553f20bab3..6ac5ac8cd8ef 100644 --- a/packages/Shell/res/values-sl/strings.xml +++ b/packages/Shell/res/values-sl/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Lupina"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Poročanje o napakah poteka"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Poročilo o napaki je posneto"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Povlecite v levo, če želite poslati sporočilo o napaki"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dotaknite se, če želite deliti sporočilo o napaki z drugimi"</string> diff --git a/packages/Shell/res/values-sq-rAL/strings.xml b/packages/Shell/res/values-sq-rAL/strings.xml index add53d85ae02..54f2aad0abe3 100644 --- a/packages/Shell/res/values-sq-rAL/strings.xml +++ b/packages/Shell/res/values-sq-rAL/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Guaska"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Raporti i gabimeve në kod është në progres"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Raporti i defektit në kod u regjistrua"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Rrëshqit majtas për të ndarë raportin e defektit në kod"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Prek për të ndarë raportin e defektit në kod"</string> diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml index bb60f3f06468..81cde0007c4b 100644 --- a/packages/Shell/res/values-sr/strings.xml +++ b/packages/Shell/res/values-sr/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Прављење извештаја о грешци је у току"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Извештај о грешци је снимљен"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Превуците улево да бисте делили извештај о грешкама"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Додирните да бисте делили извештај о грешци"</string> diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml index dd23e16e2259..2287319992f6 100644 --- a/packages/Shell/res/values-sv/strings.xml +++ b/packages/Shell/res/values-sv/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Skal"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Felrapportering pågår"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Felrapporten har skapats"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Svep åt vänster om du vill dela felrapporten"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryck om du vill dela felrapporten"</string> diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml index 3773cd70db3a..adb97dde6768 100644 --- a/packages/Shell/res/values-sw/strings.xml +++ b/packages/Shell/res/values-sw/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Ganda"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Mchakato wa kuripoti hitilafu unaendelea"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Ripoti ya hitilafu imenaswa"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Telezesha kidole kushoto ili ushiriki ripoti yako ya hitilafu"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Gusa ili ushiriki ripoti yako ya hitilafu"</string> diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml index 244c92949720..15a6242c4370 100644 --- a/packages/Shell/res/values-ta-rIN/strings.xml +++ b/packages/Shell/res/values-ta-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"ஷெல்"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"பிழை அறிக்கை செயலிலுள்ளது"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"பிழை அறிக்கைகள் படமெடுக்கப்பட்டன"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"பிழை அறிக்கையைப் பகிர இடது புறமாகத் தேய்க்கவும்"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"உங்கள் பிழை அறிக்கையைப் பகிர, தொடவும்"</string> diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml index cd9b4b7eaebc..cd951a267ff2 100644 --- a/packages/Shell/res/values-te-rIN/strings.xml +++ b/packages/Shell/res/values-te-rIN/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"షెల్"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"బగ్ నివేదిక ప్రోగ్రెస్లో ఉంది"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"బగ్ నివేదిక క్యాప్చర్ చేయబడింది"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి ఎడమవైపుకు స్వైప్ చేయండి"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి తాకండి"</string> diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml index 35b7ec9a3d8e..c61ffeb251e0 100644 --- a/packages/Shell/res/values-th/strings.xml +++ b/packages/Shell/res/values-th/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"รายงานข้อบกพร่องอยู่ระหว่างดำเนินการ"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"จับภาพรายงานข้อบกพร่องแล้ว"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"กวาดไปทางซ้ายเพื่อแชร์รายงานข้อบกพร่อง"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณ"</string> diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml index 21df2069cd23..3e9a68d2c680 100644 --- a/packages/Shell/res/values-tl/strings.xml +++ b/packages/Shell/res/values-tl/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Kasalukuyang ginagawa ang ulat ng bug"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Na-capture ang ulat ng bug"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Mag-swipe pakaliwa upang ibahagi ang iyong ulat ng bug"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pindutin upang ibahagi ang iyong ulat ng bug"</string> diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml index fa8b82b75191..f90dae03cc7a 100644 --- a/packages/Shell/res/values-tr/strings.xml +++ b/packages/Shell/res/values-tr/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Kabuk"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Hata raporu hazırlanıyor"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Hata raporu kaydedildi"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Hata raporunuzu paylaşmak için hızlıca sola kaydırın"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hata raporunuzu paylaşmak için dokunun"</string> diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml index ba667f20bf81..dac4fe0fe5c0 100644 --- a/packages/Shell/res/values-uk/strings.xml +++ b/packages/Shell/res/values-uk/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Оболонка"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Генерується повідомлення про помилку"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Звіт про помилки створено"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Проведіть пальцем ліворуч, щоб надіслати звіт про помилки"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Торкніться, щоб надіслати звіт про помилки"</string> diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml index 255aaf8c55ab..206f02a8a9c7 100644 --- a/packages/Shell/res/values-ur-rPK/strings.xml +++ b/packages/Shell/res/values-ur-rPK/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"شیل"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"بگ رپورٹ پر پیشرفت جاری ہے"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"بَگ رپورٹ کیپچر کر لی گئی"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"اپنی بگ رپورٹ کا اشتراک کرنے کیلئے بائیں سوائپ کریں"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"اپنی بَگ رپورٹ کا اشتراک کرنے کیلئے ٹچ کریں"</string> diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml index 998387e099a3..0b8c1fb1fc1f 100644 --- a/packages/Shell/res/values-uz-rUZ/strings.xml +++ b/packages/Shell/res/values-uz-rUZ/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Terminal"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Xatoliklar hisoboti yuborilmoqda…"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Xatolik hisobotini yozib olindi"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Xatolik hisobotini yuborish uchun barmog‘ingiz bilan chapga suring"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Xatolik hisobotini bo‘lishish uchun barmog‘ingizni tegizing."</string> diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml index 5c11b13684dd..9ef7cf966718 100644 --- a/packages/Shell/res/values-vi/strings.xml +++ b/packages/Shell/res/values-vi/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Đang tiến hành báo cáo lỗi"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Báo cáo lỗi đã được chụp"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Vuốt sang trái để chia sẻ báo cáo lỗi của bạn"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chạm để chia sẻ báo cáo lỗi của bạn"</string> diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml index 0a2f81bcb42a..0928bb3fd3c3 100644 --- a/packages/Shell/res/values-zh-rCN/strings.xml +++ b/packages/Shell/res/values-zh-rCN/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"Shell"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"已抓取错误报告"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑动即可分享错误报告"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"触摸即可分享您的错误报告"</string> diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml index 227d93785960..26a84c357ffb 100644 --- a/packages/Shell/res/values-zh-rHK/strings.xml +++ b/packages/Shell/res/values-zh-rHK/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"命令介面"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"已擷取錯誤報告"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string> diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml index b9f8ee8b4944..f79e58e83ee2 100644 --- a/packages/Shell/res/values-zh-rTW/strings.xml +++ b/packages/Shell/res/values-zh-rTW/strings.xml @@ -17,6 +17,8 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"殼層"</string> + <!-- no translation found for bugreport_in_progress_title (6125357428413919520) --> + <skip /> <string name="bugreport_finished_title" msgid="2293711546892863898">"已擷取錯誤報告"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"向左滑動即可分享錯誤報告"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string> diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml index b675858e19f6..2e209e1630ce 100644 --- a/packages/Shell/res/values-zu/strings.xml +++ b/packages/Shell/res/values-zu/strings.xml @@ -17,6 +17,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="3701846017049540910">"I-Shell"</string> + <string name="bugreport_in_progress_title" msgid="6125357428413919520">"Umbiko wesiphazamisi uyaqhubeka"</string> <string name="bugreport_finished_title" msgid="2293711546892863898">"Umbiko wesiphazamisi uthwetshuliwe"</string> <string name="bugreport_finished_text" product="watch" msgid="8389172248433597683">"Swayiphela kwesokunxele ukuze wabelane umbiko wesiphazamiso sakho"</string> <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Thinta ukuze wabelane ngombiko wakho wesiphazamisi"</string> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 6d1394758cfb..71904fa707e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -48,8 +48,8 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; */ public class KeyguardIndicationController { - private static final String TAG = "KeyguardIndicationController"; - private static final boolean DEBUG_CHARGING_CURRENT = false; + private static final String TAG = "KeyguardIndication"; + private static final boolean DEBUG_CHARGING_SPEED = false; private static final int MSG_HIDE_TRANSIENT = 1; private static final int MSG_CLEAR_FP_MSG = 2; @@ -72,7 +72,7 @@ public class KeyguardIndicationController { private boolean mPowerPluggedIn; private boolean mPowerCharged; private int mChargingSpeed; - private int mChargingCurrent; + private int mChargingWattage; private String mMessageToShowOnScreenOn; public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView, @@ -173,8 +173,8 @@ public class KeyguardIndicationController { } if (mPowerPluggedIn) { String indication = computePowerIndication(); - if (DEBUG_CHARGING_CURRENT) { - indication += ", " + (mChargingCurrent / 1000) + " mA"; + if (DEBUG_CHARGING_SPEED) { + indication += ", " + (mChargingWattage / 1000) + " mW"; } return indication; } @@ -231,7 +231,7 @@ public class KeyguardIndicationController { || status.status == BatteryManager.BATTERY_STATUS_FULL; mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; mPowerCharged = status.isCharged(); - mChargingCurrent = status.maxChargingCurrent; + mChargingWattage = status.maxChargingWattage; mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold); updateIndication(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index bca649110a14..fb8086c3828e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -431,6 +431,11 @@ public class NotificationContentView extends FrameLayout { */ private int calculateVisibleType() { boolean noExpandedChild = mExpandedChild == null; + + if (!noExpandedChild && mContentHeight == mExpandedChild.getHeight()) { + return VISIBLE_TYPE_EXPANDED; + } + if (mIsHeadsUp && mHeadsUpChild != null) { if (mContentHeight <= mHeadsUpChild.getHeight() || noExpandedChild) { return VISIBLE_TYPE_HEADSUP; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 83dbde5eb742..89edae316ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -59,6 +59,7 @@ public class NotificationData { public RemoteViews cachedBigContentView; public RemoteViews cachedHeadsUpContentView; public RemoteViews cachedPublicContentView; + public CharSequence remoteInputText; public Entry(StatusBarNotification n, StatusBarIconView ic) { this.key = n.getKey(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index acfe54d29e82..22c0cb9f270b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -148,6 +148,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public void onDefocus() { mController.removeRemoteInput(mEntry); + mEntry.remoteInputText = mEditText.getText(); setVisibility(INVISIBLE); } @@ -171,6 +172,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.setInnerFocusable(true); mController.addRemoteInput(mEntry); mEditText.mShowImeOnInputConnection = true; + mEditText.setText(mEntry.remoteInputText); + mEditText.setSelection(mEditText.getText().length()); mEditText.requestFocus(); } @@ -216,8 +219,11 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK) { + if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { defocusIfNeeded(); + final InputMethodManager imm = InputMethodManager.getInstance(); + imm.hideSoftInputFromWindow(getWindowToken(), 0); + return true; } return super.onKeyPreIme(keyCode, event); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 185d32d5b36a..ae6b72929d4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -1867,7 +1867,7 @@ public class NotificationStackScrollLayout extends ViewGroup } mNeedsAnimation = true; } - if (isHeadsUp(child)) { + if (isHeadsUp(child) && !mChangePositionInProgress) { mAddedHeadsUpChildren.add(child); mChildrenToAddAnimated.remove(child); } diff --git a/preloaded-classes b/preloaded-classes index e44b25e9674e..2301c4119d38 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -3822,11 +3822,4 @@ org.xml.sax.helpers.DefaultHandler org.xmlpull.v1.XmlPullParser org.xmlpull.v1.XmlPullParserException org.xmlpull.v1.XmlSerializer -<<<<<<< HEAD sun.misc.Unsafe -======= -<<<<<<< HEAD -======= -sun.misc.Unsafe ->>>>>>> 631d21f... Move StrictJarFile from libcore to framework ->>>>>>> 43ea2cc... DO NOT MERGE Move StrictJarFile from libcore to framework diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 17b3d2a8290e..2742c657d853 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -30,10 +30,14 @@ import java.util.Set; import android.app.ActivityThread; import android.app.AppOpsManager; +import android.content.IIntentSender; +import android.content.IntentSender; import android.os.Build; +import android.os.Bundle; import android.os.DeadObjectException; import android.os.Handler; import android.os.Looper; +import android.os.RemoteCallback; import android.os.SystemProperties; import android.os.TransactionTooLargeException; import android.util.ArrayMap; @@ -300,7 +304,7 @@ public final class ActiveServices { } ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, - int callingPid, int callingUid, String callingPackage, int userId) + int callingPid, int callingUid, String callingPackage, final int userId) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); @@ -340,6 +344,18 @@ public final class ActiveServices { NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked( callingUid, r.packageName, service, service.getFlags(), null, r.userId); + + // If permissions need a review before any of the app components can run, + // we do not start the service and launch a review activity if the calling app + // is in the foreground passing it a pending intent to start the service when + // review is completed. + if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, + callingUid, service, callerFg, userId)) { + return null; + } + } + if (unscheduleServiceRestartLocked(r, callingUid, false)) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r); } @@ -417,6 +433,50 @@ public final class ActiveServices { return startServiceInnerLocked(smap, service, r, callerFg, addToStarting); } + private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r, + String callingPackage, int callingUid, Intent service, boolean callerFg, + final int userId) { + if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired( + r.packageName, r.userId)) { + + // Show a permission review UI only for starting from a foreground app + if (!callerFg) { + Slog.w(TAG, "u" + r.userId + " Starting a service in package" + + r.packageName + " requires a permissions review"); + return false; + } + + IIntentSender target = mAm.getIntentSenderLocked( + ActivityManager.INTENT_SENDER_SERVICE, callingPackage, + callingUid, userId, null, null, 0, new Intent[]{service}, + new String[]{service.resolveType(mAm.mContext.getContentResolver())}, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_IMMUTABLE, null); + + final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, r.packageName); + intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target)); + + if (DEBUG_PERMISSIONS_REVIEW) { + Slog.i(TAG, "u" + r.userId + " Launching permission review for package " + + r.packageName); + } + + mAm.mHandler.post(new Runnable() { + @Override + public void run() { + mAm.mContext.startActivityAsUser(intent, new UserHandle(userId)); + } + }); + + return false; + } + + return true; + } + ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { ProcessStats.ServiceState stracker = r.getTracker(); @@ -427,7 +487,7 @@ public final class ActiveServices { synchronized (r.stats.getBatteryStats()) { r.stats.startRunningLocked(); } - String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false); + String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false); if (error != null) { return new ComponentName("!!", error); } @@ -721,8 +781,8 @@ public final class ActiveServices { } int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, - String resolvedType, IServiceConnection connection, int flags, - String callingPackage, int userId) throws TransactionTooLargeException { + String resolvedType, final IServiceConnection connection, int flags, + String callingPackage, final int userId) throws TransactionTooLargeException { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service + " type=" + resolvedType + " conn=" + connection.asBinder() + " flags=0x" + Integer.toHexString(flags)); @@ -783,6 +843,83 @@ public final class ActiveServices { } ServiceRecord s = res.record; + boolean permissionsReviewRequired = false; + + // If permissions need a review before any of the app components can run, + // we schedule binding to the service but do not start its process, then + // we launch a review activity to which is passed a callback to invoke + // when done to start the bound service's process to completing the binding. + if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired( + s.packageName, s.userId)) { + + permissionsReviewRequired = true; + + // Show a permission review UI only for binding from a foreground app + if (!callerFg) { + Slog.w(TAG, "u" + s.userId + " Binding to a service in package" + + s.packageName + " requires a permissions review"); + return 0; + } + + final ServiceRecord serviceRecord = s; + final Intent serviceIntent = service; + + RemoteCallback callback = new RemoteCallback( + new RemoteCallback.OnResultListener() { + @Override + public void onResult(Bundle result) { + synchronized(mAm) { + final long identity = Binder.clearCallingIdentity(); + try { + if (!mPendingServices.contains(serviceRecord)) { + return; + } + // If there is still a pending record, then the service + // binding request is still valid, so hook them up. We + // proceed only if the caller cleared the review requirement + // otherwise we unbind because the user didn't approve. + if (!mAm.getPackageManagerInternalLocked() + .isPermissionsReviewRequired( + serviceRecord.packageName, + serviceRecord.userId)) { + try { + bringUpServiceLocked(serviceRecord, + serviceIntent.getFlags(), + callerFg, false, false); + } catch (RemoteException e) { + /* ignore - local call */ + } + } else { + unbindServiceLocked(connection); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + }); + + final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, s.packageName); + intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback); + + if (DEBUG_PERMISSIONS_REVIEW) { + Slog.i(TAG, "u" + s.userId + " Launching permission review for package " + + s.packageName); + } + + mAm.mHandler.post(new Runnable() { + @Override + public void run() { + mAm.mContext.startActivityAsUser(intent, new UserHandle(userId)); + } + }); + } + } + final long origId = Binder.clearCallingIdentity(); try { @@ -840,7 +977,8 @@ public final class ActiveServices { if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); - if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) { + if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, + permissionsReviewRequired) != null) { return 0; } } @@ -890,6 +1028,10 @@ public final class ActiveServices { return 1; } + private void foo() { + + } + void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) { final long origId = Binder.clearCallingIdentity(); try { @@ -1366,7 +1508,7 @@ public final class ActiveServices { return; } try { - bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true); + bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true, false); } catch (TransactionTooLargeException e) { // Ignore, it's been logged and nothing upstack cares. } @@ -1410,8 +1552,9 @@ public final class ActiveServices { } } - private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, - boolean whileRestarting) throws TransactionTooLargeException { + private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, + boolean whileRestarting, boolean permissionsReviewRequired) + throws TransactionTooLargeException { //Slog.i(TAG, "Bring up service:"); //r.dump(" "); @@ -1497,7 +1640,7 @@ public final class ActiveServices { // Not running -- get it started, and enqueue this service record // to be executed when the app comes up. - if (app == null) { + if (app == null && !permissionsReviewRequired) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, "service", r.name, false, isolated, false)) == null) { String msg = "Unable to launch app " @@ -1920,6 +2063,9 @@ public final class ActiveServices { } } + // If unbound while waiting to start, remove the pending service + mPendingServices.remove(s); + if ((c.flags&Context.BIND_AUTO_CREATE) != 0) { boolean hasAutoCreate = s.hasAutoCreateConnections(); if (!hasAutoCreate) { @@ -2962,5 +3108,4 @@ public final class ActiveServices { } } } - } diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index 16c959fabddf..4f0d4d951e1c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -88,6 +88,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false; static final boolean DEBUG_VISIBLE_BEHIND = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_USAGE_STATS = DEBUG_ALL || false; + static final boolean DEBUG_PERMISSIONS_REVIEW = DEBUG_ALL || false; static final String POSTFIX_ADD_REMOVE = (APPEND_CATEGORY_NAME) ? "_AddRemove" : ""; static final String POSTFIX_APP = (APPEND_CATEGORY_NAME) ? "_App" : ""; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 99470c808b8f..bbf8652887b3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16,9 +16,65 @@ package com.android.server.am; +import static android.Manifest.permission.INTERACT_ACROSS_USERS; +import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static com.android.internal.util.XmlUtils.readBooleanAttribute; +import static com.android.internal.util.XmlUtils.readIntAttribute; +import static com.android.internal.util.XmlUtils.readLongAttribute; +import static com.android.internal.util.XmlUtils.writeBooleanAttribute; +import static com.android.internal.util.XmlUtils.writeIntAttribute; +import static com.android.internal.util.XmlUtils.writeLongAttribute; +import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; +import static com.android.server.am.ActivityManagerDebugConfig.*; +import static com.android.server.am.TaskRecord.INVALID_TASK_ID; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_LAUNCHABLE_PRIV; +import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_PINNABLE; +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.START_TAG; + import com.google.android.collect.Lists; import com.google.android.collect.Maps; +import android.Manifest; +import android.app.AppOpsManager; +import android.app.ApplicationThreadNative; +import android.app.BroadcastOptions; +import android.app.IActivityContainer; +import android.app.IActivityContainerCallback; +import android.app.IAppTask; +import android.app.ITaskStackListener; +import android.app.ProfilerInfo; +import android.app.assist.AssistContent; +import android.app.assist.AssistStructure; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.appwidget.AppWidgetManager; +import android.content.pm.PackageManagerInternal; +import android.content.pm.PermissionInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Point; +import android.graphics.Rect; +import android.os.BatteryStats; +import android.os.PersistableBundle; +import android.os.PowerManager; +import android.os.Trace; +import android.os.TransactionTooLargeException; +import android.os.WorkSource; +import android.os.storage.IMountService; +import android.os.storage.MountServiceInternal; +import android.os.storage.StorageManager; +import android.service.voice.IVoiceInteractionSession; +import android.service.voice.VoiceInteractionSession; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DebugUtils; +import android.util.SparseIntArray; +import android.view.Display; + import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.AssistUtils; @@ -1457,6 +1513,8 @@ public final class ActivityManagerService extends ActivityManagerNative final MainHandler mHandler; final UiHandler mUiHandler; + PackageManagerInternal mPackageManagerInt; + final class UiHandler extends Handler { public UiHandler() { super(com.android.server.UiThread.get().getLooper(), null, true); @@ -4397,8 +4455,10 @@ public final class ActivityManagerService extends ActivityManagerNative if (r == null) { return; } - if (r.task != null && r.task.mResizeable) { - // Fixed screen orientation isn't supported with resizeable activities. + TaskRecord task = r.task; + if (task != null && (!task.mFullscreen || !task.stack.mFullscreen)) { + // Fixed screen orientation isn't supported when activities aren't in full screen + // mode. return; } final long origId = Binder.clearCallingIdentity(); @@ -7146,22 +7206,35 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public void getProcessStatesFromPids(/*in*/ int[] pids, /*out*/ int[] states) { - mActivityManagerService.getProcessStatesForPIDs(/*in*/ pids, /*out*/ states); + mActivityManagerService.getProcessStatesAndOomScoresForPIDs( + /*in*/ pids, /*out*/ states, null); + } + + @Override + public void getProcessStatesAndOomScoresFromPids( + /*in*/ int[] pids, /*out*/ int[] states, /*out*/ int[] scores) { + mActivityManagerService.getProcessStatesAndOomScoresForPIDs( + /*in*/ pids, /*out*/ states, /*out*/ scores); } } /** * For each PID in the given input array, write the current process state - * for that process into the output array, or -1 to indicate that no - * process with the given PID exists. + * for that process into the states array, or -1 to indicate that no + * process with the given PID exists. If scores array is provided, write + * the oom score for the process into the scores array, with INVALID_ADJ + * indicating the PID doesn't exist. */ - public void getProcessStatesForPIDs(/*in*/ int[] pids, /*out*/ int[] states) { + public void getProcessStatesAndOomScoresForPIDs( + /*in*/ int[] pids, /*out*/ int[] states, /*out*/ int[] scores) { if (pids == null) { throw new NullPointerException("pids"); } else if (states == null) { throw new NullPointerException("states"); } else if (pids.length != states.length) { - throw new IllegalArgumentException("input and output arrays have different lengths!"); + throw new IllegalArgumentException("pids and states arrays have different lengths!"); + } else if (scores != null && pids.length != scores.length) { + throw new IllegalArgumentException("pids and scores arrays have different lengths!"); } synchronized (mPidsSelfLocked) { @@ -7169,6 +7242,9 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord pr = mPidsSelfLocked.get(pids[i]); states[i] = (pr == null) ? ActivityManager.PROCESS_STATE_NONEXISTENT : pr.curProcState; + if (scores != null) { + scores[i] = (pr == null) ? ProcessList.INVALID_ADJ : pr.curAdj; + } } } } @@ -9942,14 +10018,6 @@ public final class ActivityManagerService extends ActivityManagerNative } if (cpr.proc != null) { - if (false) { - if (cpr.name.flattenToShortString().equals( - "com.android.providers.calendar/.CalendarProvider2")) { - Slog.v(TAG, "****************** KILLING " - + cpr.name.flattenToShortString()); - Process.killProcess(cpr.proc.pid); - } - } checkTime(startTime, "getContentProviderImpl: before updateOomAdj"); boolean success = updateOomAdjLocked(cpr.proc); maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name); @@ -10042,6 +10110,16 @@ public final class ActivityManagerService extends ActivityManagerNative final boolean firstClass = cpr == null; if (firstClass) { final long ident = Binder.clearCallingIdentity(); + + // If permissions need a review before any of the app components can run, + // we return no provider and launch a review activity if the calling app + // is in the foreground. + if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!requestTargetProviderPermissionsReviewIfNeededLocked(cpi, r, userId)) { + return null; + } + } + try { checkTime(startTime, "getContentProviderImpl: before getApplicationInfo"); ApplicationInfo ai = @@ -10194,6 +10272,52 @@ public final class ActivityManagerService extends ActivityManagerNative return cpr != null ? cpr.newHolder(conn) : null; } + private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi, + ProcessRecord r, final int userId) { + if (getPackageManagerInternalLocked().isPermissionsReviewRequired( + cpi.packageName, r.userId)) { + + final boolean callerForeground = r != null ? r.setSchedGroup + != Process.THREAD_GROUP_BG_NONINTERACTIVE : true; + + // Show a permission review UI only for starting from a foreground app + if (!callerForeground) { + Slog.w(TAG, "u" + r.userId + " Instantiating a provider in package" + + cpi.packageName + " requires a permissions review"); + return false; + } + + final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName); + + if (DEBUG_PERMISSIONS_REVIEW) { + Slog.i(TAG, "u" + r.userId + " Launching permission review " + + "for package " + cpi.packageName); + } + + final UserHandle userHandle = new UserHandle(userId); + mHandler.post(new Runnable() { + @Override + public void run() { + mContext.startActivityAsUser(intent, userHandle); + } + }); + + return false; + } + + return true; + } + + PackageManagerInternal getPackageManagerInternalLocked() { + if (mPackageManagerInt == null) { + mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); + } + return mPackageManagerInt; + } + @Override public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) { @@ -10897,7 +11021,7 @@ public final class ActivityManagerService extends ActivityManagerNative public void stopAppSwitches() { if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires permission " + throw new SecurityException("viewquires permission " + android.Manifest.permission.STOP_APP_SWITCHES); } @@ -17759,7 +17883,7 @@ public final class ActivityManagerService extends ActivityManagerNative * @param userId is only used when persistent parameter is set to true to persist configuration * for that particular user */ - boolean updateConfigurationLocked(Configuration values, + private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean initLocale, boolean persistent, int userId) { int changes = 0; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 94a200a11c8f..e9e02c1af6f7 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -76,6 +76,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.content.res.Configuration; @@ -88,6 +89,7 @@ import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Handler; @@ -1707,6 +1709,46 @@ public final class ActivityStackSupervisor implements DisplayListener { return ActivityManager.START_SUCCESS; } + // If permissions need a review before any of the app components can run, we + // launch the review activity and pass a pending intent to start the activity + // we are to launching now after the review is completed. + if (Build.PERMISSIONS_REVIEW_REQUIRED && aInfo != null) { + if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( + aInfo.packageName, userId)) { + IIntentSender target = mService.getIntentSenderLocked( + ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, + callingUid, userId, null, null, 0, new Intent[]{intent}, + new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT + | PendingIntent.FLAG_ONE_SHOT, null); + + Intent newIntent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); + newIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + newIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName); + newIntent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target)); + if (resultRecord != null) { + newIntent.putExtra(Intent.EXTRA_RESULT_NEEDED, true); + } + newIntent.setFlags(intent.getFlags()); + intent = newIntent; + + resolvedType = null; + callingUid = realCallingUid; + callingPid = realCallingPid; + + aInfo = resolveActivity(intent, null, PackageManager.MATCH_DEFAULT_ONLY + | ActivityManagerService.STOCK_PM_FLAGS, null, userId); + + if (DEBUG_PERMISSIONS_REVIEW) { + Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, + true, false) + "} from uid " + callingUid + " on display " + + (container == null ? (mFocusedStack == null ? + Display.DEFAULT_DISPLAY : mFocusedStack.mDisplayId) : + (container.mActivityDisplay == null ? Display.DEFAULT_DISPLAY : + container.mActivityDisplay.mDisplayId))); + } + } + } + ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null, this, container, options); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index fb37edafe44c..b1609819403a 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -27,12 +27,16 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.BroadcastOptions; +import android.app.PendingIntent; import android.content.ComponentName; import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.Intent; +import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -46,6 +50,7 @@ import android.util.EventLog; import android.util.Slog; import android.util.TimeUtils; import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; import static com.android.server.am.ActivityManagerDebugConfig.*; @@ -181,7 +186,7 @@ public final class BroadcastQueue { } break; } } - }; + } private final class AppNotResponding implements Runnable { private final ProcessRecord mApp; @@ -580,6 +585,17 @@ public final class BroadcastQueue { } if (!skip) { + // If permissions need a review before any of the app components can run, we drop + // the broadcast and if the calling app is in the foreground and the broadcast is + // explicit we launch the review UI passing it a pending intent to send the skipped + // broadcast. + if (Build.PERMISSIONS_REVIEW_REQUIRED) { + if (!requestStartTargetPermissionsReviewIfNeededLocked(r, filter.packageName, + filter.owningUserId)) { + return; + } + } + // If this is not being sent as an ordered broadcast, then we // don't want to touch the fields that keep track of the current // state of ordered broadcasts. @@ -622,6 +638,54 @@ public final class BroadcastQueue { } } + private boolean requestStartTargetPermissionsReviewIfNeededLocked( + BroadcastRecord receiverRecord, String receivingPackageName, + final int receivingUserId) { + if (!mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( + receivingPackageName, receivingUserId)) { + return true; + } + + final boolean callerForeground = receiverRecord.callerApp != null + ? receiverRecord.callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE + : true; + + // Show a permission review UI only for explicit broadcast from a foreground app + if (callerForeground && receiverRecord.intent.getComponent() != null) { + IIntentSender target = mService.getIntentSenderLocked( + ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage, + receiverRecord.callingUid, receiverRecord.userId, null, null, 0, + new Intent[]{receiverRecord.intent}, + new String[]{receiverRecord.intent.resolveType(mService.mContext + .getContentResolver())}, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_IMMUTABLE, null); + + final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, receivingPackageName); + intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target)); + + if (DEBUG_PERMISSIONS_REVIEW) { + Slog.i(TAG, "u" + receivingUserId + " Launching permission review for package " + + receivingPackageName); + } + + mHandler.post(new Runnable() { + @Override + public void run() { + mService.mContext.startActivityAsUser(intent, new UserHandle(receivingUserId)); + } + }); + } else { + Slog.w(TAG, "u" + receivingUserId + " Receiving a broadcast in package" + + receivingPackageName + " requires a permissions review"); + } + + return false; + } + final void scheduleTempWhitelistLocked(int uid, long duration, BroadcastRecord r) { if (duration > Integer.MAX_VALUE) { duration = Integer.MAX_VALUE; @@ -1007,6 +1071,18 @@ public final class BroadcastQueue { } } + // If permissions need a review before any of the app components can run, we drop + // the broadcast and if the calling app is in the foreground and the broadcast is + // explicit we launch the review UI passing it a pending intent to send the skipped + // broadcast. + if (Build.PERMISSIONS_REVIEW_REQUIRED && !skip) { + if (!requestStartTargetPermissionsReviewIfNeededLocked(r, + info.activityInfo.packageName, UserHandle.getUserId( + info.activityInfo.applicationInfo.uid))) { + skip = true; + } + } + if (skip) { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Skipping delivery of ordered [" + mQueueName + "] " diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c7d1171cd4c2..358d6f80af9e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -26,6 +26,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; @@ -400,14 +401,11 @@ public class PackageManagerService extends IPackageManager.Stub { /** Permission grant: grant the permission as an install permission. */ private static final int GRANT_INSTALL = 2; - /** Permission grant: grant the permission as an install permission for a legacy app. */ - private static final int GRANT_INSTALL_LEGACY = 3; - /** Permission grant: grant the permission as a runtime one. */ - private static final int GRANT_RUNTIME = 4; + private static final int GRANT_RUNTIME = 3; /** Permission grant: grant as runtime a permission that was granted as an install time one. */ - private static final int GRANT_UPGRADE = 5; + private static final int GRANT_UPGRADE = 4; /** Canonical intent used to identify what counts as a "web browser" app */ private static final Intent sBrowserIntent; @@ -3688,6 +3686,16 @@ public class PackageManagerService extends IPackageManager.Stub { enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp); + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (Build.PERMISSIONS_REVIEW_REQUIRED + && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M + && bp.isRuntime()) { + return; + } + uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); sb = (SettingBase) pkg.mExtras; if (sb == null) { @@ -3788,6 +3796,16 @@ public class PackageManagerService extends IPackageManager.Stub { enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp); + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (Build.PERMISSIONS_REVIEW_REQUIRED + && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M + && bp.isRuntime()) { + return; + } + SettingBase sb = (SettingBase) pkg.mExtras; if (sb == null) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -3906,6 +3924,7 @@ public class PackageManagerService extends IPackageManager.Stub { flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; + flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; } synchronized (mPackages) { @@ -8641,6 +8660,8 @@ public class PackageManagerService extends IPackageManager.Stub { } final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + final boolean appSupportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion + >= Build.VERSION_CODES.M; switch (level) { case PermissionInfo.PROTECTION_NORMAL: { // For all apps normal permissions are install time ones. @@ -8648,9 +8669,13 @@ public class PackageManagerService extends IPackageManager.Stub { } break; case PermissionInfo.PROTECTION_DANGEROUS: { - if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) { + // If a permission review is required for legacy apps we represent + // their permissions as always granted runtime ones since we need + // to keep the review required permission flag per user while an + // install permission's state is shared across all users. + if (!appSupportsRuntimePermissions && !Build.PERMISSIONS_REVIEW_REQUIRED) { // For legacy apps dangerous permissions are install time ones. - grant = GRANT_INSTALL_LEGACY; + grant = GRANT_INSTALL; } else if (origPermissions.hasInstallPermission(bp.name)) { // For legacy apps that became modern, install becomes runtime. grant = GRANT_UPGRADE; @@ -8697,7 +8722,7 @@ public class PackageManagerService extends IPackageManager.Stub { switch (grant) { case GRANT_INSTALL: { // Revoke this as runtime permission to handle the case of - // a runtime permission being downgraded to an install one. + // a runtime permission being downgraded to an install one. Also in permission review mode we keep dangerous permissions for legacy apps for (int userId : UserManagerService.getInstance().getUserIds()) { if (origPermissions.getRuntimePermissionState( bp.name, userId) != null) { @@ -8717,20 +8742,12 @@ public class PackageManagerService extends IPackageManager.Stub { } } break; - case GRANT_INSTALL_LEGACY: { - // Grant an install permission. - if (permissionsState.grantInstallPermission(bp) != - PermissionsState.PERMISSION_OPERATION_FAILURE) { - changedInstallPermission = true; - } - } break; - case GRANT_RUNTIME: { // Grant previously granted runtime permissions. for (int userId : UserManagerService.getInstance().getUserIds()) { PermissionState permissionState = origPermissions .getRuntimePermissionState(bp.name, userId); - final int flags = permissionState != null + int flags = permissionState != null ? permissionState.getFlags() : 0; if (origPermissions.hasRuntimePermission(bp.name, userId)) { if (permissionsState.grantRuntimePermission(bp, userId) == @@ -8739,6 +8756,27 @@ public class PackageManagerService extends IPackageManager.Stub { changedRuntimePermissionUserIds = ArrayUtils.appendInt( changedRuntimePermissionUserIds, userId); } + // If the app supports runtime permissions no need for a review. + if (Build.PERMISSIONS_REVIEW_REQUIRED + && appSupportsRuntimePermissions + && (flags & PackageManager + .FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + flags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + // Since we changed the flags, we have to write. + changedRuntimePermissionUserIds = ArrayUtils.appendInt( + changedRuntimePermissionUserIds, userId); + } + } else if (Build.PERMISSIONS_REVIEW_REQUIRED + && !appSupportsRuntimePermissions) { + // For legacy apps that need a permission review, every new + // runtime permission is granted but it is pending a review. + if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { + permissionsState.grantRuntimePermission(bp, userId); + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + // We changed the permission and flags, hence have to write. + changedRuntimePermissionUserIds = ArrayUtils.appendInt( + changedRuntimePermissionUserIds, userId); + } } // Propagate the permission flags. permissionsState.updatePermissionFlags(bp, userId, flags, flags); @@ -13800,9 +13838,11 @@ public class PackageManagerService extends IPackageManager.Stub { return; } - final int userSettableFlags = FLAG_PERMISSION_USER_SET + // These are flags that can change base on user actions. + final int userSettableMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED - | FLAG_PERMISSION_REVOKE_ON_UPGRADE; + | FLAG_PERMISSION_REVOKE_ON_UPGRADE + | FLAG_PERMISSION_REVIEW_REQUIRED; final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED; @@ -13843,7 +13883,14 @@ public class PackageManagerService extends IPackageManager.Stub { // Always clear the user settable flags. final boolean hasInstallState = permissionsState.getInstallPermissionState( bp.name) != null; - if (permissionsState.updatePermissionFlags(bp, userId, userSettableFlags, 0)) { + // If permission review is enabled and this is a legacy app, mark the + // permission as requiring a review as this is the initial state. + int flags = 0; + if (Build.PERMISSIONS_REVIEW_REQUIRED + && ps.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + } + if (permissionsState.updatePermissionFlags(bp, userId, userSettableMask, flags)) { if (hasInstallState) { writeInstallPermissions = true; } else { @@ -13867,7 +13914,9 @@ public class PackageManagerService extends IPackageManager.Stub { != PERMISSION_OPERATION_FAILURE) { writeRuntimePermissions = true; } - } else { + // If permission review is enabled the permissions for a legacy apps + // are represented as constantly granted runtime ones, so don't revoke. + } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { // Otherwise, reset the permission. final int revokeResult = permissionsState.revokeRuntimePermission(bp, userId); switch (revokeResult) { @@ -16760,6 +16809,15 @@ public class PackageManagerService extends IPackageManager.Stub { void newUserCreated(final int userHandle) { mDefaultPermissionPolicy.grantDefaultPermissions(userHandle); + // If permission review for legacy apps is required, we represent + // dagerous permissions for such apps as always granted runtime + // permissions to keep per user flag state whether review is needed. + // Hence, if a new user is added we have to propagate dangerous + // permission grants for these legacy apps. + if (Build.PERMISSIONS_REVIEW_REQUIRED) { + updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL + | UPDATE_PERMISSIONS_REPLACE_ALL); + } } @Override @@ -17175,6 +17233,7 @@ public class PackageManagerService extends IPackageManager.Stub { packageName, userId); } } + @Override public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) { synchronized (mPackages) { @@ -17210,6 +17269,30 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + + @Override + public boolean isPermissionsReviewRequired(String packageName, int userId) { + synchronized (mPackages) { + // If we do not support permission review, done. + if (!Build.PERMISSIONS_REVIEW_REQUIRED) { + return false; + } + + PackageSetting packageSetting = mSettings.mPackages.get(packageName); + if (packageSetting == null) { + return false; + } + + // Permission review applies only to apps not supporting the new permission model. + if (packageSetting.pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) { + return false; + } + + // Legacy apps have the permission and get user consent on launch. + PermissionsState permissionsState = packageSetting.getPermissionsState(); + return permissionsState.isPermissionReviewRequired(userId); + } + } } @Override diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java index 57ef284f2758..007b73814256 100644 --- a/services/core/java/com/android/server/pm/PermissionsState.java +++ b/services/core/java/com/android/server/pm/PermissionsState.java @@ -16,11 +16,13 @@ package com.android.server.pm; +import android.content.pm.PackageManager; import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; +import android.util.SparseBooleanArray; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; @@ -64,6 +66,8 @@ public final class PermissionsState { private int[] mGlobalGids = NO_GIDS; + private SparseBooleanArray mPermissionReviewRequired; + public PermissionsState() { /* do nothing */ } @@ -116,6 +120,28 @@ public final class PermissionsState { mGlobalGids = Arrays.copyOf(other.mGlobalGids, other.mGlobalGids.length); } + + if (mPermissionReviewRequired != null) { + if (other.mPermissionReviewRequired == null) { + mPermissionReviewRequired = null; + } else { + mPermissionReviewRequired.clear(); + } + } + if (other.mPermissionReviewRequired != null) { + if (mPermissionReviewRequired == null) { + mPermissionReviewRequired = new SparseBooleanArray(); + } + final int userCount = other.mPermissionReviewRequired.size(); + for (int i = 0; i < userCount; i++) { + final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i); + mPermissionReviewRequired.put(i, reviewRequired); + } + } + } + + public boolean isPermissionReviewRequired(int userId) { + return mPermissionReviewRequired != null && mPermissionReviewRequired.get(userId); } /** @@ -357,7 +383,28 @@ public final class PermissionsState { permissionData = ensurePermissionData(permission); } - return permissionData.updateFlags(userId, flagMask, flagValues); + final int oldFlags = permissionData.getFlags(userId); + + final boolean updated = permissionData.updateFlags(userId, flagMask, flagValues); + if (updated) { + final int newFlags = permissionData.getFlags(userId); + if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0 + && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + if (mPermissionReviewRequired == null) { + mPermissionReviewRequired = new SparseBooleanArray(); + } + mPermissionReviewRequired.put(userId, true); + } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0 + && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { + if (mPermissionReviewRequired != null) { + mPermissionReviewRequired.delete(userId); + if (mPermissionReviewRequired.size() <= 0) { + mPermissionReviewRequired = null; + } + } + } + } + return updated; } public boolean updatePermissionFlagsForAllPermissions( @@ -430,6 +477,7 @@ public final class PermissionsState { public void reset() { mGlobalGids = NO_GIDS; mPermissions = null; + mPermissionReviewRequired = null; } private PermissionState getPermissionState(String name, int userId) { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 77a29f37d6ad..3301c49da578 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -257,7 +257,7 @@ class AppWindowToken extends WindowToken { if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { // In cases where there are multiple windows, we prefer the non-exiting window. This - // happens for example when when replacing windows during an activity relaunch. When + // happens for example when replacing windows during an activity relaunch. When // constructing the animation, we want the new window, not the exiting one. if (win.mExiting) { candidate = win; diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e9e09ec8c8a9..4bbf58638896 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -94,8 +94,9 @@ class DisplayContent { Region mNonResizeableRegion = new Region(); /** Save allocating when calculating rects */ - private Rect mTmpRect = new Rect(); - private Rect mTmpRect2 = new Rect(); + private final Rect mTmpRect = new Rect(); + private final Rect mTmpRect2 = new Rect(); + private final Region mTmpRegion = new Region(); /** For gathering Task objects in order. */ final ArrayList<Task> mTmpTaskHistory = new ArrayList<Task>(); @@ -403,6 +404,14 @@ class DisplayContent { if (addBackFocusedTask) { mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION); } + final WindowState inputMethod = mService.mInputMethodWindow; + if (inputMethod != null && inputMethod.isVisibleLw()) { + // If the input method is visible and the user is typing, we don't want these touch + // events to be intercepted and used to change focus. This would likely cause a + // disappearance of the input method. + inputMethod.getTouchableRegion(mTmpRegion); + mTouchExcludeRegion.op(mTmpRegion, Region.Op.UNION); + } if (mTapDetector != null) { mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index d7afdf4153c2..dbfd80d9732c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3855,10 +3855,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle); if (admin == null) { - try { - result.sendResult(null); - } catch (RemoteException e) { - } + result.sendResult(null); return; } Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); @@ -3868,10 +3865,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { null, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - try { - result.sendResult(getResultExtras(false)); - } catch (RemoteException e) { - } + result.sendResult(getResultExtras(false)); } }, null, Activity.RESULT_OK, null, null); } @@ -3891,39 +3885,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); - DevicePolicyData p = getUserData(userHandle); - validateQualityConstant(quality); - synchronized (this) { - if (p.mActivePasswordQuality != quality || p.mActivePasswordLength != length - || p.mFailedPasswordAttempts != 0 || p.mActivePasswordLetters != letters - || p.mActivePasswordUpperCase != uppercase - || p.mActivePasswordLowerCase != lowercase - || p.mActivePasswordNumeric != numbers - || p.mActivePasswordSymbols != symbols - || p.mActivePasswordNonLetter != nonletter) { - long ident = mInjector.binderClearCallingIdentity(); - try { - p.mActivePasswordQuality = quality; - p.mActivePasswordLength = length; - p.mActivePasswordLetters = letters; - p.mActivePasswordLowerCase = lowercase; - p.mActivePasswordUpperCase = uppercase; - p.mActivePasswordNumeric = numbers; - p.mActivePasswordSymbols = symbols; - p.mActivePasswordNonLetter = nonletter; - p.mFailedPasswordAttempts = 0; - saveSettingsLocked(userHandle); - updatePasswordExpirationsLocked(userHandle); - setExpirationAlarmCheckLocked(mContext, p); - sendAdminCommandToSelfAndProfilesLocked( - DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, - DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } + DevicePolicyData policy = getUserData(userHandle); + + long ident = mInjector.binderClearCallingIdentity(); + try { + synchronized (this) { + policy.mActivePasswordQuality = quality; + policy.mActivePasswordLength = length; + policy.mActivePasswordLetters = letters; + policy.mActivePasswordLowerCase = lowercase; + policy.mActivePasswordUpperCase = uppercase; + policy.mActivePasswordNumeric = numbers; + policy.mActivePasswordSymbols = symbols; + policy.mActivePasswordNonLetter = nonletter; + policy.mFailedPasswordAttempts = 0; + saveSettingsLocked(userHandle); + updatePasswordExpirationsLocked(userHandle); + setExpirationAlarmCheckLocked(mContext, policy); + sendAdminCommandToSelfAndProfilesLocked( + DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); } + } finally { + mInjector.binderRestoreCallingIdentity(ident); } } diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp index cb244eccfe21..641c34bd2dda 100644 --- a/tools/aapt/Package.cpp +++ b/tools/aapt/Package.cpp @@ -33,7 +33,7 @@ static const char* kNoCompressExt[] = { ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet", ".rtttl", ".imy", ".xmf", ".mp4", ".m4a", ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2", - ".amr", ".awb", ".wma", ".wmv" + ".amr", ".awb", ".wma", ".wmv", ".webm" }; /* fwd decls, so I can write this downward */ diff --git a/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java b/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java deleted file mode 100644 index 78aedc5a3102..000000000000 --- a/tools/layoutlib/bridge/src/android/animation/FakeAnimator.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.animation; - -/** - * A fake implementation of Animator which doesn't do anything. - */ -public class FakeAnimator extends Animator { - @Override - public long getStartDelay() { - return 0; - } - - @Override - public void setStartDelay(long startDelay) { - - } - - @Override - public Animator setDuration(long duration) { - return this; - } - - @Override - public long getDuration() { - return 0; - } - - @Override - public void setInterpolator(TimeInterpolator value) { - - } - - @Override - public boolean isRunning() { - return false; - } -} diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java index 4603b6362b0e..54021c9f988d 100644 --- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java +++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java @@ -16,9 +16,16 @@ package android.animation; +import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + /** * Delegate implementing the native methods of android.animation.PropertyValuesHolder * @@ -29,81 +36,161 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; * around to map int to instance of the delegate. * * The main goal of this class' methods are to provide a native way to access setters and getters - * on some object. In this case we want to default to using Java reflection instead so the native - * methods do nothing. + * on some object. We override these methods to use reflection since the original reflection + * implementation of the PropertyValuesHolder won't be able to access protected methods. * */ -/*package*/ class PropertyValuesHolder_Delegate { +/*package*/ +@SuppressWarnings("unused") +class PropertyValuesHolder_Delegate { + // This code is copied from android.animation.PropertyValuesHolder and must be kept in sync + // We try several different types when searching for appropriate setter/getter functions. + // The caller may have supplied values in a type that does not match the setter/getter + // functions (such as the integers 0 and 1 to represent floating point values for alpha). + // Also, the use of generics in constructors means that we end up with the Object versions + // of primitive types (Float vs. float). But most likely, the setter/getter functions + // will take primitive types instead. + // So we supply an ordered array of other types to try before giving up. + private static Class[] FLOAT_VARIANTS = {float.class, Float.class, double.class, int.class, + Double.class, Integer.class}; + private static Class[] INTEGER_VARIANTS = {int.class, Integer.class, float.class, double.class, + Float.class, Double.class}; + + private static final Object sMethodIndexLock = new Object(); + private static final Map<Long, Method> ID_TO_METHOD = new HashMap<Long, Method>(); + private static final Map<String, Long> METHOD_NAME_TO_ID = new HashMap<String, Long>(); + private static long sNextId = 1; + + private static long registerMethod(Class<?> targetClass, String methodName, Class[] types, + int nArgs) { + // Encode the number of arguments in the method name + String methodIndexName = String.format("%1$s#%2$d", methodName, nArgs); + synchronized (sMethodIndexLock) { + Long methodId = METHOD_NAME_TO_ID.get(methodIndexName); + + if (methodId != null) { + // The method was already registered + return methodId; + } + + Class[] args = new Class[nArgs]; + Method method = null; + for (Class typeVariant : types) { + for (int i = 0; i < nArgs; i++) { + args[i] = typeVariant; + } + try { + method = targetClass.getDeclaredMethod(methodName, args); + } catch (NoSuchMethodException ignore) { + } + } + + if (method != null) { + methodId = sNextId++; + ID_TO_METHOD.put(methodId, method); + METHOD_NAME_TO_ID.put(methodIndexName, methodId); + + return methodId; + } + } + + // Method not found + return 0; + } + + private static void callMethod(Object target, long methodID, Object... args) { + Method method = ID_TO_METHOD.get(methodID); + assert method != null; + + try { + method.setAccessible(true); + method.invoke(target, args); + } catch (IllegalAccessException e) { + Bridge.getLog().error(null, "Unable to update property during animation", e, null); + } catch (InvocationTargetException e) { + Bridge.getLog().error(null, "Unable to update property during animation", e, null); + } + } @LayoutlibDelegate /*package*/ static long nGetIntMethod(Class<?> targetClass, String methodName) { - // return 0 to force PropertyValuesHolder to use Java reflection. - return 0; + return nGetMultipleIntMethod(targetClass, methodName, 1); } @LayoutlibDelegate /*package*/ static long nGetFloatMethod(Class<?> targetClass, String methodName) { - // return 0 to force PropertyValuesHolder to use Java reflection. - return 0; + return nGetMultipleFloatMethod(targetClass, methodName, 1); } @LayoutlibDelegate /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName, int numParams) { - // TODO: return the right thing. - return 0; + return registerMethod(targetClass, methodName, INTEGER_VARIANTS, numParams); } @LayoutlibDelegate /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName, int numParams) { - // TODO: return the right thing. - return 0; + return registerMethod(targetClass, methodName, FLOAT_VARIANTS, numParams); } @LayoutlibDelegate /*package*/ static void nCallIntMethod(Object target, long methodID, int arg) { - // do nothing + callMethod(target, methodID, arg); } @LayoutlibDelegate /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) { - // do nothing + callMethod(target, methodID, arg); } @LayoutlibDelegate /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1, int arg2) { - // do nothing + callMethod(target, methodID, arg1, arg2); } @LayoutlibDelegate /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1, int arg2, int arg3, int arg4) { - // do nothing + callMethod(target, methodID, arg1, arg2, arg3, arg4); } @LayoutlibDelegate /*package*/ static void nCallMultipleIntMethod(Object target, long methodID, int[] args) { - // do nothing + assert args != null; + + // Box parameters + Object[] params = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + params[i] = args; + } + callMethod(target, methodID, params); } @LayoutlibDelegate /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1, float arg2) { - // do nothing + callMethod(target, methodID, arg1, arg2); } @LayoutlibDelegate /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1, float arg2, float arg3, float arg4) { - // do nothing + callMethod(target, methodID, arg1, arg2, arg3, arg4); } @LayoutlibDelegate /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID, float[] args) { - // do nothing + assert args != null; + + // Box parameters + Object[] params = new Object[args.length]; + for (int i = 0; i < args.length; i++) { + params[i] = args; + } + callMethod(target, methodID, params); } } diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java index dd2978f5c414..3c71233b1df5 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java @@ -44,7 +44,7 @@ public final class PathMeasure_Delegate { // ---- delegate data ---- // This governs how accurate the approximation of the Path is. - private static final float PRECISION = 0.002f; + private static final float PRECISION = 0.0002f; /** * Array containing the path points components. There are three components for each point: diff --git a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java index 5f0d98b35431..9677aaf5d07a 100644 --- a/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java +++ b/tools/layoutlib/bridge/src/android/os/SystemClock_Delegate.java @@ -18,6 +18,7 @@ package android.os; import com.android.layoutlib.bridge.impl.DelegateManager; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import com.android.tools.layoutlib.java.System_Delegate; /** * Delegate implementing the native methods of android.os.SystemClock @@ -30,9 +31,6 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; * */ public class SystemClock_Delegate { - private static long sBootTime = System.currentTimeMillis(); - private static long sBootTimeNano = System.nanoTime(); - /** * Returns milliseconds since boot, not counting time spent in deep sleep. * <b>Note:</b> This value may get reset occasionally (before it would @@ -42,7 +40,7 @@ public class SystemClock_Delegate { */ @LayoutlibDelegate /*package*/ static long uptimeMillis() { - return System.currentTimeMillis() - sBootTime; + return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis(); } /** @@ -52,7 +50,7 @@ public class SystemClock_Delegate { */ @LayoutlibDelegate /*package*/ static long elapsedRealtime() { - return System.currentTimeMillis() - sBootTime; + return System_Delegate.currentTimeMillis() - System_Delegate.bootTimeMillis(); } /** @@ -62,7 +60,7 @@ public class SystemClock_Delegate { */ @LayoutlibDelegate /*package*/ static long elapsedRealtimeNanos() { - return System.nanoTime() - sBootTimeNano; + return System_Delegate.nanoTime() - System_Delegate.bootTime(); } /** @@ -72,7 +70,7 @@ public class SystemClock_Delegate { */ @LayoutlibDelegate /*package*/ static long currentThreadTimeMillis() { - return System.currentTimeMillis(); + return System_Delegate.currentTimeMillis(); } /** @@ -84,7 +82,7 @@ public class SystemClock_Delegate { */ @LayoutlibDelegate /*package*/ static long currentThreadTimeMicro() { - return System.currentTimeMillis() * 1000; + return System_Delegate.currentTimeMillis() * 1000; } /** diff --git a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java index f75ee5030674..01af669e39d3 100644 --- a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java @@ -17,6 +17,8 @@ package android.view; import com.android.tools.layoutlib.annotations.LayoutlibDelegate; +import java.util.concurrent.atomic.AtomicReference; + /** * Delegate used to provide new implementation of a select few methods of {@link Choreographer} * @@ -25,9 +27,41 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; * */ public class Choreographer_Delegate { + static final AtomicReference<Choreographer> mInstance = new AtomicReference<Choreographer>(); + + @LayoutlibDelegate + public static Choreographer getInstance() { + if (mInstance.get() == null) { + mInstance.compareAndSet(null, Choreographer.getInstance_Original()); + } + + return mInstance.get(); + } @LayoutlibDelegate public static float getRefreshRate() { return 60.f; } + + @LayoutlibDelegate + static void scheduleVsyncLocked(Choreographer thisChoreographer) { + // do nothing + } + + public static void doFrame(long frameTimeNanos) { + Choreographer thisChoreographer = Choreographer.getInstance(); + + thisChoreographer.mLastFrameTimeNanos = frameTimeNanos; + + thisChoreographer.mFrameInfo.markInputHandlingStart(); + thisChoreographer.doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); + + thisChoreographer.mFrameInfo.markAnimationsStart(); + thisChoreographer.doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); + + thisChoreographer.mFrameInfo.markPerformTraversalsStart(); + thisChoreographer.doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); + + thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 48ca7d8d5fb6..683c4aabf6d9 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -183,7 +183,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { */ private static LayoutLog sCurrentLog = sDefaultLog; - private static final int LAST_SUPPORTED_FEATURE = Features.RECYCLER_VIEW_ADAPTER; + private static final int LAST_SUPPORTED_FEATURE = Features.CHOREOGRAPHER; @Override public int getApiLevel() { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java index feb25905390c..2ac212c312c0 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java @@ -23,6 +23,7 @@ import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; import com.android.ide.common.rendering.api.ViewInfo; import com.android.layoutlib.bridge.impl.RenderSessionImpl; +import com.android.tools.layoutlib.java.System_Delegate; import android.view.View; import android.view.ViewGroup; @@ -191,6 +192,21 @@ public class BridgeRenderSession extends RenderSession { } @Override + public void setSystemTimeNanos(long nanos) { + System_Delegate.setNanosTime(nanos); + } + + @Override + public void setSystemBootTimeNanos(long nanos) { + System_Delegate.setBootTimeNanos(nanos); + } + + @Override + public void setElapsedFrameTimeNanos(long nanos) { + mSession.setElapsedFrameTimeNanos(nanos); + } + + @Override public void dispose() { } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index 0ffa35733180..ec50cfe55651 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -46,6 +46,7 @@ import com.android.layoutlib.bridge.android.support.DesignLibUtil; import com.android.layoutlib.bridge.impl.binding.FakeAdapter; import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter; import com.android.resources.ResourceType; +import com.android.tools.layoutlib.java.System_Delegate; import com.android.util.Pair; import android.animation.AnimationThread; @@ -62,6 +63,7 @@ import android.graphics.Canvas; import android.preference.Preference_Delegate; import android.view.AttachInfo_Accessor; import android.view.BridgeInflater; +import android.view.Choreographer_Delegate; import android.view.IWindowManager; import android.view.IWindowManagerImpl; import android.view.Surface; @@ -120,6 +122,10 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { private int mMeasuredScreenWidth = -1; private int mMeasuredScreenHeight = -1; private boolean mIsAlphaChannelImage; + /** If >= 0, a frame will be executed */ + private long mElapsedFrameTimeNanos = -1; + /** True if one frame has been already executed to start the animations */ + private boolean mFirstFrameExecuted = false; // information being returned through the API private BufferedImage mImage; @@ -252,6 +258,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } /** + * Sets the time for which the next frame will be selected. The time is the elapsed time from + * the current system nanos time. You + */ + public void setElapsedFrameTimeNanos(long nanos) { + mElapsedFrameTimeNanos = nanos; + } + + /** * Renders the scene. * <p> * {@link #acquire(long)} must have been called before this. @@ -428,6 +442,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { gc.dispose(); } + if (mElapsedFrameTimeNanos >= 0) { + long initialTime = System_Delegate.nanoTime(); + if (!mFirstFrameExecuted) { + // The first frame will initialize the animations + Choreographer_Delegate.doFrame(initialTime); + mFirstFrameExecuted = true; + } + // Second frame will move the animations + Choreographer_Delegate.doFrame(initialTime + mElapsedFrameTimeNanos); + } mViewRoot.draw(mCanvas); } diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png Binary files differnew file mode 100644 index 000000000000..9f266278c352 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png Binary files differnew file mode 100644 index 000000000000..89009be843e7 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml new file mode 100644 index 000000000000..70d739692e29 --- /dev/null +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/indeterminate_progressbar.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:padding="16dp" + android:orientation="horizontal" + android:layout_width="fill_parent" + android:layout_height="fill_parent"> + + <ProgressBar + android:layout_height="fill_parent" + android:layout_width="fill_parent" /> + +</LinearLayout> + diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index 9ebeebd49c82..2dca07cc4faf 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -48,6 +48,8 @@ import java.io.IOException; import java.net.URL; import java.util.Arrays; import java.util.Comparator; +import java.util.Map; +import java.util.concurrent.TimeUnit; import static org.junit.Assert.fail; @@ -348,16 +350,46 @@ public class Main { renderAndVerify(params, "expand_horz_layout.png"); } + /** Test expand_layout.xml */ + @Test + public void testVectorAnimation() throws ClassNotFoundException { + // Create the layout pull parser. + LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + + "indeterminate_progressbar.xml"); + // Create LayoutLibCallback. + LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger()); + layoutLibCallback.initResources(); + + SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.V_SCROLL, 22); + + renderAndVerify(params, "animated_vector.png", TimeUnit.SECONDS.toNanos(2)); + + parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + + "indeterminate_progressbar.xml"); + params = getSessionParams(parser, ConfigGenerator.NEXUS_5, + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.V_SCROLL, 22); + renderAndVerify(params, "animated_vector_1.png", TimeUnit.SECONDS.toNanos(3)); + } + /** * Create a new rendering session and test that rendering given layout on nexus 5 * doesn't throw any exceptions and matches the provided image. + * <p/>If frameTimeNanos is >= 0 a frame will be executed during the rendering. The time + * indicates how far in the future is. */ - private void renderAndVerify(SessionParams params, String goldenFileName) + private void renderAndVerify(SessionParams params, String goldenFileName, long frameTimeNanos) throws ClassNotFoundException { // TODO: Set up action bar handler properly to test menu rendering. // Create session params. RenderSession session = sBridge.createSession(params); + if (frameTimeNanos != -1) { + session.setElapsedFrameTimeNanos(frameTimeNanos); + } + if (!session.getResult().isSuccess()) { getLogger().error(session.getResult().getException(), session.getResult().getErrorMessage()); @@ -380,6 +412,15 @@ public class Main { * Create a new rendering session and test that rendering given layout on nexus 5 * doesn't throw any exceptions and matches the provided image. */ + private void renderAndVerify(SessionParams params, String goldenFileName) + throws ClassNotFoundException { + renderAndVerify(params, goldenFileName, -1); + } + + /** + * Create a new rendering session and test that rendering given layout on nexus 5 + * doesn't throw any exceptions and matches the provided image. + */ private void renderAndVerify(String layoutFileName, String goldenFileName) throws ClassNotFoundException { // Create the layout pull parser. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java index f6c2626e4271..8f0ad01c6dc3 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java @@ -77,6 +77,8 @@ public class AsmGenerator { /** Methods to inject. FQCN of class in which method should be injected => runnable that does * the injection. */ private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap; + /** A map { FQCN => set { field names } } which should be promoted to public visibility */ + private final Map<String, Set<String>> mPromotedFields; /** * Creates a new generator that can generate the output JAR with the stubbed classes. @@ -109,20 +111,8 @@ public class AsmGenerator { // Create the map/set of methods to change to delegates mDelegateMethods = new HashMap<String, Set<String>>(); - for (String signature : createInfo.getDelegateMethods()) { - int pos = signature.indexOf('#'); - if (pos <= 0 || pos >= signature.length() - 1) { - continue; - } - String className = binaryToInternalClassName(signature.substring(0, pos)); - String methodName = signature.substring(pos + 1); - Set<String> methods = mDelegateMethods.get(className); - if (methods == null) { - methods = new HashSet<String>(); - mDelegateMethods.put(className, methods); - } - methods.add(methodName); - } + addToMap(createInfo.getDelegateMethods(), mDelegateMethods); + for (String className : createInfo.getDelegateClassNatives()) { className = binaryToInternalClassName(className); Set<String> methods = mDelegateMethods.get(className); @@ -187,10 +177,34 @@ public class AsmGenerator { returnTypes.add(binaryToInternalClassName(className)); } + mPromotedFields = new HashMap<String, Set<String>>(); + addToMap(createInfo.getPromotedFields(), mPromotedFields); + mInjectedMethodsMap = createInfo.getInjectedMethodsMap(); } /** + * For each value in the array, split the value on '#' and add the parts to the map as key + * and value. + */ + private void addToMap(String[] entries, Map<String, Set<String>> map) { + for (String entry : entries) { + int pos = entry.indexOf('#'); + if (pos <= 0 || pos >= entry.length() - 1) { + return; + } + String className = binaryToInternalClassName(entry.substring(0, pos)); + String methodOrFieldName = entry.substring(pos + 1); + Set<String> set = map.get(className); + if (set == null) { + set = new HashSet<String>(); + map.put(className, set); + } + set.add(methodOrFieldName); + } + } + + /** * Returns the list of classes that have not been renamed yet. * <p/> * The names are "internal class names" rather than FQCN, i.e. they use "/" instead "." @@ -380,6 +394,10 @@ public class AsmGenerator { } } + Set<String> promoteFields = mPromotedFields.get(className); + if (promoteFields != null && !promoteFields.isEmpty()) { + cv = new PromoteFieldClassAdapter(cv, promoteFields); + } cr.accept(cv, 0); return cw.toByteArray(); } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index e480eadc0cf6..b571c5a486e9 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -120,6 +120,11 @@ public final class CreateInfo implements ICreateInfo { } @Override + public String[] getPromotedFields() { + return PROMOTED_FIELDS; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return INJECTED_METHODS; } @@ -169,7 +174,9 @@ public final class CreateInfo implements ICreateInfo { "android.text.format.DateFormat#is24HourFormat", "android.text.Hyphenator#getSystemHyphenatorLocation", "android.util.Xml#newPullParser", + "android.view.Choreographer#getInstance", "android.view.Choreographer#getRefreshRate", + "android.view.Choreographer#scheduleVsyncLocked", "android.view.Display#updateDisplayInfoLocked", "android.view.Display#getWindowManager", "android.view.LayoutInflater#rInflate", @@ -290,6 +297,10 @@ public final class CreateInfo implements ICreateInfo { "org.kxml2.io.KXmlParser" }; + private final static String[] PROMOTED_FIELDS = new String[] { + "android.view.Choreographer#mLastFrameTimeNanos" + }; + /** * List of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java index 54b1fe628769..6c62423a2a89 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java @@ -78,6 +78,13 @@ public interface ICreateInfo { Set<String> getExcludedClasses(); /** + * Returns a list of fields which should be promoted to public visibility. The array values + * are in the form of the binary FQCN of the class containing the field and the field name + * separated by a '#'. + */ + String[] getPromotedFields(); + + /** * Returns a map from binary FQCN className to {@link InjectMethodRunnable} which will be * called to inject methods into a class. * Can be empty but must not be null. diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java new file mode 100644 index 000000000000..e4b70da2504f --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/PromoteFieldClassAdapter.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; + +import java.util.Set; + +import static org.objectweb.asm.Opcodes.ACC_PRIVATE; +import static org.objectweb.asm.Opcodes.ACC_PROTECTED; +import static org.objectweb.asm.Opcodes.ACC_PUBLIC; +import static org.objectweb.asm.Opcodes.ASM4; + +/** + * Promotes given fields to public visibility. + */ +public class PromoteFieldClassAdapter extends ClassVisitor { + + private final Set<String> mFieldNames; + private static final int ACC_NOT_PUBLIC = ~(ACC_PRIVATE | ACC_PROTECTED); + + public PromoteFieldClassAdapter(ClassVisitor cv, Set<String> fieldNames) { + super(ASM4, cv); + mFieldNames = fieldNames; + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, + Object value) { + if (mFieldNames.contains(name)) { + if ((access & ACC_PUBLIC) == 0) { + access = (access & ACC_NOT_PUBLIC) | ACC_PUBLIC; + } + } + return super.visitField(access, name, desc, signature, value); + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java index 0b85c48b4e68..5e47261ea89c 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java @@ -134,7 +134,33 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor { } }); - // Case 5: java.util.LinkedHashMap.eldest() + // Case 5: java.lang.System time calls + METHOD_REPLACERS.add(new MethodReplacer() { + @Override + public boolean isNeeded(String owner, String name, String desc, String sourceClass) { + return JAVA_LANG_SYSTEM.equals(owner) && name.equals("nanoTime"); + } + + @Override + public void replace(MethodInformation mi) { + mi.name = "nanoTime"; + mi.owner = Type.getInternalName(System_Delegate.class); + } + }); + METHOD_REPLACERS.add(new MethodReplacer() { + @Override + public boolean isNeeded(String owner, String name, String desc, String sourceClass) { + return JAVA_LANG_SYSTEM.equals(owner) && name.equals("currentTimeMillis"); + } + + @Override + public void replace(MethodInformation mi) { + mi.name = "currentTimeMillis"; + mi.owner = Type.getInternalName(System_Delegate.class); + } + }); + + // Case 6: java.util.LinkedHashMap.eldest() METHOD_REPLACERS.add(new MethodReplacer() { private final String VOID_TO_MAP_ENTRY = @@ -157,7 +183,7 @@ public class ReplaceMethodCallsAdapter extends ClassVisitor { } }); - // Case 6: android.content.Context.getClassLoader() in LayoutInflater + // Case 7: android.content.Context.getClassLoader() in LayoutInflater METHOD_REPLACERS.add(new MethodReplacer() { // When LayoutInflater asks for a class loader, we must return the class loader that // cannot return app's custom views/classes. This is so that in case of any failure diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java index 613c8d96b862..be937445c33d 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/java/System_Delegate.java @@ -18,12 +18,22 @@ package com.android.tools.layoutlib.java; import com.android.tools.layoutlib.create.ReplaceMethodCallsAdapter; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + /** * Provides dummy implementation of methods that don't exist on the host VM. + * This also providers a time control that allows to set a specific system time. * * @see ReplaceMethodCallsAdapter */ +@SuppressWarnings("unused") public class System_Delegate { + // Current system time + private static AtomicLong mNanosTime = new AtomicLong(System.nanoTime()); + // Time that the system booted up in nanos + private static AtomicLong mBootNanosTime = new AtomicLong(System.nanoTime()); + public static void log(String message) { // ignore. } @@ -31,4 +41,28 @@ public class System_Delegate { public static void log(String message, Throwable th) { // ignore. } + + public static void setNanosTime(long nanos) { + mNanosTime.set(nanos); + } + + public static void setBootTimeNanos(long nanos) { + mBootNanosTime.set(nanos); + } + + public static long nanoTime() { + return mNanosTime.get(); + } + + public static long currentTimeMillis() { + return TimeUnit.NANOSECONDS.toMillis(mNanosTime.get()); + } + + public static long bootTime() { + return mBootNanosTime.get(); + } + + public static long bootTimeMillis() { + return TimeUnit.NANOSECONDS.toMillis(mBootNanosTime.get()); + } } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java index 2c21470d6a2f..8a2235b8526c 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java @@ -138,6 +138,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return new HashMap<String, InjectMethodRunnable>(0); } @@ -213,6 +218,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return new HashMap<String, InjectMethodRunnable>(0); } @@ -296,6 +306,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { return new HashMap<String, InjectMethodRunnable>(0); } @@ -374,6 +389,11 @@ public class AsmGeneratorTest { } @Override + public String[] getPromotedFields() { + return new String[0]; + } + + @Override public Map<String, InjectMethodRunnable> getInjectedMethodsMap() { HashMap<String, InjectMethodRunnable> map = new HashMap<String, InjectMethodRunnable>(1); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index bd030e8e5f32..b054f7c8d7a2 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -665,7 +665,7 @@ public class WifiManager { * </ul> * @return a list of network configurations in the form of a list * of {@link WifiConfiguration} objects. Upon failure to fetch or - * when when Wi-Fi is turned off, it can be null. + * when Wi-Fi is turned off, it can be null. */ public List<WifiConfiguration> getConfiguredNetworks() { try { |