diff options
85 files changed, 2164 insertions, 784 deletions
diff --git a/api/current.txt b/api/current.txt index acc9c4b11024..b1e80dd2a672 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8881,6 +8881,7 @@ package android.content.pm { field public int configChanges; field public int documentLaunchMode; field public int flags; + field public android.content.pm.ActivityInfo.InitialLayout initialLayout; field public int launchMode; field public int maxRecents; field public java.lang.String parentActivityName; @@ -8894,6 +8895,15 @@ package android.content.pm { field public int uiOptions; } + public static final class ActivityInfo.InitialLayout { + ctor public ActivityInfo.InitialLayout(int, float, int, float, int); + field public final int gravity; + field public final int height; + field public final float heightFraction; + field public final int width; + field public final float widthFraction; + } + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { ctor public ApplicationInfo(); ctor public ApplicationInfo(android.content.pm.ApplicationInfo); @@ -18136,7 +18146,7 @@ package android.mtp { method public final int getThumbPixWidth(); } - public class MtpObjectInfo.Builder { + public static class MtpObjectInfo.Builder { ctor public MtpObjectInfo.Builder(); ctor public MtpObjectInfo.Builder(android.mtp.MtpObjectInfo); method public android.mtp.MtpObjectInfo build(); @@ -32512,7 +32522,7 @@ package android.text { method public static int getLayoutDirectionFromLocale(java.util.Locale); method public static int getOffsetAfter(java.lang.CharSequence, int); method public static int getOffsetBefore(java.lang.CharSequence, int); - method public static java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); + method public static deprecated java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); method public static int getTrimmedLength(java.lang.CharSequence); method public static java.lang.String htmlEncode(java.lang.String); method public static int indexOf(java.lang.CharSequence, char); diff --git a/api/system-current.txt b/api/system-current.txt index 955fb07e20a7..fe7c6dac610e 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -9139,6 +9139,7 @@ package android.content.pm { field public int configChanges; field public int documentLaunchMode; field public int flags; + field public android.content.pm.ActivityInfo.InitialLayout initialLayout; field public int launchMode; field public int maxRecents; field public java.lang.String parentActivityName; @@ -9152,6 +9153,15 @@ package android.content.pm { field public int uiOptions; } + public static final class ActivityInfo.InitialLayout { + ctor public ActivityInfo.InitialLayout(int, float, int, float, int); + field public final int gravity; + field public final int height; + field public final float heightFraction; + field public final int width; + field public final float widthFraction; + } + public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { ctor public ApplicationInfo(); ctor public ApplicationInfo(android.content.pm.ApplicationInfo); @@ -19649,7 +19659,7 @@ package android.mtp { method public final int getThumbPixWidth(); } - public class MtpObjectInfo.Builder { + public static class MtpObjectInfo.Builder { ctor public MtpObjectInfo.Builder(); ctor public MtpObjectInfo.Builder(android.mtp.MtpObjectInfo); method public android.mtp.MtpObjectInfo build(); @@ -34845,7 +34855,7 @@ package android.text { method public static int getLayoutDirectionFromLocale(java.util.Locale); method public static int getOffsetAfter(java.lang.CharSequence, int); method public static int getOffsetBefore(java.lang.CharSequence, int); - method public static java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); + method public static deprecated java.lang.CharSequence getReverse(java.lang.CharSequence, int, int); method public static int getTrimmedLength(java.lang.CharSequence); method public static java.lang.String htmlEncode(java.lang.String); method public static int indexOf(java.lang.CharSequence, char); diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 2ee586f2c3ca..7c8842ca0231 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -27,6 +27,8 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= bootanimation +LOCAL_INIT_RC := bootanim.rc + ifdef TARGET_32_BIT_SURFACEFLINGER LOCAL_32_BIT_ONLY := true endif diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc new file mode 100644 index 000000000000..ee0d0b8c042f --- /dev/null +++ b/cmds/bootanimation/bootanim.rc @@ -0,0 +1,6 @@ +service bootanim /system/bin/bootanimation + class core + user graphics + group graphics audio + disabled + oneshot diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index ab049ecbed95..0473475c1a77 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -456,7 +456,7 @@ public class RestrictionEntry implements Parcelable { if (o == this) return true; if (!(o instanceof RestrictionEntry)) return false; final RestrictionEntry other = (RestrictionEntry) o; - if (mType != other.mType || mKey.equals(other.mKey)) { + if (mType != other.mType || !mKey.equals(other.mKey)) { return false; } if (mCurrentValues == null && other.mCurrentValues == null diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 43cc63b4a3c8..876fbf55765c 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -698,6 +698,8 @@ public class ActivityInfo extends ComponentInfo */ public int lockTaskLaunchMode; + public InitialLayout initialLayout; + public ActivityInfo() { } @@ -763,9 +765,14 @@ public class ActivityInfo extends ComponentInfo } pw.println(prefix + "resizeable=" + resizeable + " lockTaskLaunchMode=" + lockTaskLaunchModeToString(lockTaskLaunchMode)); + if (initialLayout != null) { + pw.println(prefix + "initialLayout=" + initialLayout.width + "|" + + initialLayout.widthFraction + ", " + initialLayout.height + "|" + + initialLayout.heightFraction + ", " + initialLayout.gravity); + } super.dumpBack(pw, prefix); } - + public String toString() { return "ActivityInfo{" + Integer.toHexString(System.identityHashCode(this)) @@ -793,6 +800,16 @@ public class ActivityInfo extends ComponentInfo dest.writeInt(maxRecents); dest.writeInt(resizeable ? 1 : 0); dest.writeInt(lockTaskLaunchMode); + if (initialLayout != null) { + dest.writeInt(1); + dest.writeInt(initialLayout.width); + dest.writeFloat(initialLayout.widthFraction); + dest.writeInt(initialLayout.height); + dest.writeFloat(initialLayout.heightFraction); + dest.writeInt(initialLayout.gravity); + } else { + dest.writeInt(0); + } } public static final Parcelable.Creator<ActivityInfo> CREATOR @@ -822,5 +839,33 @@ public class ActivityInfo extends ComponentInfo maxRecents = source.readInt(); resizeable = (source.readInt() == 1); lockTaskLaunchMode = source.readInt(); + if (source.readInt() == 1) { + initialLayout = new InitialLayout(source); + } + } + + public static final class InitialLayout { + public InitialLayout(int width, float widthFraction, int height, float heightFraction, + int gravity) { + this.width = width; + this.widthFraction = widthFraction; + this.height = height; + this.heightFraction = heightFraction; + this.gravity = gravity; + } + + InitialLayout(Parcel source) { + width = source.readInt(); + widthFraction = source.readFloat(); + height = source.readInt(); + heightFraction = source.readFloat(); + gravity = source.readInt(); + } + + public final int width; + public final float widthFraction; + public final int height; + public final float heightFraction; + public final int gravity; } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 924df1b39cb8..6443667ab1db 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -50,6 +50,7 @@ import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.TypedValue; +import android.view.Gravity; import com.android.internal.R; import com.android.internal.util.ArrayUtils; @@ -3260,10 +3261,12 @@ public class PackageParser { owner.preferredActivityFilters.add(intent); } } else if (parser.getName().equals("meta-data")) { - if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData, + if ((a.metaData = parseMetaData(res, parser, attrs, a.metaData, outError)) == null) { return null; } + } else if (!receiver && parser.getName().equals("initial-layout")) { + parseInitialLayout(res, attrs, a); } else { if (!RIGID_PARSER) { Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":"); @@ -3296,6 +3299,43 @@ public class PackageParser { return a; } + private void parseInitialLayout(Resources res, AttributeSet attrs, Activity a) { + TypedArray sw = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestInitialLayout); + int width = -1; + float widthFraction = -1f; + int height = -1; + float heightFraction = -1f; + final int widthType = sw.getType( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_width); + if (widthType == TypedValue.TYPE_FRACTION) { + widthFraction = sw.getFraction( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_width, + 1, 1, -1); + } else if (widthType == TypedValue.TYPE_DIMENSION) { + width = sw.getDimensionPixelSize( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_width, + -1); + } + final int heightType = sw.getType( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_height); + if (heightType == TypedValue.TYPE_FRACTION) { + heightFraction = sw.getFraction( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_height, + 1, 1, -1); + } else if (heightType == TypedValue.TYPE_DIMENSION) { + height = sw.getDimensionPixelSize( + com.android.internal.R.styleable.AndroidManifestInitialLayout_activity_height, + -1); + } + int gravity = sw.getInt( + com.android.internal.R.styleable.AndroidManifestInitialLayout_gravity, + Gravity.CENTER); + sw.recycle(); + a.info.initialLayout = new ActivityInfo.InitialLayout(width, widthFraction, + height, heightFraction, gravity); + } + private Activity parseActivityAlias(Package owner, Resources res, XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, IOException { diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 4d8a7d00ba89..49cb8e2c0e72 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -462,7 +462,7 @@ public class TextUtils { /** * Returns the length that the specified CharSequence would have if - * spaces and control characters were trimmed from the start and end, + * spaces and ASCII control characters were trimmed from the start and end, * as by {@link String#trim}. */ public static int getTrimmedLength(CharSequence s) { @@ -505,7 +505,13 @@ public class TextUtils { return false; } - // XXX currently this only reverses chars, not spans + /* + * @deprecated + * Do not use. This function only reverses individual {@code char}s and not their associated + * spans. It doesn't support surrogate pairs (that correspond to non-BMP code points), + * combining sequences or conjuncts either. + */ + @Deprecated public static CharSequence getReverse(CharSequence source, int start, int end) { return new Reverser(source, start, end); @@ -1470,8 +1476,9 @@ public class TextUtils { */ public static boolean isDigitsOnly(CharSequence str) { final int len = str.length(); - for (int i = 0; i < len; i++) { - if (!Character.isDigit(str.charAt(i))) { + for (int cp, i = 0; i < len; i += Character.charCount(cp)) { + cp = Character.codePointAt(str, i); + if (!Character.isDigit(cp)) { return false; } } diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index a53da0955f0c..9f5dfa695174 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -98,7 +98,8 @@ public final class Formatter { /** {@hide} */ public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) { - float result = sizeBytes; + final boolean isNegative = (sizeBytes < 0); + float result = isNegative ? -sizeBytes : sizeBytes; int suffix = com.android.internal.R.string.byteShort; long mult = 1; if (result > 900) { @@ -154,9 +155,13 @@ public final class Formatter { roundFormat = "%.2f"; } } + + if (isNegative) { + result = -result; + } final String roundedString = String.format(roundFormat, result); - // Note this might overflow if result >= Long.MAX_VALUE / 100, but that's like 80PB so + // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so // it's okay (for now)... final long roundedBytes = (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0 diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java index 948cec71d819..080ed9ae1841 100644 --- a/core/java/android/view/DisplayListCanvas.java +++ b/core/java/android/view/DisplayListCanvas.java @@ -20,12 +20,7 @@ import android.annotation.NonNull; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.CanvasProperty; -import android.graphics.NinePatch; import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Picture; -import android.graphics.Rect; -import android.graphics.RectF; import android.util.Pools.SynchronizedPool; /** @@ -206,16 +201,12 @@ public class DisplayListCanvas extends Canvas { * Draws the specified layer onto this canvas. * * @param layer The layer to composite on this canvas - * @param x The left coordinate of the layer - * @param y The top coordinate of the layer - * @param paint The paint used to draw the layer */ - void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) { - layer.setLayerPaint(paint); - nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle(), x, y); + void drawHardwareLayer(HardwareLayer layer) { + nDrawLayer(mNativeCanvasWrapper, layer.getLayerHandle()); } - private static native void nDrawLayer(long renderer, long layer, float x, float y); + private static native void nDrawLayer(long renderer, long layer); /////////////////////////////////////////////////////////////////////////// // Drawing diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index 65ae8a649241..692ca7ba99cf 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -150,8 +150,4 @@ final class HardwareLayer { private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface, boolean isAlreadyAttached); private static native void nUpdateSurfaceTexture(long layerUpdater); - private static native void nUpdateRenderLayer(long layerUpdater, long displayList, - int left, int top, int right, int bottom); - - private static native int nGetTexName(long layerUpdater); } diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 197ea09e2d60..6b60be9c91b5 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -319,11 +319,25 @@ public class TextureView extends View { */ @Override public final void draw(Canvas canvas) { - // NOTE: Maintain this carefully (see View.java) + // NOTE: Maintain this carefully (see View#draw) mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN; - applyUpdate(); - applyTransformMatrix(); + /* Simplify drawing to guarantee the layer is the only thing drawn - so e.g. no background, + scrolling, or fading edges. This guarantees all drawing is in the layer, so drawing + properties (alpha, layer paint) affect all of the content of a TextureView. */ + + if (canvas.isHardwareAccelerated()) { + DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas; + + HardwareLayer layer = getHardwareLayer(); + if (layer != null) { + applyUpdate(); + applyTransformMatrix(); + + mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date + displayListCanvas.drawHardwareLayer(layer); + } + } } /** @@ -359,12 +373,7 @@ public class TextureView extends View { invalidate(true); } - @Override HardwareLayer getHardwareLayer() { - // NOTE: Maintain these two lines very carefully (see View.java) - mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; - mPrivateFlags &= ~PFLAG_DIRTY_MASK; - if (mLayer == null) { if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { return null; @@ -402,9 +411,6 @@ public class TextureView extends View { mSurface.setDefaultBufferSize(getWidth(), getHeight()); } - applyUpdate(); - applyTransformMatrix(); - return mLayer; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a4925184f181..b17f88fc7497 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15002,16 +15002,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * If this View draws with a HardwareLayer, returns it. - * Otherwise returns null - * - * TODO: Only TextureView uses this, can we eliminate it? - */ - HardwareLayer getHardwareLayer() { - return null; - } - - /** * Destroys all hardware rendering resources. This method is invoked * when the system needs to reclaim resources. Upon execution of this * method, you should free any OpenGL resources created by the view. @@ -15161,10 +15151,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, canvas.setHighContrastText(mAttachInfo.mHighContrastText); try { - final HardwareLayer layer = getHardwareLayer(); - if (layer != null && layer.isValid()) { - canvas.drawHardwareLayer(layer, 0, 0, mLayerPaint); - } else if (layerType == LAYER_TYPE_SOFTWARE) { + if (layerType == LAYER_TYPE_SOFTWARE) { buildDrawingCache(true); Bitmap cache = getDrawingCache(true); if (cache != null) { diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index be26c2479d92..55e23b14b7d0 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -20,6 +20,7 @@ import static android.app.ActivityManager.FIRST_DYNAMIC_STACK_ID; import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; +import static android.app.ActivityManager.INVALID_STACK_ID; import static android.view.View.MeasureSpec.AT_MOST; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.getMode; @@ -5250,7 +5251,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @return Returns the workspace stack id which contains this window. **/ private int getWorkspaceId() { - int workspaceId = FULLSCREEN_WORKSPACE_STACK_ID; + int workspaceId = INVALID_STACK_ID; WindowControllerCallback callback = getWindowControllerCallback(); if (callback != null) { try { @@ -5259,6 +5260,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow."); } } + if (workspaceId == INVALID_STACK_ID) { + return FULLSCREEN_WORKSPACE_STACK_ID; + } return workspaceId; } diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp index f7b5dc24cea1..dbd7c89805ab 100644 --- a/core/jni/android/graphics/Path.cpp +++ b/core/jni/android/graphics/Path.cpp @@ -519,6 +519,9 @@ static JNINativeMethod methods[] = { int register_android_graphics_Path(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/graphics/Path", methods, NELEM(methods)); + + static_assert(0 == SkPath::kCW_Direction, "direction_mismatch"); + static_assert(1 == SkPath::kCCW_Direction, "direction_mismatch"); } } diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp index 3833325631c7..9c1d6b17608b 100644 --- a/core/jni/android_view_DisplayListCanvas.cpp +++ b/core/jni/android_view_DisplayListCanvas.cpp @@ -139,10 +139,10 @@ static void android_view_DisplayListCanvas_drawRenderNode(JNIEnv* env, // ---------------------------------------------------------------------------- static void android_view_DisplayListCanvas_drawLayer(JNIEnv* env, jobject clazz, - jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) { + jlong rendererPtr, jlong layerPtr) { DisplayListCanvas* renderer = reinterpret_cast<DisplayListCanvas*>(rendererPtr); DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); - renderer->drawLayer(layer, x, y); + renderer->drawLayer(layer); } // ---------------------------------------------------------------------------- @@ -192,7 +192,7 @@ static JNINativeMethod gMethods[] = { { "nCreateDisplayListCanvas", "(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas }, { "nResetDisplayListCanvas", "(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas }, - { "nDrawLayer", "(JJFF)V", (void*) android_view_DisplayListCanvas_drawLayer }, + { "nDrawLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer }, { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureWidth }, { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureHeight }, diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp index 9e49afb45790..36ba892b51c4 100644 --- a/core/jni/android_view_HardwareLayer.cpp +++ b/core/jni/android_view_HardwareLayer.cpp @@ -79,12 +79,6 @@ static void android_view_HardwareLayer_updateSurfaceTexture(JNIEnv* env, jobject layer->updateTexImage(); } -static jint android_view_HardwareLayer_getTexName(JNIEnv* env, jobject clazz, - jlong layerUpdaterPtr) { - DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); - return layer->backingLayer()->getTextureId(); -} - // ---------------------------------------------------------------------------- // JNI Glue // ---------------------------------------------------------------------------- @@ -98,8 +92,6 @@ static JNINativeMethod gMethods[] = { { "nSetSurfaceTexture", "(JLandroid/graphics/SurfaceTexture;Z)V", (void*) android_view_HardwareLayer_setSurfaceTexture }, { "nUpdateSurfaceTexture", "(J)V", (void*) android_view_HardwareLayer_updateSurfaceTexture }, - - { "nGetTexName", "(J)I", (void*) android_view_HardwareLayer_getTexName }, }; int register_android_view_HardwareLayer(JNIEnv* env) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 2fcce675bb37..fefd5a7abba3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -786,6 +786,46 @@ android:protectionLevel="normal" android:permissionFlags="hidden"/> + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.WRITE_SMS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.MANAGE_ACCOUNTS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.USE_CREDENTIALS" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.SUBSCRIBED_FEEDS_READ" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + + <!-- @hide We need to keep this around for backwards compatibility --> + <permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE" + android:protectionLevel="normal" + android:permissionFlags="hidden"/> + <!-- ====================================================================== --> <!-- INSTALL PERMISSIONS --> <!-- ====================================================================== --> diff --git a/core/res/res/drawable/scrollbar_handle_material.xml b/core/res/res/drawable/scrollbar_handle_material.xml index 56fececcc2c2..33efbbac8690 100644 --- a/core/res/res/drawable/scrollbar_handle_material.xml +++ b/core/res/res/drawable/scrollbar_handle_material.xml @@ -17,6 +17,9 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android" android:tint="?attr/colorControlNormal" android:shape="rectangle"> - <solid android:color="#84ffffff" /> - <size android:width="4dp" /> + <solid + android:color="#84ffffff" /> + <size + android:width="4dp" + android:height="4dp" /> </shape> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 1a45b3ac75b3..74fd1eccd4db 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2195,4 +2195,18 @@ <attr name="name" /> </declare-styleable> + <!-- <code>initial-layout</code> tag allows configuring the initial layout for the activity + within multi-window environment. --> + <declare-styleable name="AndroidManifestInitialLayout" parent="AndroidManifestActivity"> + <!-- Initial width of the activity. Can be either a fixed value or fraction, in which case + the width will be constructed as a fraction of the total available width. --> + <attr name="activity_width" format="dimension|fraction" /> + <!-- Initial height of the activity. Can be either a fixed value or fraction, in which case + the height will be constructed as a fraction of the total available height. --> + <attr name="activity_height" format="dimension|fraction" /> + <!-- Where to initially position the activity inside the available space. Uses constants + defined in {@link android.view.Gravity}. --> + <attr name="gravity" /> + </declare-styleable> + </resources> diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java index f3b81377bd07..03ae9dc3f510 100644 --- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java +++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java @@ -19,6 +19,8 @@ import android.os.SystemClock; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; +import static android.test.MoreAsserts.assertNotEqual; + public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> { private ValueAnimator a1; private ValueAnimator a2; @@ -27,6 +29,11 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn private final static long TOLERANCE = 100; // ms private final static long POLL_INTERVAL = 100; // ms + private final static float A1_START_VALUE = 0f; + private final static float A1_END_VALUE = 1f; + private final static int A2_START_VALUE = 100; + private final static int A2_END_VALUE = 200; + public ValueAnimatorTests() { super(BasicAnimatorActivity.class); } @@ -34,8 +41,8 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn @Override public void setUp() throws Exception { super.setUp(); - a1 = ValueAnimator.ofFloat(0, 1f).setDuration(300); - a2 = ValueAnimator.ofInt(100, 200).setDuration(500); + a1 = ValueAnimator.ofFloat(A1_START_VALUE, A1_END_VALUE).setDuration(300); + a2 = ValueAnimator.ofInt(A2_START_VALUE, A2_END_VALUE).setDuration(500); } @Override @@ -379,6 +386,55 @@ public class ValueAnimatorTests extends ActivityInstrumentationTestCase2<BasicAn assertTrue(Math.abs(entireSpan - frameDelta) < TOLERANCE); } + @SmallTest + public void testEndValue() throws Throwable { + final MyListener l1 = new MyListener(); + a1.addListener(l1); + + final MyListener l2 = new MyListener(); + a2.addListener(l2); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + a1.start(); + a2.start(); + } + }); + + Thread.sleep(POLL_INTERVAL); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + // Animation has started but not finished, check animated values against end values + assertFalse(l1.endCalled); + assertFalse(l2.endCalled); + assertNotEqual(A1_END_VALUE, a1.getAnimatedValue()); + assertNotEqual(A1_END_VALUE, a2.getAnimatedValue()); + + // Force a2 to end. + a2.end(); + } + }); + + Thread.sleep(a1.getTotalDuration()); + + runTestOnUiThread(new Runnable() { + @Override + public void run() { + assertFalse(l1.cancelCalled); + assertTrue(l1.endCalled); + assertFalse(l2.cancelCalled); + assertTrue(l2.endCalled); + + // By now a1 should have finished normally and a2 has skipped to the end, check + // their end values. + assertEquals(A1_END_VALUE, ((Float) (a1.getAnimatedValue())).floatValue()); + assertEquals(A2_END_VALUE, ((Integer) (a2.getAnimatedValue())).intValue()); + } + }); + } + class MyUpdateListener implements ValueAnimator.AnimatorUpdateListener { boolean wasRunning = false; long firstRunningFrameTime = -1; diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java index d2e2131c2da0..be6e7ea5965e 100644 --- a/core/tests/coretests/src/android/text/format/FormatterTest.java +++ b/core/tests/coretests/src/android/text/format/FormatterTest.java @@ -56,14 +56,14 @@ public class FormatterTest extends AndroidTestCase { public void testFormatBytes() { setLocale(Locale.ENGLISH); - checkFormatBytes(0, true, "0.00", 0); - checkFormatBytes(0, false, "0.00", 0); + checkFormatBytes(0, true, "0", 0); + checkFormatBytes(0, false, "0", 0); - checkFormatBytes(1, true, "1.0", 1); - checkFormatBytes(1, false, "1.00", 1); + checkFormatBytes(1, true, "1", 1); + checkFormatBytes(1, false, "1", 1); checkFormatBytes(12, true, "12", 12); - checkFormatBytes(12, false, "12.00", 12); + checkFormatBytes(12, false, "12", 12); checkFormatBytes(123, true, "123", 123); checkFormatBytes(123, false, "123", 123); @@ -80,13 +80,15 @@ public class FormatterTest extends AndroidTestCase { checkFormatBytes(9123000, true, "8.7", 9122611); checkFormatBytes(9123000, false, "8.70", 9122611); - // The method doesn't really support negative values, but apparently people pass -1... - checkFormatBytes(-1, true, "-1.00", -1); - checkFormatBytes(-1, false, "-1.00", -1); + checkFormatBytes(-1, true, "-1", -1); + checkFormatBytes(-1, false, "-1", -1); + + checkFormatBytes(-912, true, "-0.89", -911); + checkFormatBytes(-912, false, "-0.89", -911); // Missing FLAG_CALCULATE_ROUNDED case. BytesResult r = Formatter.formatBytes(getContext().getResources(), 1, 0); - assertEquals("1.00", r.value); + assertEquals("1", r.value); assertEquals(0, r.roundedBytes); // Didn't pass FLAG_CALCULATE_ROUNDED // Make sure it works on different locales. diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 0b94f8b2c57a..49d9115319e8 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -18,7 +18,7 @@ package android.widget; import android.app.Activity; import android.content.Intent; -import android.test.AndroidTestCase; +import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.GetChars; import android.text.Selection; @@ -27,11 +27,15 @@ import android.text.Spannable; /** * TextViewTest tests {@link TextView}. */ -public class TextViewTest extends AndroidTestCase { +public class TextViewTest extends ActivityInstrumentationTestCase2<TextViewActivity> { + + public TextViewTest() { + super(TextViewActivity.class); + } @SmallTest public void testArray() throws Exception { - TextView tv = new TextView(mContext); + TextView tv = new TextView(getActivity()); char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!' }; @@ -59,25 +63,34 @@ public class TextViewTest extends AndroidTestCase { assertEquals('\0', c2[5]); } + @SmallTest public void testProcessTextActivityResultNonEditable() { - TextView tv = new TextView(mContext); + final TextView tv = new TextView(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); tv.setTextIsSelectable(true); Selection.setSelection((Spannable) tv.getText(), 0, tv.getText().length()); - CharSequence newText = "Text is replaced."; - Intent data = new Intent(); - data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); - tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); + // We need to run this in the UI thread, as it will create a Toast. + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + CharSequence newText = "Text is replaced."; + Intent data = new Intent(); + data.putExtra(Intent.EXTRA_PROCESS_TEXT, newText); + tv.onActivityResult(TextView.PROCESS_TEXT_REQUEST_CODE, Activity.RESULT_OK, data); + } + }); + getInstrumentation().waitForIdleSync(); // This is a TextView, which can't be modified. Hence no change should have been made. assertEquals(originalText, tv.getText().toString()); } + @SmallTest public void testProcessTextActivityResultEditable() { - EditText tv = new EditText(mContext); + EditText tv = new EditText(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); @@ -92,8 +105,9 @@ public class TextViewTest extends AndroidTestCase { assertEquals(newText, tv.getText().toString()); } + @SmallTest public void testProcessTextActivityResultCancel() { - EditText tv = new EditText(mContext); + EditText tv = new EditText(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); @@ -108,8 +122,9 @@ public class TextViewTest extends AndroidTestCase { assertEquals(originalText, tv.getText().toString()); } + @SmallTest public void testProcessTextActivityNoData() { - EditText tv = new EditText(mContext); + EditText tv = new EditText(getActivity()); CharSequence originalText = "This is some text."; tv.setText(originalText, TextView.BufferType.SPANNABLE); assertEquals(originalText, tv.getText().toString()); diff --git a/docs/html/training/wearables/watch-faces/index.jd b/docs/html/training/wearables/watch-faces/index.jd index b54463684033..a329fda071bc 100644 --- a/docs/html/training/wearables/watch-faces/index.jd +++ b/docs/html/training/wearables/watch-faces/index.jd @@ -66,7 +66,7 @@ Drawing Watch Faces</a></dt> Showing Information in Watch Faces</a></dt> <dd>Learn how to incorporate contextual information into your watch face.</dd> <dt><a href="{@docRoot}training/wearables/watch-faces/interacting.html"> -Making Your Watch Face Interactive </a></dt> +Creating Interactive Watch Faces</a></dt> <dd>Learn how to enable the user to interact with your watch face.</dd> <dt><a href="{@docRoot}training/wearables/watch-faces/configuration.html"> Providing Configuration Activities</a></dt> diff --git a/docs/html/training/wearables/watch-faces/interacting.jd b/docs/html/training/wearables/watch-faces/interacting.jd index 4f2486cf416f..5a44fdeeb300 100644 --- a/docs/html/training/wearables/watch-faces/interacting.jd +++ b/docs/html/training/wearables/watch-faces/interacting.jd @@ -62,10 +62,10 @@ implementation.</p> reserves gestures such as drag and long-press for system UI elements. Therefore, the system does not send raw touch events to the watch face. Instead, the system forwards specific commands to the <a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.Engine.html#onTapCommand(int, int, int, long)"> -method. +onTapCommand()</a> method. <p>The system sends the first command, -<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.html#TAP_TYPE_TOUCH"</a> +<a href="{@docRoot}reference/android/support/wearable/watchface/WatchFaceService.html#TAP_TYPE_TOUCH"> {@code TAP_TYPE_TOUCH}</a>, when the user initially touches the screen. This event lets you provide visual feedback to the user on touch. Your app should not launch a UI when this event triggers. Launching a UI prevents drag events from opening the app diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java index 0e9823d1661f..91e704a901cb 100644 --- a/graphics/java/android/graphics/Path.java +++ b/graphics/java/android/graphics/Path.java @@ -486,9 +486,9 @@ public class Path { */ public enum Direction { /** clockwise */ - CW (1), // must match enum in SkPath.h + CW (0), // must match enum in SkPath.h /** counter-clockwise */ - CCW (2); // must match enum in SkPath.h + CCW (1); // must match enum in SkPath.h Direction(int ni) { nativeInt = ni; diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java index 32f6a895f35d..464f3de5d29b 100644 --- a/graphics/java/android/graphics/drawable/RippleDrawable.java +++ b/graphics/java/android/graphics/drawable/RippleDrawable.java @@ -855,7 +855,8 @@ public class RippleDrawable extends LayerDrawable { // Position the shader to account for canvas translation. if (mMaskShader != null) { - mMaskMatrix.setTranslate(-x, -y); + final Rect bounds = getBounds(); + mMaskMatrix.setTranslate(bounds.left - x, bounds.top - y); mMaskShader.setLocalMatrix(mMaskMatrix); } diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp index 54fb5f233fce..e307ad91885f 100644 --- a/libs/hwui/CanvasState.cpp +++ b/libs/hwui/CanvasState.cpp @@ -155,17 +155,20 @@ void CanvasState::concatMatrix(const Matrix4& matrix) { /////////////////////////////////////////////////////////////////////////////// bool CanvasState::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); + mSnapshot->clip(left, top, right, bottom, op); + mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } bool CanvasState::clipPath(const SkPath* path, SkRegion::Op op) { - mDirtyClip |= mSnapshot->clipPath(*path, op); + mSnapshot->clipPath(*path, op); + mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } bool CanvasState::clipRegion(const SkRegion* region, SkRegion::Op op) { - mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); + mSnapshot->clipRegionTransformed(*region, op); + mDirtyClip = true; return !mSnapshot->clipIsEmpty(); } diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp index b1a68447fc21..8e7efb4e35d6 100644 --- a/libs/hwui/ClipArea.cpp +++ b/libs/hwui/ClipArea.cpp @@ -32,9 +32,7 @@ static bool intersect(Rect& r, const Rect& r2) { } static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { - Vertex v; - v.x = x; - v.y = y; + Vertex v = {x, y}; transform.mapPoint(v.x, v.y); transformedBounds.expandToCoverVertex(v.x, v.y); } @@ -187,7 +185,7 @@ SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { */ ClipArea::ClipArea() - : mMode(kModeRectangle) { + : mMode(Mode::Rectangle) { } /* @@ -200,45 +198,46 @@ void ClipArea::setViewportDimensions(int width, int height) { } void ClipArea::setEmpty() { - mMode = kModeRectangle; + mMode = Mode::Rectangle; mClipRect.setEmpty(); mClipRegion.setEmpty(); mRectangleList.setEmpty(); } void ClipArea::setClip(float left, float top, float right, float bottom) { - mMode = kModeRectangle; + mMode = Mode::Rectangle; mClipRect.set(left, top, right, bottom); mClipRegion.setEmpty(); } -bool ClipArea::clipRectWithTransform(float left, float top, float right, +void ClipArea::clipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { Rect r(left, top, right, bottom); - return clipRectWithTransform(r, transform, op); + clipRectWithTransform(r, transform, op); } -bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, +void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { switch (mMode) { - case kModeRectangle: - return rectangleModeClipRectWithTransform(r, transform, op); - case kModeRectangleList: - return rectangleListModeClipRectWithTransform(r, transform, op); - case kModeRegion: - return regionModeClipRectWithTransform(r, transform, op); + case Mode::Rectangle: + rectangleModeClipRectWithTransform(r, transform, op); + break; + case Mode::RectangleList: + rectangleListModeClipRectWithTransform(r, transform, op); + break; + case Mode::Region: + regionModeClipRectWithTransform(r, transform, op); + break; } - return false; } -bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { +void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { enterRegionMode(); mClipRegion.op(region, op); onClipRegionUpdated(); - return true; } -bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, +void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { SkMatrix skTransform; transform->copyTo(skTransform); @@ -246,7 +245,7 @@ bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, path.transform(skTransform, &transformed); SkRegion region; regionFromPath(transformed, region); - return clipRegion(region, op); + clipRegion(region, op); } /* @@ -257,19 +256,20 @@ void ClipArea::enterRectangleMode() { // Entering rectangle mode discards any // existing clipping information from the other modes. // The only way this occurs is by a clip setting operation. - mMode = kModeRectangle; + mMode = Mode::Rectangle; } -bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r, +void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { if (op == SkRegion::kReplace_Op && transform->rectToRect()) { mClipRect = r; transform->mapRect(mClipRect); - return true; + return; } else if (op != SkRegion::kIntersect_Op) { enterRegionMode(); - return regionModeClipRectWithTransform(r, transform, op); + regionModeClipRectWithTransform(r, transform, op); + return; } if (transform->rectToRect()) { @@ -279,19 +279,18 @@ bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r, if (!hasIntersection) { mClipRect.setEmpty(); } - return true; + return; } enterRectangleListMode(); - return rectangleListModeClipRectWithTransform(r, transform, op); + rectangleListModeClipRectWithTransform(r, transform, op); } -bool ClipArea::rectangleModeClipRectWithTransform(float left, float top, +void ClipArea::rectangleModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { Rect r(left, top, right, bottom); - bool result = rectangleModeClipRectWithTransform(r, transform, op); + rectangleModeClipRectWithTransform(r, transform, op); mClipRect = mRectangleList.calculateBounds(); - return result; } /* @@ -302,25 +301,24 @@ void ClipArea::enterRectangleListMode() { // Is is only legal to enter rectangle list mode from // rectangle mode, since rectangle list mode cannot represent // all clip areas that can be represented by a region. - ALOG_ASSERT(mMode == kModeRectangle); - mMode = kModeRectangleList; + ALOG_ASSERT(mMode == Mode::Rectangle); + mMode = Mode::RectangleList; mRectangleList.set(mClipRect, Matrix4::identity()); } -bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, +void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) { enterRegionMode(); - return regionModeClipRectWithTransform(r, transform, op); + regionModeClipRectWithTransform(r, transform, op); } - return true; } -bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top, +void ClipArea::rectangleListModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { Rect r(left, top, right, bottom); - return rectangleListModeClipRectWithTransform(r, transform, op); + rectangleListModeClipRectWithTransform(r, transform, op); } /* @@ -329,9 +327,9 @@ bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top, void ClipArea::enterRegionMode() { Mode oldMode = mMode; - mMode = kModeRegion; - if (oldMode != kModeRegion) { - if (oldMode == kModeRectangle) { + mMode = Mode::Region; + if (oldMode != Mode::Region) { + if (oldMode == Mode::Rectangle) { mClipRegion.setRect(mClipRect.left, mClipRect.top, mClipRect.right, mClipRect.bottom); } else { @@ -341,20 +339,18 @@ void ClipArea::enterRegionMode() { } } -bool ClipArea::regionModeClipRectWithTransform(const Rect& r, +void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { SkPath transformedRect = pathFromTransformedRectangle(r, *transform); SkRegion transformedRectRegion; regionFromPath(transformedRect, transformedRectRegion); mClipRegion.op(transformedRectRegion, op); onClipRegionUpdated(); - return true; } -bool ClipArea::regionModeClipRectWithTransform(float left, float top, +void ClipArea::regionModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op) { - return regionModeClipRectWithTransform(Rect(left, top, right, bottom), - transform, op); + regionModeClipRectWithTransform(Rect(left, top, right, bottom), transform, op); } void ClipArea::onClipRegionUpdated() { diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h index 51ef27b4e9cc..38fefe5ab097 100644 --- a/libs/hwui/ClipArea.h +++ b/libs/hwui/ClipArea.h @@ -80,6 +80,13 @@ private: }; class ClipArea { +private: + enum class Mode { + Rectangle, + Region, + RectangleList + }; + public: ClipArea(); @@ -91,12 +98,12 @@ public: void setEmpty(); void setClip(float left, float top, float right, float bottom); - bool clipRectWithTransform(float left, float top, float right, float bottom, - const mat4* transform, SkRegion::Op op = SkRegion::kIntersect_Op); - bool clipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op = SkRegion::kIntersect_Op); - bool clipRegion(const SkRegion& region, SkRegion::Op op = SkRegion::kIntersect_Op); - bool clipPathWithTransform(const SkPath& path, const mat4* transform, + void clipRectWithTransform(float left, float top, float right, float bottom, + const mat4* transform, SkRegion::Op op); + void clipRectWithTransform(const Rect& r, const mat4* transform, + SkRegion::Op op); + void clipRegion(const SkRegion& region, SkRegion::Op op); + void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op); const Rect& getClipRect() const { @@ -112,41 +119,39 @@ public: } bool isRegion() const { - return kModeRegion == mMode; + return Mode::Region == mMode; } bool isSimple() const { - return mMode == kModeRectangle; + return mMode == Mode::Rectangle; } bool isRectangleList() const { - return mMode == kModeRectangleList; + return mMode == Mode::RectangleList; } private: void enterRectangleMode(); - bool rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - bool rectangleModeClipRectWithTransform(float left, float top, float right, + void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); + void rectangleModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op); void enterRectangleListMode(); - bool rectangleListModeClipRectWithTransform(float left, float top, + void rectangleListModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op); - bool rectangleListModeClipRectWithTransform(const Rect& r, + void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); void enterRegionModeFromRectangleMode(); void enterRegionModeFromRectangleListMode(); void enterRegionMode(); - bool regionModeClipRectWithTransform(const Rect& r, const mat4* transform, + void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op); - bool regionModeClipRectWithTransform(float left, float top, float right, + void regionModeClipRectWithTransform(float left, float top, float right, float bottom, const mat4* transform, SkRegion::Op op); void ensureClipRegion(); void onClipRegionUpdated(); - bool clipRegionOp(float left, float top, float right, float bottom, - SkRegion::Op op); SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); @@ -158,12 +163,6 @@ private: pathAsRegion.setPath(path, createViewportRegion()); } - enum Mode { - kModeRectangle, - kModeRegion, - kModeRectangleList - }; - Mode mMode; Rect mViewportBounds; Rect mClipRect; diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index bb149fe09d69..506bfad08c1b 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -216,11 +216,11 @@ void DisplayListCanvas::drawRenderNode(RenderNode* renderNode) { addRenderNodeOp(op); } -void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) { +void DisplayListCanvas::drawLayer(DeferredLayerUpdater* layerHandle) { // We ref the DeferredLayerUpdater due to its thread-safe ref-counting // semantics. mDisplayListData->ref(layerHandle); - addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y)); + addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer())); } void DisplayListCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index f29e835c4858..392bb3e5d85f 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -91,7 +91,7 @@ public: // ---------------------------------------------------------------------------- // HWUI Canvas draw operations - special // ---------------------------------------------------------------------------- - void drawLayer(DeferredLayerUpdater* layerHandle, float x, float y); + void drawLayer(DeferredLayerUpdater* layerHandle); void drawRenderNode(RenderNode* renderNode); // TODO: rename for consistency diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 8bb892f8e9d4..14126a9e31a7 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1524,23 +1524,21 @@ private: class DrawLayerOp : public DrawOp { public: - DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(nullptr), mLayer(layer), mX(x), mY(y) {} + DrawLayerOp(Layer* layer) + : DrawOp(nullptr), mLayer(layer) {} virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawLayer(mLayer, mX, mY); + renderer.drawLayer(mLayer); } virtual void output(int level, uint32_t logFlags) const override { - OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY); + OP_LOG("Draw Layer %p", mLayer); } virtual const char* name() override { return "DrawLayer"; } private: Layer* mLayer; - float mX; - float mY; }; }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index b35c0179193a..5692d7e120ff 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2268,7 +2268,7 @@ void OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { mDirty = true; } -void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { +void OpenGLRenderer::drawLayer(Layer* layer) { if (!layer) { return; } @@ -2284,7 +2284,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { bool clipRequired = false; const bool rejected = mState.calculateQuickRejectForScissor( - x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), + 0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &clipRequired, nullptr, false); if (rejected) { @@ -2313,7 +2313,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { .setMeshTexturedIndexedQuads(layer->mesh, layer->meshElementCount) .setFillLayer(layer->getTexture(), layer->getColorFilter(), getLayerAlpha(layer), layer->getMode(), Blend::ModeOrderSwap::NoSwap) .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewOffsetRectSnap(x, y, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight())) + .setModelViewOffsetRectSnap(0, 0, Rect(0, 0, layer->layer.getWidth(), layer->layer.getHeight())) .build(); DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, renderGlop(glop)); #if DEBUG_LAYERS_AS_REGIONS @@ -2326,7 +2326,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { SkPaint paint; paint.setColor(0x7f00ff00); - drawColorRect(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight(), &paint); + drawColorRect(0, 0, layer->layer.getWidth(), layer->layer.getHeight(), &paint); } } layer->hasDrawnSinceUpdate = true; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 45662a77cfdd..af85e8c72619 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -35,6 +35,7 @@ #include <SkBitmap.h> #include <SkCanvas.h> #include <SkColorFilter.h> +#include <SkDrawLooper.h> #include <SkMatrix.h> #include <SkPaint.h> #include <SkRegion.h> @@ -187,7 +188,7 @@ public: const SkPaint* paint, int flags); void drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); - void drawLayer(Layer* layer, float x, float y); + void drawLayer(Layer* layer); void drawBitmap(const SkBitmap* bitmap, const SkPaint* paint); void drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 1ac368f423b8..b25a4ac9558d 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -38,7 +38,8 @@ namespace uirenderer { // Vertex shaders snippets /////////////////////////////////////////////////////////////////////////////// -const char* gVS_Header_Attributes = +const char* gVS_Header_Start = + "#version 100\n" "attribute vec4 position;\n"; const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n"; @@ -132,6 +133,8 @@ const char* gVS_Footer = // Fragment shaders snippets /////////////////////////////////////////////////////////////////////////////// +const char* gFS_Header_Start = + "#version 100\n"; const char* gFS_Header_Extension_FramebufferFetch = "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; const char* gFS_Header_Extension_ExternalTexture = @@ -457,7 +460,7 @@ static inline size_t gradientIndex(const ProgramDescription& description) { String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { // Add attributes - String8 shader(gVS_Header_Attributes); + String8 shader(gVS_Header_Start); if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Attributes_TexCoords); } @@ -543,7 +546,7 @@ static bool shaderOp(const ProgramDescription& description, String8& shader, } String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) { - String8 shader; + String8 shader(gFS_Header_Start); const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode; if (blendFramebuffer) { diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 1414fd541909..7d09c0beaf2a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -875,7 +875,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { renderer.setBaseTransform(initialTransform); if (drawLayer) { - handler(new (alloc) DrawLayerOp(mLayer, 0, 0), + handler(new (alloc) DrawLayerOp(mLayer), renderer.getSaveCount() - 1, properties().getClipToBounds()); } else { const int saveCountOffset = renderer.getSaveCount() - 1; diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index ca19a423e5c8..4d60b8dd0e7c 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -83,24 +83,24 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) // Clipping /////////////////////////////////////////////////////////////////////////////// -bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { +void Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) { flags |= Snapshot::kFlagClipSet; - return mClipArea->clipRegion(region, op); + mClipArea->clipRegion(region, op); } -bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { +void Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) { flags |= Snapshot::kFlagClipSet; - return mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op); + mClipArea->clipRectWithTransform(left, top, right, bottom, transform, op); } -bool Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { +void Snapshot::clipPath(const SkPath& path, SkRegion::Op op) { flags |= Snapshot::kFlagClipSet; - return mClipArea->clipPathWithTransform(path, transform, op); + mClipArea->clipPathWithTransform(path, transform, op); } void Snapshot::setClip(float left, float top, float right, float bottom) { - mClipArea->setClip(left, top, right, bottom); flags |= Snapshot::kFlagClipSet; + mClipArea->setClip(left, top, right, bottom); } bool Snapshot::hasPerspectiveTransform() const { diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index af6ad727da85..cf8f11c80058 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -124,26 +124,25 @@ public: * the specified operation. The specified rectangle is transformed * by this snapshot's trasnformation. */ - bool clip(float left, float top, float right, float bottom, - SkRegion::Op op = SkRegion::kIntersect_Op); + void clip(float left, float top, float right, float bottom, SkRegion::Op op); /** * Modifies the current clip with the new clip rectangle and * the specified operation. The specified rectangle is considered * already transformed. */ - bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); + void clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op); /** * Modifies the current clip with the specified region and operation. * The specified region is considered already transformed. */ - bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op); + void clipRegionTransformed(const SkRegion& region, SkRegion::Op op); /** * Modifies the current clip with the specified path and operation. */ - bool clipPath(const SkPath& path, SkRegion::Op op); + void clipPath(const SkPath& path, SkRegion::Op op); /** * Sets the current clip. diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java index d2824b556315..f79d52ee8c56 100644 --- a/media/java/android/mtp/MtpObjectInfo.java +++ b/media/java/android/mtp/MtpObjectInfo.java @@ -256,7 +256,7 @@ public final class MtpObjectInfo { /** * Builds a new object info instance. */ - public class Builder { + public static class Builder { private MtpObjectInfo mObjectInfo; public Builder() { diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index ad804f32fd9f..9dd3861177e9 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -430,12 +430,14 @@ static jobject android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) { MtpDevice* device = get_device_from_object(env, thiz); - if (!device) + if (!device) { return JNI_FALSE; + } // Updating existing objects is not supported. - if (env->GetIntField(info, field_objectInfo_handle) != -1) + if (env->GetIntField(info, field_objectInfo_handle) != -1) { return JNI_FALSE; + } MtpObjectInfo* object_info = new MtpObjectInfo(-1); object_info->mStorageID = env->GetIntField(info, field_objectInfo_storageId); @@ -456,17 +458,21 @@ android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) object_info->mSequenceNumber = env->GetIntField(info, field_objectInfo_sequenceNumber); jstring name_jstring = (jstring) env->GetObjectField(info, field_objectInfo_name); - const char* name_string = env->GetStringUTFChars(name_jstring, NULL); - object_info->mName = strdup(name_string); - env->ReleaseStringUTFChars(name_jstring, name_string); + if (name_jstring != NULL) { + const char* name_string = env->GetStringUTFChars(name_jstring, NULL); + object_info->mName = strdup(name_string); + env->ReleaseStringUTFChars(name_jstring, name_string); + } object_info->mDateCreated = env->GetLongField(info, field_objectInfo_dateCreated) / 1000LL; object_info->mDateModified = env->GetLongField(info, field_objectInfo_dateModified) / 1000LL; jstring keywords_jstring = (jstring) env->GetObjectField(info, field_objectInfo_keywords); - const char* keywords_string = env->GetStringUTFChars(keywords_jstring, NULL); - object_info->mKeywords = strdup(keywords_string); - env->ReleaseStringUTFChars(keywords_jstring, keywords_string); + if (keywords_jstring != NULL) { + const char* keywords_string = env->GetStringUTFChars(keywords_jstring, NULL); + object_info->mKeywords = strdup(keywords_string); + env->ReleaseStringUTFChars(keywords_jstring, keywords_string); + } int object_handle = device->sendObjectInfo(object_info); if (object_handle == -1) { diff --git a/packages/DocumentsUI/res/layout/item_doc_grid.xml b/packages/DocumentsUI/res/layout/item_doc_grid.xml index 1001e105bcd0..71e618bf4314 100644 --- a/packages/DocumentsUI/res/layout/item_doc_grid.xml +++ b/packages/DocumentsUI/res/layout/item_doc_grid.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_item_height" android:layout_margin="@dimen/grid_item_margin" @@ -140,4 +140,4 @@ android:contentDescription="@null" android:duplicateParentState="true" /> -</FrameLayout> +</com.android.documentsui.GridItem> diff --git a/packages/DocumentsUI/res/layout/item_loading_grid.xml b/packages/DocumentsUI/res/layout/item_loading_grid.xml index 005a111beabd..147dfd449724 100644 --- a/packages/DocumentsUI/res/layout/item_loading_grid.xml +++ b/packages/DocumentsUI/res/layout/item_loading_grid.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_height" android:orientation="horizontal"> @@ -26,4 +26,4 @@ android:indeterminate="true" style="?android:attr/progressBarStyle" /> -</FrameLayout> +</com.android.documentsui.GridItem> diff --git a/packages/DocumentsUI/res/layout/item_message_grid.xml b/packages/DocumentsUI/res/layout/item_message_grid.xml index 385563df64f6..45d61a506bb5 100644 --- a/packages/DocumentsUI/res/layout/item_message_grid.xml +++ b/packages/DocumentsUI/res/layout/item_message_grid.xml @@ -14,7 +14,7 @@ limitations under the License. --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.documentsui.GridItem xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/grid_height" android:paddingStart="?android:attr/listPreferredItemPaddingStart" @@ -48,4 +48,4 @@ </LinearLayout> -</FrameLayout> +</com.android.documentsui.GridItem> diff --git a/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java b/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java new file mode 100644 index 000000000000..f2590592c52a --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/BandSelectMatrix.java @@ -0,0 +1,645 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.documentsui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.base.Preconditions; + +import android.graphics.Point; +import android.graphics.Rect; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.RecyclerView.LayoutManager; +import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.util.SparseBooleanArray; +import android.view.View; + +/** + * Provides a band selection item model for views within a RecyclerView. This class queries the + * RecyclerView to determine where its items are placed; then, once band selection is underway, it + * alerts listeners of which items are covered by the selections. + */ +public final class BandSelectMatrix extends RecyclerView.OnScrollListener { + + private final RecyclerViewHelper mHelper; + private final List<OnSelectionChangedListener> mOnSelectionChangedListeners = new ArrayList<>(); + + // Map from the x-value of the left side of an item to an ordered list of metadata of all items + // whose x-values are the same. The list is ordered by the y-values of the items in the column. + // For example, if the first column of the view starts at an x-value of 5, mColumns.get(5) would + // return a list of all items in that column, with the top-most item first in the list and the + // bottom-most item last in the list. + private final Map<Integer, List<ItemData>> mColumns = new HashMap<>(); + + // List of limits along the x-axis. For example, if the view has two columns, this list will + // have two elements, each of which lists the lower- and upper-limits of the x-values of the + // view items. This list is sorted from furthest left to furthest right. + private final List<Limits> mXLimitsList = new ArrayList<>(); + + // Like mXLimitsList, but for y-coordinates. Note that this list only contains items which have + // been in the viewport. Thus, limits which exist in an area of the view to which the view has + // not scrolled are not present in the list. + private final List<Limits> mYLimitsList = new ArrayList<>(); + + // The adapter positions which have been recorded so far. + private final SparseBooleanArray mRecordedPositions = new SparseBooleanArray(); + + // Array passed to registered OnSelectionChangedListeners. One array is created and reused + // throughout the lifetime of the object. + private final SparseBooleanArray mSelectionForListeners = new SparseBooleanArray(); + + // The current pointer (in absolute positioning from the top of the view). + private Point mPointer = null; + + // The bounds of the band selection. + private RelativePoint mRelativeOrigin; + private RelativePoint mRelativePointer; + + BandSelectMatrix(RecyclerViewHelper helper) { + mHelper = helper; + mHelper.addOnScrollListener(this); + } + + BandSelectMatrix(RecyclerView rv) { + this(new RuntimeRecyclerViewHelper(rv)); + } + + /** + * Stops listening to the view's scrolls. Call this function before discarding a + * BandSelecMatrix object to prevent memory leaks. + */ + void stopListening() { + mHelper.removeOnScrollListener(this); + } + + /** + * Start a band select operation at the given point. + * @param relativeOrigin The origin of the band select operation, relative to the viewport. + * For example, if the view is scrolled to the bottom, the top-left of the viewport would + * have a relative origin of (0, 0), even though its absolute point has a higher y-value. + */ + void startSelection(Point relativeOrigin) { + Point absoluteOrigin = mHelper.createAbsolutePoint(relativeOrigin); + mPointer = new Point(absoluteOrigin.x, absoluteOrigin.y); + + processVisibleChildren(); + mRelativeOrigin = new RelativePoint(absoluteOrigin); + mRelativePointer = new RelativePoint(mPointer); + computeCurrentSelection(); + notifyListeners(); + } + + /** + * Resizes the selection by adjusting the pointer (i.e., the corner of the selection opposite + * the origin. + * @param relativePointer The pointer (opposite of the origin) of the band select operation, + * relative to the viewport. For example, if the view is scrolled to the bottom, the + * top-left of the viewport would have a relative origin of (0, 0), even though its absolute + * point has a higher y-value. + */ + void resizeSelection(Point relativePointer) { + mPointer = mHelper.createAbsolutePoint(relativePointer); + handlePointerMoved(); + } + + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (mPointer == null) { + return; + } + + mPointer.x += dx; + mPointer.y += dy; + processVisibleChildren(); + handlePointerMoved(); + } + + /** + * Queries the view for all children and records their location metadata. + */ + private void processVisibleChildren() { + for (int i = 0; i < mHelper.getVisibleChildCount(); i++) { + int adapterPosition = mHelper.getAdapterPositionAt(i); + if (!mRecordedPositions.get(adapterPosition)) { + mRecordedPositions.put(adapterPosition, true); + captureItemLayoutData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition); + } + } + } + + /** + * Updates the limits lists and column map with the given item metadata. + * @param absoluteChildRect The absolute rectangle for the child view being processed. + * @param adapterPosition The position of the child view being processed. + */ + private void captureItemLayoutData(Rect absoluteChildRect, int adapterPosition) { + if (mXLimitsList.size() != mHelper.getNumColumns()) { + // If not all x-limits have been recorded, record this one. + recordLimits( + mXLimitsList, new Limits(absoluteChildRect.left, absoluteChildRect.right)); + } + + if (mYLimitsList.size() != mHelper.getNumRows()) { + // If not all y-limits have been recorded, record this one. + recordLimits( + mYLimitsList, new Limits(absoluteChildRect.top, absoluteChildRect.bottom)); + } + + List<ItemData> columnList = mColumns.get(absoluteChildRect.left); + if (columnList == null) { + columnList = new ArrayList<ItemData>(); + mColumns.put(absoluteChildRect.left, columnList); + } + recordItemData( + columnList, new ItemData(adapterPosition, absoluteChildRect.top)); + } + + /** + * Ensures limits exists within the sorted list limitsList, and adds it to the list if it does + * not exist. + */ + private static void recordLimits(List<Limits> limitsList, Limits limits) { + int index = Collections.binarySearch(limitsList, limits); + if (index < 0) { + limitsList.add(~index, limits); + } + } + + /** + * Ensures itemData exists within the sorted list itemDataList, and adds it to the list if it + * does not exist. + */ + private static void recordItemData(List<ItemData> itemDataList, ItemData itemData) { + int index = Collections.binarySearch(itemDataList, itemData); + if (index < 0) { + itemDataList.add(~index, itemData); + } + } + + /** + * Handles a moved pointer; this function determines whether the pointer movement resulted in a + * selection change and, if it has, notifies listeners of this change. + */ + private void handlePointerMoved() { + RelativePoint old = mRelativePointer; + mRelativePointer = new RelativePoint(mPointer); + if (old != null && mRelativePointer.equals(old)) { + return; + } + + computeCurrentSelection(); + notifyListeners(); + } + + /** + * Computes the currently-selected items. + */ + private void computeCurrentSelection() { + Rect selectionRect = mRelativePointer.computeBounds(mRelativeOrigin); + computePositionsCoveredByRect(selectionRect); + } + + /** + * Notifies all listeners of a selection change. Note that this function simply passes + * mSelectionForListeners, so computeCurrentSelection() should be called before this function. + */ + private void notifyListeners() { + for (OnSelectionChangedListener listener : mOnSelectionChangedListeners) { + listener.onSelectionChanged(mSelectionForListeners); + } + } + + /** + * @param rect Rectangle including all covered items. + */ + private void computePositionsCoveredByRect(@Nullable Rect rect) { + mSelectionForListeners.clear(); + if (rect == null) { + // If there is no bounding rectangle, there are no items selected, so just return early. + return; + } + + int columnIndex = Collections.binarySearch(mXLimitsList, new Limits(rect.left, rect.left)); + Preconditions.checkState(columnIndex >= 0); + + for (; columnIndex < mXLimitsList.size() && + mXLimitsList.get(columnIndex).lowerLimit <= rect.right; columnIndex++) { + List<ItemData> positions = + mColumns.get(mXLimitsList.get(columnIndex).lowerLimit); + int rowIndex = Collections.binarySearch(positions, new ItemData(0, rect.top)); + if (rowIndex < 0) { + // If band select occurs after the last item in a row with fewer items than columns, + // go to the next column. This situation occurs in the last row of the grid when the + // total number of items is not a multiple of the number of columns (e.g., when 10 + // items exist in a grid with 4 columns). + continue; + } + + for (; rowIndex < positions.size() && + positions.get(rowIndex).offset <= rect.bottom; rowIndex++) { + mSelectionForListeners.append(positions.get(rowIndex).position, true); + } + } + } + + /** + * Provides functionality for interfacing with the view. In practice, RecyclerViewMatrixHelper + * should be used; this interface exists solely for the purpose of decoupling the view from + * this class so that the view can be mocked out for tests. + */ + interface RecyclerViewHelper { + public void addOnScrollListener(RecyclerView.OnScrollListener listener); + public void removeOnScrollListener(RecyclerView.OnScrollListener listener); + public Point createAbsolutePoint(Point relativePoint); + public int getVisibleChildCount(); + public int getTotalChildCount(); + public int getNumColumns(); + public int getNumRows(); + public int getAdapterPositionAt(int index); + public Rect getAbsoluteRectForChildViewAt(int index); + } + + /** + * Concrete MatrixHelper implementation for use within the Files app. + */ + static class RuntimeRecyclerViewHelper implements RecyclerViewHelper { + private final RecyclerView mRecyclerView; + + RuntimeRecyclerViewHelper(RecyclerView rv) { + mRecyclerView = rv; + } + + @Override + public int getAdapterPositionAt(int index) { + View child = mRecyclerView.getChildAt(index); + return mRecyclerView.getChildViewHolder(child).getAdapterPosition(); + } + + @Override + public void addOnScrollListener(OnScrollListener listener) { + mRecyclerView.addOnScrollListener(listener); + } + + @Override + public void removeOnScrollListener(OnScrollListener listener) { + mRecyclerView.removeOnScrollListener(listener); + } + + @Override + public Point createAbsolutePoint(Point relativePoint) { + return new Point(relativePoint.x + mRecyclerView.computeHorizontalScrollOffset(), + relativePoint.y + mRecyclerView.computeVerticalScrollOffset()); + } + + @Override + public Rect getAbsoluteRectForChildViewAt(int index) { + final View child = mRecyclerView.getChildAt(index); + final Rect childRect = new Rect(); + child.getHitRect(childRect); + childRect.left += mRecyclerView.computeHorizontalScrollOffset(); + childRect.right += mRecyclerView.computeHorizontalScrollOffset(); + childRect.top += mRecyclerView.computeVerticalScrollOffset(); + childRect.bottom += mRecyclerView.computeVerticalScrollOffset(); + return childRect; + } + + @Override + public int getVisibleChildCount() { + return mRecyclerView.getChildCount(); + } + + @Override + public int getTotalChildCount() { + return mRecyclerView.getAdapter().getItemCount(); + } + + @Override + public int getNumColumns() { + LayoutManager layoutManager = mRecyclerView.getLayoutManager(); + if (layoutManager instanceof GridLayoutManager) { + return ((GridLayoutManager) layoutManager).getSpanCount(); + } + + // Otherwise, it is a list with 1 column. + return 1; + } + + @Override + public int getNumRows() { + int numFullColumns = getTotalChildCount() / getNumColumns(); + boolean hasPartiallyFullColumn = getTotalChildCount() % getNumColumns() != 0; + return numFullColumns + (hasPartiallyFullColumn ? 1 : 0); + } + } + + /** + * Listener for changes in which items have been band selected. + */ + interface OnSelectionChangedListener { + public void onSelectionChanged(SparseBooleanArray updatedSelection); + } + + void addOnSelectionChangedListener(OnSelectionChangedListener listener) { + mOnSelectionChangedListeners.add(listener); + } + + void removeOnSelectionChangedListener(OnSelectionChangedListener listener) { + mOnSelectionChangedListeners.remove(listener); + } + + /** + * Metadata for an item in the view, consisting of the adapter position and the offset from the + * top of the view (in pixels). Stored in the mColumns map to model the item grid. + */ + private static class ItemData implements Comparable<ItemData> { + int position; + int offset; + + ItemData(int position, int offset) { + this.position = position; + this.offset = offset; + } + + @Override + public int compareTo(ItemData other) { + // The list of columns is sorted via the offset from the top, so PositionMetadata + // objects with lower y-values are befor those with higher y-values. + return offset - other.offset; + } + } + + /** + * Limits of a view item. For example, if an item's left side is at x-value 5 and its right side + * is at x-value 10, the limits would be from 5 to 10. Used to record the left- and right sides + * of item columns and the top- and bottom sides of item rows so that it can be determined + * whether the pointer is located within the bounds of an item. + */ + private static class Limits implements Comparable<Limits> { + int lowerLimit; + int upperLimit; + + Limits(int lowerLimit, int upperLimit) { + this.lowerLimit = lowerLimit; + this.upperLimit = upperLimit; + } + + @Override + public int compareTo(Limits other) { + return lowerLimit - other.lowerLimit; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof Limits)) { + return false; + } + + return ((Limits) other).lowerLimit == lowerLimit && + ((Limits) other).upperLimit == upperLimit; + } + } + + /** + * The location of a coordinate relative to items. This class represents a general area of the + * view as it relates to band selection rather than an explicit point. For example, two + * different points within an item are considered to have the same "location" because band + * selection originating within the item would select the same items no matter which point + * was used. Same goes for points between items as well as those at the very beginning or end + * of the view. + * + * Tracking a coordinate (e.g., an x-value) as a CoordinateLocation instead of as an int has the + * advantage of tying the value to the Limits of items along that axis. This allows easy + * selection of items within those Limits as opposed to a search through every item to see if a + * given coordinate value falls within those Limits. + */ + private static class RelativeCoordinate + implements Comparable<RelativeCoordinate> { + /** + * Location describing points after the last known item. + */ + static final int AFTER_LAST_ITEM = 0; + + /** + * Location describing points before the first known item. + */ + static final int BEFORE_FIRST_ITEM = 1; + + /** + * Location describing points between two items. + */ + static final int BETWEEN_TWO_ITEMS = 2; + + /** + * Location describing points within the limits of one item. + */ + static final int WITHIN_LIMITS = 3; + + /** + * The type of this coordinate, which is one of AFTER_LAST_ITEM, BEFORE_FIRST_ITEM, + * BETWEEN_TWO_ITEMS, or WITHIN_LIMITS. + */ + final int type; + + /** + * The limits before the coordinate; only populated when type == WITHIN_LIMITS or type == + * BETWEEN_TWO_ITEMS. + */ + Limits limitsBeforeCoordinate; + + /** + * The limits after the coordinate; only populated when type == BETWEEN_TWO_ITEMS. + */ + Limits limitsAfterCoordinate; + + // Limits of the first known item; only populated when type == BEFORE_FIRST_ITEM. + Limits mFirstKnownItem; + // Limits of the last known item; only populated when type == AFTER_LAST_ITEM. + Limits mLastKnownItem; + + /** + * @param limitsList The sorted limits list for the coordinate type. If this + * CoordinateLocation is an x-value, mXLimitsList should be passed; otherwise, + * mYLimitsList should be pased. + * @param value The coordinate value. + */ + RelativeCoordinate(List<Limits> limitsList, int value) { + Limits dummyLimits = new Limits(value, value); + int index = Collections.binarySearch(limitsList, dummyLimits); + + if (index >= 0) { + this.type = WITHIN_LIMITS; + this.limitsBeforeCoordinate = limitsList.get(index); + } else if (~index == 0) { + this.type = BEFORE_FIRST_ITEM; + this.mFirstKnownItem = limitsList.get(0); + } else if (~index == limitsList.size()) { + Limits lastLimits = limitsList.get(limitsList.size() - 1); + if (lastLimits.lowerLimit <= value && value <= lastLimits.upperLimit) { + this.type = WITHIN_LIMITS; + this.limitsBeforeCoordinate = lastLimits; + } else { + this.type = AFTER_LAST_ITEM; + this.mLastKnownItem = lastLimits; + } + } else { + Limits limitsBeforeIndex = limitsList.get(~index - 1); + if (limitsBeforeIndex.lowerLimit <= value && value <= limitsBeforeIndex.upperLimit) { + this.type = WITHIN_LIMITS; + this.limitsBeforeCoordinate = limitsList.get(~index - 1); + } else { + this.type = BETWEEN_TWO_ITEMS; + this.limitsBeforeCoordinate = limitsList.get(~index - 1); + this.limitsAfterCoordinate = limitsList.get(~index); + } + } + } + + int toComparisonValue() { + if (type == BEFORE_FIRST_ITEM) { + return mFirstKnownItem.lowerLimit - 1; + } else if (type == AFTER_LAST_ITEM) { + return mLastKnownItem.upperLimit + 1; + } else if (type == BETWEEN_TWO_ITEMS) { + return limitsBeforeCoordinate.upperLimit + 1; + } else { + return limitsBeforeCoordinate.lowerLimit; + } + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RelativeCoordinate)) { + return false; + } + + RelativeCoordinate otherCoordinate = (RelativeCoordinate) other; + return toComparisonValue() == otherCoordinate.toComparisonValue(); + } + + @Override + public int compareTo(RelativeCoordinate other) { + return toComparisonValue() - other.toComparisonValue(); + } + } + + /** + * The location of a point relative to the Limits of nearby items; consists of both an x- and + * y-RelativeCoordinateLocation. + */ + private class RelativePoint { + final RelativeCoordinate xLocation; + final RelativeCoordinate yLocation; + + RelativePoint(Point point) { + this.xLocation = new RelativeCoordinate(mXLimitsList, point.x); + this.yLocation = new RelativeCoordinate(mYLimitsList, point.y); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RelativePoint)) { + return false; + } + + RelativePoint otherPoint = (RelativePoint) other; + return xLocation.equals(otherPoint.xLocation) && yLocation.equals(otherPoint.yLocation); + } + + /** + * Generates a rectangle which contains the items selected by the two points. + * @param other The other PointLocation. A rectangle will be formed between "this" and + * "other". + * @return The rectangle, or null if no items were selected. + */ + Rect computeBounds(RelativePoint other) { + if (!areItemsCoveredBySelection(mRelativePointer, mRelativeOrigin)) { + return null; + } + + RelativeCoordinate minXLocation = + xLocation.compareTo(other.xLocation) < 0 ? xLocation : other.xLocation; + RelativeCoordinate maxXLocation = + minXLocation == xLocation ? other.xLocation : xLocation; + RelativeCoordinate minYLocation = + yLocation.compareTo(other.yLocation) < 0 ? yLocation : other.yLocation; + RelativeCoordinate maxYLocation = + minYLocation == yLocation ? other.yLocation : yLocation; + + Rect rect = new Rect(); + rect.left = getCoordinateValue(minXLocation, mXLimitsList, true); + rect.right = getCoordinateValue(maxXLocation, mXLimitsList, false); + rect.top = getCoordinateValue(minYLocation, mYLimitsList, true); + rect.bottom = getCoordinateValue(maxYLocation, mYLimitsList, false); + return rect; + } + + int getCoordinateValue(RelativeCoordinate coordinate, + List<Limits> limitsList, boolean isStartOfRange) { + switch (coordinate.type) { + case RelativeCoordinate.BEFORE_FIRST_ITEM: + return limitsList.get(0).lowerLimit; + case RelativeCoordinate.AFTER_LAST_ITEM: + return limitsList.get(limitsList.size() - 1).upperLimit; + case RelativeCoordinate.BETWEEN_TWO_ITEMS: + if (isStartOfRange) { + return coordinate.limitsAfterCoordinate.lowerLimit; + } else { + return coordinate.limitsBeforeCoordinate.upperLimit; + } + case RelativeCoordinate.WITHIN_LIMITS: + return coordinate.limitsBeforeCoordinate.lowerLimit; + } + + throw new RuntimeException("Invalid coordinate value."); + } + } + + private static boolean areItemsCoveredBySelection( + RelativePoint first, RelativePoint second) { + return doesCoordinateLocationCoverItems(first.xLocation, second.xLocation) && + doesCoordinateLocationCoverItems(first.yLocation, second.yLocation); + } + + private static boolean doesCoordinateLocationCoverItems( + RelativeCoordinate pointerCoordinate, + RelativeCoordinate originCoordinate) { + if (pointerCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM && + originCoordinate.type == RelativeCoordinate.BEFORE_FIRST_ITEM) { + return false; + } + + if (pointerCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM && + originCoordinate.type == RelativeCoordinate.AFTER_LAST_ITEM) { + return false; + } + + if (pointerCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS && + originCoordinate.type == RelativeCoordinate.BETWEEN_TWO_ITEMS && + pointerCoordinate.limitsBeforeCoordinate.equals(originCoordinate) && + pointerCoordinate.limitsAfterCoordinate.equals(originCoordinate)) { + return false; + } + + return true; + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index 9b8d84723c03..a804e9a5ca19 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java @@ -22,7 +22,6 @@ import static com.android.documentsui.DirectoryFragment.ANIM_SIDE; import static com.android.documentsui.DirectoryFragment.ANIM_UP; import static com.android.internal.util.Preconditions.checkArgument; -import android.annotation.Nullable; import android.app.Activity; import android.app.Fragment; import android.content.Intent; @@ -38,6 +37,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; +import android.support.annotation.Nullable; import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; @@ -60,8 +60,6 @@ import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; -import com.google.common.collect.Maps; - import libcore.io.IoUtils; import java.io.FileNotFoundException; @@ -371,7 +369,7 @@ abstract class BaseActivity extends Activity { public String currentSearch; /** Instance state for every shown directory */ - public HashMap<String, SparseArray<Parcelable>> dirState = Maps.newHashMap(); + public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>(); /** Currently copying file */ public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<DocumentInfo>(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index f9275951b9df..e408e6eb821d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.app.AlertDialog; import android.app.Dialog; @@ -36,7 +36,6 @@ import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; -import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 5223d760e96d..704e60781dd5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -24,14 +24,13 @@ import static com.android.documentsui.BaseActivity.State.MODE_GRID; import static com.android.documentsui.BaseActivity.State.MODE_LIST; import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorInt; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkState; -import android.annotation.NonNull; import android.app.Activity; import android.app.ActivityManager; import android.app.Fragment; @@ -98,7 +97,7 @@ import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.RootInfo; import com.android.internal.util.Preconditions; -import com.google.android.collect.Lists; +import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collections; @@ -235,8 +234,7 @@ public class DirectoryFragment extends Fragment { public void onLayoutChange( View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - int thumbSize = getResources().getDimensionPixelSize(R.dimen.grid_width); - mColumnCount = pickColumnCount(thumbSize); + mColumnCount = calculateColumnCount(); if (mGridLayout != null) { mGridLayout.setSpanCount(mColumnCount); } @@ -535,8 +533,6 @@ public class DirectoryFragment extends Fragment { updateLayout(state.derivedMode); - final int thumbSize = getResources().getDimensionPixelSize(R.dimen.icon_size); - mThumbSize = new Point(thumbSize, thumbSize); mRecView.setAdapter(mAdapter); } @@ -573,13 +569,15 @@ public class DirectoryFragment extends Fragment { mThumbSize = new Point(thumbSize, thumbSize); } - private int pickColumnCount(final int thumbSize) { - int itemPadding = - getResources().getDimensionPixelSize(R.dimen.grid_item_margin); + private int calculateColumnCount() { + int cellWidth = getResources().getDimensionPixelSize(R.dimen.grid_width); + int cellMargin = 2 * getResources().getDimensionPixelSize(R.dimen.grid_item_margin); int viewPadding = mRecView.getPaddingLeft() + mRecView.getPaddingRight(); + checkState(mRecView.getWidth() > 0); int columnCount = Math.max(1, - (mRecView.getWidth() - viewPadding) / (thumbSize + itemPadding)); + (mRecView.getWidth() - viewPadding) / (cellWidth + cellMargin)); + return columnCount; } @@ -753,7 +751,7 @@ public class DirectoryFragment extends Fragment { Intent intent; // Filter out directories - those can't be shared. - List<DocumentInfo> docsForSend = Lists.newArrayList(); + List<DocumentInfo> docsForSend = new ArrayList<>(); for (DocumentInfo doc: docs) { if (!Document.MIME_TYPE_DIR.equals(doc.mimeType)) { docsForSend.add(doc); @@ -774,8 +772,8 @@ public class DirectoryFragment extends Fragment { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.addCategory(Intent.CATEGORY_DEFAULT); - final ArrayList<String> mimeTypes = Lists.newArrayList(); - final ArrayList<Uri> uris = Lists.newArrayList(); + final ArrayList<String> mimeTypes = new ArrayList<>(); + final ArrayList<Uri> uris = new ArrayList<>(); for (DocumentInfo doc : docsForSend) { mimeTypes.add(doc.mimeType); uris.add(doc.derivedUri); @@ -956,7 +954,7 @@ public class DirectoryFragment extends Fragment { private final Context mContext; private final LayoutInflater mInflater; // TODO: Bring back support for footers. - private final List<Footer> mFooters = Lists.newArrayList(); + private final List<Footer> mFooters = new ArrayList<>(); private Cursor mCursor; private int mCursorCount; @@ -1330,7 +1328,7 @@ public class DirectoryFragment extends Fragment { return MimePredicate.mimeMatches(state.acceptMimes, docMimeType); } - private @NonNull List<DocumentInfo> getSelectedDocuments() { + private List<DocumentInfo> getSelectedDocuments() { Selection sel = mSelectionManager.getSelection(new Selection()); return getItemsAsDocuments(sel); } @@ -1570,6 +1568,7 @@ public class DirectoryFragment extends Fragment { final Cursor cursor = mAdapter.getItem(position); checkNotNull(cursor, "Cursor cannot be null."); final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor); + return Lists.newArrayList(doc); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index a8a61d28c222..0edb2413b0ca 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -16,12 +16,12 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN; +import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorInt; import android.content.AsyncTaskLoader; @@ -31,8 +31,6 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.CancellationSignal; -import android.os.Handler; -import android.os.Looper; import android.os.OperationCanceledException; import android.os.RemoteException; import android.provider.DocumentsContract; diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java index 237057570d49..6ba07fbbaf6a 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentClipper.java @@ -16,8 +16,6 @@ package com.android.documentsui; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipboardManager; import android.content.ContentProviderClient; @@ -26,15 +24,15 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.provider.DocumentsContract; +import android.support.annotation.Nullable; import android.util.Log; import com.android.documentsui.model.DocumentInfo; import com.android.internal.util.Preconditions; -import com.google.android.collect.Lists; - import libcore.io.IoUtils; +import java.util.ArrayList; import java.util.List; /** @@ -78,7 +76,7 @@ final class DocumentClipper { * Returns a list of Documents as decoded from Clipboard primary clipdata. * This should be run from inside an AsyncTask. */ - public @NonNull List<DocumentInfo> getClippedDocuments() { + public List<DocumentInfo> getClippedDocuments() { return getDocumentsFromClipData(mClipboard.getPrimaryClip()); } @@ -86,9 +84,9 @@ final class DocumentClipper { * Returns a list of Documents as decoded in clipData. * This should be run from inside an AsyncTask. */ - public @NonNull List<DocumentInfo> getDocumentsFromClipData(ClipData clipData) { + public List<DocumentInfo> getDocumentsFromClipData(ClipData clipData) { Preconditions.checkNotNull(clipData); - final List<DocumentInfo> srcDocs = Lists.newArrayList(); + final List<DocumentInfo> srcDocs = new ArrayList<>(); int count = clipData.getItemCount(); if (count == 0) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java index 1cbc221e1057..17a1161f7c25 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java +++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import static com.android.documentsui.model.DocumentInfo.getCursorLong; import static com.android.documentsui.model.DocumentInfo.getCursorString; diff --git a/packages/DocumentsUI/src/com/android/documentsui/GridItem.java b/packages/DocumentsUI/src/com/android/documentsui/GridItem.java new file mode 100644 index 000000000000..990dca70f9c6 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/GridItem.java @@ -0,0 +1,51 @@ +/* + * 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; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * A FrameLayout subclass used by DirectoryFragment. Ensures that the resulting grid item is always + * square. + */ +public class GridItem extends FrameLayout { + public GridItem(Context context) { + super(context); + } + + public GridItem(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public GridItem(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Grid layout uses item width to figure out the number of columns, then dynamically fits + // rows into the view. The upshot of this is that changing the item width will mess up the + // grid layout - so to make the items square, throw out the height and use the width for + // both dimensions. The grid layout will correctly adjust the row height. + // + // Note that this code will need to be changed if the layout manager's orientation is + // changed from VERTICAL to HORIZONTAL. + super.onMeasure(widthMeasureSpec, widthMeasureSpec); + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java index b43fedf3d1fb..99592659a7fb 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java +++ b/packages/DocumentsUI/src/com/android/documentsui/IconUtils.java @@ -23,13 +23,11 @@ import android.graphics.drawable.Drawable; import android.provider.DocumentsContract.Document; import android.util.TypedValue; -import com.google.android.collect.Maps; - import java.util.HashMap; public class IconUtils { - private static HashMap<String, Integer> sMimeIcons = Maps.newHashMap(); + private static HashMap<String, Integer> sMimeIcons = new HashMap<>(); private static void add(String mimeType, int resId) { if (sMimeIcons.put(mimeType, resId) != null) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java index 02edd0c87448..f87fe4ccfa46 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/MultiSelectManager.java @@ -31,7 +31,7 @@ import android.view.GestureDetector.OnGestureListener; import android.view.MotionEvent; import android.view.View; -import com.google.common.annotations.VisibleForTesting; +import android.support.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.List; diff --git a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java index f94aebd50d61..b0e332faa3fc 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java +++ b/packages/DocumentsUI/src/com/android/documentsui/ProviderExecutor.java @@ -20,8 +20,6 @@ import android.os.AsyncTask; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.google.android.collect.Lists; -import com.google.android.collect.Maps; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -32,7 +30,7 @@ import java.util.concurrent.LinkedBlockingQueue; public class ProviderExecutor extends Thread implements Executor { @GuardedBy("sExecutors") - private static HashMap<String, ProviderExecutor> sExecutors = Maps.newHashMap(); + private static HashMap<String, ProviderExecutor> sExecutors = new HashMap<>(); public static ProviderExecutor forAuthority(String authority) { synchronized (sExecutors) { @@ -53,7 +51,7 @@ public class ProviderExecutor extends Thread implements Executor { private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<Runnable>(); - private final ArrayList<WeakReference<Preemptable>> mPreemptable = Lists.newArrayList(); + private final ArrayList<WeakReference<Preemptable>> mPreemptable = new ArrayList<>(); private void preempt() { synchronized (mPreemptable) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java index 878c4c217588..4685c41fc2b6 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java +++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java @@ -18,7 +18,6 @@ package com.android.documentsui; import static com.android.documentsui.model.DocumentInfo.getCursorString; -import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipDescription; import android.content.ComponentName; @@ -28,6 +27,7 @@ import android.database.Cursor; import android.net.Uri; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; +import android.support.annotation.Nullable; import android.util.Log; import com.android.documentsui.BaseActivity.DocumentContext; diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java index f5908c55cfa2..1a7095a054c3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -16,8 +16,8 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED; +import static com.android.documentsui.Shared.TAG; import android.app.ActivityManager; import android.content.AsyncTaskLoader; @@ -36,14 +36,14 @@ import android.util.Log; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; -import com.google.android.collect.Maps; -import com.google.common.collect.Lists; + import com.google.common.util.concurrent.AbstractFuture; import libcore.io.IoUtils; import java.io.Closeable; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -53,7 +53,7 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { - private static final boolean LOGD = true; + private static final boolean DEBUG = false; // TODO: clean up cursor ownership so background thread doesn't traverse // previously returned cursors for filtering/sorting; this currently races @@ -81,7 +81,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { private final RootsCache mRoots; private final State mState; - private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap(); + private final HashMap<RootInfo, RecentTask> mTasks = new HashMap<>(); private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED; @@ -196,7 +196,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { // Collect all finished tasks boolean allDone = true; - List<Cursor> cursors = Lists.newArrayList(); + List<Cursor> cursors = new ArrayList<>(); for (RecentTask task : mTasks.values()) { if (task.isDone()) { try { @@ -221,7 +221,7 @@ public class RecentLoader extends AsyncTaskLoader<DirectoryResult> { } } - if (LOGD) { + if (DEBUG) { Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done"); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java index e11d7d967961..662822ee35d3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.app.Fragment; import android.app.FragmentManager; @@ -50,7 +50,6 @@ import com.android.documentsui.RecentsProvider.RecentColumns; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; import com.android.documentsui.model.RootInfo; -import com.google.android.collect.Lists; import libcore.io.IoUtils; @@ -157,7 +156,7 @@ public class RecentsCreateFragment extends Fragment { @Override public List<DocumentStack> loadInBackground(Uri uri, CancellationSignal signal) { final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState); - final ArrayList<DocumentStack> result = Lists.newArrayList(); + final ArrayList<DocumentStack> result = new ArrayList<>(); final ContentResolver resolver = getContext().getContentResolver(); final Cursor cursor = resolver.query( diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index fbcb938692af..05f7d8dd11e3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -16,7 +16,7 @@ package com.android.documentsui; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.content.ContentProviderClient; import android.content.ContentResolver; @@ -39,14 +39,14 @@ import android.util.Log; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.google.android.collect.Lists; -import com.google.android.collect.Sets; +import android.support.annotation.VisibleForTesting; + import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import libcore.io.IoUtils; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -74,10 +74,10 @@ public class RootsCache { @GuardedBy("mLock") private Multimap<String, RootInfo> mRoots = ArrayListMultimap.create(); @GuardedBy("mLock") - private HashSet<String> mStoppedAuthorities = Sets.newHashSet(); + private HashSet<String> mStoppedAuthorities = new HashSet<>(); @GuardedBy("mObservedAuthorities") - private final HashSet<String> mObservedAuthorities = Sets.newHashSet(); + private final HashSet<String> mObservedAuthorities = new HashSet<>(); public RootsCache(Context context) { mContext = context; @@ -159,7 +159,7 @@ public class RootsCache { private final String mFilterPackage; private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create(); - private final HashSet<String> mTaskStoppedAuthorities = Sets.newHashSet(); + private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>(); /** * Update all roots. @@ -251,7 +251,7 @@ public class RootsCache { } } - final List<RootInfo> roots = Lists.newArrayList(); + final List<RootInfo> roots = new ArrayList<>(); final Uri rootsUri = DocumentsContract.buildRootsUri(authority); ContentProviderClient client = null; @@ -350,7 +350,7 @@ public class RootsCache { @VisibleForTesting static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) { - final List<RootInfo> matching = Lists.newArrayList(); + final List<RootInfo> matching = new ArrayList<>(); for (RootInfo root : roots) { final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0; final boolean supportsIsChild = (root.flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0; diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index fd67a77254a6..c02184b72b2f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -44,8 +44,8 @@ import android.widget.TextView; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.RootInfo; -import com.google.common.collect.Lists; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -293,8 +293,8 @@ public class RootsFragment extends Fragment { RootItem audio = null; RootItem downloads = null; - final List<RootInfo> clouds = Lists.newArrayList(); - final List<RootInfo> locals = Lists.newArrayList(); + final List<RootInfo> clouds = new ArrayList<>(); + final List<RootInfo> locals = new ArrayList<>(); for (RootInfo root : roots) { if (root.isRecents()) { @@ -338,7 +338,7 @@ public class RootsFragment extends Fragment { final List<ResolveInfo> infos = pm.queryIntentActivities( includeApps, PackageManager.MATCH_DEFAULT_ONLY); - final List<AppItem> apps = Lists.newArrayList(); + final List<AppItem> apps = new ArrayList<>(); // Omit ourselves from the list for (ResolveInfo info : infos) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java index 088e3fa70d43..ae959f91dec0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java +++ b/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java @@ -22,8 +22,6 @@ import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListAdapter; -import com.google.android.collect.Lists; - import java.util.ArrayList; /** @@ -31,7 +29,7 @@ import java.util.ArrayList; * provide a header, and correctly handling item types across child adapters. */ public class SectionedListAdapter extends BaseAdapter { - private ArrayList<SectionAdapter> mSections = Lists.newArrayList(); + private ArrayList<SectionAdapter> mSections = new ArrayList<>(); public interface SectionAdapter extends ListAdapter { public View getHeaderView(View convertView, ViewGroup parent); diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java new file mode 100644 index 000000000000..b414ee3269d8 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java @@ -0,0 +1,24 @@ +/* + * 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; + +/** + * @hide + */ +public final class Shared { + public static final String TAG = "Documents"; +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java index a935a0053429..7ca3954036bc 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java @@ -20,7 +20,6 @@ import static com.android.documentsui.DirectoryFragment.ANIM_DOWN; import static com.android.documentsui.DirectoryFragment.ANIM_NONE; import static com.android.documentsui.DirectoryFragment.ANIM_UP; -import android.annotation.Nullable; import android.app.Activity; import android.app.FragmentManager; import android.content.ActivityNotFoundException; @@ -33,6 +32,7 @@ import android.net.Uri; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Root; +import android.support.annotation.Nullable; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; diff --git a/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java b/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java index 1a5bb0c099a8..7bb662c72cf5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/UriDerivativeLoader.java @@ -27,16 +27,16 @@ import android.os.OperationCanceledException; * changes while started, manages {@link CancellationSignal}, and caches * returned results. */ -public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { +public abstract class UriDerivativeLoader<Param, Res> extends AsyncTaskLoader<Res> { final ForceLoadContentObserver mObserver; - private final P mParam; + private final Param mParam; - private R mResult; + private Res mResult; private CancellationSignal mCancellationSignal; @Override - public final R loadInBackground() { + public final Res loadInBackground() { synchronized (this) { if (isLoadInBackgroundCanceled()) { throw new OperationCanceledException(); @@ -52,7 +52,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } } - public abstract R loadInBackground(P param, CancellationSignal signal); + public abstract Res loadInBackground(Param param, CancellationSignal signal); @Override public void cancelLoadInBackground() { @@ -66,12 +66,12 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } @Override - public void deliverResult(R result) { + public void deliverResult(Res result) { if (isReset()) { closeQuietly(result); return; } - R oldResult = mResult; + Res oldResult = mResult; mResult = result; if (isStarted()) { @@ -83,7 +83,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } } - public UriDerivativeLoader(Context context, P param) { + public UriDerivativeLoader(Context context, Param param) { super(context); mObserver = new ForceLoadContentObserver(); mParam = param; @@ -105,7 +105,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { } @Override - public void onCanceled(R result) { + public void onCanceled(Res result) { closeQuietly(result); } @@ -122,7 +122,7 @@ public abstract class UriDerivativeLoader<P, R> extends AsyncTaskLoader<R> { getContext().getContentResolver().unregisterContentObserver(mObserver); } - private void closeQuietly(R result) { + private void closeQuietly(Res result) { if (result instanceof AutoCloseable) { try { ((AutoCloseable) result).close(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java index 5d5f2ebabf2e..cc981e1eaaad 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java @@ -24,7 +24,6 @@ import android.os.Parcel; import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; -import android.provider.DocumentsContract.Root; import android.provider.DocumentsProvider; import android.text.TextUtils; diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java b/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java index 2a29cbc23d0c..e21dd4930678 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java +++ b/packages/DocumentsUI/src/com/android/documentsui/model/DurableUtils.java @@ -16,7 +16,7 @@ package com.android.documentsui.model; -import static com.android.documentsui.DocumentsActivity.TAG; +import static com.android.documentsui.Shared.TAG; import android.os.BadParcelableException; import android.os.Parcel; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java new file mode 100644 index 000000000000..f15a6430388c --- /dev/null +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/BandSelectMatrixTest.java @@ -0,0 +1,277 @@ +/* + * 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; + +import static org.junit.Assert.*; + +import com.android.documentsui.BandSelectMatrix; + +import android.graphics.Point; +import android.graphics.Rect; +import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.util.SparseBooleanArray; + +import org.junit.After; +import org.junit.Test; + +public class BandSelectMatrixTest { + + private static final int VIEW_PADDING_PX = 5; + private static final int CHILD_VIEW_EDGE_PX = 100; + private static final int VIEWPORT_HEIGHT = 500; + + private static BandSelectMatrix matrix; + private static TestHelper helper; + private static SparseBooleanArray lastSelection; + private static int viewWidth; + + private static void setUp(int numChildren, int numColumns) { + helper = new TestHelper(numChildren, numColumns); + viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX); + matrix = new BandSelectMatrix(helper); + matrix.addOnSelectionChangedListener(new BandSelectMatrix.OnSelectionChangedListener() { + + @Override + public void onSelectionChanged(SparseBooleanArray updatedSelection) { + lastSelection = updatedSelection; + } + }); + } + + @After + public void tearDown() { + matrix = null; + helper = null; + lastSelection = null; + } + + @Test + public void testSelectionLeftOfItems() { + setUp(20, 5); + matrix.startSelection(new Point(0, 10)); + matrix.resizeSelection(new Point(1, 11)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionRightOfItems() { + setUp(20, 4); + matrix.startSelection(new Point(viewWidth - 1, 10)); + matrix.resizeSelection(new Point(viewWidth - 2, 11)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionAboveItems() { + setUp(20, 4); + matrix.startSelection(new Point(10, 0)); + matrix.resizeSelection(new Point(11, 1)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionBelowItems() { + setUp(5, 4); + matrix.startSelection(new Point(10, VIEWPORT_HEIGHT - 1)); + matrix.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2)); + assertSelected(new int[0]); + } + + @Test + public void testVerticalSelectionBetweenItems() { + setUp(20, 4); + matrix.startSelection(new Point(106, 0)); + matrix.resizeSelection(new Point(107, 200)); + assertSelected(new int[0]); + } + + @Test + public void testHorizontalSelectionBetweenItems() { + setUp(20, 4); + matrix.startSelection(new Point(0, 105)); + matrix.resizeSelection(new Point(200, 106)); + assertSelected(new int[0]); + } + + @Test + public void testGrowingAndShrinkingSelection() { + setUp(20, 4); + matrix.startSelection(new Point(0, 0)); + matrix.resizeSelection(new Point(5, 5)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(109, 109)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(110, 109)); + assertSelected(new int[] {0, 1}); + matrix.resizeSelection(new Point(110, 110)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(214, 214)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(215, 214)); + assertSelected(new int[] {0, 1, 2, 4, 5, 6}); + matrix.resizeSelection(new Point(214, 214)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(110, 110)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(110, 109)); + assertSelected(new int[] {0, 1}); + matrix.resizeSelection(new Point(109, 109)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(5, 5)); + assertSelected(new int[] {0}); + matrix.resizeSelection(new Point(0, 0)); + assertSelected(new int[0]); + } + + @Test + public void testSelectionMovingAroundOrigin() { + setUp(16, 4); + matrix.startSelection(new Point(210, 210)); + matrix.resizeSelection(new Point(viewWidth - 1, 0)); + assertSelected(new int[] {2, 3, 6, 7}); + matrix.resizeSelection(new Point(0, 0)); + assertSelected(new int[] {0, 1, 4, 5}); + matrix.resizeSelection(new Point(0, 420)); + assertSelected(new int[] {8, 9, 12, 13}); + matrix.resizeSelection(new Point(viewWidth - 1, 420)); + assertSelected(new int[] {10, 11, 14, 15}); + } + + @Test + public void testScrollingBandSelect() { + setUp(40, 4); + matrix.startSelection(new Point(0, 0)); + matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); + assertSelected(new int[] {0, 4, 8, 12, 16}); + scroll(CHILD_VIEW_EDGE_PX); + assertSelected(new int[] {0, 4, 8, 12, 16, 20}); + matrix.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1)); + assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21}); + scroll(CHILD_VIEW_EDGE_PX); + assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25}); + scroll(-2 * CHILD_VIEW_EDGE_PX); + assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17}); + matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1)); + assertSelected(new int[] {0, 4, 8, 12, 16}); + } + + private static void assertSelected(int[] selectedPositions) { + assertEquals(selectedPositions.length, lastSelection.size()); + for (int position : selectedPositions) { + assertTrue(lastSelection.get(position)); + } + } + + private static void scroll(int dy) { + assertTrue(helper.verticalOffset + VIEWPORT_HEIGHT + dy <= helper.getTotalHeight()); + helper.verticalOffset += dy; + matrix.onScrolled(null, 0, dy); + } + + private static final class TestHelper implements BandSelectMatrix.RecyclerViewHelper { + + public int horizontalOffset = 0; + public int verticalOffset = 0; + private final int mNumColumns; + private final int mNumRows; + private final int mNumChildren; + + public TestHelper(int numChildren, int numColumns) { + mNumChildren = numChildren; + mNumColumns = numColumns; + mNumRows = (int) Math.ceil((double) numChildren / mNumColumns); + } + + private int getTotalHeight() { + return CHILD_VIEW_EDGE_PX * mNumRows + VIEW_PADDING_PX * (mNumRows + 1); + } + + private int getFirstVisibleRowIndex() { + return verticalOffset / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + } + + private int getLastVisibleRowIndex() { + int lastVisibleRowUncapped = + (VIEWPORT_HEIGHT + verticalOffset - 1) / (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + return Math.min(lastVisibleRowUncapped, mNumRows - 1); + } + + private int getNumItemsInRow(int index) { + assertTrue(index >= 0 && index < mNumRows); + if (index == mNumRows - 1 && mNumChildren % mNumColumns != 0) { + return mNumChildren % mNumColumns; + } + + return mNumColumns; + } + + @Override + public void addOnScrollListener(OnScrollListener listener) {} + + @Override + public void removeOnScrollListener(OnScrollListener listener) {} + + @Override + public Point createAbsolutePoint(Point relativePoint) { + return new Point( + relativePoint.x + horizontalOffset, relativePoint.y + verticalOffset); + } + + @Override + public int getVisibleChildCount() { + int childCount = 0; + for (int i = getFirstVisibleRowIndex(); i <= getLastVisibleRowIndex(); i++) { + childCount += getNumItemsInRow(i); + } + return childCount; + } + + @Override + public int getAdapterPositionAt(int index) { + return index + mNumColumns * (getFirstVisibleRowIndex()); + } + + @Override + public Rect getAbsoluteRectForChildViewAt(int index) { + int adapterPosition = getAdapterPositionAt(index); + int rowIndex = adapterPosition / mNumColumns; + int columnIndex = adapterPosition % mNumColumns; + + Rect rect = new Rect(); + rect.top = VIEW_PADDING_PX + rowIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + rect.bottom = rect.top + CHILD_VIEW_EDGE_PX - 1; + rect.left = VIEW_PADDING_PX + columnIndex * (CHILD_VIEW_EDGE_PX + VIEW_PADDING_PX); + rect.right = rect.left + CHILD_VIEW_EDGE_PX - 1; + return rect; + } + + @Override + public int getTotalChildCount() { + return mNumChildren; + } + + @Override + public int getNumColumns() { + return mNumColumns; + } + + @Override + public int getNumRows() { + return mNumRows; + } + } +} diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java index 8c5bac15442d..132570674b48 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsCacheTest.java @@ -21,7 +21,8 @@ import android.test.suitebuilder.annotation.SmallTest; import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; -import com.google.android.collect.Lists; + +import com.google.common.collect.Lists; import java.util.List; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java index c2f176221f4b..6a2e03a3b809 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java @@ -36,8 +36,6 @@ import android.provider.DocumentsProvider; import android.support.annotation.VisibleForTesting; import android.util.Log; -import com.google.android.collect.Maps; - import libcore.io.IoUtils; import java.io.File; @@ -101,7 +99,7 @@ public class StubProvider extends DocumentsProvider { }); } // Create new roots. - mRoots = Maps.newHashMap(); + mRoots = new HashMap<>(); for (String rootId : rootIds) { final RootInfo rootInfo = new RootInfo(rootId, getSize(rootId)); mRoots.put(rootId, rootInfo); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java index 4ffe799119ec..055327090eb4 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/UnitTests.java @@ -22,6 +22,7 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses({ + BandSelectMatrixTest.class, MultiSelectManager_SelectionTest.class, MultiSelectManagerTest.class }) diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java index 98775b36f796..7126694bc4a5 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocument.java @@ -32,6 +32,7 @@ class MtpDocument { private final Date mDateModified; private final int mSize; private final int mThumbSize; + private final boolean mReadOnly; /** * Constructor for root document. @@ -40,9 +41,10 @@ class MtpDocument { this(DUMMY_HANDLE_FOR_ROOT, 0x3001, // Directory. root.mDescription, - null, // Unknown, + null, // Unknown name. (int) Math.min(root.mMaxCapacity - root.mFreeSpace, Integer.MAX_VALUE), - 0); + 0, // Total size. + true); // Writable. } MtpDocument(MtpObjectInfo objectInfo) { @@ -51,7 +53,8 @@ class MtpDocument { objectInfo.getName(), objectInfo.getDateModified() != 0 ? new Date(objectInfo.getDateModified()) : null, objectInfo.getCompressedSize(), - objectInfo.getThumbCompressedSize()); + objectInfo.getThumbCompressedSize(), + objectInfo.getProtectionStatus() != 0); } MtpDocument(int objectHandle, @@ -59,13 +62,15 @@ class MtpDocument { String name, Date dateModified, int size, - int thumbSize) { + int thumbSize, + boolean readOnly) { this.mObjectHandle = objectHandle; this.mFormat = format; this.mName = name; this.mDateModified = dateModified; this.mSize = size; this.mThumbSize = thumbSize; + this.mReadOnly = readOnly; } void addToCursor(Identifier rootIdentifier, MatrixCursor.RowBuilder builder) { @@ -82,7 +87,7 @@ class MtpDocument { builder.add(Document.COLUMN_DOCUMENT_ID, identifier.toDocumentId()); builder.add(Document.COLUMN_DISPLAY_NAME, mName); - builder.add(Document.COLUMN_MIME_TYPE, getMimeType()); + builder.add(Document.COLUMN_MIME_TYPE, formatTypeToMimeType(mFormat)); builder.add( Document.COLUMN_LAST_MODIFIED, mDateModified != null ? mDateModified.getTime() : null); @@ -90,9 +95,9 @@ class MtpDocument { builder.add(Document.COLUMN_SIZE, mSize); } - private String getMimeType() { + static String formatTypeToMimeType(int format) { // TODO: Add complete list of mime types. - switch (mFormat) { + switch (format) { case 0x3001: return DocumentsContract.Document.MIME_TYPE_DIR; case 0x3009: @@ -100,7 +105,21 @@ class MtpDocument { case 0x3801: return "image/jpeg"; default: - return ""; + return "application/octet-stream"; + } + } + + static int mimeTypeToFormatType(String mimeType) { + // TODO: Add complete list of mime types. + switch (mimeType.toLowerCase()) { + case Document.MIME_TYPE_DIR: + return 0x3001; + case "audio/mp3": + return 0x3009; + case "image/jpeg": + return 0x3801; + default: + return 0x3000; // Undefined object. } } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 61b9fc53b98f..031cc074672a 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -96,7 +96,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { final Identifier rootIdentifier = new Identifier(root.mDeviceId, root.mStorageId); final MatrixCursor.RowBuilder builder = cursor.newRow(); builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId()); - builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD); + builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); builder.add(Root.COLUMN_TITLE, root.mDescription); builder.add( Root.COLUMN_DOCUMENT_ID, @@ -214,6 +214,24 @@ public class MtpDocumentsProvider extends DocumentsProvider { mDocumentLoader.clearCache(); } + @Override + public String createDocument(String parentDocumentId, String mimeType, String displayName) + throws FileNotFoundException { + try { + final Identifier parentId = Identifier.createFromDocumentId(parentDocumentId); + final int objectHandle = mMtpManager.createDocument( + parentId.mDeviceId, parentId.mStorageId, parentId.mObjectHandle, mimeType, + displayName); + final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId, + objectHandle).toDocumentId(); + notifyChildDocumentsChange(parentDocumentId); + return documentId; + } catch (IOException error) { + Log.e(TAG, error.getMessage()); + throw new FileNotFoundException(error.getMessage()); + } + } + void openDevice(int deviceId) throws IOException { mMtpManager.openDevice(deviceId); mRootScanner.scanNow(); diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java index 3afc173fcc52..27ba794d3883 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java @@ -21,7 +21,10 @@ import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.mtp.MtpDevice; +import android.mtp.MtpObjectInfo; import android.os.ParcelFileDescriptor; +import android.provider.DocumentsContract.Document; +import android.provider.DocumentsContract; import android.util.SparseArray; import java.io.FileNotFoundException; @@ -134,6 +137,22 @@ class MtpManager { } } + synchronized int createDocument(int deviceId, int storageId, int parentObjectHandle, + String mimeType, String name) throws IOException { + final MtpDevice device = getDevice(deviceId); + final MtpObjectInfo objectInfo = new MtpObjectInfo.Builder() + .setName(name) + .setStorageId(storageId) + .setParent(parentObjectHandle) + .setFormat(MtpDocument.mimeTypeToFormatType(mimeType)) + .build(); + final MtpObjectInfo result = device.sendObjectInfo(objectInfo); + if (result == null) { + throw new IOException("Failed to create a document"); + } + return result.getObjectHandle(); + } + synchronized int getParent(int deviceId, int objectHandle) throws IOException { final MtpDevice device = getDevice(deviceId); final int result = (int) device.getParent(objectHandle); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java index 55041478f105..1e015bdc78d5 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/DocumentLoaderTest.java @@ -91,7 +91,8 @@ public class DocumentLoaderTest extends AndroidTestCase { "file" + objectHandle, new Date(), 1024, - 0 /* thumbnail size */)); + 0 /* thumbnail size */, + false /* not read only */)); } manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments); } diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index c1da59f5cb43..f06e2ffacf9a 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -210,7 +210,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "image.jpg" /* display name */, new Date(1422716400000L) /* modified date */, 1024 * 1024 * 5 /* file size */, - 1024 * 50 /* thumbnail size */)); + 1024 * 50 /* thumbnail size */, + true /* read only */)); final Cursor cursor = mProvider.queryDocument("0_1_2", null); assertEquals(1, cursor.getCount()); @@ -257,7 +258,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "image.jpg" /* display name */, new Date(0) /* modified date */, 1024 * 1024 * 5 /* file size */, - 1024 * 50 /* thumbnail size */)); + 1024 * 50 /* thumbnail size */, + true /* read only */)); final Cursor cursor = mProvider.queryChildDocuments("0_0_0", null, null); assertEquals(1, cursor.getCount()); @@ -302,7 +304,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { "image.jpg" /* display name */, new Date(1422716400000L) /* modified date */, 1024 * 1024 * 5 /* file size */, - 1024 * 50 /* thumbnail size */)); + 1024 * 50 /* thumbnail size */, + false /* not read only */)); mMtpManager.setParent(0, 1, 2); mProvider.deleteDocument("0_0_1"); assertEquals(1, mResolver.getChangeCount( diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java index 096a5c43e12c..f52d75502e7d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java @@ -54,29 +54,7 @@ public class TetherUtil { public static boolean setWifiTethering(boolean enable, Context context) { final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - final ContentResolver cr = context.getContentResolver(); - /** - * Disable Wifi if enabling tethering - */ - int wifiState = wifiManager.getWifiState(); - if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) || - (wifiState == WifiManager.WIFI_STATE_ENABLED))) { - wifiManager.setWifiEnabled(false); - Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1); - } - - boolean success = wifiManager.setWifiApEnabled(null, enable); - /** - * If needed, restore Wifi on tether disable - */ - if (!enable) { - int wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE, 0); - if (wifiSavedState == 1) { - wifiManager.setWifiEnabled(true); - Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0); - } - } - return success; + return wifiManager.setWifiApEnabled(null, enable); } public static boolean isWifiTetherEnabled(Context context) { diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 34aeb60a1104..f72474983182 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -401,21 +401,40 @@ public class BackupManagerService { public boolean isSystemRestore; public String[] filterSet; - // Restore a single package + /** + * Restore a single package; no kill after restore + */ RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, - long _token, PackageInfo _pkg, int _pmToken) { + long _token, PackageInfo _pkg) { transport = _transport; dirName = _dirName; observer = _obs; token = _token; pkgInfo = _pkg; - pmToken = _pmToken; + pmToken = 0; isSystemRestore = false; filterSet = null; } - // Restore everything possible. This is the form that Setup Wizard or similar - // restore UXes use. + /** + * Restore at install: PM token needed, kill after restore + */ + RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, + long _token, String _pkgName, int _pmToken) { + transport = _transport; + dirName = _dirName; + observer = _obs; + token = _token; + pkgInfo = null; + pmToken = _pmToken; + isSystemRestore = false; + filterSet = new String[] { _pkgName }; + } + + /** + * Restore everything possible. This is the form that Setup Wizard or similar + * restore UXes use. + */ RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token) { transport = _transport; @@ -428,8 +447,10 @@ public class BackupManagerService { filterSet = null; } - // Restore some set of packages. Leave this one up to the caller to specify - // whether it's to be considered a system-level restore. + /** + * Restore some set of packages. Leave this one up to the caller to specify + * whether it's to be considered a system-level restore. + */ RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs, long _token, String[] _filterSet, boolean _isSystemRestore) { transport = _transport; @@ -9137,19 +9158,13 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // This can throw and so *must* happen before the wakelock is acquired String dirName = transport.transportDirName(); - // We can use a synthetic PackageInfo here because: - // 1. We know it's valid, since the Package Manager supplied the name - // 2. Only the packageName field will be used by the restore code - PackageInfo pkg = new PackageInfo(); - pkg.packageName = packageName; - mWakelock.acquire(); if (MORE_DEBUG) { Slog.d(TAG, "Restore at install of " + packageName); } Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); msg.obj = new RestoreParams(transport, dirName, null, - restoreSet, pkg, token); + restoreSet, packageName, token); mBackupHandler.sendMessage(msg); } catch (RemoteException e) { // Binding to the transport broke; back off and proceed with the installation. @@ -9528,8 +9543,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF Slog.d(TAG, "restorePackage() : " + packageName); } Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE); - msg.obj = new RestoreParams(mRestoreTransport, dirName, - observer, token, app, 0); + msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token, app); mBackupHandler.sendMessage(msg); } finally { Binder.restoreCallingIdentity(oldId); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 55a26599a92d..c1b07538c9dc 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.DOCKED_STACK_ID; import static android.app.ActivityManager.HOME_STACK_ID; +import static android.app.ActivityManager.INVALID_STACK_ID; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; @@ -9005,8 +9006,7 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack == null) { - throw new IllegalArgumentException( - "getActivityStackId: No stack for token=" + token); + return INVALID_STACK_ID; } return stack.mStackId; } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 222945c2f594..e87dcdea77e6 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH; import com.android.server.input.InputApplicationHandle; import com.android.server.input.InputWindowHandle; import com.android.server.wm.WindowManagerService.DragInputEventReceiver; @@ -413,7 +414,7 @@ class DragState { continue; } - child.getTaskBounds(mTmpRect); + child.getTaskBounds(mTmpRect, !BOUNDS_FOR_TOUCH); if (!mTmpRect.contains(x, y)) { // outside of this window's activity stack == don't tell about drags continue; diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 65f26c1c0afa..b3244ffb980f 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH; import android.app.ActivityManagerNative; import android.graphics.Rect; import android.os.RemoteException; @@ -177,7 +178,7 @@ final class InputMonitor implements InputManagerService.WindowManagerCallbacks { if (modal && child.mAppToken != null) { // Limit the outer touch to the activity stack region. flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; - child.getTaskBounds(mTmpRect); + child.getTaskBounds(mTmpRect, BOUNDS_FOR_TOUCH); inputWindowHandle.touchableRegion.set(mTmpRect); } else { // Not modal or full screen modal diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index b871d7f0f501..d0962f46ddcf 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -98,6 +99,8 @@ class WallpaperController { private static final int WALLPAPER_DRAW_TIMEOUT = 2; private int mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; + private final FindWallpaperTargetResult mFindResults = new FindWallpaperTargetResult(); + public WallpaperController(WindowManagerService service) { mService = service; } @@ -466,39 +469,24 @@ class WallpaperController { } } - boolean adjustWallpaperWindows() { - mService.mInnerFields.mWallpaperMayChange = false; - boolean targetChanged = false; - - // TODO(multidisplay): Wallpapers on main screen only. - final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo(); - final int dw = displayInfo.logicalWidth; - final int dh = displayInfo.logicalHeight; + private void findWallpaperTarget(WindowList windows, FindWallpaperTargetResult result) { final WindowAnimator winAnimator = mService.mAnimator; - - // First find top-most window that has asked to be on top of the - // wallpaper; all wallpapers go behind it. - final WindowList windows = mService.getDefaultWindowListLocked(); - int N = windows.size(); + result.reset(); WindowState w = null; - WindowState foundW = null; - int foundI = 0; - WindowState topCurW = null; - int topCurI = 0; int windowDetachedI = -1; - int i = N; - while (i > 0) { - i--; + boolean resetTopWallpaper = false; + boolean inFreeformSpace = false; + for (int i = windows.size() - 1; i >= 0; i--) { w = windows.get(i); if ((w.mAttrs.type == TYPE_WALLPAPER)) { - if (topCurW == null) { - topCurW = w; - topCurI = i; + if (result.topWallpaper == null || resetTopWallpaper) { + result.setTopWallpaper(w, i); + resetTopWallpaper = false; } continue; } - topCurW = null; + resetTopWallpaper = true; if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) { // If this window's app token is hidden and not animating, // it is of no interest to us. @@ -511,23 +499,24 @@ class WallpaperController { if (DEBUG_WALLPAPER) Slog.v(TAG, "Win #" + i + " " + w + ": isOnScreen=" + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState); - // If the app is executing an animation because the keyguard is going away, keep the - // wallpaper during the animation so it doesn't flicker out. - final boolean hasWallpaper = (w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 - || (w.mAppToken != null - && w.mWinAnimator.mKeyguardGoingAwayAnimation); + if (!inFreeformSpace) { + TaskStack stack = w.getStack(); + inFreeformSpace = stack != null && stack.mStackId == FREEFORM_WORKSPACE_STACK_ID; + } + + // If the app is executing an animation because the keyguard is going away, + // keep the wallpaper during the animation so it doesn't flicker out. + final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0 + || (w.mAppToken != null && w.mWinAnimator.mKeyguardGoingAwayAnimation); if (hasWallpaper && w.isOnScreen() && (mWallpaperTarget == w || w.isDrawFinishedLw())) { - if (DEBUG_WALLPAPER) Slog.v(TAG, - "Found wallpaper target: #" + i + "=" + w); - foundW = w; - foundI = i; + if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: #" + i + "=" + w); + result.setWallpaperTarget(w, i); if (w == mWallpaperTarget && w.mWinAnimator.isAnimating()) { - // The current wallpaper target is animating, so we'll - // look behind it for another possible target and figure - // out what is going on below. - if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w - + ": token animating, looking behind."); + // The current wallpaper target is animating, so we'll look behind it for + // another possible target and figure out what is going on later. + if (DEBUG_WALLPAPER) Slog.v(TAG, + "Win " + w + ": token animating, looking behind."); continue; } break; @@ -536,76 +525,78 @@ class WallpaperController { } } - if (foundW == null && windowDetachedI >= 0) { + if (result.wallpaperTarget == null && windowDetachedI >= 0) { if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, - "Found animating detached wallpaper activity: #" + i + "=" + w); - foundW = w; - foundI = windowDetachedI; + "Found animating detached wallpaper activity: #" + windowDetachedI + "=" + w); + result.setWallpaperTarget(w, windowDetachedI); + } + if (result.wallpaperTarget == null && inFreeformSpace) { + // In freeform mode we set the wallpaper as its own target, so we don't need an + // additional window to make it visible. + result.setWallpaperTarget(result.topWallpaper, result.topWallpaperIndex); } + } - if (mWallpaperTarget != foundW - && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "New wallpaper target: " + foundW - + " oldTarget: " + mWallpaperTarget); - } + private boolean updateWallpaperWindowsTarget( + WindowList windows, FindWallpaperTargetResult result) { + + boolean targetChanged = false; + WindowState wallpaperTarget = result.wallpaperTarget; + int wallpaperTargetIndex = result.wallpaperTargetIndex; + + if (mWallpaperTarget != wallpaperTarget + && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != wallpaperTarget)) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "New wallpaper target: " + wallpaperTarget + " oldTarget: " + mWallpaperTarget); mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; WindowState oldW = mWallpaperTarget; - mWallpaperTarget = foundW; + mWallpaperTarget = wallpaperTarget; targetChanged = true; - // Now what is happening... if the current and new targets are - // animating, then we are in our super special mode! - if (foundW != null && oldW != null) { + // Now what is happening... if the current and new targets are animating, + // then we are in our super special mode! + if (wallpaperTarget != null && oldW != null) { boolean oldAnim = oldW.isAnimatingLw(); - boolean foundAnim = foundW.isAnimatingLw(); - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "New animation: " + foundAnim - + " old animation: " + oldAnim); - } + boolean foundAnim = wallpaperTarget.isAnimatingLw(); + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "New animation: " + foundAnim + " old animation: " + oldAnim); if (foundAnim && oldAnim) { int oldI = windows.indexOf(oldW); - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "New i: " + foundI + " old i: " + oldI); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "New i: " + wallpaperTargetIndex + " old i: " + oldI); if (oldI >= 0) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Animating wallpapers: old#" + oldI - + "=" + oldW + "; new#" + foundI - + "=" + foundW); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Animating wallpapers: old#" + oldI + "=" + oldW + "; new#" + + wallpaperTargetIndex + "=" + wallpaperTarget); // Set the new target correctly. - if (foundW.mAppToken != null && foundW.mAppToken.hiddenRequested) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Old wallpaper still the target."); - } + if (wallpaperTarget.mAppToken != null + && wallpaperTarget.mAppToken.hiddenRequested) { + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Old wallpaper still the target."); mWallpaperTarget = oldW; - foundW = oldW; - foundI = oldI; + wallpaperTarget = oldW; + wallpaperTargetIndex = oldI; } - // Now set the upper and lower wallpaper targets - // correctly, and make sure that we are positioning - // the wallpaper below the lower. - else if (foundI > oldI) { + // Now set the upper and lower wallpaper targets correctly, + // and make sure that we are positioning the wallpaper below the lower. + else if (wallpaperTargetIndex > oldI) { // The new target is on top of the old one. - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Found target above old target."); - } - mUpperWallpaperTarget = foundW; + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Found target above old target."); + mUpperWallpaperTarget = wallpaperTarget; mLowerWallpaperTarget = oldW; - foundW = oldW; - foundI = oldI; + wallpaperTarget = oldW; + wallpaperTargetIndex = oldI; } else { // The new target is below the old one. - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "Found target below old target."); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, + "Found target below old target."); mUpperWallpaperTarget = oldW; - mLowerWallpaperTarget = foundW; + mLowerWallpaperTarget = wallpaperTarget; } } } @@ -614,29 +605,36 @@ class WallpaperController { } else if (mLowerWallpaperTarget != null) { // Is it time to stop animating? if (!mLowerWallpaperTarget.isAnimatingLw() || !mUpperWallpaperTarget.isAnimatingLw()) { - if (DEBUG_WALLPAPER_LIGHT) { - Slog.v(TAG, "No longer animating wallpaper targets!"); - } + if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "No longer animating wallpaper targets!"); mLowerWallpaperTarget = null; mUpperWallpaperTarget = null; - mWallpaperTarget = foundW; + mWallpaperTarget = wallpaperTarget; targetChanged = true; } } - boolean visible = foundW != null; + result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex); + return targetChanged; + } + + boolean updateWallpaperWindowsTargetByLayer( + WindowList windows, FindWallpaperTargetResult result) { + + WindowState wallpaperTarget = result.wallpaperTarget; + int wallpaperTargetIndex = result.wallpaperTargetIndex; + boolean visible = wallpaperTarget != null; + if (visible) { - // The window is visible to the compositor... but is it visible - // to the user? That is what the wallpaper cares about. - visible = isWallpaperVisible(foundW); + // The window is visible to the compositor...but is it visible to the user? + // That is what the wallpaper cares about. + visible = isWallpaperVisible(wallpaperTarget); if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper visibility: " + visible); - // If the wallpaper target is animating, we may need to copy - // its layer adjustment. Only do this if we are not transfering - // between two wallpaper targets. + // If the wallpaper target is animating, we may need to copy its layer adjustment. + // Only do this if we are not transferring between two wallpaper targets. mWallpaperAnimLayerAdjustment = - (mLowerWallpaperTarget == null && foundW.mAppToken != null) - ? foundW.mAppToken.mAppAnimator.animLayerAdjustment : 0; + (mLowerWallpaperTarget == null && wallpaperTarget.mAppToken != null) + ? wallpaperTarget.mAppToken.mAppAnimator.animLayerAdjustment : 0; final int maxLayer = (mService.mPolicy.getMaxWallpaperLayer() * TYPE_LAYER_MULTIPLIER) + TYPE_LAYER_OFFSET; @@ -645,52 +643,37 @@ class WallpaperController { // need to be sure to also be behind any of its attached windows, // AND any starting window associated with it, AND below the // maximum layer the policy allows for wallpapers. - while (foundI > 0) { - WindowState wb = windows.get(foundI - 1); + while (wallpaperTargetIndex > 0) { + WindowState wb = windows.get(wallpaperTargetIndex - 1); if (wb.mBaseLayer < maxLayer && - wb.mAttachedWindow != foundW && - (foundW.mAttachedWindow == null || - wb.mAttachedWindow != foundW.mAttachedWindow) && - (wb.mAttrs.type != TYPE_APPLICATION_STARTING || - foundW.mToken == null || wb.mToken != foundW.mToken)) { + wb.mAttachedWindow != wallpaperTarget && + (wallpaperTarget.mAttachedWindow == null || + wb.mAttachedWindow != wallpaperTarget.mAttachedWindow) && + (wb.mAttrs.type != TYPE_APPLICATION_STARTING + || wallpaperTarget.mToken == null + || wb.mToken != wallpaperTarget.mToken)) { // This window is not related to the previous one in any // interesting way, so stop here. break; } - foundW = wb; - foundI--; + wallpaperTarget = wb; + wallpaperTargetIndex--; } } else { if (DEBUG_WALLPAPER) Slog.v(TAG, "No wallpaper target"); } - if (foundW == null && topCurW != null) { - // There is no wallpaper target, so it goes at the bottom. - // We will assume it is the same place as last time, if known. - foundW = topCurW; - foundI = topCurI+1; - } else { - // Okay i is the position immediately above the wallpaper. Look at - // what is below it for later. - foundW = foundI > 0 ? windows.get(foundI - 1) : null; - } + result.setWallpaperTarget(wallpaperTarget, wallpaperTargetIndex); + return visible; + } - if (visible) { - if (mWallpaperTarget.mWallpaperX >= 0) { - mLastWallpaperX = mWallpaperTarget.mWallpaperX; - mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; - } - if (mWallpaperTarget.mWallpaperY >= 0) { - mLastWallpaperY = mWallpaperTarget.mWallpaperY; - mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; - } - if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { - mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; - } - if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { - mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; - } - } + boolean updateWallpaperWindowsPlacement(WindowList windows, + WindowState wallpaperTarget, int wallpaperTargetIndex, boolean visible) { + + // TODO(multidisplay): Wallpapers on main screen only. + final DisplayInfo displayInfo = mService.getDefaultDisplayContentLocked().getDisplayInfo(); + final int dw = displayInfo.logicalWidth; + final int dh = displayInfo.logicalHeight; // Start stepping backwards from here, ensuring that our wallpaper windows // are correctly placed. @@ -722,41 +705,40 @@ class WallpaperController { + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); // First, if this window is at the current index, then all is well. - if (wallpaper == foundW) { - foundI--; - foundW = foundI > 0 ? windows.get(foundI - 1) : null; + if (wallpaper == wallpaperTarget) { + wallpaperTargetIndex--; + wallpaperTarget = wallpaperTargetIndex > 0 + ? windows.get(wallpaperTargetIndex - 1) : null; continue; } // The window didn't match... the current wallpaper window, - // wherever it is, is in the wrong place, so make sure it is - // not in the list. + // wherever it is, is in the wrong place, so make sure it is not in the list. int oldIndex = windows.indexOf(wallpaper); if (oldIndex >= 0) { - if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Wallpaper removing at " - + oldIndex + ": " + wallpaper); + if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, + "Wallpaper removing at " + oldIndex + ": " + wallpaper); windows.remove(oldIndex); mService.mWindowsChanged = true; - if (oldIndex < foundI) { - foundI--; + if (oldIndex < wallpaperTargetIndex) { + wallpaperTargetIndex--; } } // Now stick it in. For apps over wallpaper keep the wallpaper at the bottommost // layer. For keyguard over wallpaper put the wallpaper under the keyguard. int insertionIndex = 0; - if (visible && foundW != null) { - final int type = foundW.mAttrs.type; - final int privateFlags = foundW.mAttrs.privateFlags; + if (visible && wallpaperTarget != null) { + final int type = wallpaperTarget.mAttrs.type; + final int privateFlags = wallpaperTarget.mAttrs.privateFlags; if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 || type == TYPE_KEYGUARD_SCRIM) { - insertionIndex = windows.indexOf(foundW); + insertionIndex = windows.indexOf(wallpaperTarget); } } - if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) { - Slog.v(TAG, "Moving wallpaper " + wallpaper - + " from " + oldIndex + " to " + insertionIndex); - } + if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, + "Moving wallpaper " + wallpaper + + " from " + oldIndex + " to " + insertionIndex); windows.add(insertionIndex, wallpaper); mService.mWindowsChanged = true; @@ -764,6 +746,53 @@ class WallpaperController { } } + return changed; + } + + boolean adjustWallpaperWindows() { + mService.mInnerFields.mWallpaperMayChange = false; + + final WindowList windows = mService.getDefaultWindowListLocked(); + // First find top-most window that has asked to be on top of the wallpaper; + // all wallpapers go behind it. + findWallpaperTarget(windows, mFindResults); + final boolean targetChanged = updateWallpaperWindowsTarget(windows, mFindResults); + final boolean visible = updateWallpaperWindowsTargetByLayer(windows, mFindResults); + WindowState wallpaperTarget = mFindResults.wallpaperTarget; + int wallpaperTargetIndex = mFindResults.wallpaperTargetIndex; + + if (wallpaperTarget == null && mFindResults.topWallpaper != null) { + // There is no wallpaper target, so it goes at the bottom. + // We will assume it is the same place as last time, if known. + wallpaperTarget = mFindResults.topWallpaper; + wallpaperTargetIndex = mFindResults.topWallpaperIndex + 1; + } else { + // Okay i is the position immediately above the wallpaper. + // Look at what is below it for later. + wallpaperTarget = wallpaperTargetIndex > 0 + ? windows.get(wallpaperTargetIndex - 1) : null; + } + + if (visible) { + if (mWallpaperTarget.mWallpaperX >= 0) { + mLastWallpaperX = mWallpaperTarget.mWallpaperX; + mLastWallpaperXStep = mWallpaperTarget.mWallpaperXStep; + } + if (mWallpaperTarget.mWallpaperY >= 0) { + mLastWallpaperY = mWallpaperTarget.mWallpaperY; + mLastWallpaperYStep = mWallpaperTarget.mWallpaperYStep; + } + if (mWallpaperTarget.mWallpaperDisplayOffsetX != Integer.MIN_VALUE) { + mLastWallpaperDisplayOffsetX = mWallpaperTarget.mWallpaperDisplayOffsetX; + } + if (mWallpaperTarget.mWallpaperDisplayOffsetY != Integer.MIN_VALUE) { + mLastWallpaperDisplayOffsetY = mWallpaperTarget.mWallpaperDisplayOffsetY; + } + } + + final boolean changed = updateWallpaperWindowsPlacement( + windows, wallpaperTarget, wallpaperTargetIndex, visible); + if (targetChanged && DEBUG_WALLPAPER_LIGHT) Slog.d(TAG, "New wallpaper: target=" + mWallpaperTarget + " lower=" + mLowerWallpaperTarget + " upper=" + mUpperWallpaperTarget); @@ -859,4 +888,29 @@ class WallpaperController { } } } + + /** Helper class for storing the results of a wallpaper target find operation. */ + final private static class FindWallpaperTargetResult { + int topWallpaperIndex = 0; + WindowState topWallpaper = null; + int wallpaperTargetIndex = 0; + WindowState wallpaperTarget = null; + + void setTopWallpaper(WindowState win, int index) { + topWallpaper = win; + topWallpaperIndex = index; + } + + void setWallpaperTarget(WindowState win, int index) { + wallpaperTarget = win; + wallpaperTargetIndex = index; + } + + void reset() { + topWallpaperIndex = 0; + topWallpaper = null; + wallpaperTargetIndex = 0; + wallpaperTarget = null; + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7e7d009a8675..029609d6d213 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -114,6 +114,7 @@ import android.view.WindowManagerPolicy.PointerEventListener; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import static com.android.server.wm.WindowState.BOUNDS_FOR_TOUCH; import com.android.internal.app.IAssistScreenshotReceiver; import com.android.internal.app.IBatteryStats; import com.android.internal.util.FastPrintWriter; @@ -597,6 +598,7 @@ public class WindowManagerService extends IWindowManager.Stub final InputManagerService mInputManager; final DisplayManagerInternal mDisplayManagerInternal; final DisplayManager mDisplayManager; + final Display[] mDisplays; // Who is holding the screen on. Session mHoldingScreenOn; @@ -870,8 +872,8 @@ public class WindowManagerService extends IWindowManager.Stub mFxSession = new SurfaceSession(); mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE); - Display[] displays = mDisplayManager.getDisplays(); - for (Display display : displays) { + mDisplays = mDisplayManager.getDisplays(); + for (Display display : mDisplays) { createDisplayContentLocked(display); } @@ -5633,7 +5635,7 @@ public class WindowManagerService extends IWindowManager.Stub int right = wf.right - cr.right; int bottom = wf.bottom - cr.bottom; frame.union(left, top, right, bottom); - ws.getTaskBounds(stackBounds); + ws.getTaskBounds(stackBounds, !BOUNDS_FOR_TOUCH); if (!frame.intersect(stackBounds)) { // Set frame empty if there's no intersection. frame.setEmpty(); @@ -7003,7 +7005,9 @@ public class WindowManagerService extends IWindowManager.Stub } public void displayReady() { - displayReady(Display.DEFAULT_DISPLAY); + for (Display display : mDisplays) { + displayReady(display.getDisplayId()); + } synchronized(mWindowMap) { final DisplayContent displayContent = getDefaultDisplayContentLocked(); @@ -8553,312 +8557,322 @@ public class WindowManagerService extends IWindowManager.Stub * @return bitmap indicating if another pass through layout must be made. */ public int handleAppTransitionReadyLocked(WindowList windows) { - int changes = 0; - int i; int appsCount = mOpeningApps.size(); - boolean goodToGo = true; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Checking " + appsCount + " opening apps (frozen=" - + mDisplayFrozen + " timeout=" - + mAppTransition.isTimeout() + ")..."); - if (!mAppTransition.isTimeout()) { - for (i = 0; i < appsCount && goodToGo; i++) { - AppWindowToken wtoken = mOpeningApps.valueAt(i); - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Check opening app=" + wtoken + ": allDrawn=" - + wtoken.allDrawn + " startingDisplayed=" - + wtoken.startingDisplayed + " startingMoved=" - + wtoken.startingMoved); - if (!wtoken.allDrawn && !wtoken.startingDisplayed - && !wtoken.startingMoved) { - goodToGo = false; - } - } - - if (goodToGo && mWallpaperControllerLocked.isWallpaperVisible()) { - goodToGo &= mWallpaperControllerLocked.wallpaperTransitionReady(); - } + if (!checkIfTransitionGoodToGo(appsCount)) { + return 0; } - if (goodToGo) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); - int transit = mAppTransition.getAppTransition(); - if (mSkipAppTransitionAnimation) { - transit = AppTransition.TRANSIT_UNSET; - } - mSkipAppTransitionAnimation = false; - mNoAnimationNotifyOnTransitionFinished.clear(); - - mH.removeMessages(H.APP_TRANSITION_TIMEOUT); - - rebuildAppWindowListLocked(); - - // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper - final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); - final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating() - ? null : wallpaperTarget; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); + int transit = mAppTransition.getAppTransition(); + if (mSkipAppTransitionAnimation) { + transit = AppTransition.TRANSIT_UNSET; + } + mSkipAppTransitionAnimation = false; + mNoAnimationNotifyOnTransitionFinished.clear(); - mInnerFields.mWallpaperMayChange = false; + mH.removeMessages(H.APP_TRANSITION_TIMEOUT); - // The top-most window will supply the layout params, - // and we will determine it below. - LayoutParams animLp = null; - int bestAnimLayer = -1; - boolean fullscreenAnim = false; - boolean voiceInteraction = false; + rebuildAppWindowListLocked(); - final WindowState lowerWallpaperTarget = - mWallpaperControllerLocked.getLowerWallpaperTarget(); - final WindowState upperWallpaperTarget = - mWallpaperControllerLocked.getUpperWallpaperTarget(); + mInnerFields.mWallpaperMayChange = false; + + // The top-most window will supply the layout params, + // and we will determine it below. + LayoutParams animLp = null; + int bestAnimLayer = -1; + boolean fullscreenAnim = false; + boolean voiceInteraction = false; + + final WindowState lowerWallpaperTarget = + mWallpaperControllerLocked.getLowerWallpaperTarget(); + final WindowState upperWallpaperTarget = + mWallpaperControllerLocked.getUpperWallpaperTarget(); + + boolean openingAppHasWallpaper = false; + boolean closingAppHasWallpaper = false; + final AppWindowToken lowerWallpaperAppToken; + final AppWindowToken upperWallpaperAppToken; + if (lowerWallpaperTarget == null) { + lowerWallpaperAppToken = upperWallpaperAppToken = null; + } else { + lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken; + upperWallpaperAppToken = upperWallpaperTarget.mAppToken; + } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New wallpaper target=" + wallpaperTarget - + ", oldWallpaper=" + oldWallpaper - + ", lower target=" + lowerWallpaperTarget - + ", upper target=" + upperWallpaperTarget); - - boolean openingAppHasWallpaper = false; - boolean closingAppHasWallpaper = false; - final AppWindowToken lowerWallpaperAppToken; - final AppWindowToken upperWallpaperAppToken; - if (lowerWallpaperTarget == null) { - lowerWallpaperAppToken = upperWallpaperAppToken = null; + int i; + // Do a first pass through the tokens for two + // things: + // (1) Determine if both the closing and opening + // app token sets are wallpaper targets, in which + // case special animations are needed + // (since the wallpaper needs to stay static + // behind them). + // (2) Find the layout params of the top-most + // application window in the tokens, which is + // what will control the animation theme. + final int closingAppsCount = mClosingApps.size(); + appsCount = closingAppsCount + mOpeningApps.size(); + for (i = 0; i < appsCount; i++) { + final AppWindowToken wtoken; + if (i < closingAppsCount) { + wtoken = mClosingApps.valueAt(i); + if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { + closingAppHasWallpaper = true; + } } else { - lowerWallpaperAppToken = lowerWallpaperTarget.mAppToken; - upperWallpaperAppToken = upperWallpaperTarget.mAppToken; - } - - // Do a first pass through the tokens for two - // things: - // (1) Determine if both the closing and opening - // app token sets are wallpaper targets, in which - // case special animations are needed - // (since the wallpaper needs to stay static - // behind them). - // (2) Find the layout params of the top-most - // application window in the tokens, which is - // what will control the animation theme. - final int closingAppsCount = mClosingApps.size(); - appsCount = closingAppsCount + mOpeningApps.size(); - for (i = 0; i < appsCount; i++) { - final AppWindowToken wtoken; - if (i < closingAppsCount) { - wtoken = mClosingApps.valueAt(i); - if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { - closingAppHasWallpaper = true; - } - } else { - wtoken = mOpeningApps.valueAt(i - closingAppsCount); - if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { - openingAppHasWallpaper = true; - } + wtoken = mOpeningApps.valueAt(i - closingAppsCount); + if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { + openingAppHasWallpaper = true; } + } - voiceInteraction |= wtoken.voiceInteraction; + voiceInteraction |= wtoken.voiceInteraction; - if (wtoken.appFullscreen) { - WindowState ws = wtoken.findMainWindow(); - if (ws != null) { + if (wtoken.appFullscreen) { + WindowState ws = wtoken.findMainWindow(); + if (ws != null) { + animLp = ws.mAttrs; + bestAnimLayer = ws.mLayer; + fullscreenAnim = true; + } + } else if (!fullscreenAnim) { + WindowState ws = wtoken.findMainWindow(); + if (ws != null) { + if (ws.mLayer > bestAnimLayer) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; - fullscreenAnim = true; - } - } else if (!fullscreenAnim) { - WindowState ws = wtoken.findMainWindow(); - if (ws != null) { - if (ws.mLayer > bestAnimLayer) { - animLp = ws.mAttrs; - bestAnimLayer = ws.mLayer; - } } } } + } - mAnimateWallpaperWithTarget = false; - if (closingAppHasWallpaper && openingAppHasWallpaper) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); - switch (transit) { - case AppTransition.TRANSIT_ACTIVITY_OPEN: - case AppTransition.TRANSIT_TASK_OPEN: - case AppTransition.TRANSIT_TASK_TO_FRONT: - transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; - break; - case AppTransition.TRANSIT_ACTIVITY_CLOSE: - case AppTransition.TRANSIT_TASK_CLOSE: - case AppTransition.TRANSIT_TASK_TO_BACK: - transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; - break; + transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper, + closingAppHasWallpaper, lowerWallpaperTarget, upperWallpaperTarget); + + // If all closing windows are obscured, then there is + // no need to do an animation. This is the case, for + // example, when this transition is being done behind + // the lock screen. + if (!mPolicy.allowAppAnimationsLw()) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Animations disallowed by keyguard or dream."); + animLp = null; + } + + processApplicationsAnimatingInPlace(transit); + + AppWindowToken topClosingApp = null; + int topClosingLayer = 0; + appsCount = mClosingApps.size(); + for (i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mClosingApps.valueAt(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); + appAnimator.clearThumbnail(); + appAnimator.animation = null; + wtoken.inPendingTransaction = false; + setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); + wtoken.updateReportedVisibilityLocked(); + // Force the allDrawn flag, because we want to start + // this guy's animations regardless of whether it's + // gotten drawn. + wtoken.allDrawn = true; + wtoken.deferClearAllDrawn = false; + // Ensure that apps that are mid-starting are also scheduled to have their + // starting windows removed after the animation is complete + if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) { + scheduleRemoveStartingWindowLocked(wtoken); + } + mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + + if (animLp != null) { + int layer = -1; + for (int j = 0; j < wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mWinAnimator.mAnimLayer > layer) { + layer = win.mWinAnimator.mAnimLayer; + } + } + if (topClosingApp == null || layer > topClosingLayer) { + topClosingApp = wtoken; + topClosingLayer = layer; } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit: " + AppTransition.appTransitionToString(transit)); - } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty() - && !mOpeningApps.contains(oldWallpaper.mAppToken)) { - // We are transitioning from an activity with - // a wallpaper to one without. - transit = AppTransition.TRANSIT_WALLPAPER_CLOSE; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit away from wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) { - // We are transitioning from an activity without - // a wallpaper to now showing the wallpaper - transit = AppTransition.TRANSIT_WALLPAPER_OPEN; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit into wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else { - mAnimateWallpaperWithTarget = true; } + } - // If all closing windows are obscured, then there is - // no need to do an animation. This is the case, for - // example, when this transition is being done behind - // the lock screen. - if (!mPolicy.allowAppAnimationsLw()) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Animations disallowed by keyguard or dream."); - animLp = null; - } - - // Process all applications animating in place - if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) { - // Find the focused window - final WindowState win = - findFocusedWindowLocked(getDefaultDisplayContentLocked()); - if (win != null) { - final AppWindowToken wtoken = win.mAppToken; - final AppWindowAnimator appAnimator = wtoken.mAppAnimator; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now animating app in place " + wtoken); - appAnimator.clearThumbnail(); - appAnimator.animation = null; - updateTokenInPlaceLocked(wtoken, transit); - wtoken.updateReportedVisibilityLocked(); - - appAnimator.mAllAppWinAnimators.clear(); - final int N = wtoken.allAppWindows.size(); - for (int j = 0; j < N; j++) { - appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); - } - mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); - mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); - } - } - - AppWindowToken topClosingApp = null; - int topClosingLayer = 0; - appsCount = mClosingApps.size(); - for (i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mClosingApps.valueAt(i); - final AppWindowAnimator appAnimator = wtoken.mAppAnimator; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); + AppWindowToken topOpeningApp = null; + appsCount = mOpeningApps.size(); + for (i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mOpeningApps.valueAt(i); + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + + if (!appAnimator.usingTransferredAnimation) { appAnimator.clearThumbnail(); appAnimator.animation = null; - wtoken.inPendingTransaction = false; - setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); - wtoken.updateReportedVisibilityLocked(); - // Force the allDrawn flag, because we want to start - // this guy's animations regardless of whether it's - // gotten drawn. - wtoken.allDrawn = true; - wtoken.deferClearAllDrawn = false; - // Ensure that apps that are mid-starting are also scheduled to have their - // starting windows removed after the animation is complete - if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) { - scheduleRemoveStartingWindowLocked(wtoken); - } - mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + } + wtoken.inPendingTransaction = false; + if (!setTokenVisibilityLocked( + wtoken, animLp, true, transit, false, voiceInteraction)){ + // This token isn't going to be animating. Add it to the list of tokens to + // be notified of app transition complete since the notification will not be + // sent be the app window animator. + mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); + } + wtoken.updateReportedVisibilityLocked(); + wtoken.waitingToShow = false; - if (animLp != null) { - int layer = -1; - for (int j = 0; j < wtoken.windows.size(); j++) { - WindowState win = wtoken.windows.get(j); - if (win.mWinAnimator.mAnimLayer > layer) { - layer = win.mWinAnimator.mAnimLayer; - } - } - if (topClosingApp == null || layer > topClosingLayer) { - topClosingApp = wtoken; - topClosingLayer = layer; + appAnimator.mAllAppWinAnimators.clear(); + final int windowsCount = wtoken.allAppWindows.size(); + for (int j = 0; j < windowsCount; j++) { + appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); + } + mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); + mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + + int topOpeningLayer = 0; + if (animLp != null) { + int layer = -1; + for (int j = 0; j < wtoken.windows.size(); j++) { + WindowState win = wtoken.windows.get(j); + if (win.mWinAnimator.mAnimLayer > layer) { + layer = win.mWinAnimator.mAnimLayer; } } + if (topOpeningApp == null || layer > topOpeningLayer) { + topOpeningApp = wtoken; + topOpeningLayer = layer; + } } + createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer); + } - AppWindowToken topOpeningApp = null; - appsCount = mOpeningApps.size(); - for (i = 0; i < appsCount; i++) { - AppWindowToken wtoken = mOpeningApps.valueAt(i); - final AppWindowAnimator appAnimator = wtoken.mAppAnimator; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); + AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : + topOpeningApp.mAppAnimator; + AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : + topClosingApp.mAppAnimator; - if (!appAnimator.usingTransferredAnimation) { - appAnimator.clearThumbnail(); - appAnimator.animation = null; - } - wtoken.inPendingTransaction = false; - if (!setTokenVisibilityLocked( - wtoken, animLp, true, transit, false, voiceInteraction)){ - // This token isn't going to be animating. Add it to the list of tokens to - // be notified of app transition complete since the notification will not be - // sent be the app window animator. - mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); - } + mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator); + mAppTransition.postAnimationCallback(); + mAppTransition.clear(); + + mOpeningApps.clear(); + mClosingApps.clear(); + + // This has changed the visibility of windows, so perform + // a new layout to get them all up-to-date. + getDefaultDisplayContentLocked().layoutNeeded = true; + + // TODO(multidisplay): IMEs are only supported on the default display. + if (windows == getDefaultWindowListLocked() + && !moveInputMethodWindowsIfNeededLocked(true)) { + assignLayersLocked(windows); + } + updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/); + mFocusMayChange = false; + notifyActivityDrawnForKeyguard(); + return WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT + | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; + + } + + private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper, + boolean closingAppHasWallpaper, WindowState lowerWallpaperTarget, + WindowState upperWallpaperTarget) { + // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper + final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); + final WindowState oldWallpaper = mWallpaperControllerLocked.isWallpaperTargetAnimating() + ? null : wallpaperTarget; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New wallpaper target=" + wallpaperTarget + + ", oldWallpaper=" + oldWallpaper + + ", lower target=" + lowerWallpaperTarget + + ", upper target=" + upperWallpaperTarget); + mAnimateWallpaperWithTarget = false; + if (closingAppHasWallpaper && openingAppHasWallpaper) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); + switch (transit) { + case AppTransition.TRANSIT_ACTIVITY_OPEN: + case AppTransition.TRANSIT_TASK_OPEN: + case AppTransition.TRANSIT_TASK_TO_FRONT: + transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; + break; + case AppTransition.TRANSIT_ACTIVITY_CLOSE: + case AppTransition.TRANSIT_TASK_CLOSE: + case AppTransition.TRANSIT_TASK_TO_BACK: + transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; + break; + } + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit: " + AppTransition.appTransitionToString(transit)); + } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty() + && !mOpeningApps.contains(oldWallpaper.mAppToken)) { + // We are transitioning from an activity with + // a wallpaper to one without. + transit = AppTransition.TRANSIT_WALLPAPER_CLOSE; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit away from wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()) { + // We are transitioning from an activity without + // a wallpaper to now showing the wallpaper + transit = AppTransition.TRANSIT_WALLPAPER_OPEN; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit into wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else { + mAnimateWallpaperWithTarget = true; + } + return transit; + } + + private void processApplicationsAnimatingInPlace(int transit) { + if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) { + // Find the focused window + final WindowState win = + findFocusedWindowLocked(getDefaultDisplayContentLocked()); + if (win != null) { + final AppWindowToken wtoken = win.mAppToken; + final AppWindowAnimator appAnimator = wtoken.mAppAnimator; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now animating app in place " + wtoken); + appAnimator.clearThumbnail(); + appAnimator.animation = null; + updateTokenInPlaceLocked(wtoken, transit); wtoken.updateReportedVisibilityLocked(); - wtoken.waitingToShow = false; appAnimator.mAllAppWinAnimators.clear(); - final int windowsCount = wtoken.allAppWindows.size(); - for (int j = 0; j < windowsCount; j++) { + final int N = wtoken.allAppWindows.size(); + for (int j = 0; j < N; j++) { appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); } - mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); + mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); + } + } + } - int topOpeningLayer = 0; - if (animLp != null) { - int layer = -1; - for (int j = 0; j < wtoken.windows.size(); j++) { - WindowState win = wtoken.windows.get(j); - if (win.mWinAnimator.mAnimLayer > layer) { - layer = win.mWinAnimator.mAnimLayer; - } - } - if (topOpeningApp == null || layer > topOpeningLayer) { - topOpeningApp = wtoken; - topOpeningLayer = layer; - } + private boolean checkIfTransitionGoodToGo(int appsCount) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Checking " + appsCount + " opening apps (frozen=" + + mDisplayFrozen + " timeout=" + + mAppTransition.isTimeout() + ")..."); + if (!mAppTransition.isTimeout()) { + for (int i = 0; i < appsCount; i++) { + AppWindowToken wtoken = mOpeningApps.valueAt(i); + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "Check opening app=" + wtoken + ": allDrawn=" + + wtoken.allDrawn + " startingDisplayed=" + + wtoken.startingDisplayed + " startingMoved=" + + wtoken.startingMoved); + if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { + return false; } - createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer); } - AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : - topOpeningApp.mAppAnimator; - AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : - topClosingApp.mAppAnimator; - - mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator); - mAppTransition.postAnimationCallback(); - mAppTransition.clear(); - - mOpeningApps.clear(); - mClosingApps.clear(); - - // This has changed the visibility of windows, so perform - // a new layout to get them all up-to-date. - changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT - | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; - getDefaultDisplayContentLocked().layoutNeeded = true; - - // TODO(multidisplay): IMEs are only supported on the default display. - if (windows == getDefaultWindowListLocked() - && !moveInputMethodWindowsIfNeededLocked(true)) { - assignLayersLocked(windows); - } - updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/); - mFocusMayChange = false; - notifyActivityDrawnForKeyguard(); + // If the wallpaper is visible, we need to check it's ready too. + return !mWallpaperControllerLocked.isWallpaperVisible() || + mWallpaperControllerLocked.wallpaperTransitionReady(); } - - return changes; + return true; } private void createThumbnailAppAnimator(int transit, AppWindowToken appToken, diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index b56b1f9cadc6..092a6d1ad348 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -80,8 +80,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { static final String TAG = "WindowState"; // The minimal size of a window within the usable area of the freeform stack. - static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48; - static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32; + private static final int MINIMUM_VISIBLE_WIDTH_IN_DP = 48; + private static final int MINIMUM_VISIBLE_HEIGHT_IN_DP = 32; + + // The thickness of a window resize handle outside the window bounds on the free form workspace + // to capture touch events in that area. + private static final int RESIZE_HANDLE_WIDTH_IN_DP = 10; + + static final boolean BOUNDS_FOR_TOUCH = true; final WindowManagerService mService; final WindowManagerPolicy mPolicy; @@ -541,9 +547,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { mHaveFrame = true; final Task task = mAppToken != null ? getTask() : null; - final boolean isFreeFormWorkspace = task != null && task.mStack != null && - task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; final boolean nonFullscreenTask = task != null && !task.isFullscreen(); + final boolean freeformWorkspace = inFreeformWorkspace(); if (nonFullscreenTask) { task.getBounds(mContainingFrame); final WindowState imeWin = mService.mInputMethodWindow; @@ -553,7 +558,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mContainingFrame.top -= mContainingFrame.bottom - cf.bottom; } - if (isFreeFormWorkspace) { + if (freeformWorkspace) { // In free form mode we have only to set the rectangle if it wasn't set already. No // need to intersect it with the (visible) "content frame" since it is allowed to // be outside the visible desktop. @@ -669,7 +674,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Make sure the content and visible frames are inside of the // final window frame. - if (isFreeFormWorkspace && !mFrame.isEmpty()) { + if (freeformWorkspace && !mFrame.isEmpty()) { // Keep the frame out of the blocked system area, limit it in size to the content area // and make sure that there is always a minimum visible so that the user can drag it // into a usable area.. @@ -910,10 +915,22 @@ final class WindowState implements WindowManagerPolicy.WindowState { return mDisplayContent.getHomeStack(); } - void getTaskBounds(Rect bounds) { + /** + * Retrieves the bounds for a task. + * @param bounds The rect which gets the bounds. + * @param forTouch Pass in BOUNDS_FOR_TOUCH to get touch related bounds, otherwise visible + * bounds will be returned. + */ + void getTaskBounds(Rect bounds, boolean forTouch) { final Task task = getTask(); if (task != null) { task.getBounds(bounds); + if (forTouch == BOUNDS_FOR_TOUCH) { + if (inFreeformWorkspace()) { + final int delta = calculatePixelFromDp(RESIZE_HANDLE_WIDTH_IN_DP); + bounds.inset(-delta, -delta); + } + } return; } bounds.set(mFrame); @@ -1612,6 +1629,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + private boolean inFreeformWorkspace() { + final Task task = getTask(); + return task != null && task.mStack != null && + task.mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID; + } + private int calculatePixelFromDp(int dp) { final Configuration serviceConfig = mService.mCurConfiguration; // TODO(multidisplay): Update Dp to that of display stack is on. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7fc56ec6c294..471994a05696 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4994,12 +4994,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { IPackageManager pm = AppGlobals.getPackageManager(); long id = Binder.clearCallingIdentity(); try { - // Removing those that go from the managed profile to the primary user. + UserInfo parent = mUserManager.getProfileParent(callingUserId); + if (parent == null) { + Slog.e(LOG_TAG, "Cannot call clearCrossProfileIntentFilter if there is no " + + "parent"); + return; + } + // Removing those that go from the managed profile to the parent. pm.clearCrossProfileIntentFilters(callingUserId, who.getPackageName()); - // And those that go from the primary user to the managed profile. + // And those that go from the parent to the managed profile. // If we want to support multiple managed profiles, we will have to only remove // those that have callingUserId as their target. - pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER, who.getPackageName()); + pm.clearCrossProfileIntentFilters(parent.id, who.getPackageName()); } catch (RemoteException re) { // Shouldn't happen } finally { diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 263c5e9b3ce9..31f1d7fdc61d 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -358,6 +358,13 @@ public class CarrierConfigManager { public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; /** + * Determines whether conference calls are supported by a carrier. When {@code true}, + * conference calling is supported, {@code false otherwise}. + * @hide + */ + public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; + + /** * If this is true, the SIM card (through Customer Service Profile EF file) will be able to * prevent manual operator selection. If false, this SIM setting will be ignored and manual * operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more @@ -457,6 +464,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false); sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100); + sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); |