diff options
45 files changed, 790 insertions, 395 deletions
diff --git a/api/current.txt b/api/current.txt index f5e3966..7d1c203 100644 --- a/api/current.txt +++ b/api/current.txt @@ -29583,6 +29583,7 @@ package android.widget { public class ViewAnimator extends android.widget.FrameLayout { ctor public ViewAnimator(android.content.Context); ctor public ViewAnimator(android.content.Context, android.util.AttributeSet); + method public boolean getAnimateFirstView(); method public android.view.View getCurrentView(); method public int getDisplayedChild(); method public android.view.animation.Animation getInAnimation(); diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index a52f74a..ab64747 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -241,7 +241,6 @@ int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy) { char src_data_dir[PKG_PATH_MAX]; char pkg_path[PKG_PATH_MAX]; - char media_path[PATH_MAX]; DIR *d; struct dirent *de; struct stat s; @@ -250,9 +249,6 @@ int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy) if (create_persona_path(src_data_dir, src_persona)) { return -1; } - if (create_persona_media_path(media_path, (userid_t) target_persona) == -1) { - return -1; - } d = opendir(src_data_dir); if (d != NULL) { @@ -281,10 +277,10 @@ int clone_persona_data(uid_t src_persona, uid_t target_persona, int copy) closedir(d); } - // ensure /data/media/<user_id> exists - if (ensure_dir(media_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + if (ensure_media_user_dirs((userid_t) target_persona) == -1) { return -1; } + return 0; } diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c index d51004a..d559639 100644 --- a/cmds/installd/installd.c +++ b/cmds/installd/installd.c @@ -333,19 +333,16 @@ int initialize_globals() { int initialize_directories() { int res = -1; - int version = 0; - FILE* file; // Read current filesystem layout version to handle upgrade paths char version_path[PATH_MAX]; - if (snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path) > PATH_MAX) { - return -1; - } - file = fopen(version_path, "r"); - if (file != NULL) { - fscanf(file, "%d", &version); - fclose(file); + snprintf(version_path, PATH_MAX, "%s.layout_version", android_data_dir.path); + + int oldVersion; + if (fs_read_atomic_int(version_path, &oldVersion) == -1) { + oldVersion = 0; } + int version = oldVersion; // /data/user char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX); @@ -376,16 +373,12 @@ int initialize_directories() { } } - // /data/media/0 - char owner_media_dir[PATH_MAX]; - create_persona_media_path(owner_media_dir, 0); - if (version == 0) { // Introducing multi-user, so migrate /data/media contents into /data/media/0 - ALOGD("Migrating /data/media for multi-user"); + ALOGD("Upgrading /data/media for multi-user"); // Ensure /data/media - if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { goto fail; } @@ -402,10 +395,14 @@ int initialize_directories() { } // Create /data/media again - if (ensure_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { + if (fs_prepare_dir(android_media_dir.path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { goto fail; } + // /data/media/0 + char owner_media_dir[PATH_MAX]; + snprintf(owner_media_dir, PATH_MAX, "%s0", android_media_dir.path); + // Move any owner data into place if (access(media_tmp_dir, F_OK) == 0) { if (rename(media_tmp_dir, owner_media_dir) == -1) { @@ -433,8 +430,7 @@ int initialize_directories() { // /data/media/<user_id> snprintf(user_media_dir, PATH_MAX, "%s%s", android_media_dir.path, name); - if (ensure_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - ALOGE("Failed to ensure %s: %s", user_media_dir, strerror(errno)); + if (fs_prepare_dir(user_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { goto fail; } } @@ -445,22 +441,46 @@ int initialize_directories() { version = 1; } - // Ensure /data/media/0 is always ready - if (ensure_dir(owner_media_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { - goto fail; + // /data/media/obb + char media_obb_dir[PATH_MAX]; + snprintf(media_obb_dir, PATH_MAX, "%sobb", android_media_dir.path); + + if (version == 1) { + // Introducing /data/media/obb for sharing OBB across users; migrate + // any existing OBB files from owner. + ALOGD("Upgrading to shared /data/media/obb"); + + // /data/media/0/Android/obb + char owner_obb_path[PATH_MAX]; + snprintf(owner_obb_path, PATH_MAX, "%s0/Android/obb", android_media_dir.path); + + // Only move if target doesn't already exist + if (access(media_obb_dir, F_OK) != 0 && access(owner_obb_path, F_OK) == 0) { + if (rename(owner_obb_path, media_obb_dir) == -1) { + ALOGE("Failed to move OBB from owner: %s", strerror(errno)); + goto fail; + } + } + + version = 2; } - // Persist our current version - file = fopen(version_path, "w"); - if (file != NULL) { - fprintf(file, "%d", version); - fsync(fileno(file)); - fclose(file); - } else { - ALOGE("Failed to save version to %s: %s", version_path, strerror(errno)); + if (ensure_media_user_dirs(0) == -1) { + ALOGE("Failed to setup media for user 0"); + goto fail; + } + if (fs_prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { goto fail; } + // Persist layout version if changed + if (version != oldVersion) { + if (fs_write_atomic_int(version_path, version) == -1) { + ALOGE("Failed to save version to %s: %s", version_path, strerror(errno)); + goto fail; + } + } + // Success! res = 0; diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h index 3201427..5b81d2c 100644 --- a/cmds/installd/installd.h +++ b/cmds/installd/installd.h @@ -32,6 +32,7 @@ #include <sys/types.h> #include <sys/wait.h> +#include <cutils/fs.h> #include <cutils/sockets.h> #include <cutils/log.h> #include <cutils/properties.h> diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c index 80247f1..625a35e 100644 --- a/cmds/installd/utils.c +++ b/cmds/installd/utils.c @@ -991,39 +991,14 @@ char *build_string3(char *s1, char *s2, char *s3) { return result; } -/* Ensure that directory exists with given mode and owners. */ -int ensure_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) { - // Check if path needs to be created - struct stat sb; - if (stat(path, &sb) == -1) { - if (errno == ENOENT) { - goto create; - } else { - ALOGE("Failed to stat(%s): %s", path, strerror(errno)); - return -1; - } - } - - // Exists, verify status - if (sb.st_mode == mode || sb.st_uid == uid || sb.st_gid == gid) { - return 0; - } else { - goto fixup; - } - -create: - if (mkdir(path, mode) == -1) { - ALOGE("Failed to mkdir(%s): %s", path, strerror(errno)); - return -1; - } +/* Ensure that /data/media directories are prepared for given user. */ +int ensure_media_user_dirs(userid_t userid) { + char media_user_path[PATH_MAX]; + char path[PATH_MAX]; -fixup: - if (chown(path, uid, gid) == -1) { - ALOGE("Failed to chown(%s, %d, %d): %s", path, uid, gid, strerror(errno)); - return -1; - } - if (chmod(path, mode) == -1) { - ALOGE("Failed to chown(%s, %d): %s", path, mode, strerror(errno)); + // Ensure /data/media/<userid> exists + create_persona_media_path(media_user_path, userid); + if (fs_prepare_dir(media_user_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW) == -1) { return -1; } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 851b8df..d5fca4d 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -584,6 +584,8 @@ public class Process { } if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) { argsForZygote.add("--mount-external-multiuser"); + } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) { + argsForZygote.add("--mount-external-multiuser-all"); } argsForZygote.add("--target-sdk-version=" + targetSdkVersion); diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index 0114a41..23337f0 100644 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -226,17 +226,12 @@ public class GestureDetector { */ private boolean mIsDoubleTapping; - private float mLastMotionY; - private float mLastMotionX; + private float mLastFocusX; + private float mLastFocusY; + private float mDownFocusX; + private float mDownFocusY; private boolean mIsLongpressEnabled; - - /** - * True if we are at a target API level of >= Froyo or the developer can - * explicitly set it. If true, input events with > 1 pointer will be ignored - * so we can work side by side with multitouch gesture detectors. - */ - private boolean mIgnoreMultitouch; /** * Determines speed during touch scrolling @@ -349,8 +344,16 @@ public class GestureDetector { * @throws NullPointerException if {@code listener} is null. */ public GestureDetector(Context context, OnGestureListener listener, Handler handler) { - this(context, listener, handler, context != null && - context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO); + if (handler != null) { + mHandler = new GestureHandler(handler); + } else { + mHandler = new GestureHandler(); + } + mListener = listener; + if (listener instanceof OnDoubleTapListener) { + setOnDoubleTapListener((OnDoubleTapListener) listener); + } + init(context); } /** @@ -362,31 +365,19 @@ public class GestureDetector { * @param listener the listener invoked for all the callbacks, this must * not be null. * @param handler the handler to use - * @param ignoreMultitouch whether events involving more than one pointer should - * be ignored. * * @throws NullPointerException if {@code listener} is null. */ public GestureDetector(Context context, OnGestureListener listener, Handler handler, - boolean ignoreMultitouch) { - if (handler != null) { - mHandler = new GestureHandler(handler); - } else { - mHandler = new GestureHandler(); - } - mListener = listener; - if (listener instanceof OnDoubleTapListener) { - setOnDoubleTapListener((OnDoubleTapListener) listener); - } - init(context, ignoreMultitouch); + boolean unused) { + this(context, listener, handler); } - private void init(Context context, boolean ignoreMultitouch) { + private void init(Context context) { if (mListener == null) { throw new NullPointerException("OnGestureListener must not be null"); } mIsLongpressEnabled = true; - mIgnoreMultitouch = ignoreMultitouch; // Fallback to support pre-donuts releases int touchSlop, doubleTapSlop, doubleTapTouchSlop; @@ -456,34 +447,40 @@ public class GestureDetector { } final int action = ev.getAction(); - final float y = ev.getY(); - final float x = ev.getX(); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); + final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; + final int skipIndex = pointerUp ? ev.getActionIndex() : -1; + + // Determine focal point + float sumX = 0, sumY = 0; + final int count = ev.getPointerCount(); + for (int i = 0; i < count; i++) { + if (skipIndex == i) continue; + sumX += ev.getX(i); + sumY += ev.getY(i); + } + final int div = pointerUp ? count - 1 : count; + final float focusX = sumX / div; + final float focusY = sumY / div; + boolean handled = false; switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_DOWN: - if (mIgnoreMultitouch) { - // Multitouch event - abort. - cancel(); - } + mDownFocusX = mLastFocusX = focusX; + mDownFocusY = mLastFocusY = focusY; + // Cancel long press and taps + cancelTaps(); break; case MotionEvent.ACTION_POINTER_UP: - // Ending a multitouch gesture and going back to 1 finger - if (mIgnoreMultitouch && ev.getPointerCount() == 2) { - int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0; - mLastMotionX = ev.getX(index); - mLastMotionY = ev.getY(index); - mVelocityTracker.recycle(); - mVelocityTracker = VelocityTracker.obtain(); - } + mDownFocusX = mLastFocusX = focusX; + mDownFocusY = mLastFocusY = focusY; break; case MotionEvent.ACTION_DOWN: @@ -504,8 +501,8 @@ public class GestureDetector { } } - mLastMotionX = x; - mLastMotionY = y; + mDownFocusX = mLastFocusX = focusX; + mDownFocusY = mLastFocusY = focusY; if (mCurrentDownEvent != null) { mCurrentDownEvent.recycle(); } @@ -525,22 +522,22 @@ public class GestureDetector { break; case MotionEvent.ACTION_MOVE: - if (mInLongPress || (mIgnoreMultitouch && ev.getPointerCount() > 1)) { + if (mInLongPress) { break; } - final float scrollX = mLastMotionX - x; - final float scrollY = mLastMotionY - y; + final float scrollX = mLastFocusX - focusX; + final float scrollY = mLastFocusY - focusY; if (mIsDoubleTapping) { // Give the move events of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mAlwaysInTapRegion) { - final int deltaX = (int) (x - mCurrentDownEvent.getX()); - final int deltaY = (int) (y - mCurrentDownEvent.getY()); + final int deltaX = (int) (focusX - mDownFocusX); + final int deltaY = (int) (focusY - mDownFocusY); int distance = (deltaX * deltaX) + (deltaY * deltaY); if (distance > mTouchSlopSquare) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); - mLastMotionX = x; - mLastMotionY = y; + mLastFocusX = focusX; + mLastFocusY = focusY; mAlwaysInTapRegion = false; mHandler.removeMessages(TAP); mHandler.removeMessages(SHOW_PRESS); @@ -551,8 +548,8 @@ public class GestureDetector { } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); - mLastMotionX = x; - mLastMotionY = y; + mLastFocusX = focusX; + mLastFocusY = focusY; } break; @@ -571,9 +568,10 @@ public class GestureDetector { // A fling must travel the minimum tap distance final VelocityTracker velocityTracker = mVelocityTracker; + final int pointerId = ev.getPointerId(0); velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); - final float velocityY = velocityTracker.getYVelocity(); - final float velocityX = velocityTracker.getXVelocity(); + final float velocityY = velocityTracker.getYVelocity(pointerId); + final float velocityX = velocityTracker.getXVelocity(pointerId); if ((Math.abs(velocityY) > mMinimumFlingVelocity) || (Math.abs(velocityX) > mMinimumFlingVelocity)){ @@ -622,6 +620,18 @@ public class GestureDetector { } } + private void cancelTaps() { + mHandler.removeMessages(SHOW_PRESS); + mHandler.removeMessages(LONG_PRESS); + mHandler.removeMessages(TAP); + mIsDoubleTapping = false; + mAlwaysInTapRegion = false; + mAlwaysInBiggerTapRegion = false; + if (mInLongPress) { + mInLongPress = false; + } + } + private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp, MotionEvent secondDown) { if (!mAlwaysInBiggerTapRegion) { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 6ce73e1..745e1b8 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -17625,23 +17625,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // use use a height of 1, and then wack the matrix each time we // actually use it. shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP); - paint.setShader(shader); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + this.host = host; } public void setFadeColor(int color) { - if (color != 0 && color != mLastColor) { + if (color != mLastColor) { mLastColor = color; - color |= 0xFF000000; - - shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000, - color & 0x00FFFFFF, Shader.TileMode.CLAMP); - paint.setShader(shader); - // Restore the default transfer mode (src_over) - paint.setXfermode(null); + if (color != 0) { + shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000, + color & 0x00FFFFFF, Shader.TileMode.CLAMP); + paint.setShader(shader); + // Restore the default transfer mode (src_over) + paint.setXfermode(null); + } else { + shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP); + paint.setShader(shader); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } } } diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java index e16dd06..591b87f 100644 --- a/core/java/android/webkit/WebViewClassic.java +++ b/core/java/android/webkit/WebViewClassic.java @@ -687,6 +687,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc // It's used to dismiss the dialog in destroy if not done before. private AlertDialog mListBoxDialog = null; + // Reference to the save password dialog so it can be dimissed in + // destroy if not done before. + private AlertDialog mSavePasswordDialog = null; + static final String LOGTAG = "webview"; private ZoomManager mZoomManager; @@ -1836,7 +1840,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc neverRemember.getData().putString("password", password); neverRemember.obj = resumeMsg; - new AlertDialog.Builder(mContext) + mSavePasswordDialog = new AlertDialog.Builder(mContext) .setTitle(com.android.internal.R.string.save_password_label) .setMessage(com.android.internal.R.string.save_password_message) .setPositiveButton(com.android.internal.R.string.save_password_notnow, @@ -1847,6 +1851,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc resumeMsg.sendToTarget(); mResumeMsg = null; } + mSavePasswordDialog = null; } }) .setNeutralButton(com.android.internal.R.string.save_password_remember, @@ -1857,6 +1862,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc remember.sendToTarget(); mResumeMsg = null; } + mSavePasswordDialog = null; } }) .setNegativeButton(com.android.internal.R.string.save_password_never, @@ -1867,6 +1873,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc neverRemember.sendToTarget(); mResumeMsg = null; } + mSavePasswordDialog = null; } }) .setOnCancelListener(new OnCancelListener() { @@ -1876,6 +1883,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc resumeMsg.sendToTarget(); mResumeMsg = null; } + mSavePasswordDialog = null; } }).show(); // Return true so that WebViewCore will pause while the dialog is @@ -2115,6 +2123,10 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc mListBoxDialog.dismiss(); mListBoxDialog = null; } + if (mSavePasswordDialog != null) { + mSavePasswordDialog.dismiss(); + mSavePasswordDialog = null; + } if (mWebViewCore != null) { // Tell WebViewCore to destroy itself synchronized (this) { diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java index 6a68240..eee914e 100644 --- a/core/java/android/widget/ViewAnimator.java +++ b/core/java/android/widget/ViewAnimator.java @@ -329,8 +329,21 @@ public class ViewAnimator extends FrameLayout { } /** + * Returns whether the current View should be animated the first time the ViewAnimator + * is displayed. + * + * @return true if the current View will be animated the first time it is displayed, + * false otherwise. + * + * @see #setAnimateFirstView(boolean) + */ + public boolean getAnimateFirstView() { + return mAnimateFirstTime; + } + + /** * Indicates whether the current View should be animated the first time - * the ViewAnimation is displayed. + * the ViewAnimator is displayed. * * @param animate True to animate the current View the first time it is displayed, * false otherwise. diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index d6f1807..d24513a 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -529,6 +529,8 @@ class ZygoteConnection { niceName = arg.substring(arg.indexOf('=') + 1); } else if (arg.equals("--mount-external-multiuser")) { mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; + } else if (arg.equals("--mount-external-multiuser-all")) { + mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL; } else { break; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 96eb54a..abb9c0f 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -725,6 +725,13 @@ android:description="@string/permdesc_mediaStorageWrite" android:protectionLevel="signature|system" /> + <!-- Allows an application to access all multi-user external storage @hide --> + <permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" + android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS" + android:label="@string/permlab_sdcardAccessAll" + android:description="@string/permdesc_sdcardAccessAll" + android:protectionLevel="signature" /> + <!-- ============================================ --> <!-- Permissions for low-level system interaction --> <!-- ============================================ --> diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png Binary files differnew file mode 100644 index 0000000..c30eb1c --- /dev/null +++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_left.9.png diff --git a/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png Binary files differnew file mode 100644 index 0000000..e5d5771 --- /dev/null +++ b/core/res/res/drawable-nodpi/kg_widget_overscroll_layer_right.9.png diff --git a/core/res/res/layout-land/keyguard_host_view.xml b/core/res/res/layout-land/keyguard_host_view.xml index ca0fa1d..b404155 100644 --- a/core/res/res/layout-land/keyguard_host_view.xml +++ b/core/res/res/layout-land/keyguard_host_view.xml @@ -26,7 +26,7 @@ android:layout_height="match_parent" android:orientation="horizontal"> - <com.android.internal.policy.impl.keyguard.KeyguardWidgetView + <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager android:id="@+id/app_widget_container" android:layout_width="0dip" android:layout_height="match_parent" @@ -35,8 +35,8 @@ <!-- TODO: Remove this once supported as a widget --> <include layout="@layout/keyguard_status_view"/> + </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager> - </com.android.internal.policy.impl.keyguard.KeyguardWidgetView> <ViewFlipper android:id="@+id/view_flipper" diff --git a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml index c552885..e77f584 100644 --- a/core/res/res/layout-sw600dp-land/keyguard_host_view.xml +++ b/core/res/res/layout-sw600dp-land/keyguard_host_view.xml @@ -26,7 +26,7 @@ android:layout_height="match_parent" android:orientation="horizontal"> - <com.android.internal.policy.impl.keyguard.KeyguardWidgetView + <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager android:id="@+id/app_widget_container" android:layout_width="0dip" android:layout_height="match_parent" @@ -36,7 +36,7 @@ <!-- TODO: Remove this once supported as a widget --> <include layout="@layout/keyguard_status_view"/> - </com.android.internal.policy.impl.keyguard.KeyguardWidgetView> + </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager> <FrameLayout android:layout_width="0dip" diff --git a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml index 675b69b..50636f1 100644 --- a/core/res/res/layout-sw600dp-port/keyguard_host_view.xml +++ b/core/res/res/layout-sw600dp-port/keyguard_host_view.xml @@ -28,7 +28,7 @@ android:gravity="center_horizontal" android:clipChildren="false"> - <com.android.internal.policy.impl.keyguard.KeyguardWidgetView + <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager android:id="@+id/app_widget_container" android:layout_width="match_parent" android:layout_height="0dip" @@ -38,7 +38,7 @@ <!-- TODO: Remove this once supported as a widget --> <include layout="@layout/keyguard_status_view"/> - </com.android.internal.policy.impl.keyguard.KeyguardWidgetView> + </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager> <ViewFlipper android:id="@+id/view_flipper" diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml index 6f6611d..d516369 100644 --- a/core/res/res/layout/keyguard_selector_view.xml +++ b/core/res/res/layout/keyguard_selector_view.xml @@ -26,7 +26,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - <com.android.internal.policy.impl.keyguard.KeyguardWidgetView + <com.android.internal.policy.impl.keyguard.KeyguardWidgetPager android:id="@+id/app_widget_container" android:layout_width="match_parent" android:layout_height="0dip" @@ -34,7 +34,7 @@ android:visibility="gone"> <!-- TODO: Remove this once supported as a widget --> <include layout="@layout/keyguard_status_view"/> - </com.android.internal.policy.impl.keyguard.KeyguardWidgetView> + </com.android.internal.policy.impl.keyguard.KeyguardWidgetPager> <RelativeLayout android:layout_width="wrap_content" diff --git a/core/res/res/layout/keyguard_status_view.xml b/core/res/res/layout/keyguard_status_view.xml index 3560ea2..f8d05b7 100644 --- a/core/res/res/layout/keyguard_status_view.xml +++ b/core/res/res/layout/keyguard_status_view.xml @@ -18,97 +18,102 @@ --> <!-- This is a view that shows general status information in Keyguard. --> -<com.android.internal.policy.impl.keyguard.KeyguardStatusView +<com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal"> - <com.android.internal.widget.DigitalClock android:id="@+id/time" - android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin" - android:layout_marginBottom="12dip" - android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin" - android:layout_gravity="end"> + <com.android.internal.policy.impl.keyguard.KeyguardStatusView + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_horizontal"> - <!-- Because we can't have multi-tone fonts, we render two TextViews, one on - top of the other. Hence the redundant layout... --> - <TextView android:id="@*android:id/timeDisplayBackground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@*android:color/lockscreen_clock_background" - /> + <com.android.internal.widget.DigitalClock android:id="@+id/time" + android:layout_marginTop="@*android:dimen/keyguard_lockscreen_status_line_clockfont_top_margin" + android:layout_marginBottom="12dip" + android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin" + android:layout_gravity="end"> - <TextView android:id="@*android:id/timeDisplayForeground" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:singleLine="true" - android:ellipsize="none" - android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size" - android:textAppearance="?android:attr/textAppearanceMedium" - android:layout_marginBottom="6dip" - android:textColor="@*android:color/lockscreen_clock_foreground" - android:layout_alignStart="@*android:id/timeDisplayBackground" - android:layout_alignTop="@*android:id/timeDisplayBackground" - /> + <!-- Because we can't have multi-tone fonts, we render two TextViews, one on + top of the other. Hence the redundant layout... --> + <TextView android:id="@*android:id/timeDisplayBackground" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="none" + android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginBottom="6dip" + android:textColor="@*android:color/lockscreen_clock_background" + /> + + <TextView android:id="@*android:id/timeDisplayForeground" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="none" + android:textSize="@*android:dimen/keyguard_lockscreen_clock_font_size" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_marginBottom="6dip" + android:textColor="@*android:color/lockscreen_clock_foreground" + android:layout_alignStart="@*android:id/timeDisplayBackground" + android:layout_alignTop="@*android:id/timeDisplayBackground" + /> + + </com.android.internal.widget.DigitalClock> - </com.android.internal.widget.DigitalClock> + <LinearLayout + android:orientation="horizontal" + android:layout_gravity="end" + android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"> - <LinearLayout - android:orientation="horizontal" - android:layout_gravity="end" - android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin"> + <TextView + android:id="@*android:id/date" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size" + /> + + <TextView + android:id="@*android:id/alarm_status" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dip" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size" + android:drawablePadding="4dip" + /> + + </LinearLayout> <TextView - android:id="@*android:id/date" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:id="@*android:id/status1" + android:layout_gravity="end" + android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin" android:singleLine="true" android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size" + android:drawablePadding="4dip" /> <TextView - android:id="@*android:id/alarm_status" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="16dip" + android:id="@*android:id/carrier" + android:layout_gravity="end" + android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin" android:singleLine="true" android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceMedium" android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" + android:textColor="?android:attr/textColorSecondary" /> - </LinearLayout> - - <TextView - android:id="@*android:id/status1" - android:layout_gravity="end" - android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size" - android:drawablePadding="4dip" - /> - - <TextView - android:id="@*android:id/carrier" - android:layout_gravity="end" - android:layout_marginEnd="@*android:dimen/keyguard_lockscreen_status_line_font_right_margin" - android:singleLine="true" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="@*android:dimen/keyguard_lockscreen_status_line_font_size" - android:textColor="?android:attr/textColorSecondary" - /> - -</com.android.internal.policy.impl.keyguard.KeyguardStatusView> - + </com.android.internal.policy.impl.keyguard.KeyguardStatusView> +</com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index cc4d05a..372a1ee 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -251,4 +251,6 @@ <!-- Height of widget view in keyguard. --> <dimen name="kg_widget_view_height">0dp</dimen> + <!-- Padding surrounding each widget page --> + <dimen name="kg_widget_page_padding">10dp</dimen> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index b110a40..6414df8 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1237,6 +1237,7 @@ <java-symbol type="dimen" name="navigation_bar_height_landscape" /> <java-symbol type="dimen" name="navigation_bar_width" /> <java-symbol type="dimen" name="status_bar_height" /> + <java-symbol type="dimen" name="kg_widget_page_padding" /> <java-symbol type="drawable" name="ic_jog_dial_sound_off" /> <java-symbol type="drawable" name="ic_jog_dial_sound_on" /> <java-symbol type="drawable" name="ic_jog_dial_unlock" /> @@ -1254,6 +1255,8 @@ <java-symbol type="drawable" name="jog_tab_target_yellow" /> <java-symbol type="drawable" name="menu_background" /> <java-symbol type="drawable" name="stat_sys_secure" /> + <java-symbol type="drawable" name="kg_widget_overscroll_layer_left" /> + <java-symbol type="drawable" name="kg_widget_overscroll_layer_right" /> <java-symbol type="id" name="action_mode_bar_stub" /> <java-symbol type="id" name="alarm_status" /> <java-symbol type="id" name="backspace" /> @@ -3749,5 +3752,5 @@ <public type="attr" name="listPreferredItemPaddingStart" /> <public type="attr" name="listPreferredItemPaddingEnd" /> <public type="attr" name="singleUser" /> - + </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index b0437a2..3178af0 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1616,6 +1616,11 @@ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> <string name="permdesc_mediaStorageWrite" product="default">Allows the app to modify the contents of the internal media storage.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=30] --> + <string name="permlab_sdcardAccessAll">access external storage of all users</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_sdcardAccessAll">Allows the app to access external storage for all users.</string> + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_cache_filesystem">access the cache filesystem</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 27e198c..6b08e7f 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -328,19 +328,19 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float glyph->mCacheTexture); } -CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { +CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) { CachedGlyphInfo* cachedGlyph = NULL; ssize_t index = mCachedGlyphs.indexOfKey(textUnit); if (index >= 0) { cachedGlyph = mCachedGlyphs.valueAt(index); } else { - cachedGlyph = cacheGlyph(paint, textUnit); + cachedGlyph = cacheGlyph(paint, textUnit, precaching); } // Is the glyph still in texture cache? if (!cachedGlyph->mIsValid) { const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit); - updateGlyphCache(paint, skiaGlyph, cachedGlyph); + updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching); } return cachedGlyph; @@ -438,7 +438,7 @@ void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { break; } - CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); + CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true); glyphsCount++; } @@ -529,7 +529,8 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len } } -void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { +void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, + bool precaching) { glyph->mAdvanceX = skiaGlyph.fAdvanceX; glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; @@ -542,7 +543,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp // Get the bitmap for the glyph paint->findImage(skiaGlyph); - mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY); + mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching); if (!glyph->mIsValid) { return; @@ -567,7 +568,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp mState->mUploadTexture = true; } -CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { +CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) { CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); @@ -575,7 +576,7 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { newGlyph->mGlyphIndex = skiaGlyph.fID; newGlyph->mIsValid = false; - updateGlyphCache(paint, skiaGlyph, newGlyph); + updateGlyphCache(paint, skiaGlyph, newGlyph, precaching); return newGlyph; } @@ -762,7 +763,7 @@ CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph, } void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, - uint32_t* retOriginX, uint32_t* retOriginY) { + uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) { checkInit(); cachedGlyph->mIsValid = false; // If the glyph is too tall, don't cache it @@ -779,15 +780,16 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); - // If the new glyph didn't fit, flush the state so far and invalidate everything if (!cacheTexture) { - flushAllAndInvalidate(); - - // Try to fit it again - cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); + if (!precaching) { + // If the new glyph didn't fit and we are not just trying to precache it, + // clear out the cache and try again + flushAllAndInvalidate(); + cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY); + } - // if we still don't fit, something is wrong and we shouldn't draw if (!cacheTexture) { + // either the glyph didn't fit or we're precaching and will cache it when we draw return; } } diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index febae17..8d0d21d 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -243,8 +243,9 @@ protected: void invalidateTextureCache(CacheTexture *cacheTexture = NULL); - CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph); - void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph); + CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching); + void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, + bool precaching); void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, @@ -258,7 +259,7 @@ protected: void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, SkPathMeasure& measure, SkPoint* position, SkVector* tangent); - CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit); + CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false); static glyph_t nextGlyph(const uint16_t** srcPtr) { const uint16_t* src = *srcPtr; @@ -364,7 +365,7 @@ protected: void initTextTexture(); CacheTexture* createCacheTexture(int width, int height, bool allocate); void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, - uint32_t *retOriginX, uint32_t *retOriginY); + uint32_t *retOriginX, uint32_t *retOriginY, bool precaching); CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY); void flushAllAndInvalidate(); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 726b57c7..2e4e349 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -217,10 +217,12 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, float amount = (pos - start) / distance; float oppAmount = 1.0f - amount; - *p++ = uint8_t(startR * oppAmount + endR * amount); - *p++ = uint8_t(startG * oppAmount + endG * amount); - *p++ = uint8_t(startB * oppAmount + endB * amount); - *p++ = uint8_t(startA * oppAmount + endA * amount); + const float alpha = startA * oppAmount + endA * amount; + const float a = alpha / 255.0f; + *p++ = uint8_t(a * (startR * oppAmount + endR * amount)); + *p++ = uint8_t(a * (startG * oppAmount + endG * amount)); + *p++ = uint8_t(a * (startB * oppAmount + endB * amount)); + *p++ = uint8_t(alpha); } for (int i = 1; i < GRADIENT_TEXTURE_HEIGHT; i++) { diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 09dbf78..8da9f66 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -440,7 +440,7 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, mode = SkXfermode::kSrcOver_Mode; } - createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo); + createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo); } return count; @@ -508,44 +508,56 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot * buffer is left untouched until the first drawing operation. Only when * something actually gets drawn are the layers regions cleared. */ -bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, - float right, float bottom, int alpha, SkXfermode::Mode mode, - int flags, GLuint previousFbo) { +bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom, + int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) { LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag; // Window coordinates of the layer + Rect clip; Rect bounds(left, top, right, bottom); - if (!fboLayer) { - mSnapshot->transform->mapRect(bounds); - - // Layers only make sense if they are in the framebuffer's bounds - if (bounds.intersect(*snapshot->clipRect)) { - // We cannot work with sub-pixels in this case - bounds.snapToPixelBoundaries(); - - // When the layer is not an FBO, we may use glCopyTexImage so we - // need to make sure the layer does not extend outside the bounds - // of the framebuffer - if (!bounds.intersect(snapshot->previous->viewport)) { - bounds.setEmpty(); - } - } else { + Rect untransformedBounds(bounds); + mSnapshot->transform->mapRect(bounds); + + // Layers only make sense if they are in the framebuffer's bounds + if (bounds.intersect(*mSnapshot->clipRect)) { + // We cannot work with sub-pixels in this case + bounds.snapToPixelBoundaries(); + + // When the layer is not an FBO, we may use glCopyTexImage so we + // need to make sure the layer does not extend outside the bounds + // of the framebuffer + if (!bounds.intersect(mSnapshot->previous->viewport)) { bounds.setEmpty(); + } else if (fboLayer) { + clip.set(bounds); + mat4 inverse; + inverse.loadInverse(*mSnapshot->transform); + inverse.mapRect(clip); + clip.snapToPixelBoundaries(); + if (clip.intersect(untransformedBounds)) { + clip.translate(-left, -top); + bounds.set(untransformedBounds); + } else { + clip.setEmpty(); + } } + } else { + bounds.setEmpty(); } if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize || - bounds.getHeight() > mCaches.maxTextureSize) { - snapshot->empty = fboLayer; + bounds.getHeight() > mCaches.maxTextureSize || + (fboLayer && clip.isEmpty())) { + mSnapshot->empty = fboLayer; } else { - snapshot->invisible = snapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer); + mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer); } // Bail out if we won't draw in this snapshot - if (snapshot->invisible || snapshot->empty) { + if (mSnapshot->invisible || mSnapshot->empty) { return false; } @@ -563,23 +575,23 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, layer->setBlend(true); // Save the layer in the snapshot - snapshot->flags |= Snapshot::kFlagIsLayer; - snapshot->layer = layer; + mSnapshot->flags |= Snapshot::kFlagIsLayer; + mSnapshot->layer = layer; if (fboLayer) { - return createFboLayer(layer, bounds, snapshot, previousFbo); + return createFboLayer(layer, bounds, clip, previousFbo); } else { // Copy the framebuffer into the layer layer->bindTexture(); if (!bounds.isEmpty()) { if (layer->isEmpty()) { glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, - bounds.left, snapshot->height - bounds.bottom, + bounds.left, mSnapshot->height - bounds.bottom, layer->getWidth(), layer->getHeight(), 0); layer->setEmpty(false); } else { glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, - snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); + mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); } // Enqueue the buffer coordinates to clear the corresponding region later @@ -590,35 +602,20 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, return true; } -bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot, - GLuint previousFbo) { +bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) { layer->setFbo(mCaches.fboCache.get()); - snapshot->region = &snapshot->layer->region; - snapshot->flags |= Snapshot::kFlagFboTarget; - - Rect clip(bounds); - snapshot->transform->mapRect(clip); - clip.intersect(*snapshot->clipRect); - clip.snapToPixelBoundaries(); - clip.intersect(snapshot->previous->viewport); - - mat4 inverse; - inverse.loadInverse(*mSnapshot->transform); - - inverse.mapRect(clip); - clip.snapToPixelBoundaries(); - clip.intersect(bounds); - clip.translate(-bounds.left, -bounds.top); - - snapshot->flags |= Snapshot::kFlagIsFboLayer; - snapshot->fbo = layer->getFbo(); - snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); - snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->height = bounds.getHeight(); - snapshot->flags |= Snapshot::kFlagDirtyOrtho; - snapshot->orthoMatrix.load(mOrthoMatrix); + mSnapshot->region = &mSnapshot->layer->region; + mSnapshot->flags |= Snapshot::kFlagFboTarget; + + mSnapshot->flags |= Snapshot::kFlagIsFboLayer; + mSnapshot->fbo = layer->getFbo(); + mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); + mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); + mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + mSnapshot->height = bounds.getHeight(); + mSnapshot->flags |= Snapshot::kFlagDirtyOrtho; + mSnapshot->orthoMatrix.load(mOrthoMatrix); // Bind texture to FBO glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index d3b98a4..2369f47 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -380,7 +380,7 @@ private: * * @return True if the layer was successfully created, false otherwise */ - bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, + bool createLayer(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); /** @@ -391,8 +391,7 @@ private: * @param bounds The bounds of the layer * @param previousFbo The name of the current framebuffer */ - bool createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot, - GLuint previousFbo); + bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo); /** * Compose the specified layer as a region. diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 8916efd..9013fd5 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -46,11 +46,12 @@ static inline bool isPowerOfTwo(unsigned int n) { } static inline void bindUniformColor(int slot, uint32_t color) { + const float a = ((color >> 24) & 0xff) / 255.0f; glUniform4f(slot, - ((color >> 16) & 0xff) / 255.0f, - ((color >> 8) & 0xff) / 255.0f, - ((color ) & 0xff) / 255.0f, - ((color >> 24) & 0xff) / 255.0f); + a * ((color >> 16) & 0xff) / 255.0f, + a * ((color >> 8) & 0xff) / 255.0f, + a * ((color ) & 0xff) / 255.0f, + a); } /////////////////////////////////////////////////////////////////////////////// @@ -154,10 +155,6 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, // Uniforms bindTexture(texture, mWrapS, mWrapT); - // Assume linear here; we should really check the transform in - // ::updateTransforms() but we don't have the texture object - // available at that point. The optimization is not worth the - // effort for now. texture->setFilter(GL_LINEAR); glUniform1i(program->getUniform("bitmapSampler"), textureSlot); @@ -166,14 +163,6 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height); } -void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView, - const Snapshot& snapshot) { - mat4 textureTransform; - computeScreenSpaceMatrix(textureTransform, modelView); - glUniformMatrix4fv(program->getUniform("textureTransform"), 1, - GL_FALSE, &textureTransform.data[0]); -} - /////////////////////////////////////////////////////////////////////////////// // Linear gradient shader /////////////////////////////////////////////////////////////////////////////// @@ -257,13 +246,6 @@ void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelV glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } -void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView, - const Snapshot& snapshot) { - mat4 screenSpace; - computeScreenSpaceMatrix(screenSpace, modelView); - glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); -} - /////////////////////////////////////////////////////////////////////////////// // Circular gradient shader /////////////////////////////////////////////////////////////////////////////// @@ -384,13 +366,6 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } -void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView, - const Snapshot& snapshot) { - mat4 screenSpace; - computeScreenSpaceMatrix(screenSpace, modelView); - glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); -} - /////////////////////////////////////////////////////////////////////////////// // Compose shader /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index a710b86..2687592 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -82,10 +82,6 @@ struct SkiaShader { mGradientCache = gradientCache; } - virtual void updateTransforms(Program* program, const mat4& modelView, - const Snapshot& snapshot) { - } - uint32_t getGenerationId() { return mGenerationId; } @@ -148,7 +144,6 @@ struct SkiaBitmapShader: public SkiaShader { void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit); - void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); private: SkiaBitmapShader() { @@ -172,7 +167,6 @@ struct SkiaLinearGradientShader: public SkiaShader { void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit); - void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); private: SkiaLinearGradientShader() { @@ -197,7 +191,6 @@ struct SkiaSweepGradientShader: public SkiaShader { virtual void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, GLuint* textureUnit); - void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); protected: SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml index 67e13eb..fbbd7e5 100644 --- a/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/navigation_bar.xml @@ -143,8 +143,13 @@ <com.android.systemui.statusbar.policy.DeadZone android:id="@+id/deadzone" - android:layout_height="@dimen/navigation_bar_deadzone_size" + android:layout_height="match_parent" android:layout_width="match_parent" + systemui:minSize="@dimen/navigation_bar_deadzone_size" + systemui:maxSize="@dimen/navigation_bar_deadzone_size_max" + systemui:holdTime="@integer/navigation_bar_deadzone_hold" + systemui:decayTime="@integer/navigation_bar_deadzone_decay" + systemui:orientation="horizontal" android:layout_gravity="top" /> </FrameLayout> @@ -269,8 +274,13 @@ <com.android.systemui.statusbar.policy.DeadZone android:id="@+id/deadzone" - android:layout_height="@dimen/navigation_bar_deadzone_size" + android:layout_height="match_parent" android:layout_width="match_parent" + systemui:minSize="@dimen/navigation_bar_deadzone_size" + systemui:maxSize="@dimen/navigation_bar_deadzone_size_max" + systemui:holdTime="@integer/navigation_bar_deadzone_hold" + systemui:decayTime="@integer/navigation_bar_deadzone_decay" + systemui:orientation="vertical" android:layout_gravity="top" /> </FrameLayout> diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index d41040d..33b5dbb 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -147,8 +147,13 @@ <com.android.systemui.statusbar.policy.DeadZone android:id="@+id/deadzone" - android:layout_height="@dimen/navigation_bar_deadzone_size" + android:layout_height="match_parent" android:layout_width="match_parent" + systemui:minSize="@dimen/navigation_bar_deadzone_size" + systemui:maxSize="@dimen/navigation_bar_deadzone_size_max" + systemui:holdTime="@integer/navigation_bar_deadzone_hold" + systemui:decayTime="@integer/navigation_bar_deadzone_decay" + systemui:orientation="horizontal" android:layout_gravity="top" /> </FrameLayout> @@ -276,9 +281,14 @@ <com.android.systemui.statusbar.policy.DeadZone android:id="@+id/deadzone" - android:layout_width="@dimen/navigation_bar_deadzone_size" android:layout_height="match_parent" - android:layout_gravity="left" + android:layout_width="match_parent" + systemui:minSize="@dimen/navigation_bar_deadzone_size" + systemui:maxSize="@dimen/navigation_bar_deadzone_size_max" + systemui:holdTime="@integer/navigation_bar_deadzone_hold" + systemui:decayTime="@integer/navigation_bar_deadzone_decay" + systemui:orientation="vertical" + android:layout_gravity="top" /> </FrameLayout> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 48fb21f..047570f 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -35,5 +35,17 @@ <declare-styleable name="RecentsPanelView"> <attr name="recentItemLayout" format="reference" /> </declare-styleable> + <declare-styleable name="DeadZone"> + <attr name="minSize" format="dimension" /> + <attr name="maxSize" format="dimension" /> + <attr name="holdTime" format="integer" /> + <attr name="decayTime" format="integer" /> + <attr name="orientation" /> + </declare-styleable> + + <attr name="orientation"> + <enum name="horizontal" value="0" /> + <enum name="vertical" value="1" /> + </attr> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 1cd7904..3337573 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -72,5 +72,9 @@ <!-- Whether we're using the tablet-optimized recents interface (we use this value at runtime for some things) --> <integer name="status_bar_recents_bg_gradient_degrees">90</integer> + + <!-- decay duration (from size_max -> size), in ms --> + <integer name="navigation_bar_deadzone_hold">333</integer> + <integer name="navigation_bar_deadzone_decay">333</integer> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 94465e2..0d7cdb1 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -63,6 +63,8 @@ <!-- thickness (height) of the dead zone at the top of the navigation bar, reducing false presses on navbar buttons; approx 2mm --> <dimen name="navigation_bar_deadzone_size">12dp</dimen> + <!-- size of the dead zone when touches have recently occurred elsewhere on screen --> + <dimen name="navigation_bar_deadzone_size_max">32dp</dimen> <!-- Height of notification icons in the status bar --> <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 33973b6..dcc2e57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -45,13 +45,12 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.DelegateViewHelper; +import com.android.systemui.statusbar.policy.DeadZone; public class NavigationBarView extends LinearLayout { final static boolean DEBUG = false; final static String TAG = "PhoneStatusBar/NavigationBarView"; - final static boolean DEBUG_DEADZONE = false; - final static boolean NAVBAR_ALWAYS_AT_RIGHT = true; final static boolean ANIMATE_HIDE_TRANSITION = false; // turned off because it introduces unsightly delay when videos goes to full screen @@ -71,6 +70,7 @@ public class NavigationBarView extends LinearLayout { private Drawable mBackIcon, mBackLandIcon, mBackAltIcon, mBackAltLandIcon; private DelegateViewHelper mDelegateHelper; + private DeadZone mDeadZone; // workaround for LayoutTransitions leaving the nav buttons in a weird state (bug 5549288) final static boolean WORKAROUND_INVALID_LAYOUT = true; @@ -109,10 +109,14 @@ public class NavigationBarView extends LinearLayout { @Override public boolean onTouchEvent(MotionEvent event) { + if (mDeadZone != null && event.getAction() == MotionEvent.ACTION_OUTSIDE) { + mDeadZone.poke(event); + } if (mDelegateHelper != null) { - mDelegateHelper.onInterceptTouchEvent(event); + boolean ret = mDelegateHelper.onInterceptTouchEvent(event); + if (ret) return true; } - return true; + return super.onTouchEvent(event); } @Override @@ -335,15 +339,13 @@ public class NavigationBarView extends LinearLayout { mCurrentView = mRotatedViews[rot]; mCurrentView.setVisibility(View.VISIBLE); + mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone); + // force the low profile & disabled states into compliance setLowProfile(mLowProfile, false, true /* force */); setDisabledFlags(mDisabledFlags, true /* force */); setMenuVisibility(mShowMenu, true /* force */); - if (DEBUG_DEADZONE) { - mCurrentView.findViewById(R.id.deadzone).setBackgroundColor(0x808080FF); - } - if (DEBUG) { Slog.d(TAG, "reorient(): rot=" + mDisplay.getRotation()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 9d2678a..9b4ee38 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -639,6 +639,7 @@ public class PhoneStatusBar extends BaseStatusBar { | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, PixelFormat.OPAQUE); // this will allow the navbar to run in an overlay on devices that support this diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java index 19fbe96..6ffca2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeadZone.java @@ -16,26 +16,145 @@ package com.android.systemui.statusbar.policy; +import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.os.SystemClock; import android.util.AttributeSet; +import android.util.Slog; import android.view.MotionEvent; import android.view.View; import com.android.systemui.R; public class DeadZone extends View { + public static final String TAG = "DeadZone"; + + public static final boolean DEBUG = false; + public static final int HORIZONTAL = 0; + public static final int VERTICAL = 1; + + private boolean mShouldFlash; + private float mFlashFrac = 0f; + + private int mSizeMax; + private int mSizeMin; + // Upon activity elsewhere in the UI, the dead zone will hold steady for + // mHold ms, then move back over the course of mDecay ms + private int mHold, mDecay; + private boolean mVertical; + private long mLastPokeTime; + + private final Runnable mDebugFlash = new Runnable() { + @Override + public void run() { + ObjectAnimator.ofFloat(DeadZone.this, "flash", 1f, 0f).setDuration(150).start(); + } + }; + public DeadZone(Context context, AttributeSet attrs) { this(context, attrs, 0); } public DeadZone(Context context, AttributeSet attrs, int defStyle) { super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DeadZone, + defStyle, 0); + + mHold = a.getInteger(R.styleable.DeadZone_holdTime, 0); + mDecay = a.getInteger(R.styleable.DeadZone_decayTime, 0); + + mSizeMin = a.getDimensionPixelSize(R.styleable.DeadZone_minSize, 0); + mSizeMax = a.getDimensionPixelSize(R.styleable.DeadZone_maxSize, 0); + + int index = a.getInt(R.styleable.DeadZone_orientation, -1); + mVertical = (index == VERTICAL); + + if (DEBUG) + Slog.v(TAG, this + " size=[" + mSizeMin + "-" + mSizeMax + "] hold=" + mHold + + (mVertical ? " vertical" : " horizontal")); + + setFlashOnTouchCapture(true); + } + + static float lerp(float a, float b, float f) { + return (b - a) * f + a; + } + + private float getSize(long now) { + if (mSizeMax == 0) + return 0; + long dt = (now - mLastPokeTime); + if (dt > mHold + mDecay) + return mSizeMin; + if (dt < mHold) + return mSizeMax; + return (int) lerp(mSizeMax, mSizeMin, (float) (dt - mHold) / mDecay); + } + + public void setFlashOnTouchCapture(boolean dbg) { + mShouldFlash = dbg; + mFlashFrac = 0f; + postInvalidate(); } // I made you a touch event @Override - public boolean onTouchEvent (MotionEvent event) { - return true; // but I eated it + public boolean onTouchEvent(MotionEvent event) { + if (DEBUG) + Slog.v(TAG, this + " onTouch: " + MotionEvent.actionToString(event.getAction())); + + final int action = event.getAction(); + if (action == MotionEvent.ACTION_OUTSIDE) { + poke(event); + } else if (action == MotionEvent.ACTION_DOWN) { + if (DEBUG) + Slog.v(TAG, this + " ACTION_DOWN: " + event.getX() + "," + event.getY()); + int size = (int) getSize(event.getEventTime()); + if ((mVertical && event.getX() < size) || event.getY() < size) { + if (DEBUG) + Slog.v(TAG, "eating click!"); + if (mShouldFlash) { + post(mDebugFlash); + postInvalidate(); + } + return true; // but I eated it + } + } + return false; + } + + public void poke(MotionEvent event) { + mLastPokeTime = event.getEventTime(); + if (DEBUG) + Slog.v(TAG, "poked! size=" + getSize(mLastPokeTime)); + postInvalidate(); + } + + public void setFlash(float f) { + mFlashFrac = f; + postInvalidate(); } -} + public float getFlash() { + return mFlashFrac; + } + + @Override + public void onDraw(Canvas can) { + if (!mShouldFlash || mFlashFrac <= 0f) { + return; + } + + final int size = (int) getSize(SystemClock.uptimeMillis()); + can.clipRect(0, 0, mVertical ? size : can.getWidth(), mVertical ? can.getHeight() : size); + final float frac = DEBUG ? (mFlashFrac - 0.5f) + 0.5f : mFlashFrac; + can.drawARGB((int) (frac * 0xFF), 0xDD, 0xEE, 0xAA); + + if (DEBUG && size > mSizeMin) + // crazy aggressive redrawing here, for debugging only + postInvalidateDelayed(100); + } +} diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index ef31dd6..2b6e856 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -3141,7 +3141,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onServiceDisconnected(ComponentName name) {} }; - if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) { + if (mContext.bindService( + intent, conn, Context.BIND_AUTO_CREATE, UserHandle.USER_CURRENT)) { mScreenshotConnection = conn; mHandler.postDelayed(mScreenshotTimeout, 10000); } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java index 0ad3a6a..d74a5e7 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java @@ -70,7 +70,7 @@ public class KeyguardHostView extends KeyguardViewBase { private static final int SECURITY_ACCOUNT_ID = R.id.keyguard_account_view; private AppWidgetHost mAppWidgetHost; - private ViewGroup mAppWidgetContainer; + private KeyguardWidgetPager mAppWidgetContainer; private ViewFlipper mViewFlipper; private Button mEmergencyDialerButton; private boolean mEnableMenuKey; @@ -121,7 +121,7 @@ public class KeyguardHostView extends KeyguardViewBase { @Override protected void onFinishInflate() { - mAppWidgetContainer = (ViewGroup) findViewById(R.id.app_widget_container); + mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container); mAppWidgetContainer.setVisibility(VISIBLE); // View Flipper @@ -182,7 +182,7 @@ public class KeyguardHostView extends KeyguardViewBase { } void addWidget(AppWidgetHostView view) { - mAppWidgetContainer.addView(view); + mAppWidgetContainer.addWidget(view); } private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { @@ -221,6 +221,11 @@ public class KeyguardHostView extends KeyguardViewBase { mViewMediatorCallback.keyguardDoneDrawing(); } + @Override + public void setOnDismissRunnable(Runnable runnable) { + KeyguardHostView.this.setOnDismissRunnable(runnable); + } + }; public void takeEmergencyCallAction() { @@ -273,7 +278,7 @@ public class KeyguardHostView extends KeyguardViewBase { final android.app.PendingIntent pendingIntent, final Intent fillInIntent) { if (pendingIntent.isActivity()) { - mLaunchRunnable = new Runnable() { + setOnDismissRunnable(new Runnable() { public void run() { try { // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? @@ -292,7 +297,7 @@ public class KeyguardHostView extends KeyguardViewBase { "unknown exception: ", e); } } - }; + }); mCallback.dismiss(false); return true; @@ -307,6 +312,14 @@ public class KeyguardHostView extends KeyguardViewBase { requestFocus(); } + /** + * Sets a runnable to run when keyguard is dismissed + * @param runnable + */ + protected void setOnDismissRunnable(Runnable runnable) { + mLaunchRunnable = runnable; + } + private KeyguardSecurityView getSecurityView(int securitySelectorId) { final int children = mViewFlipper.getChildCount(); for (int child = 0; child < children; child++) { diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java index 1a4a40b..36342a5 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityCallback.java @@ -57,6 +57,15 @@ public interface KeyguardSecurityCallback { */ void showBackupUnlock(); + /** + * Used to inform keyguard when the current view is done drawing. + */ void keyguardDoneDrawing(); + /** + * Sets a runnable to launch after the user enters their + * @param runnable + */ + void setOnDismissRunnable(Runnable runnable); + } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java index b69697f..a38b247 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java @@ -61,7 +61,7 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) .getAssistIntent(mContext, UserHandle.USER_CURRENT); if (assistIntent != null) { - launchActivity(assistIntent); + launchActivity(assistIntent, false); } else { Log.w(TAG, "Failed to get intent for assist activity"); } @@ -69,7 +69,7 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri break; case com.android.internal.R.drawable.ic_lockscreen_camera: - launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA)); + launchCamera(); mCallback.userActivity(0); break; @@ -128,6 +128,16 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri this(context, null); } + protected void launchCamera() { + if (mLockPatternUtils.isSecure()) { + // Launch the secure version of the camera + launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE), true); + } else { + // Launch the normal camera + launchActivity(new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA), false); + } + } + public KeyguardSelectorView(Context context, AttributeSet attrs) { super(context, attrs); mLockPatternUtils = new LockPatternUtils(getContext()); @@ -217,21 +227,36 @@ public class KeyguardSelectorView extends LinearLayout implements KeyguardSecuri /** * Launches the said intent for the current foreground user. * @param intent + * @param showsWhileLocked true if the activity can be run on top of keyguard. + * See {@link WindowManager#FLAG_SHOW_WHEN_LOCKED} */ - private void launchActivity(Intent intent) { + private void launchActivity(final Intent intent, boolean showsWhileLocked) { intent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); - try { - ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); - } catch (RemoteException e) { - Log.w(TAG, "can't dismiss keyguard on launch"); - } - try { - mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "Activity not found for intent + " + intent.getAction()); + boolean isSecure = mLockPatternUtils.isSecure(); + if (!isSecure || showsWhileLocked) { + if (!isSecure) try { + ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity(); + } catch (RemoteException e) { + Log.w(TAG, "can't dismiss keyguard on launch"); + } + try { + mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "Activity not found for intent + " + intent.getAction()); + } + } else { + // Create a runnable to start the activity and ask the user to enter their + // credentials. + mCallback.setOnDismissRunnable(new Runnable() { + @Override + public void run() { + mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } + }); + mCallback.dismiss(false); } } diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java new file mode 100644 index 0000000..d778129 --- /dev/null +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 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.internal.policy.impl.keyguard; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.NinePatchDrawable; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import com.android.internal.R; + +public class KeyguardWidgetFrame extends FrameLayout { + private final static PorterDuffXfermode sAddBlendMode = + new PorterDuffXfermode(PorterDuff.Mode.ADD); + private static int sWidgetPagePadding; + private static Drawable sLeftOverscrollDrawable; + private static Drawable sRightOverscrollDrawable; + + private Drawable mForegroundDrawable; + private final Rect mForegroundRect = new Rect(); + private int mForegroundAlpha = 0; + + public KeyguardWidgetFrame(Context context) { + this(context, null, 0); + } + + public KeyguardWidgetFrame(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public KeyguardWidgetFrame(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + if (sLeftOverscrollDrawable == null) { + Resources res = context.getResources(); + sLeftOverscrollDrawable = res.getDrawable( + com.android.internal.R.drawable.kg_widget_overscroll_layer_left); + sRightOverscrollDrawable = res.getDrawable( + com.android.internal.R.drawable.kg_widget_overscroll_layer_right); + sWidgetPagePadding = + res.getDimensionPixelSize(com.android.internal.R.dimen.kg_widget_page_padding); + } + setPadding(sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding, sWidgetPagePadding); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (mForegroundAlpha > 0) { + mForegroundDrawable.setBounds(mForegroundRect); + Paint p = ((NinePatchDrawable) mForegroundDrawable).getPaint(); + p.setXfermode(sAddBlendMode); + mForegroundDrawable.draw(canvas); + p.setXfermode(null); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mForegroundRect.set(sWidgetPagePadding, sWidgetPagePadding, + w - sWidgetPagePadding, h - sWidgetPagePadding); + } + + void setOverScrollAmount(float r, boolean left) { + if (left && mForegroundDrawable != sLeftOverscrollDrawable) { + mForegroundDrawable = sLeftOverscrollDrawable; + } else if (!left && mForegroundDrawable != sRightOverscrollDrawable) { + mForegroundDrawable = sRightOverscrollDrawable; + } + + mForegroundAlpha = (int) Math.round((r * 255)); + mForegroundDrawable.setAlpha(mForegroundAlpha); + invalidate(); + } +} diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java index 120f8f8..7d077e2 100644 --- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetView.java +++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java @@ -16,39 +16,53 @@ package com.android.internal.policy.impl.keyguard; import android.animation.TimeInterpolator; +import android.appwidget.AppWidgetHostView; import android.content.Context; import android.util.AttributeSet; +import android.view.Gravity; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; -public class KeyguardWidgetView extends PagedView { +import android.widget.FrameLayout; - public KeyguardWidgetView(Context context, AttributeSet attrs) { +public class KeyguardWidgetPager extends PagedView { + ZInterpolator mZInterpolator = new ZInterpolator(0.5f); + private static float CAMERA_DISTANCE = 10000; + private static float TRANSITION_SCALE_FACTOR = 0.74f; + private static float TRANSITION_PIVOT = 0.65f; + private static float TRANSITION_MAX_ROTATION = 30; + private static final boolean PERFORM_OVERSCROLL_ROTATION = true; + private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f); + private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4); + + public KeyguardWidgetPager(Context context, AttributeSet attrs) { this(context, attrs, 0); } - public KeyguardWidgetView(Context context) { + public KeyguardWidgetPager(Context context) { this(null, null, 0); } - public KeyguardWidgetView(Context context, AttributeSet attrs, int defStyle) { + public KeyguardWidgetPager(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } - @Override - protected void onFinishInflate() { - super.onFinishInflate(); + /* + * We wrap widgets in a special frame which handles drawing the overscroll foreground. + */ + public void addWidget(AppWidgetHostView widget) { + KeyguardWidgetFrame frame = new KeyguardWidgetFrame(getContext()); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + lp.gravity = Gravity.CENTER; + // The framework adds a default padding to AppWidgetHostView. We don't need this padding + // for the Keyguard, so we override it to be 0. + widget.setPadding(0, 0, 0, 0); + frame.addView(widget, lp); + addView(frame); } - ZInterpolator mZInterpolator = new ZInterpolator(0.5f); - private static float CAMERA_DISTANCE = 1500; - private static float TRANSITION_SCALE_FACTOR = 0.74f; - private static float TRANSITION_PIVOT = 0.65f; - private static float TRANSITION_MAX_ROTATION = 30; - private static final boolean PERFORM_OVERSCROLL_ROTATION = true; - private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f); - private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4); /* * This interpolator emulates the rate at which the perceived scale of an object changes * as its distance from a camera increases. When this interpolator is applied to a scale @@ -68,6 +82,11 @@ public class KeyguardWidgetView extends PagedView { } } + @Override + protected void overScroll(float amount) { + acceleratedOverScroll(amount); + } + // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack. @Override protected void screenScrolled(int screenCenter) { @@ -103,6 +122,10 @@ public class KeyguardWidgetView extends PagedView { // Overscroll to the left v.setPivotX(TRANSITION_PIVOT * pageWidth); v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); + if (v instanceof KeyguardWidgetFrame) { + ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress), + true); + } scale = 1.0f; alpha = 1.0f; // On the first page, we don't want the page to have any lateral motion @@ -113,6 +136,10 @@ public class KeyguardWidgetView extends PagedView { v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress); scale = 1.0f; alpha = 1.0f; + if (v instanceof KeyguardWidgetFrame) { + ((KeyguardWidgetFrame) v).setOverScrollAmount(Math.abs(scrollProgress), + false); + } // On the last page, we don't want the page to have any lateral motion. translationX = 0; } else { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index d8c8dfa..a061d58 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1991,13 +1991,19 @@ public final class ActivityManagerService extends ActivityManagerNative try { final PackageManager pm = mContext.getPackageManager(); gids = pm.getPackageGids(app.info.packageName); + + if (Environment.isExternalStorageEmulated()) { + if (pm.checkPermission( + android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE, + app.info.packageName) == PERMISSION_GRANTED) { + mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL; + } else { + mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; + } + } } catch (PackageManager.NameNotFoundException e) { Slog.w(TAG, "Unable to retrieve gids", e); } - - if (Environment.isExternalStorageEmulated()) { - mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER; - } } if (mFactoryTest != SystemServer.FACTORY_TEST_OFF) { if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL @@ -10742,7 +10748,25 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("File descriptors passed in Intent"); } - checkValidCaller(Binder.getCallingUid(), userId); + if (userId != UserHandle.getCallingUserId()) { + // Requesting a different user, make sure that they have permission + if (checkComponentPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Binder.getCallingPid(), Binder.getCallingUid(), -1, true) + == PackageManager.PERMISSION_GRANTED) { + // Translate to the current user id, if caller wasn't aware + if (userId == UserHandle.USER_CURRENT) { + userId = mCurrentUserId; + } + } else { + String msg = "Permission Denial: Request to bindService as user " + userId + + " but is calling from user " + UserHandle.getCallingUserId() + + "; this requires " + + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } synchronized(this) { return mServices.bindServiceLocked(caller, token, service, resolvedType, diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java index 541650e..dabcf2f 100644 --- a/services/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/java/com/android/server/net/LockdownVpnTracker.java @@ -55,6 +55,7 @@ public class LockdownVpnTracker { private static final int MAX_ERROR_COUNT = 4; private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET"; + private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS"; private final Context mContext; private final INetworkManagementService mNetService; @@ -84,9 +85,9 @@ public class LockdownVpnTracker { mVpn = Preconditions.checkNotNull(vpn); mProfile = Preconditions.checkNotNull(profile); - final Intent intent = new Intent(ACTION_LOCKDOWN_RESET); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mResetIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); + final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET); + resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0); } private BroadcastReceiver mResetReceiver = new BroadcastReceiver() { @@ -115,7 +116,7 @@ public class LockdownVpnTracker { final boolean egressChanged = egressProp == null || !TextUtils.equals(mAcceptedEgressIface, egressProp.getInterfaceName()); if (egressDisconnected || egressChanged) { - clearSourceRules(); + clearSourceRulesLocked(); mAcceptedEgressIface = null; mVpn.stopLegacyVpn(); } @@ -150,7 +151,7 @@ public class LockdownVpnTracker { showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected); try { - clearSourceRules(); + clearSourceRulesLocked(); mNetService.setFirewallInterfaceRule(iface, true); mNetService.setFirewallEgressSourceRule(sourceAddr, true); @@ -167,7 +168,13 @@ public class LockdownVpnTracker { } public void init() { - Slog.d(TAG, "init()"); + synchronized (mStateLock) { + initLocked(); + } + } + + private void initLocked() { + Slog.d(TAG, "initLocked()"); mVpn.setEnableNotifications(false); @@ -188,7 +195,13 @@ public class LockdownVpnTracker { } public void shutdown() { - Slog.d(TAG, "shutdown()"); + synchronized (mStateLock) { + shutdownLocked(); + } + } + + private void shutdownLocked() { + Slog.d(TAG, "shutdownLocked()"); mAcceptedEgressIface = null; mErrorCount = 0; @@ -200,7 +213,7 @@ public class LockdownVpnTracker { } catch (RemoteException e) { throw new RuntimeException("Problem setting firewall rules", e); } - clearSourceRules(); + clearSourceRulesLocked(); hideNotification(); mContext.unregisterReceiver(mResetReceiver); @@ -208,15 +221,15 @@ public class LockdownVpnTracker { } public void reset() { - // cycle tracker, reset error count, and trigger retry - shutdown(); - init(); synchronized (mStateLock) { + // cycle tracker, reset error count, and trigger retry + shutdownLocked(); + initLocked(); handleStateChangedLocked(); } } - private void clearSourceRules() { + private void clearSourceRulesLocked() { try { if (mAcceptedIface != null) { mNetService.setFirewallInterfaceRule(mAcceptedIface, false); |
