Merge "Add Calls.TRANSCRIPTION to CallLog (2/2)" into lmp-dev
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a193a34..1f2f18a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.app.Activity;
import android.content.AbstractRestrictionsProvider;
import android.content.ComponentName;
@@ -2083,7 +2084,6 @@
/**
* @hide
- * @SystemApi
* Sets the given component as an active admin and registers the package as the profile
* owner for this user. The package must already be installed and there shouldn't be
* an existing profile owner registered for this user. Also, this method must be called
@@ -2097,6 +2097,7 @@
* @throws IllegalArgumentException if packageName is null, the package isn't installed, or
* the user has already been set up.
*/
+ @SystemApi
public boolean setActiveProfileOwner(ComponentName admin, String ownerName)
throws IllegalArgumentException {
if (mService != null) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index b033780..d14f226 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -88,8 +88,8 @@
private final float mLightY;
private final float mLightZ;
private final float mLightRadius;
- private final float mAmbientShadowAlpha;
- private final float mSpotShadowAlpha;
+ private final int mAmbientShadowAlpha;
+ private final int mSpotShadowAlpha;
private long mNativeProxy;
private boolean mInitialized = false;
@@ -104,8 +104,10 @@
mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
- mAmbientShadowAlpha = a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0);
- mSpotShadowAlpha = a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0);
+ mAmbientShadowAlpha = Math.round(
+ 255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0));
+ mSpotShadowAlpha = Math.round(
+ 255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0));
a.recycle();
long rootNodePtr = nCreateRootRenderNode();
@@ -208,7 +210,9 @@
mSurfaceHeight = height;
}
mRootNode.setLeftTopRightBottom(-mInsetLeft, -mInsetTop, mSurfaceWidth, mSurfaceHeight);
- nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight, lightX, mLightY, mLightZ, mLightRadius);
+ nSetup(mNativeProxy, mSurfaceWidth, mSurfaceHeight,
+ lightX, mLightY, mLightZ, mLightRadius,
+ mAmbientShadowAlpha, mSpotShadowAlpha);
}
@Override
@@ -453,7 +457,8 @@
private static native void nUpdateSurface(long nativeProxy, Surface window);
private static native void nPauseSurface(long nativeProxy, Surface window);
private static native void nSetup(long nativeProxy, int width, int height,
- float lightX, float lightY, float lightZ, float lightRadius);
+ float lightX, float lightY, float lightZ, float lightRadius,
+ int ambientShadowAlpha, int spotShadowAlpha);
private static native void nSetOpaque(long nativeProxy, boolean opaque);
private static native int nSyncAndDrawFrame(long nativeProxy,
long frameTimeNanos, long recordDuration, float density);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cc41669..92703ab 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -88,7 +88,7 @@
* </pre>
* <p>See {@link android.content.Intent} for more information.</p>
*
- * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
+ * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
* or set the entire Activity window as a WebView during {@link
* android.app.Activity#onCreate(Bundle) onCreate()}:</p>
* <pre class="prettyprint">
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 988d461..ec08a4f 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -219,9 +219,11 @@
static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
jint width, jint height,
- jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius) {
+ jfloat lightX, jfloat lightY, jfloat lightZ, jfloat lightRadius,
+ jint ambientShadowAlpha, jint spotShadowAlpha) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- proxy->setup(width, height, Vector3(lightX, lightY, lightZ), lightRadius);
+ proxy->setup(width, height, Vector3(lightX, lightY, lightZ), lightRadius,
+ ambientShadowAlpha, spotShadowAlpha);
}
static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
@@ -358,7 +360,7 @@
{ "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
{ "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
- { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup },
+ { "nSetup", "(JIIFFFFII)V", (void*) android_view_ThreadedRenderer_setup },
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
{ "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
diff --git a/docs/html/guide/topics/resources/localization.jd b/docs/html/guide/topics/resources/localization.jd
index e86d4c9..1ee6606 100644
--- a/docs/html/guide/topics/resources/localization.jd
+++ b/docs/html/guide/topics/resources/localization.jd
@@ -402,8 +402,7 @@
resolution and density of the device screen may differ, which could affect
the display of strings and drawables in your UI.</p>
-<p>To change the locale on a device, use the Settings application (Home >
-Menu > Settings > Locale & text > Select locale). </p>
+<p>To change the locale or language on a device, use the Settings application.</p>
<h3 id="emulator">Testing on an Emulator</h3>
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index 2d29420..69b7d2d 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -132,7 +132,7 @@
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
- .inflate(R.layout.my_text_view, parent);
+ .inflate(R.layout.my_text_view, parent, false);
// set the view's size, margins, paddings and layout parameters
...
ViewHolder vh = new ViewHolder(v);
diff --git a/graphics/java/android/graphics/BlurMaskFilter.java b/graphics/java/android/graphics/BlurMaskFilter.java
index 939af52..f3064f8 100644
--- a/graphics/java/android/graphics/BlurMaskFilter.java
+++ b/graphics/java/android/graphics/BlurMaskFilter.java
@@ -25,10 +25,25 @@
public class BlurMaskFilter extends MaskFilter {
public enum Blur {
- NORMAL(0), //!< blur inside and outside of the original border
- SOLID(1), //!< include the original mask, blur outside
- OUTER(2), //!< just blur outside the original border
- INNER(3); //!< just blur inside the original border
+ /**
+ * Blur inside and outside the original border.
+ */
+ NORMAL(0),
+
+ /**
+ * Draw solid inside the border, blur outside.
+ */
+ SOLID(1),
+
+ /**
+ * Draw nothing inside the border, blur outside.
+ */
+ OUTER(2),
+
+ /**
+ * Blur inside the border, draw nothing outside.
+ */
+ INNER(3);
Blur(int value) {
native_int = value;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5a96132..4f81066 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -132,19 +132,21 @@
///////////////////////////////////////////////////////////////////////////////
OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
- : mCaches(Caches::getInstance())
+ : mFrameStarted(false)
+ , mCaches(Caches::getInstance())
, mExtensions(Extensions::getInstance())
- , mRenderState(renderState) {
+ , mRenderState(renderState)
+ , mScissorOptimizationDisabled(false)
+ , mCountOverdraw(false)
+ , mLightCenter(FLT_MIN, FLT_MIN, FLT_MIN)
+ , mLightRadius(FLT_MIN)
+ , mAmbientShadowAlpha(0)
+ , mSpotShadowAlpha(0) {
// *set* draw modifiers to be 0
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
-
- mFrameStarted = false;
- mCountOverdraw = false;
-
- mScissorOptimizationDisabled = false;
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -163,6 +165,14 @@
}
}
+void OpenGLRenderer::initLight(const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ mLightCenter = lightCenter;
+ mLightRadius = lightRadius;
+ mAmbientShadowAlpha = ambientShadowAlpha;
+ mSpotShadowAlpha = spotShadowAlpha;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Setup
///////////////////////////////////////////////////////////////////////////////
@@ -3172,13 +3182,13 @@
SkPaint paint;
paint.setAntiAlias(true); // want to use AlphaVertex
- if (ambientShadowVertexBuffer && mCaches.propertyAmbientShadowStrength > 0) {
- paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0);
+ if (ambientShadowVertexBuffer && mAmbientShadowAlpha > 0) {
+ paint.setARGB(casterAlpha * mAmbientShadowAlpha, 0, 0, 0);
drawVertexBuffer(*ambientShadowVertexBuffer, &paint);
}
- if (spotShadowVertexBuffer && mCaches.propertySpotShadowStrength > 0) {
- paint.setARGB(casterAlpha * mCaches.propertySpotShadowStrength, 0, 0, 0);
+ if (spotShadowVertexBuffer && mSpotShadowAlpha > 0) {
+ paint.setARGB(casterAlpha * mSpotShadowAlpha, 0, 0, 0);
drawVertexBuffer(*spotShadowVertexBuffer, &paint);
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 4e7844b..f698b45 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -124,6 +124,8 @@
virtual ~OpenGLRenderer();
void initProperties();
+ void initLight(const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
virtual void onViewportInitialized();
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
@@ -1010,6 +1012,12 @@
bool mSkipOutlineClip;
+ // Lighting + shadows
+ Vector3 mLightCenter;
+ float mLightRadius;
+ uint8_t mAmbientShadowAlpha;
+ uint8_t mSpotShadowAlpha;
+
friend class Layer;
friend class TextSetupFunctor;
friend class DrawBitmapOp;
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index ccd3ba5..40a21e4 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -80,14 +80,6 @@
virtual void setViewport(int width, int height) = 0;
/**
- * Sets the position and size of the spot shadow casting light.
- *
- * @param lightCenter The light's Y position, relative to the render target's top left
- * @param lightRadius The light's radius
- */
- virtual void initializeLight(const Vector3& lightCenter, float lightRadius) = 0;
-
- /**
* Prepares the renderer to draw a frame. This method must be invoked
* at the beginning of each frame. When this method is invoked, the
* entire drawing surface is assumed to be redrawn.
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index 95c0ee5..140c6e8 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -23,10 +23,13 @@
namespace android {
namespace uirenderer {
-StatefulBaseRenderer::StatefulBaseRenderer() :
- mDirtyClip(false), mWidth(-1), mHeight(-1),
- mSaveCount(1), mFirstSnapshot(new Snapshot), mSnapshot(mFirstSnapshot),
- mLightCenter(FLT_MIN, FLT_MIN, FLT_MIN), mLightRadius(FLT_MIN) {
+StatefulBaseRenderer::StatefulBaseRenderer()
+ : mDirtyClip(false)
+ , mWidth(-1)
+ , mHeight(-1)
+ , mSaveCount(1)
+ , mFirstSnapshot(new Snapshot)
+ , mSnapshot(mFirstSnapshot) {
}
void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop,
@@ -45,11 +48,6 @@
onViewportInitialized();
}
-void StatefulBaseRenderer::initializeLight(const Vector3& lightCenter, float lightRadius) {
- mLightCenter = lightCenter;
- mLightRadius = lightRadius;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Save (layer)
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index e8e024f..25cc832 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -52,7 +52,6 @@
* the render target.
*/
virtual void setViewport(int width, int height);
- virtual void initializeLight(const Vector3& lightCenter, float lightRadius);
void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom);
// getters
@@ -161,10 +160,6 @@
// Current state
// TODO: should become private, once hooks needed by OpenGLRenderer are added
sp<Snapshot> mSnapshot;
-
- Vector3 mLightCenter;
- float mLightRadius;
-
}; // class StatefulBaseRenderer
}; // namespace uirenderer
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 2147810..756f660 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -110,10 +110,11 @@
// and such to prevent from trying to render into this surface
}
-void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius) {
+void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
if (!mCanvas) return;
mCanvas->setViewport(width, height);
- mCanvas->initializeLight(lightCenter, lightRadius);
+ mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha);
}
void CanvasContext::setOpaque(bool opaque) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1bab1b1..2a01027 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -53,7 +53,8 @@
bool initialize(ANativeWindow* window);
void updateSurface(ANativeWindow* window);
void pauseSurface(ANativeWindow* window);
- void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
+ void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setOpaque(bool opaque);
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 91f5801..1e91eb5 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -39,6 +39,8 @@
#define CREATE_BRIDGE3(name, a1, a2, a3) CREATE_BRIDGE(name, a1,a2,a3,,,,,)
#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,)
#define CREATE_BRIDGE5(name, a1, a2, a3, a4, a5) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,,,)
+#define CREATE_BRIDGE6(name, a1, a2, a3, a4, a5, a6) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,,)
+#define CREATE_BRIDGE7(name, a1, a2, a3, a4, a5, a6, a7) CREATE_BRIDGE(name, a1,a2,a3,a4,a5,a6,a7,)
#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
typedef struct { \
a1; a2; a3; a4; a5; a6; a7; a8; \
@@ -152,19 +154,24 @@
postAndWait(task);
}
-CREATE_BRIDGE5(setup, CanvasContext* context, int width, int height,
- Vector3 lightCenter, int lightRadius) {
- args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius);
+CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height,
+ Vector3 lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
+ args->context->setup(args->width, args->height, args->lightCenter, args->lightRadius,
+ args->ambientShadowAlpha, args->spotShadowAlpha);
return NULL;
}
-void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius) {
+void RenderProxy::setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
SETUP_TASK(setup);
args->context = mContext;
args->width = width;
args->height = height;
args->lightCenter = lightCenter;
args->lightRadius = lightRadius;
+ args->ambientShadowAlpha = ambientShadowAlpha;
+ args->spotShadowAlpha = spotShadowAlpha;
post(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 0027403..28d0173 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -67,7 +67,8 @@
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
- ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
+ ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
+ uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
ANDROID_API void setOpaque(bool opaque);
ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
float density);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 86c3a92..a1b1aec 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -121,6 +121,7 @@
private static final int STATE_CREATE_FILE_FAILED = 4;
private static final int STATE_PRINTER_UNAVAILABLE = 5;
private static final int STATE_UPDATE_SLOW = 6;
+ private static final int STATE_PRINT_COMPLETED = 7;
private static final int UI_STATE_PREVIEW = 0;
private static final int UI_STATE_ERROR = 1;
@@ -304,6 +305,10 @@
spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_QUEUED, null);
} break;
+ case STATE_PRINT_COMPLETED: {
+ spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_COMPLETED, null);
+ } break;
+
case STATE_CREATE_FILE_FAILED: {
spooler.setPrintJobState(mPrintJob.getId(), PrintJobInfo.STATE_FAILED,
getString(R.string.print_write_error_message));
@@ -539,6 +544,8 @@
private void onStartCreateDocumentActivityResult(int resultCode, Intent data) {
if (resultCode == RESULT_OK && data != null) {
+ setState(STATE_PRINT_COMPLETED);
+ updateOptionsUi();
Uri uri = data.getData();
mPrintedDocument.writeContent(getContentResolver(), uri);
// Calling finish here does not invoke lifecycle callbacks but we
@@ -706,7 +713,8 @@
private static boolean isFinalState(int state) {
return state == STATE_PRINT_CONFIRMED
- || state == STATE_PRINT_CANCELED;
+ || state == STATE_PRINT_CANCELED
+ || state == STATE_PRINT_COMPLETED;
}
private void updateSelectedPagesFromPreview() {
@@ -1060,6 +1068,7 @@
}
if (mState == STATE_PRINT_CONFIRMED
+ || mState == STATE_PRINT_COMPLETED
|| mState == STATE_PRINT_CANCELED
|| mState == STATE_UPDATE_FAILED
|| mState == STATE_CREATE_FILE_FAILED
diff --git a/packages/SystemUI/res/drawable/ic_chevron_left.xml b/packages/SystemUI/res/drawable/ic_chevron_left.xml
new file mode 100644
index 0000000..27c2034
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_chevron_left.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24dp"
+ android:height="24dp"/>
+
+ <viewport
+ android:viewportWidth="36.0"
+ android:viewportHeight="36.0"/>
+
+ <path
+ android:fill="#ffffffff"
+ android:pathData="M23.1,11.1l-2.1000004,-2.1000004 -9.0,9.0 9.0,9.0 2.1000004,-2.1000004 -6.8999996,-6.8999996z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index db5983b..42fb740 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,7 +22,7 @@
android:layout_height="match_parent"
android:layout_width="match_parent"
>
- <com.android.systemui.statusbar.AlphaImageView
+ <com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/camera_button"
android:layout_height="64dp"
android:layout_width="64dp"
@@ -32,7 +32,7 @@
android:scaleType="center"
android:contentDescription="@string/accessibility_camera_button" />
- <com.android.systemui.statusbar.AlphaImageView
+ <com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/phone_button"
android:layout_height="64dp"
android:layout_width="64dp"
@@ -52,7 +52,7 @@
android:textColor="#ffffff"
android:textAppearance="?android:attr/textAppearanceSmall"/>
- <com.android.systemui.statusbar.AlphaImageView
+ <com.android.systemui.statusbar.KeyguardAffordanceView
android:id="@+id/lock_icon"
android:layout_width="64dp"
android:layout_height="64dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9bc2b0d..4e536e4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -295,6 +295,15 @@
<!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
<dimen name="keyguard_min_swipe_amount">75dp</dimen>
+ <!-- The minimum background radius when swiping to a side for the camera / phone affordances. -->
+ <dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
+
+ <!-- The grow amount for the camera and phone circles when hinting -->
+ <dimen name="hint_grow_amount_sideways">60dp</dimen>
+
+ <!-- The chevron padding to the circle when hinting -->
+ <dimen name="hint_chevron_circle_padding">16dp</dimen>
+
<!-- Volume panel dialog y offset -->
<dimen name="volume_panel_top">0dp</dimen>
@@ -317,9 +326,6 @@
<!-- Move distance for the unlock hint animation on the lockscreen -->
<dimen name="hint_move_distance">75dp</dimen>
- <!-- Move distance for the other hint animations on the lockscreen (phone, camera)-->
- <dimen name="hint_move_distance_sideways">60dp</dimen>
-
<!-- The width of the region on the left/right edge of the screen for performing the camera/
phone hints. -->
<dimen name="edge_tap_area_width">48dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 0a288d9..04fc02c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -244,9 +244,6 @@
// the user switches to home. We know it is safe to do at this
// point, so make sure new activity switches are now allowed.
ActivityManagerNative.getDefault().resumeAppSwitches();
- // Also, notifications can be launched from the lock screen,
- // so dismiss the lock screen when the activity starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
} catch (RemoteException e) {
}
@@ -1012,8 +1009,6 @@
expanded.setExpandedChild(bigContentViewLocal);
}
- PackageManager pm = mContext.getPackageManager();
-
// now the public version
View publicViewLocal = null;
if (publicNotification != null) {
@@ -1034,6 +1029,9 @@
}
if (publicViewLocal == null) {
+ PackageManager pm = getPackageManagerForUser(
+ entry.notification.getUser().getIdentifier());
+
// Add a basic notification template
publicViewLocal = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.notification_template_material_base,
@@ -1153,9 +1151,6 @@
// the user switches to home. We know it is safe to do at this
// point, so make sure new activity switches are now allowed.
ActivityManagerNative.getDefault().resumeAppSwitches();
- // Also, notifications can be launched from the lock screen,
- // so dismiss the lock screen when the activity starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
} catch (RemoteException e) {
}
@@ -1670,4 +1665,26 @@
// Ignore.
}
}
+
+ /**
+ * @return a PackageManger for userId or if userId is < 0 (USER_ALL etc) then
+ * return PackageManager for mContext
+ */
+ protected PackageManager getPackageManagerForUser(int userId) {
+ Context contextForUser = mContext;
+ // UserHandle defines special userId as negative values, e.g. USER_ALL
+ if (userId >= 0) {
+ try {
+ // Create a context for the correct user so if a package isn't installed
+ // for user 0 we can still load information about the package.
+ contextForUser =
+ mContext.createPackageContextAsUser(mContext.getPackageName(),
+ Context.CONTEXT_RESTRICTED,
+ new UserHandle(userId));
+ } catch (NameNotFoundException e) {
+ // Shouldn't fail to find the package name for system ui.
+ }
+ }
+ return contextForUser.getPackageManager();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
new file mode 100644
index 0000000..845e0ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.ImageView;
+import com.android.systemui.R;
+
+/**
+ * An ImageView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class KeyguardAffordanceView extends ImageView {
+
+ private static final long CIRCLE_APPEAR_DURATION = 80;
+ private static final long CIRCLE_DISAPPEAR_MAX_DURATION = 200;
+ private static final long NORMAL_ANIMATION_DURATION = 200;
+ public static final float MAX_ICON_SCALE_AMOUNT = 1.5f;
+ public static final float MIN_ICON_SCALE_AMOUNT = 0.8f;
+
+ private final int mMinBackgroundRadius;
+ private final Paint mCirclePaint;
+ private final Interpolator mAppearInterpolator;
+ private final Interpolator mDisappearInterpolator;
+ private final int mInverseColor;
+ private final int mNormalColor;
+ private final ArgbEvaluator mColorInterpolator;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final Drawable mArrowDrawable;
+ private final int mHintChevronPadding;
+ private float mCircleRadius;
+ private int mCenterX;
+ private int mCenterY;
+ private ValueAnimator mCircleAnimator;
+ private ValueAnimator mAlphaAnimator;
+ private ValueAnimator mScaleAnimator;
+ private ValueAnimator mArrowAnimator;
+ private float mCircleStartValue;
+ private boolean mCircleWillBeHidden;
+ private int[] mTempPoint = new int[2];
+ private float mImageScale;
+ private int mCircleColor;
+ private boolean mIsLeft;
+ private float mArrowAlpha = 0.0f;
+ private AnimatorListenerAdapter mCircleEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCircleAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter mScaleEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScaleAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter mAlphaEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAlphaAnimator = null;
+ }
+ };
+ private AnimatorListenerAdapter mArrowEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mArrowAnimator = null;
+ }
+ };
+
+ public KeyguardAffordanceView(Context context) {
+ this(context, null);
+ }
+
+ public KeyguardAffordanceView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public KeyguardAffordanceView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mCirclePaint = new Paint();
+ mCirclePaint.setAntiAlias(true);
+ mCircleColor = 0xffffffff;
+ mCirclePaint.setColor(mCircleColor);
+
+ mNormalColor = 0xffffffff;
+ mInverseColor = 0xff000000;
+ mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_affordance_min_background_radius);
+ mHintChevronPadding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.hint_chevron_circle_padding);
+ mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.linear_out_slow_in);
+ mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_linear_in);
+ mColorInterpolator = new ArgbEvaluator();
+ mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.3f);
+ mArrowDrawable = context.getDrawable(R.drawable.ic_chevron_left);
+ mArrowDrawable.setBounds(0, 0, mArrowDrawable.getIntrinsicWidth(),
+ mArrowDrawable.getIntrinsicHeight());
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mCenterX = getWidth() / 2;
+ mCenterY = getHeight() / 2;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ drawBackgroundCircle(canvas);
+ drawArrow(canvas);
+ canvas.save();
+ updateIconColor();
+ canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
+ super.onDraw(canvas);
+ canvas.restore();
+ }
+
+ private void drawArrow(Canvas canvas) {
+ if (mArrowAlpha > 0) {
+ canvas.save();
+ canvas.translate(mCenterX, mCenterY);
+ if (mIsLeft) {
+ canvas.scale(-1.0f, 1.0f);
+ }
+ canvas.translate(- mCircleRadius - mHintChevronPadding
+ - mArrowDrawable.getIntrinsicWidth() / 2,
+ - mArrowDrawable.getIntrinsicHeight() / 2);
+ mArrowDrawable.setAlpha((int) (mArrowAlpha * 255));
+ mArrowDrawable.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ private void updateIconColor() {
+ Drawable drawable = getDrawable().mutate();
+ float alpha = mCircleRadius / mMinBackgroundRadius;
+ alpha = Math.min(1.0f, alpha);
+ int color = (int) mColorInterpolator.evaluate(alpha, mNormalColor, mInverseColor);
+ drawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
+ }
+
+ private void drawBackgroundCircle(Canvas canvas) {
+ if (mCircleRadius > 0) {
+ updateCircleColor();
+ canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+ }
+ }
+
+ private void updateCircleColor() {
+ float fraction = 0.5f + 0.5f * Math.max(0.0f, Math.min(1.0f,
+ (mCircleRadius - mMinBackgroundRadius) / (0.5f * mMinBackgroundRadius)));
+ int color = Color.argb((int) (Color.alpha(mCircleColor) * fraction),
+ Color.red(mCircleColor),
+ Color.green(mCircleColor), Color.blue(mCircleColor));
+ mCirclePaint.setColor(color);
+ }
+
+ public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
+ cancelAnimator(mCircleAnimator);
+ float maxCircleSize = getMaxCircleSize();
+ ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
+ velocity, maxCircleSize);
+ animatorToRadius.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimationEndRunnable.run();
+ }
+ });
+ animatorToRadius.start();
+ setImageAlpha(0, true);
+ }
+
+ private float getMaxCircleSize() {
+ getLocationInWindow(mTempPoint);
+ float rootWidth = getRootView().getWidth();
+ float width = mTempPoint[0] + mCenterX;
+ width = Math.max(rootWidth - width, width);
+ float height = mTempPoint[1] + mCenterY;
+ return (float) Math.hypot(width, height);
+ }
+
+ public void setCircleRadius(float circleRadius) {
+ setCircleRadius(circleRadius, false);
+ }
+
+ public void setCircleRadiusWithoutAnimation(float circleRadius) {
+ cancelAnimator(mCircleAnimator);
+ setCircleRadius(circleRadius, true);
+ }
+
+ private void setCircleRadius(float circleRadius, boolean noAnimation) {
+
+ // Check if we need a new animation
+ boolean radiusHidden = (mCircleAnimator != null && mCircleWillBeHidden)
+ || (mCircleAnimator == null && mCircleRadius == 0.0f);
+ boolean nowHidden = circleRadius == 0.0f;
+ boolean radiusNeedsAnimation = (radiusHidden != nowHidden) && !noAnimation;
+ if (!radiusNeedsAnimation) {
+ if (mCircleAnimator == null) {
+ mCircleRadius = circleRadius;
+ invalidate();
+ } else if (!mCircleWillBeHidden) {
+
+ // We just update the end value
+ float diff = circleRadius - mMinBackgroundRadius;
+ PropertyValuesHolder[] values = mCircleAnimator.getValues();
+ values[0].setFloatValues(mCircleStartValue + diff, circleRadius);
+ mCircleAnimator.setCurrentPlayTime(mCircleAnimator.getCurrentPlayTime());
+ }
+ } else {
+ cancelAnimator(mCircleAnimator);
+ ValueAnimator animator = getAnimatorToRadius(circleRadius);
+ Interpolator interpolator = circleRadius == 0.0f
+ ? mDisappearInterpolator
+ : mAppearInterpolator;
+ animator.setInterpolator(interpolator);
+ float durationFactor = Math.abs(mCircleRadius - circleRadius)
+ / (float) mMinBackgroundRadius;
+ long duration = (long) (CIRCLE_APPEAR_DURATION * durationFactor);
+ duration = Math.min(duration, CIRCLE_DISAPPEAR_MAX_DURATION);
+ animator.setDuration(duration);
+ animator.start();
+ }
+ }
+
+ private ValueAnimator getAnimatorToRadius(float circleRadius) {
+ ValueAnimator animator = ValueAnimator.ofFloat(mCircleRadius, circleRadius);
+ mCircleAnimator = animator;
+ mCircleStartValue = mCircleRadius;
+ mCircleWillBeHidden = circleRadius == 0.0f;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mCircleRadius = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(mCircleEndListener);
+ return animator;
+ }
+
+ private void cancelAnimator(Animator animator) {
+ if (animator != null) {
+ animator.cancel();
+ }
+ }
+
+ public void setImageScale(float imageScale, boolean animate) {
+ setImageScale(imageScale, animate, -1, null);
+ }
+
+ /**
+ * Sets the scale of the containing image
+ *
+ * @param imageScale The new Scale.
+ * @param animate Should an animation be performed
+ * @param duration If animate, whats the duration? When -1 we take the default duration
+ * @param interpolator If animate, whats the interpolator? When null we take the default
+ * interpolator.
+ */
+ public void setImageScale(float imageScale, boolean animate, long duration,
+ Interpolator interpolator) {
+ cancelAnimator(mScaleAnimator);
+ if (!animate) {
+ mImageScale = imageScale;
+ invalidate();
+ } else {
+ ValueAnimator animator = ValueAnimator.ofFloat(mImageScale, imageScale);
+ mScaleAnimator = animator;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mImageScale = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(mScaleEndListener);
+ if (interpolator == null) {
+ interpolator = imageScale == 0.0f
+ ? mDisappearInterpolator
+ : mAppearInterpolator;
+ }
+ animator.setInterpolator(interpolator);
+ if (duration == -1) {
+ float durationFactor = Math.abs(mImageScale - imageScale)
+ / (1.0f - MIN_ICON_SCALE_AMOUNT);
+ durationFactor = Math.min(1.0f, durationFactor);
+ duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ }
+ animator.setDuration(duration);
+ animator.start();
+ }
+ }
+
+ public void setImageAlpha(float alpha, boolean animate) {
+ setImageAlpha(alpha, animate, -1, null, null);
+ }
+
+ /**
+ * Sets the alpha of the containing image
+ *
+ * @param alpha The new alpha.
+ * @param animate Should an animation be performed
+ * @param duration If animate, whats the duration? When -1 we take the default duration
+ * @param interpolator If animate, whats the interpolator? When null we take the default
+ * interpolator.
+ */
+ public void setImageAlpha(float alpha, boolean animate, long duration,
+ Interpolator interpolator, Runnable runnable) {
+ cancelAnimator(mAlphaAnimator);
+ int endAlpha = (int) (alpha * 255);
+ if (!animate) {
+ setImageAlpha(endAlpha);
+ } else {
+ int currentAlpha = getImageAlpha();
+ ValueAnimator animator = ValueAnimator.ofInt(currentAlpha, endAlpha);
+ mAlphaAnimator = animator;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ setImageAlpha((int) animation.getAnimatedValue());
+ }
+ });
+ animator.addListener(mAlphaEndListener);
+ if (interpolator == null) {
+ interpolator = alpha == 0.0f
+ ? mDisappearInterpolator
+ : mAppearInterpolator;
+ }
+ animator.setInterpolator(interpolator);
+ if (duration == -1) {
+ float durationFactor = Math.abs(currentAlpha - endAlpha) / 255f;
+ durationFactor = Math.min(1.0f, durationFactor);
+ duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ }
+ animator.setDuration(duration);
+ if (runnable != null) {
+ animator.addListener(getEndListener(runnable));
+ }
+ animator.start();
+ }
+ }
+
+ private Animator.AnimatorListener getEndListener(final Runnable runnable) {
+ return new AnimatorListenerAdapter() {
+ boolean mCancelled;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCancelled) {
+ runnable.run();
+ }
+ }
+ };
+ }
+
+ public float getCircleRadius() {
+ return mCircleRadius;
+ }
+
+ public void showArrow(boolean show) {
+ cancelAnimator(mArrowAnimator);
+ float targetAlpha = show ? 1.0f : 0.0f;
+ if (mArrowAlpha == targetAlpha) {
+ return;
+ }
+ ValueAnimator animator = ValueAnimator.ofFloat(mArrowAlpha, targetAlpha);
+ mArrowAnimator = animator;
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mArrowAlpha = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ });
+ animator.addListener(mArrowEndListener);
+ Interpolator interpolator = show
+ ? mAppearInterpolator
+ : mDisappearInterpolator;
+ animator.setInterpolator(interpolator);
+ float durationFactor = Math.abs(mArrowAlpha - targetAlpha);
+ long duration = (long) (NORMAL_ANIMATION_DURATION * durationFactor);
+ animator.setDuration(duration);
+ animator.start();
+ }
+
+ public void setIsLeft(boolean left) {
+ mIsLeft = left;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
new file mode 100644
index 0000000..a8a0cb1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
+
+/**
+ * A touch handler of the keyguard which is responsible for launching phone and camera affordances.
+ */
+public class KeyguardAffordanceHelper {
+
+ public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
+ public static final long HINT_PHASE1_DURATION = 200;
+ private static final long HINT_PHASE2_DURATION = 350;
+ private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.15f;
+ private static final int HINT_CIRCLE_OPEN_DURATION = 500;
+
+ private final Context mContext;
+
+ private FlingAnimationUtils mFlingAnimationUtils;
+ private Callback mCallback;
+ private int mTrackingPointer;
+ private VelocityTracker mVelocityTracker;
+ private boolean mSwipingInProgress;
+ private float mInitialTouchX;
+ private float mInitialTouchY;
+ private float mTranslation;
+ private float mTranslationOnDown;
+ private int mTouchSlop;
+ private int mMinTranslationAmount;
+ private int mMinFlingVelocity;
+ private int mHintGrowAmount;
+ private final KeyguardAffordanceView mLeftIcon;
+ private final KeyguardAffordanceView mCenterIcon;
+ private final KeyguardAffordanceView mRightIcon;
+ private Interpolator mAppearInterpolator;
+ private Interpolator mDisappearInterpolator;
+ private Animator mSwipeAnimator;
+ private int mMinBackgroundRadius;
+ private boolean mMotionPerformedByUser;
+ private PowerManager mPM;
+ private AnimatorListenerAdapter mFlingEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeAnimator = null;
+ setSwipingInProgress(false);
+ }
+ };
+ private Runnable mAnimationEndRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onAnimationToSideEnded();
+ }
+ };
+
+ KeyguardAffordanceHelper(Callback callback, Context context) {
+ mContext = context;
+ mCallback = callback;
+ mLeftIcon = mCallback.getLeftIcon();
+ mLeftIcon.setIsLeft(true);
+ mCenterIcon = mCallback.getCenterIcon();
+ mRightIcon = mCallback.getRightIcon();
+ updateIcon(mLeftIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ updateIcon(mCenterIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ updateIcon(mRightIcon, 0.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+ initDimens();
+ mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ }
+
+ private void initDimens() {
+ final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+ mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_min_swipe_amount);
+ mMinBackgroundRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_affordance_min_background_radius);
+ mHintGrowAmount =
+ mContext.getResources().getDimensionPixelSize(R.dimen.hint_grow_amount_sideways);
+ mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
+ mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.linear_out_slow_in);
+ mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
+ android.R.interpolator.fast_out_linear_in);
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ int pointerIndex = event.findPointerIndex(mTrackingPointer);
+ if (pointerIndex < 0) {
+ pointerIndex = 0;
+ mTrackingPointer = event.getPointerId(pointerIndex);
+ }
+ final float y = event.getY(pointerIndex);
+ final float x = event.getX(pointerIndex);
+
+ boolean isUp = false;
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ if (mSwipingInProgress) {
+ cancelAnimation();
+ }
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTranslationOnDown = mTranslation;
+ initVelocityTracker();
+ trackMovement(event);
+ mMotionPerformedByUser = false;
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ final int upPointer = event.getPointerId(event.getActionIndex());
+ if (mTrackingPointer == upPointer) {
+ // gesture is ongoing, find a new pointer to track
+ final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+ final float newY = event.getY(newIndex);
+ final float newX = event.getX(newIndex);
+ mTrackingPointer = event.getPointerId(newIndex);
+ mInitialTouchY = newY;
+ mInitialTouchX = newX;
+ mTranslationOnDown = mTranslation;
+ }
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ final float w = x - mInitialTouchX;
+ trackMovement(event);
+ if (((leftSwipePossible() && w > mTouchSlop)
+ || (rightSwipePossible() && w < -mTouchSlop))
+ && Math.abs(w) > Math.abs(y - mInitialTouchY)
+ && !mSwipingInProgress) {
+ cancelAnimation();
+ mInitialTouchY = y;
+ mInitialTouchX = x;
+ mTranslationOnDown = mTranslation;
+ setSwipingInProgress(true);
+ }
+ if (mSwipingInProgress) {
+ setTranslation(mTranslationOnDown + x - mInitialTouchX, false, false);
+ }
+ break;
+
+ case MotionEvent.ACTION_UP:
+ isUp = true;
+ case MotionEvent.ACTION_CANCEL:
+ mTrackingPointer = -1;
+ trackMovement(event);
+ if (mSwipingInProgress) {
+ flingWithCurrentVelocity(!isUp);
+ }
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ break;
+ }
+ return true;
+ }
+
+ private void setSwipingInProgress(boolean inProgress) {
+ mSwipingInProgress = inProgress;
+ if (inProgress) {
+ mCallback.onSwipingStarted();
+ }
+ }
+
+ private boolean rightSwipePossible() {
+ return mRightIcon.getVisibility() == View.VISIBLE;
+ }
+
+ private boolean leftSwipePossible() {
+ return mLeftIcon.getVisibility() == View.VISIBLE;
+ }
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ public void startHintAnimation(boolean right, Runnable onFinishedListener) {
+
+ startHintAnimationPhase1(right, onFinishedListener);
+ }
+
+ private void startHintAnimationPhase1(final boolean right, final Runnable onFinishedListener) {
+ final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
+ targetView.showArrow(true);
+ ValueAnimator animator = getAnimatorToRadius(right, mHintGrowAmount);
+ animator.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mCancelled) {
+ mSwipeAnimator = null;
+ onFinishedListener.run();
+ targetView.showArrow(false);
+ } else {
+ startUnlockHintAnimationPhase2(right, onFinishedListener);
+ }
+ }
+ });
+ animator.setInterpolator(mAppearInterpolator);
+ animator.setDuration(HINT_PHASE1_DURATION);
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ /**
+ * Phase 2: Move back.
+ */
+ private void startUnlockHintAnimationPhase2(boolean right, final Runnable onFinishedListener) {
+ final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
+ ValueAnimator animator = getAnimatorToRadius(right, 0);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeAnimator = null;
+ targetView.showArrow(false);
+ onFinishedListener.run();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ targetView.showArrow(false);
+ }
+ });
+ animator.setInterpolator(mDisappearInterpolator);
+ animator.setDuration(HINT_PHASE2_DURATION);
+ animator.setStartDelay(HINT_CIRCLE_OPEN_DURATION);
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ private ValueAnimator getAnimatorToRadius(final boolean right, int radius) {
+ final KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
+ ValueAnimator animator = ValueAnimator.ofFloat(targetView.getCircleRadius(), radius);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float newRadius = (float) animation.getAnimatedValue();
+ targetView.setCircleRadiusWithoutAnimation(newRadius);
+ float translation = getTranslationFromRadius(newRadius);
+ mTranslation = right ? -translation : translation;
+ updateIconsFromRadius(targetView, newRadius);
+ }
+ });
+ return animator;
+ }
+
+ private void cancelAnimation() {
+ if (mSwipeAnimator != null) {
+ mSwipeAnimator.cancel();
+ }
+ }
+
+ private void flingWithCurrentVelocity(boolean forceSnapBack) {
+ float vel = getCurrentVelocity();
+
+ // We snap back if the current translation is not far enough
+ boolean snapBack = Math.abs(mTranslation) < Math.abs(mTranslationOnDown)
+ + mMinTranslationAmount;
+
+ // or if the velocity is in the opposite direction.
+ boolean velIsInWrongDirection = vel * mTranslation < 0;
+ snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
+ vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+ fling(vel, snapBack || forceSnapBack);
+ }
+
+ private void fling(float vel, final boolean snapBack) {
+ float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
+ target = snapBack ? 0 : target;
+
+ ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+ mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslation = (float) animation.getAnimatedValue();
+ }
+ });
+ animator.addListener(mFlingEndListener);
+ if (!snapBack) {
+ startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable);
+ mCallback.onAnimationToSideStarted(mTranslation < 0);
+ } else {
+ reset(true);
+ }
+ animator.start();
+ mSwipeAnimator = animator;
+ }
+
+ private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
+ KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+ targetView.finishAnimation(velocity, mAnimationEndRunnable);
+ }
+
+ private void setTranslation(float translation, boolean isReset, boolean animateReset) {
+ translation = rightSwipePossible() ? translation : Math.max(0, translation);
+ translation = leftSwipePossible() ? translation : Math.min(0, translation);
+ float absTranslation = Math.abs(translation);
+ if (absTranslation > Math.abs(mTranslationOnDown) + mMinTranslationAmount ||
+ mMotionPerformedByUser) {
+ userActivity();
+ mMotionPerformedByUser = true;
+ }
+ if (translation != mTranslation || isReset) {
+ KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
+ KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
+ float alpha = absTranslation / mMinTranslationAmount;
+
+ // We interpolate the alpha of the other icons to 0
+ float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
+ fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
+
+ // We interpolate the alpha of the targetView to 1
+ alpha = fadeOutAlpha + alpha;
+
+ boolean animateIcons = isReset && animateReset;
+ float radius = getRadiusFromTranslation(absTranslation);
+ if (!isReset) {
+ updateIcon(targetView, radius, alpha, false);
+ } else {
+ updateIcon(targetView, 0.0f, fadeOutAlpha, animateIcons);
+ }
+ updateIcon(otherView, 0.0f, fadeOutAlpha, animateIcons);
+ updateIcon(mCenterIcon, 0.0f, fadeOutAlpha, animateIcons);
+
+ mTranslation = translation;
+ }
+ }
+
+ private void updateIconsFromRadius(KeyguardAffordanceView targetView, float newRadius) {
+ float alpha = newRadius / mMinBackgroundRadius;
+
+ // We interpolate the alpha of the other icons to 0
+ float fadeOutAlpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - alpha);
+ fadeOutAlpha = Math.max(0.0f, fadeOutAlpha);
+
+ // We interpolate the alpha of the targetView to 1
+ alpha = fadeOutAlpha + alpha;
+ KeyguardAffordanceView otherView = targetView == mRightIcon ? mLeftIcon : mRightIcon;
+ updateIconAlpha(targetView, alpha, false);
+ updateIconAlpha(otherView, fadeOutAlpha, false);
+ updateIconAlpha(mCenterIcon, fadeOutAlpha, false);
+ }
+
+ private float getTranslationFromRadius(float circleSize) {
+ float translation = (circleSize - mMinBackgroundRadius) / BACKGROUND_RADIUS_SCALE_FACTOR;
+ return Math.max(0, translation);
+ }
+
+ private float getRadiusFromTranslation(float translation) {
+ return translation * BACKGROUND_RADIUS_SCALE_FACTOR + mMinBackgroundRadius;
+ }
+
+
+ private void userActivity() {
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ }
+
+ public void animateHideLeftRightIcon() {
+ updateIcon(mRightIcon, 0f, 0f, true);
+ updateIcon(mLeftIcon, 0f, 0f, true);
+ }
+
+ private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
+ boolean animate) {
+ if (view.getVisibility() != View.VISIBLE) {
+ return;
+ }
+ view.setCircleRadius(circleRadius);
+ updateIconAlpha(view, alpha, animate);
+ }
+
+ private void updateIconAlpha(KeyguardAffordanceView view, float alpha, boolean animate) {
+ float scale = getScale(alpha);
+ alpha = Math.min(1.0f, alpha);
+ view.setImageAlpha(alpha, animate);
+ view.setImageScale(scale, animate);
+ }
+
+ private float getScale(float alpha) {
+ float scale = alpha / SWIPE_RESTING_ALPHA_AMOUNT * 0.2f +
+ KeyguardAffordanceView.MIN_ICON_SCALE_AMOUNT;
+ return Math.min(scale, KeyguardAffordanceView.MAX_ICON_SCALE_AMOUNT);
+ }
+
+ private void trackMovement(MotionEvent event) {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(event);
+ }
+ }
+
+ private void initVelocityTracker() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ }
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+
+ private float getCurrentVelocity() {
+ if (mVelocityTracker == null) {
+ return 0;
+ }
+ mVelocityTracker.computeCurrentVelocity(1000);
+ return mVelocityTracker.getXVelocity();
+ }
+
+ public void onConfigurationChanged() {
+ initDimens();
+ }
+
+ public void reset(boolean animate) {
+ if (mSwipeAnimator != null) {
+ mSwipeAnimator.cancel();
+ }
+ setTranslation(0.0f, true, animate);
+ setSwipingInProgress(false);
+ }
+
+ public interface Callback {
+
+ /**
+ * Notifies the callback when an animation to a side page was started.
+ *
+ * @param rightPage Is the page animated to the right page?
+ */
+ void onAnimationToSideStarted(boolean rightPage);
+
+ /**
+ * Notifies the callback the animation to a side page has ended.
+ */
+ void onAnimationToSideEnded();
+
+ float getPageWidth();
+
+ void onSwipingStarted();
+
+ KeyguardAffordanceView getLeftIcon();
+
+ KeyguardAffordanceView getCenterIcon();
+
+ KeyguardAffordanceView getRightIcon();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 74bc698..b9f012c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -39,6 +39,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
/**
* Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
@@ -56,9 +57,9 @@
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
- private ImageView mCameraImageView;
- private ImageView mPhoneImageView;
- private ImageView mLockIcon;
+ private KeyguardAffordanceView mCameraImageView;
+ private KeyguardAffordanceView mPhoneImageView;
+ private KeyguardAffordanceView mLockIcon;
private View mIndicationText;
private ActivityStarter mActivityStarter;
@@ -87,9 +88,9 @@
protected void onFinishInflate() {
super.onFinishInflate();
mLockPatternUtils = new LockPatternUtils(mContext);
- mCameraImageView = (ImageView) findViewById(R.id.camera_button);
- mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
- mLockIcon = (ImageView) findViewById(R.id.lock_icon);
+ mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
+ mPhoneImageView = (KeyguardAffordanceView) findViewById(R.id.phone_button);
+ mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon);
mIndicationText = findViewById(R.id.keyguard_indication_text);
watchForCameraPolicyChanges();
watchForAccessibilityChanges();
@@ -98,6 +99,8 @@
mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
mUnlockMethodCache.addListener(this);
updateTrust();
+ setClipChildren(false);
+ setClipToPadding(false);
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -228,15 +231,15 @@
mLockIcon.setImageResource(iconRes);
}
- public ImageView getPhoneImageView() {
+ public KeyguardAffordanceView getPhoneView() {
return mPhoneImageView;
}
- public ImageView getCameraImageView() {
+ public KeyguardAffordanceView getCameraView() {
return mCameraImageView;
}
- public ImageView getLockIcon() {
+ public KeyguardAffordanceView getLockIcon() {
return mLockIcon;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
deleted file mode 100644
index d5f9619..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
+++ /dev/null
@@ -1,496 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.os.PowerManager;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewPropertyAnimator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-
-import java.util.ArrayList;
-
-/**
- * A touch handler of the Keyguard which is responsible for swiping the content left or right.
- */
-public class KeyguardPageSwipeHelper {
-
- private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
- public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
- public static final long HINT_PHASE1_DURATION = 250;
- private static final long HINT_PHASE2_DURATION = 450;
-
- private final Context mContext;
-
- private FlingAnimationUtils mFlingAnimationUtils;
- private Callback mCallback;
- private int mTrackingPointer;
- private VelocityTracker mVelocityTracker;
- private boolean mSwipingInProgress;
- private float mInitialTouchX;
- private float mInitialTouchY;
- private float mTranslation;
- private float mTranslationOnDown;
- private int mTouchSlop;
- private int mMinTranslationAmount;
- private int mMinFlingVelocity;
- private int mHintDistance;
- private final View mLeftIcon;
- private final View mCenterIcon;
- private final View mRightIcon;
- private Interpolator mFastOutSlowIn;
- private Interpolator mBounceInterpolator;
- private Animator mSwipeAnimator;
- private boolean mCallbackCalled;
-
- KeyguardPageSwipeHelper(Callback callback, Context context) {
- mContext = context;
- mCallback = callback;
- mLeftIcon = mCallback.getLeftIcon();
- mCenterIcon = mCallback.getCenterIcon();
- mRightIcon = mCallback.getRightIcon();
- updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
- updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
- updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
- initDimens();
- }
-
- private void initDimens() {
- final ViewConfiguration configuration = ViewConfiguration.get(mContext);
- mTouchSlop = configuration.getScaledTouchSlop();
- mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
- mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_min_swipe_amount);
- mHintDistance =
- mContext.getResources().getDimensionPixelSize(R.dimen.hint_move_distance_sideways);
- mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
- mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
- android.R.interpolator.fast_out_slow_in);
- mBounceInterpolator = new BounceInterpolator();
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- int pointerIndex = event.findPointerIndex(mTrackingPointer);
- if (pointerIndex < 0) {
- pointerIndex = 0;
- mTrackingPointer = event.getPointerId(pointerIndex);
- }
- final float y = event.getY(pointerIndex);
- final float x = event.getX(pointerIndex);
-
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- if (mSwipingInProgress) {
- cancelAnimations();
- }
- mInitialTouchY = y;
- mInitialTouchX = x;
- mTranslationOnDown = mTranslation;
- initVelocityTracker();
- trackMovement(event);
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- final int upPointer = event.getPointerId(event.getActionIndex());
- if (mTrackingPointer == upPointer) {
- // gesture is ongoing, find a new pointer to track
- final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
- final float newY = event.getY(newIndex);
- final float newX = event.getX(newIndex);
- mTrackingPointer = event.getPointerId(newIndex);
- mInitialTouchY = newY;
- mInitialTouchX = newX;
- mTranslationOnDown = mTranslation;
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- final float w = x - mInitialTouchX;
- trackMovement(event);
- if (((leftSwipePossible() && w > mTouchSlop)
- || (rightSwipePossible() && w < -mTouchSlop))
- && Math.abs(w) > Math.abs(y - mInitialTouchY)
- && !mSwipingInProgress) {
- cancelAnimations();
- mInitialTouchY = y;
- mInitialTouchX = x;
- mTranslationOnDown = mTranslation;
- mSwipingInProgress = true;
- }
- if (mSwipingInProgress) {
- setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
- }
- break;
-
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mTrackingPointer = -1;
- trackMovement(event);
- if (mSwipingInProgress) {
- flingWithCurrentVelocity();
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- break;
- }
- return true;
- }
-
- private boolean rightSwipePossible() {
- return mRightIcon.getVisibility() == View.VISIBLE;
- }
-
- private boolean leftSwipePossible() {
- return mLeftIcon.getVisibility() == View.VISIBLE;
- }
-
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
-
- public void startHintAnimation(boolean right, Runnable onFinishedListener) {
- startHintAnimationPhase1(right, onFinishedListener);
- }
-
- /**
- * Phase 1: Move everything sidewards.
- */
- private void startHintAnimationPhase1(boolean right, final Runnable onFinishedListener) {
- float target = right ? -mHintDistance : mHintDistance;
- startHintTranslationAnimations(target, HINT_PHASE1_DURATION, mFastOutSlowIn);
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mCancelled) {
- mSwipeAnimator = null;
- onFinishedListener.run();
- } else {
- startUnlockHintAnimationPhase2(onFinishedListener);
- }
- }
- });
- animator.setInterpolator(mFastOutSlowIn);
- animator.setDuration(HINT_PHASE1_DURATION);
- animator.start();
- mSwipeAnimator = animator;
- }
-
- /**
- * Phase 2: Move back.
- */
- private void startUnlockHintAnimationPhase2(final Runnable onFinishedListener) {
- startHintTranslationAnimations(0f /* target */, HINT_PHASE2_DURATION, mBounceInterpolator);
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, 0f);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimator = null;
- onFinishedListener.run();
- }
- });
- animator.setInterpolator(mBounceInterpolator);
- animator.setDuration(HINT_PHASE2_DURATION);
- animator.start();
- mSwipeAnimator = animator;
- }
-
- private void startHintTranslationAnimations(float target, long duration,
- Interpolator interpolator) {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View targetView : targetViews) {
- targetView.animate()
- .setDuration(duration)
- .setInterpolator(interpolator)
- .translationX(target);
- }
- }
-
- private void cancelAnimations() {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View target : targetViews) {
- target.animate().cancel();
- }
- View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
- targetView.animate().cancel();
- if (mSwipeAnimator != null) {
- mSwipeAnimator.cancel();
- hideInactiveIcons(true);
- }
- }
-
- private void flingWithCurrentVelocity() {
- float vel = getCurrentVelocity();
-
- // We snap back if the current translation is not far enough
- boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
-
- // or if the velocity is in the opposite direction.
- boolean velIsInWrongDirection = vel * mTranslation < 0;
- snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
- vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
- fling(vel, snapBack);
- }
-
- private void fling(float vel, final boolean snapBack) {
- float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
- target = snapBack ? 0 : target;
-
- // translation Animation
- startTranslationAnimations(vel, target);
-
- // animate left / right icon
- startIconAnimation(vel, snapBack, target);
-
- ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
- mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mTranslation = (float) animation.getAnimatedValue();
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeAnimator = null;
- mSwipingInProgress = false;
- if (!snapBack && !mCallbackCalled && !mCancelled) {
-
- // ensure that the callback is called eventually
- mCallback.onAnimationToSideStarted(mTranslation < 0);
- mCallbackCalled = true;
- }
- }
- });
- if (!snapBack) {
- mCallbackCalled = false;
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- int frameNumber;
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (frameNumber == 2 && !mCallbackCalled) {
-
- // we have to wait for the second frame for this call,
- // until the render thread has definitely kicked in, to avoid a lag.
- mCallback.onAnimationToSideStarted(mTranslation < 0);
- mCallbackCalled = true;
- }
- frameNumber++;
- }
- });
- } else {
- showAllIcons(true);
- }
- animator.start();
- mSwipeAnimator = animator;
- }
-
- private void startTranslationAnimations(float vel, float target) {
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View targetView : targetViews) {
- ViewPropertyAnimator animator = targetView.animate();
- mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
- animator.translationX(target);
- }
- }
-
- private void startIconAnimation(float vel, boolean snapBack, float target) {
- float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
- float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
- View targetView = mTranslation > 0
- ? mLeftIcon
- : mRightIcon;
- if (targetView.getVisibility() == View.VISIBLE) {
- ViewPropertyAnimator iconAnimator = targetView.animate();
- mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
- iconAnimator.scaleX(scale);
- iconAnimator.scaleY(scale);
- iconAnimator.alpha(alpha);
- }
- }
-
- private void setTranslation(float translation, boolean isReset) {
- translation = rightSwipePossible() ? translation : Math.max(0, translation);
- translation = leftSwipePossible() ? translation : Math.min(0, translation);
- if (translation != mTranslation || isReset) {
- ArrayList<View> translatedViews = mCallback.getTranslationViews();
- for (View view : translatedViews) {
- view.setTranslationX(translation);
- }
- if (translation == 0.0f) {
- boolean animate = !isReset;
- showAllIcons(animate);
- } else {
- View targetView = translation > 0 ? mLeftIcon : mRightIcon;
- float progress = Math.abs(translation) / mCallback.getPageWidth();
- progress = Math.min(progress, 1.0f);
- float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
- float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
- updateIcon(targetView, scale, alpha, false);
- View otherView = translation < 0 ? mLeftIcon : mRightIcon;
- if (mTranslation * translation <= 0) {
- // The sign of the translation has changed so we need to hide the other icons
- updateIcon(otherView, 0, 0, true);
- updateIcon(mCenterIcon, 0, 0, true);
- }
- }
- mTranslation = translation;
- }
- }
-
- public void showAllIcons(boolean animate) {
- float scale = 1.0f;
- float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
- updateIcon(mRightIcon, scale, alpha, animate);
- updateIcon(mCenterIcon, scale, alpha, animate);
- updateIcon(mLeftIcon, scale, alpha, animate);
- }
-
- public void animateHideLeftRightIcon() {
- updateIcon(mRightIcon, 0f, 0f, true);
- updateIcon(mLeftIcon, 0f, 0f, true);
- }
-
- private void hideInactiveIcons(boolean animate){
- View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
- updateIcon(otherView, 0, 0, animate);
- updateIcon(mCenterIcon, 0, 0, animate);
- }
-
- private void updateIcon(View view, float scale, float alpha, boolean animate) {
- if (view.getVisibility() != View.VISIBLE) {
- return;
- }
- if (!animate) {
- view.animate().cancel();
- view.setAlpha(alpha);
- view.setScaleX(scale);
- view.setScaleY(scale);
- // TODO: remove this invalidate once the property setters invalidate it properly
- view.invalidate();
- } else {
- if (view.getAlpha() != alpha || view.getScaleX() != scale) {
- view.animate()
- .setInterpolator(mFastOutSlowIn)
- .alpha(alpha)
- .scaleX(scale)
- .scaleY(scale);
- }
- }
- }
-
- private void trackMovement(MotionEvent event) {
- if (mVelocityTracker != null) {
- mVelocityTracker.addMovement(event);
- }
- }
-
- private void initVelocityTracker() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- }
- mVelocityTracker = VelocityTracker.obtain();
- }
-
- private float getCurrentVelocity() {
- if (mVelocityTracker == null) {
- return 0;
- }
- mVelocityTracker.computeCurrentVelocity(1000);
- return mVelocityTracker.getXVelocity();
- }
-
- public void onConfigurationChanged() {
- initDimens();
- }
-
- public void reset() {
- if (mSwipeAnimator != null) {
- mSwipeAnimator.cancel();
- }
- ArrayList<View> targetViews = mCallback.getTranslationViews();
- for (View view : targetViews) {
- view.animate().cancel();
- }
- setTranslation(0.0f, true);
- mSwipingInProgress = false;
- }
-
- public boolean isSwipingInProgress() {
- return mSwipingInProgress;
- }
-
- public interface Callback {
-
- /**
- * Notifies the callback when an animation to a side page was started.
- *
- * @param rightPage Is the page animated to the right page?
- */
- void onAnimationToSideStarted(boolean rightPage);
-
- float getPageWidth();
-
- ArrayList<View> getTranslationViews();
-
- View getLeftIcon();
-
- View getCenterIcon();
-
- View getRightIcon();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 688c0d8..af30266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -26,7 +26,7 @@
import android.widget.FrameLayout;
import com.android.systemui.qs.QSPanel;
-import com.android.systemui.qs.tiles.UserDetailView;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
/**
* Container for image of the multi user switcher (tappable).
@@ -34,6 +34,8 @@
public class MultiUserSwitch extends FrameLayout implements View.OnClickListener {
private QSPanel mQsPanel;
+ private KeyguardUserSwitcher mKeyguardUserSwitcher;
+ private boolean mKeyguardMode;
public MultiUserSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -49,12 +51,26 @@
mQsPanel = qsPanel;
}
+ public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+ mKeyguardUserSwitcher = keyguardUserSwitcher;
+ }
+
+ public void setKeyguardMode(boolean keyguardShowing) {
+ mKeyguardMode = keyguardShowing;
+ }
+
@Override
public void onClick(View v) {
final UserManager um = UserManager.get(getContext());
if (um.isUserSwitcherEnabled()) {
- mQsPanel.showDetailAdapter(true,
- mQsPanel.getHost().getUserSwitcherController().userDetailAdapter);
+ if (mKeyguardMode) {
+ if (mKeyguardUserSwitcher != null) {
+ mKeyguardUserSwitcher.show();
+ }
+ } else {
+ mQsPanel.showDetailAdapter(true,
+ mQsPanel.getHost().getUserSwitcherController().userDetailAdapter);
+ }
} else {
Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
getContext(), v, ContactsContract.Profile.CONTENT_URI,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index fc0f2d5..11a38b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.MirrorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -49,7 +50,7 @@
public class NotificationPanelView extends PanelView implements
ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
- KeyguardPageSwipeHelper.Callback {
+ KeyguardAffordanceHelper.Callback {
// Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
// changed.
@@ -59,7 +60,7 @@
private static final float HEADER_RUBBERBAND_FACTOR = 2.15f;
private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
- private KeyguardPageSwipeHelper mPageSwiper;
+ private KeyguardAffordanceHelper mAfforanceHelper;
private StatusBarHeaderView mHeader;
private View mQsContainer;
private QSPanel mQsPanel;
@@ -124,7 +125,6 @@
private boolean mIsExpanding;
private boolean mBlockTouches;
- private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
private int mNotificationScrimWaitDistance;
private boolean mTwoFingerQsExpand;
private boolean mTwoFingerQsExpandPossible;
@@ -135,6 +135,10 @@
*/
private int mScrollYOverride = -1;
private boolean mQsAnimatorExpand;
+ private boolean mIsLaunchTransitionFinished;
+ private boolean mIsLaunchTransitionRunning;
+ private Runnable mLaunchAnimationEndRunnable;
+ private boolean mOnlyAffordanceInThisMotion;
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -167,9 +171,7 @@
mFastOutLinearInterpolator = AnimationUtils.loadInterpolator(getContext(),
android.R.interpolator.fast_out_linear_in);
mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
- mSwipeTranslationViews.add(mNotificationStackScroller);
- mSwipeTranslationViews.add(mKeyguardStatusView);
- mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
+ mAfforanceHelper = new KeyguardAffordanceHelper(this, getContext());
}
@Override
@@ -297,9 +299,10 @@
@Override
public void resetViews() {
+ mIsLaunchTransitionFinished = false;
mBlockTouches = false;
mUnlockIconActive = false;
- mPageSwiper.reset();
+ mAfforanceHelper.reset(true);
closeQs();
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
true /* cancelAnimators */);
@@ -354,6 +357,7 @@
if (mBlockTouches) {
return false;
}
+ resetDownStates(event);
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
@@ -430,6 +434,12 @@
return !mQsExpanded && super.onInterceptTouchEvent(event);
}
+ private void resetDownStates(MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ mOnlyAffordanceInThisMotion = false;
+ }
+ }
+
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -464,15 +474,14 @@
if (mBlockTouches) {
return false;
}
- // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
- // implementation.
+ resetDownStates(event);
if ((!mIsExpanding || mHintAnimationRunning)
&& !mQsExpanded
&& mStatusBar.getBarState() != StatusBarState.SHADE) {
- mPageSwiper.onTouchEvent(event);
- if (mPageSwiper.isSwipingInProgress()) {
- return true;
- }
+ mAfforanceHelper.onTouchEvent(event);
+ }
+ if (mOnlyAffordanceInThisMotion) {
+ return true;
}
if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
&& mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
@@ -951,20 +960,16 @@
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
boolean active = getMaxPanelHeight() - getExpandedHeight() > mUnlockMoveDistance;
+ KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
if (active && !mUnlockIconActive && mTracking) {
- mKeyguardBottomArea.getLockIcon().animate()
- .alpha(1f)
- .scaleY(LOCK_ICON_ACTIVE_SCALE)
- .scaleX(LOCK_ICON_ACTIVE_SCALE)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(150);
+ lockIcon.setImageAlpha(1.0f, true, 150, mFastOutLinearInterpolator, null);
+ lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
+ mFastOutLinearInterpolator);
} else if (!active && mUnlockIconActive && mTracking) {
- mKeyguardBottomArea.getLockIcon().animate()
- .alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT)
- .scaleY(1f)
- .scaleX(1f)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(150);
+ lockIcon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT, true,
+ 150, mFastOutLinearInterpolator, null);
+ lockIcon.setImageScale(1.0f, true, 150,
+ mFastOutLinearInterpolator);
}
mUnlockIconActive = active;
}
@@ -1093,7 +1098,7 @@
super.onTrackingStarted();
if (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED) {
- mPageSwiper.animateHideLeftRightIcon();
+ mAfforanceHelper.animateHideLeftRightIcon();
}
}
@@ -1106,16 +1111,15 @@
}
if (expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
- mPageSwiper.showAllIcons(true);
+ if (!mHintAnimationRunning) {
+ mAfforanceHelper.reset(true);
+ }
}
if (!expand && (mStatusBar.getBarState() == StatusBarState.KEYGUARD
|| mStatusBar.getBarState() == StatusBarState.SHADE_LOCKED)) {
- mKeyguardBottomArea.getLockIcon().animate()
- .alpha(0f)
- .scaleX(2f)
- .scaleY(2f)
- .setInterpolator(mFastOutLinearInterpolator)
- .setDuration(100);
+ KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
+ lockIcon.setImageAlpha(0.0f, true, 100, mFastOutLinearInterpolator, null);
+ lockIcon.setImageScale(2.0f, true, 100, mFastOutLinearInterpolator);
}
}
@@ -1141,7 +1145,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mPageSwiper.onConfigurationChanged();
+ mAfforanceHelper.onConfigurationChanged();
}
@Override
@@ -1159,6 +1163,8 @@
@Override
public void onAnimationToSideStarted(boolean rightPage) {
boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
+ mIsLaunchTransitionRunning = true;
+ mLaunchAnimationEndRunnable = null;
if (start) {
mKeyguardBottomArea.launchPhone();
} else {
@@ -1168,20 +1174,29 @@
}
@Override
+ public void onAnimationToSideEnded() {
+ mIsLaunchTransitionRunning = false;
+ mIsLaunchTransitionFinished = true;
+ if (mLaunchAnimationEndRunnable != null) {
+ mLaunchAnimationEndRunnable.run();
+ mLaunchAnimationEndRunnable = null;
+ }
+ }
+
+ @Override
protected void onEdgeClicked(boolean right) {
if ((right && getRightIcon().getVisibility() != View.VISIBLE)
|| (!right && getLeftIcon().getVisibility() != View.VISIBLE)) {
return;
}
mHintAnimationRunning = true;
- mPageSwiper.startHintAnimation(right, new Runnable() {
+ mAfforanceHelper.startHintAnimation(right, new Runnable() {
@Override
public void run() {
mHintAnimationRunning = false;
mStatusBar.onHintFinished();
}
});
- startHighlightIconAnimation(right ? getRightIcon() : getLeftIcon());
boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? right : !right;
if (start) {
mStatusBar.onPhoneHintStarted();
@@ -1199,17 +1214,14 @@
/**
* Starts the highlight (making it fully opaque) animation on an icon.
*/
- private void startHighlightIconAnimation(final View icon) {
- icon.animate()
- .alpha(1.0f)
- .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION)
- .setInterpolator(mFastOutSlowInInterpolator)
- .withEndAction(new Runnable() {
+ private void startHighlightIconAnimation(final KeyguardAffordanceView icon) {
+ icon.setImageAlpha(1.0f, true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
+ mFastOutSlowInInterpolator, new Runnable() {
@Override
public void run() {
- icon.animate().alpha(KeyguardPageSwipeHelper.SWIPE_RESTING_ALPHA_AMOUNT)
- .setDuration(KeyguardPageSwipeHelper.HINT_PHASE1_DURATION)
- .setInterpolator(mFastOutSlowInInterpolator);
+ icon.setImageAlpha(KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT,
+ true, KeyguardAffordanceHelper.HINT_PHASE1_DURATION,
+ mFastOutSlowInInterpolator, null);
}
});
}
@@ -1220,27 +1232,28 @@
}
@Override
- public ArrayList<View> getTranslationViews() {
- return mSwipeTranslationViews;
+ public void onSwipingStarted() {
+ requestDisallowInterceptTouchEvent(true);
+ mOnlyAffordanceInThisMotion = true;
}
@Override
- public View getLeftIcon() {
+ public KeyguardAffordanceView getLeftIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getCameraImageView()
- : mKeyguardBottomArea.getPhoneImageView();
+ ? mKeyguardBottomArea.getCameraView()
+ : mKeyguardBottomArea.getPhoneView();
}
@Override
- public View getCenterIcon() {
+ public KeyguardAffordanceView getCenterIcon() {
return mKeyguardBottomArea.getLockIcon();
}
@Override
- public View getRightIcon() {
+ public KeyguardAffordanceView getRightIcon() {
return getLayoutDirection() == LAYOUT_DIRECTION_RTL
- ? mKeyguardBottomArea.getPhoneImageView()
- : mKeyguardBottomArea.getCameraImageView();
+ ? mKeyguardBottomArea.getPhoneView()
+ : mKeyguardBottomArea.getCameraView();
}
@Override
@@ -1282,4 +1295,16 @@
public boolean shouldDelayChildPressedState() {
return true;
}
+
+ public boolean isLaunchTransitionFinished() {
+ return mIsLaunchTransitionFinished;
+ }
+
+ public boolean isLaunchTransitionRunning() {
+ return mIsLaunchTransitionRunning;
+ }
+
+ public void setLaunchTransitionEndRunnable(Runnable r) {
+ mLaunchAnimationEndRunnable = r;
+ }
}
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 e4e67c9..b3b70e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -53,7 +53,6 @@
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
@@ -63,6 +62,7 @@
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -70,6 +70,7 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -201,6 +202,9 @@
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
+ public static final int FADE_KEYGUARD_START_DELAY = 100;
+ public static final int FADE_KEYGUARD_DURATION = 300;
+
PhoneStatusBarPolicy mIconPolicy;
// These are no longer handled by the policy, because we need custom strategies for them
@@ -441,6 +445,8 @@
private final ShadeUpdates mShadeUpdates = new ShadeUpdates();
private int mDrawCount;
+ private Runnable mLaunchTransitionEndRunnable;
+ private boolean mLaunchTransitionFadingAway;
private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
| ViewState.LOCATION_TOP_STACK_PEEKING
@@ -731,9 +737,6 @@
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
- mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
- (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader);
-
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
@@ -769,6 +772,11 @@
mUserSwitcherController = new UserSwitcherController(mContext);
mKeyguardMonitor = new KeyguardMonitor();
+ mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
+ (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader,
+ mUserSwitcherController);
+
+
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
if (mQSPanel != null) {
@@ -1728,7 +1736,8 @@
}
private int adjustDisableFlags(int state) {
- if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) {
+ if (!mLaunchTransitionFadingAway
+ && (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit)) {
state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
state |= StatusBarManager.DISABLE_SYSTEM_INFO;
}
@@ -2725,13 +2734,18 @@
dismissKeyguardThenExecute(new OnDismissAction() {
@Override
public boolean onDismiss() {
- try {
- // Dismiss the lock screen when Settings starts.
- ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
- } catch (RemoteException e) {
- }
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ AsyncTask.execute(new Runnable() {
+ public void run() {
+ try {
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ mContext.startActivityAsUser(
+ intent, new UserHandle(UserHandle.USER_CURRENT));
+ mWindowManagerService.overridePendingAppTransition(null, 0, 0, null);
+ } catch (RemoteException e) {
+ }
+ }
+ });
animateCollapsePanels();
return DELAY_DISMISS_TO_ACTIVITY_LAUNCH;
@@ -2795,9 +2809,20 @@
};
@Override
- protected void dismissKeyguardThenExecute(OnDismissAction action) {
+ protected void dismissKeyguardThenExecute(final OnDismissAction action) {
if (mStatusBarKeyguardViewManager.isShowing()) {
- mStatusBarKeyguardViewManager.dismissWithAction(action);
+ if (UnlockMethodCache.getInstance(mContext).isMethodInsecure()
+ && mNotificationPanel.isLaunchTransitionRunning()) {
+ action.onDismiss();
+ mNotificationPanel.setLaunchTransitionEndRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarKeyguardViewManager.dismiss();
+ }
+ });
+ } else {
+ mStatusBarKeyguardViewManager.dismissWithAction(action);
+ }
} else {
action.onDismiss();
}
@@ -3170,6 +3195,52 @@
mLeaveOpenOnKeyguardHide = false;
}
+ public boolean isInLaunchTransition() {
+ return mNotificationPanel.isLaunchTransitionRunning()
+ || mNotificationPanel.isLaunchTransitionFinished();
+ }
+
+ /**
+ * Fades the content of the keyguard away after the launch transition is done.
+ *
+ * @param beforeFading the runnable to be run when the circle is fully expanded and the fading
+ * starts
+ * @param endRunnable the runnable to be run when the transition is done
+ */
+ public void fadeKeyguardAfterLaunchTransition(final Runnable beforeFading,
+ final Runnable endRunnable) {
+ Runnable hideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mLaunchTransitionFadingAway = true;
+ if (beforeFading != null) {
+ beforeFading.run();
+ }
+ mNotificationPanel.setAlpha(1);
+ mNotificationPanel.animate()
+ .alpha(0)
+ .setStartDelay(FADE_KEYGUARD_START_DELAY)
+ .setDuration(FADE_KEYGUARD_DURATION)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mNotificationPanel.setAlpha(1);
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
+ mLaunchTransitionFadingAway = false;
+ }
+ });
+ }
+ };
+ if (mNotificationPanel.isLaunchTransitionRunning()) {
+ mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
+ } else {
+ hideRunnable.run();
+ }
+ }
+
public void hideKeyguard() {
setBarState(StatusBarState.SHADE);
if (mLeaveOpenOnKeyguardHide) {
@@ -3204,8 +3275,8 @@
private void updatePublicMode() {
setLockscreenPublicMode(
- (mStatusBarKeyguardViewManager.isShowing() ||
- mStatusBarKeyguardViewManager.isOccluded())
+ (mStatusBarKeyguardViewManager.isShowing() ||
+ mStatusBarKeyguardViewManager.isOccluded())
&& mStatusBarKeyguardViewManager.isSecure());
}
@@ -3315,7 +3386,7 @@
private void showBouncer() {
if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
- mWaitingForKeyguardExit = true;
+ mWaitingForKeyguardExit = mStatusBarKeyguardViewManager.isShowing();
mStatusBarKeyguardViewManager.dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index cf930bd..eb42401 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -71,7 +71,6 @@
private boolean mAnimationStarted;
private boolean mDozing;
private int mTeasesRemaining;
-
private final Interpolator mInterpolator = new DecelerateInterpolator();
public ScrimController(View scrimBehind, View scrimInFront) {
@@ -149,7 +148,10 @@
}
private void updateScrims() {
- if ((!mKeyguardShowing && !mBouncerShowing) || mAnimateKeyguardFadingOut) {
+ if (mAnimateKeyguardFadingOut) {
+ setScrimInFrontColor(0f);
+ setScrimBehindColor(0f);
+ }else if (!mKeyguardShowing && !mBouncerShowing) {
updateScrimNormal();
setScrimInFrontColor(0);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 2f88e21..1290cd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -35,6 +35,7 @@
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
import com.android.systemui.statusbar.policy.UserInfoController;
/**
@@ -97,6 +98,7 @@
private ActivityStarter mActivityStarter;
private BatteryController mBatteryController;
private QSPanel mQSPanel;
+ private boolean mHasKeyguardUserSwitcher;
private final Rect mClipBounds = new Rect();
private final StatusIconClipper mStatusIconClipper = new StatusIconClipper();
@@ -373,7 +375,11 @@
private void updateClickTargets() {
setClickable(!mKeyguardShowing || mExpanded);
mDateTime.setClickable(mExpanded);
- mMultiUserSwitch.setClickable(mExpanded);
+
+ boolean keyguardSwitcherAvailable =
+ mHasKeyguardUserSwitcher && mKeyguardShowing && !mExpanded;
+ mMultiUserSwitch.setClickable(mExpanded || keyguardSwitcherAvailable);
+ mMultiUserSwitch.setKeyguardMode(keyguardSwitcherAvailable);
mSystemIconsSuperContainer.setClickable(mExpanded);
}
@@ -509,6 +515,11 @@
mMultiUserSwitch.setQsPanel(qsp);
}
+ public void setKeyguarUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+ mHasKeyguardUserSwitcher = true;
+ mMultiUserSwitch.setKeyguardUserSwitcher(keyguardUserSwitcher);
+ }
+
@Override
public boolean shouldDelayChildPressedState() {
return true;
@@ -522,7 +533,6 @@
}
public void setKeyguardUserSwitcherShowing(boolean showing) {
- // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
mKeyguardUserSwitcherShowing = showing;
updateVisibilities();
updateSystemIconsLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 93dcf90..af21f25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -176,6 +176,19 @@
}
public void setOccluded(boolean occluded) {
+ if (occluded && !mOccluded && mShowing) {
+ if (mPhoneStatusBar.isInLaunchTransition()) {
+ mOccluded = true;
+ mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
+ new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardOccluded(true);
+ }
+ });
+ return;
+ }
+ }
mOccluded = occluded;
mStatusBarWindowManager.setKeyguardOccluded(occluded);
reset();
@@ -188,29 +201,50 @@
/**
* Hides the keyguard view
*/
- public void hide(long startTime, long fadeoutDuration) {
+ public void hide(long startTime, final long fadeoutDuration) {
mShowing = false;
long uptimeMillis = SystemClock.uptimeMillis();
- long delay = startTime - uptimeMillis;
- if (delay < 0) {
- delay = 0;
+ long delay = Math.max(0, startTime - uptimeMillis);
+
+ if (mPhoneStatusBar.isInLaunchTransition() ) {
+ mPhoneStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardShowing(false);
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
+ mBouncer.animateHide(PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
+ PhoneStatusBar.FADE_KEYGUARD_DURATION);
+ updateStates();
+ mScrimController.animateKeyguardFadingOut(
+ PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
+ PhoneStatusBar.FADE_KEYGUARD_DURATION, null);
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardFadingAway(false);
+ mViewMediatorCallback.keyguardGone();
+ }
+ });
+ } else {
+ mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
+ mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
+ mStatusBarWindowManager.setKeyguardShowing(false);
+ mBouncer.animateHide(delay, fadeoutDuration);
+ mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setKeyguardFadingAway(false);
+ mPhoneStatusBar.finishKeyguardFadingAway();
+ }
+ });
+ mViewMediatorCallback.keyguardGone();
+ updateStates();
}
- mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
- mPhoneStatusBar.hideKeyguard();
- mStatusBarWindowManager.setKeyguardFadingAway(true);
- mStatusBarWindowManager.setKeyguardShowing(false);
- mBouncer.animateHide(delay, fadeoutDuration);
- mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
- @Override
- public void run() {
- mStatusBarWindowManager.setKeyguardFadingAway(false);
- mPhoneStatusBar.finishKeyguardFadingAway();
- }
- });
- mViewMediatorCallback.keyguardGone();
- updateStates();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index a3f3819..b0bab48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -16,62 +16,56 @@
package com.android.systemui.statusbar.policy;
-import com.android.systemui.BitmapHelper;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.StatusBarHeaderView;
import com.android.systemui.statusbar.phone.UserAvatarView;
-import android.app.ActivityManagerNative;
import android.content.Context;
-import android.content.pm.UserInfo;
-import android.graphics.Bitmap;
-import android.os.AsyncTask;
-import android.os.RemoteException;
-import android.os.UserManager;
-import android.util.Log;
+import android.database.DataSetObserver;
+import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
-import android.view.WindowManagerGlobal;
import android.widget.TextView;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Manages the user switcher on the Keyguard.
*/
-public class KeyguardUserSwitcher implements View.OnClickListener {
+public class KeyguardUserSwitcher {
private static final String TAG = "KeyguardUserSwitcher";
+ private static final boolean ALWAYS_ON = false;
+ private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
+ "lockscreenSimpleUserSwitcher";
- private final Context mContext;
private final ViewGroup mUserSwitcher;
- private final UserManager mUserManager;
private final StatusBarHeaderView mHeader;
+ private final Adapter mAdapter;
+ private final boolean mSimpleUserSwitcher;
public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
- StatusBarHeaderView header) {
- mContext = context;
- if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher)) {
+ StatusBarHeaderView header, UserSwitcherController userSwitcherController) {
+ if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) {
mUserSwitcher = (ViewGroup) userSwitcher.inflate();
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mHeader = header;
- refresh();
+ mHeader.setKeyguarUserSwitcher(this);
+ mAdapter = new Adapter(context, userSwitcherController);
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+ mSimpleUserSwitcher = Settings.Global.getInt(context.getContentResolver(),
+ SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
} else {
mUserSwitcher = null;
- mUserManager = null;
mHeader = null;
+ mAdapter = null;
+ mSimpleUserSwitcher = false;
}
}
public void setKeyguard(boolean keyguard) {
if (mUserSwitcher != null) {
- // TODO: Cache showUserSwitcherOnKeyguard().
- if (keyguard && showUserSwitcherOnKeyguard()) {
+ if (keyguard && shouldExpandByDefault()) {
show();
- refresh();
} else {
hide();
}
@@ -79,24 +73,11 @@
}
/**
- * @return true if the user switcher should be shown on the lock screen.
+ * @return true if the user switcher should be expanded by default on the lock screen.
* @see android.os.UserManager#isUserSwitcherEnabled()
*/
- private boolean showUserSwitcherOnKeyguard() {
- // TODO: Set isEdu. The edu provisioning process can add settings to Settings.Global.
- boolean isEdu = false;
- if (isEdu) {
- return true;
- }
- List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
- int N = users.size();
- int switchableUsers = 0;
- for (int i = 0; i < N; i++) {
- if (users.get(i).supportsSwitchTo()) {
- switchableUsers++;
- }
- }
- return switchableUsers > 1;
+ private boolean shouldExpandByDefault() {
+ return mSimpleUserSwitcher || mAdapter.getSwitchableUsers() > 1;
}
public void show() {
@@ -116,100 +97,76 @@
}
private void refresh() {
- if (mUserSwitcher != null) {
- new AsyncTask<Void, Void, ArrayList<UserData>>() {
- @Override
- protected ArrayList<UserData> doInBackground(Void... params) {
- return loadUsers();
- }
-
- @Override
- protected void onPostExecute(ArrayList<UserData> userInfos) {
- bind(userInfos);
- }
- }.execute((Void[]) null);
- }
- }
-
- private void bind(ArrayList<UserData> userList) {
- mUserSwitcher.removeAllViews();
- int N = userList.size();
+ final int childCount = mUserSwitcher.getChildCount();
+ final int adapterCount = mAdapter.getCount();
+ final int N = Math.max(childCount, adapterCount);
for (int i = 0; i < N; i++) {
- mUserSwitcher.addView(inflateUser(userList.get(i)));
- }
- // TODO: add Guest
- // TODO: add (+) button
- }
-
- private View inflateUser(UserData user) {
- View v = LayoutInflater.from(mUserSwitcher.getContext()).inflate(
- R.layout.keyguard_user_switcher_item, mUserSwitcher, false);
- TextView name = (TextView) v.findViewById(R.id.name);
- UserAvatarView picture = (UserAvatarView) v.findViewById(R.id.picture);
- name.setText(user.userInfo.name);
- picture.setActivated(user.isCurrent);
- if (user.userInfo.isGuest()) {
- picture.setDrawable(mContext.getResources().getDrawable(R.drawable.ic_account_circle));
- } else {
- picture.setBitmap(user.userIcon);
- }
- v.setOnClickListener(this);
- v.setTag(user.userInfo);
- // TODO: mark which user is current for accessibility.
- return v;
- }
-
- @Override
- public void onClick(View v) {
- switchUser(((UserInfo)v.getTag()).id);
- }
-
- // TODO: Factor out logic below and share with QS implementation.
-
- private ArrayList<UserData> loadUsers() {
- ArrayList<UserInfo> users = (ArrayList<UserInfo>) mUserManager
- .getUsers(true /* excludeDying */);
- int N = users.size();
- ArrayList<UserData> result = new ArrayList<>(N);
- int currentUser = -1;
- try {
- currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
- } catch (RemoteException e) {
- Log.e(TAG, "Couln't get current user.", e);
- }
- final int avatarSize
- = mContext.getResources().getDimensionPixelSize(R.dimen.max_avatar_size);
- for (int i = 0; i < N; i++) {
- UserInfo user = users.get(i);
- if (user.supportsSwitchTo()) {
- boolean isCurrent = user.id == currentUser;
- final Bitmap picture = BitmapHelper.createCircularClip(
- mUserManager.getUserIcon(user.id),
- avatarSize, avatarSize);
- result.add(new UserData(user, picture, isCurrent));
+ if (i < adapterCount) {
+ View oldView = null;
+ if (i < childCount) {
+ oldView = mUserSwitcher.getChildAt(i);
+ }
+ View newView = mAdapter.getView(i, oldView, mUserSwitcher);
+ if (oldView == null) {
+ // We ran out of existing views. Add it at the end.
+ mUserSwitcher.addView(newView);
+ } else if (oldView != newView) {
+ // We couldn't rebind the view. Replace it.
+ mUserSwitcher.removeViewAt(i);
+ mUserSwitcher.addView(newView, i);
+ }
+ } else {
+ int lastIndex = mUserSwitcher.getChildCount() - 1;
+ mUserSwitcher.removeViewAt(lastIndex);
}
}
- return result;
}
- private void switchUser(int userId) {
- try {
- WindowManagerGlobal.getWindowManagerService().lockNow(null);
- ActivityManagerNative.getDefault().switchUser(userId);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't switch user.", e);
+ public final DataSetObserver mDataSetObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ refresh();
}
- }
+ };
- private static class UserData {
- final UserInfo userInfo;
- final Bitmap userIcon;
- final boolean isCurrent;
+ public static class Adapter extends UserSwitcherController.BaseUserAdapter implements
+ View.OnClickListener {
- UserData(UserInfo userInfo, Bitmap userIcon, boolean isCurrent) {
- this.userInfo = userInfo;
- this.userIcon = userIcon;
- this.isCurrent = isCurrent;
+ private Context mContext;
+
+ public Adapter(Context context, UserSwitcherController controller) {
+ super(controller);
+ mContext = context;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ UserSwitcherController.UserRecord item = getItem(position);
+
+ if (convertView == null
+ || !(convertView.getTag() instanceof UserSwitcherController.UserRecord)) {
+ convertView = LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_user_switcher_item, parent, false);
+ convertView.setOnClickListener(this);
+ }
+
+ TextView nameView = (TextView) convertView.findViewById(R.id.name);
+ UserAvatarView pictureView = (UserAvatarView) convertView.findViewById(R.id.picture);
+
+ nameView.setText(getName(mContext, item));
+ if (item.picture == null) {
+ pictureView.setDrawable(mContext.getDrawable(R.drawable.ic_account_circle_qs));
+ } else {
+ pictureView.setBitmap(item.picture);
+ }
+ convertView.setActivated(item.isCurrent);
+ convertView.setTag(item);
+ return convertView;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switchTo(((UserSwitcherController.UserRecord)v.getTag()));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 7bf2c34..7c00c7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -123,7 +123,7 @@
if (info.isGuest()) {
guestRecord = new UserRecord(info, null /* picture */,
true /* isGuest */, isCurrent);
- } else if (!info.isManagedProfile()) {
+ } else if (info.supportsSwitchTo()) {
Bitmap picture = bitmaps.get(info.id);
if (picture == null) {
picture = mUserManager.getUserIcon(info.id);
@@ -303,6 +303,18 @@
return item.info.name;
}
}
+
+ public int getSwitchableUsers() {
+ int result = 0;
+ ArrayList<UserRecord> users = mController.mUsers;
+ int N = users.size();
+ for (int i = 0; i < N; i++) {
+ if (users.get(i).info != null) {
+ result++;
+ }
+ }
+ return result;
+ }
}
public static final class UserRecord {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 39bbf72..35568cf 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3322,4 +3322,23 @@
}
return false;
}
+
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ * @hide
+ */
+ public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
+ try {
+ return getITelephony().invokeOemRilRequestRaw(oemReq, oemResp);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return -1;
+ }
}
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index 5f243a0..1413e58 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -55,4 +55,15 @@
* Else ({@code event} is 1), meaning the specified service is added to the IMS connection.
*/
void registrationServiceCapabilityChanged(int serviceClass, int event);
+
+ /**
+ * Notifies the application when features on a particular service enabled or
+ * disabled successfully based on user preferences.
+ *
+ * @param serviceClass a service class specified in {@link ImsServiceClass}
+ * @param enabledFeatures features enabled as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param disabledFeatures features disabled as defined in com.android.ims.ImsConfig#FeatureConstants.
+ */
+ void registrationFeatureCapabilityChanged(int serviceClass,
+ out int[] enabledFeatures, out int[] disabledFeatures);
}
diff --git a/telephony/java/com/android/ims/internal/IImsService.aidl b/telephony/java/com/android/ims/internal/IImsService.aidl
index d992124..869cd9f 100644
--- a/telephony/java/com/android/ims/internal/IImsService.aidl
+++ b/telephony/java/com/android/ims/internal/IImsService.aidl
@@ -51,4 +51,15 @@
* Config interface to get/set IMS service/capability parameters.
*/
IImsConfig getConfigInterface();
+
+ /**
+ * Used for turning on IMS when its in OFF state.
+ */
+ void turnOnIms();
+
+ /**
+ * Used for turning off IMS when its in ON state.
+ * When IMS is OFF, device will behave as CSFB'ed.
+ */
+ void turnOffIms();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 8c37e3d..886de40 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -737,5 +737,16 @@
* @return true if the operation was executed correctly.
*/
boolean setOperatorBrandOverride(String iccId, String brand);
+
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ */
+ int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
}