diff options
Diffstat (limited to 'packages/SystemUI/src')
35 files changed, 939 insertions, 358 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 3ef8316..19a1b11 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -255,15 +255,10 @@ public class AlternateRecentsComponent { /** Loads the first task thumbnail */ Bitmap loadFirstTaskThumbnail() { SystemServicesProxy ssp = mSystemServicesProxy; - List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1, - UserHandle.CURRENT.getIdentifier()); - for (ActivityManager.RecentTaskInfo t : tasks) { - // Skip tasks in the home stack - if (ssp.isInHomeStack(t.persistentId)) { - return null; - } + List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1); - return ssp.getTaskThumbnail(t.persistentId); + for (ActivityManager.RunningTaskInfo t : tasks) { + return ssp.getTaskThumbnail(t.id); } return null; } @@ -286,17 +281,6 @@ public class AlternateRecentsComponent { return (tasks.size() > 1); } - /** Returns whether the base intent of the top task stack was launched with the flag - * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */ - boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) { - if (tasks.size() > 0) { - ActivityManager.RecentTaskInfo t = tasks.get(0); - Console.log(t.baseIntent.toString()); - return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; - } - return false; - } - /** Converts from the device rotation to the degree */ float getDegreesForRotation(int value) { switch (value) { @@ -416,16 +400,14 @@ public class AlternateRecentsComponent { } // Otherwise, Recents is not the front-most activity and we should animate into it. If - // the activity at the root of the top task stack is excluded from recents, or if that - // task stack is in the home stack, then we just do a simple transition. Otherwise, we - // animate to the rects defined by the Recents service, which can differ depending on the - // number of items in the list. + // the activity at the root of the top task stack in the home stack, then we just do a + // simple transition. Otherwise, we animate to the rects defined by the Recents service, + // which can differ depending on the number of items in the list. List<ActivityManager.RecentTaskInfo> recentTasks = - ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier()); + ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier()); Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect; - boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks); - boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents && + boolean useThumbnailTransition = !isTopTaskHome && hasValidTaskRects(); if (useThumbnailTransition) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java deleted file mode 100644 index 95ab8e8..0000000 --- a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.android.systemui.recents; - -import android.animation.TimeInterpolator; - -/** - * A pre-baked bezier-curved interpolator for quantum-paper transitions. - */ -public class BakedBezierInterpolator implements TimeInterpolator { - public static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator(); - - /** - * Use the INSTANCE variable instead of instantiating. - */ - private BakedBezierInterpolator() { - super(); - } - - /** - * Lookup table values. - * Generated using a Bezier curve from (0,0) to (1,1) with control points: - * P0 (0,0) - * P1 (0.4, 0) - * P2 (0.2, 1.0) - * P3 (1.0, 1.0) - * - * Values sampled with x at regular intervals between 0 and 1. - */ - private static final float[] VALUES = new float[] { - 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f, - 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f, - 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f, - 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f, - 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f, - 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f, - 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f, - 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f, - 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f, - 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f - }; - - private static final float STEP_SIZE = 1.0f / (VALUES.length - 1); - - @Override - public float getInterpolation(float input) { - if (input >= 1.0f) { - return 1.0f; - } - - if (input <= 0f) { - return 0f; - } - - int position = Math.min( - (int)(input * (VALUES.length - 1)), - VALUES.length - 2); - - float quantized = position * STEP_SIZE; - float difference = input - quantized; - float weight = difference / STEP_SIZE; - - return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]); - } - -} diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 1d6a76c..90998da 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -32,7 +32,7 @@ public class Constants { // Enables the use of theme colors as the task bar background public static final boolean EnableTaskBarThemeColors = true; // Enables the info pane on long-press - public static final boolean EnableInfoPane = true; + public static final boolean EnableInfoPane = false; // Enables the search bar layout public static final boolean EnableSearchLayout = true; // Enables the dynamic shadows behind each task diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 463cf74..9afc1cb 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -23,6 +23,8 @@ import android.content.res.Resources; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.TypedValue; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import com.android.systemui.R; @@ -42,6 +44,8 @@ public class RecentsConfiguration { public float animationPxMovementPerSecond; + public Interpolator defaultBezierInterpolator; + public int filteringCurrentViewsMinAnimDuration; public int filteringNewViewsMinAnimDuration; public int taskBarEnterAnimDuration; @@ -121,7 +125,6 @@ public class RecentsConfiguration { res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment); searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); - taskBarViewDefaultBackgroundColor = res.getColor(R.color.recents_task_bar_default_background_color); taskBarViewDefaultTextColor = @@ -131,6 +134,9 @@ public class RecentsConfiguration { taskBarViewDarkTextColor = res.getColor(R.color.recents_task_bar_dark_text_color); + defaultBezierInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + // Update the search widget id SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0); searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index f3e411f..1ca0476 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -35,7 +35,6 @@ import com.android.systemui.recents.model.TaskStack; import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -362,7 +361,7 @@ public class RecentsTaskLoader { return mSystemServicesProxy; } - private List<ActivityManager.RecentTaskInfo> getRecentTasks(Context context) { + private List<ActivityManager.RecentTaskInfo> getRecentTasks() { long t1 = System.currentTimeMillis(); SystemServicesProxy ssp = mSystemServicesProxy; @@ -375,23 +374,6 @@ public class RecentsTaskLoader { Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|tasks]", "" + tasks.size()); - // Remove home/recents tasks - Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); - while (iter.hasNext()) { - ActivityManager.RecentTaskInfo t = iter.next(); - - // Skip tasks in the home stack - if (ssp.isInHomeStack(t.persistentId)) { - iter.remove(); - continue; - } - // Skip tasks from this Recents package - if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) { - iter.remove(); - continue; - } - } - return tasks; } @@ -408,7 +390,7 @@ public class RecentsTaskLoader { // Get the recent tasks SystemServicesProxy ssp = mSystemServicesProxy; - List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(context); + List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(); // Add each task to the task stack t1 = System.currentTimeMillis(); @@ -554,14 +536,16 @@ public class RecentsTaskLoader { } /** Completely removes the resource data from the pool. */ - public void deleteTaskData(Task t) { + public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) { Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|deleteTask]", t); mLoadQueue.removeTask(t); mThumbnailCache.remove(t.key); mApplicationIconCache.remove(t.key); - t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); + if (notifyTaskDataUnloaded) { + t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); + } } /** Stops the task loader and clears all pending tasks */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java index b41555f..8d82883 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -30,7 +30,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -40,6 +39,7 @@ import android.os.UserManager; import android.util.Pair; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Random; @@ -54,7 +54,7 @@ public class SystemServicesProxy { IPackageManager mIpm; UserManager mUm; SearchManager mSm; - String mPackage; + String mRecentsPackage; ComponentName mAssistComponent; Bitmap mDummyIcon; @@ -67,7 +67,7 @@ public class SystemServicesProxy { mUm = (UserManager) context.getSystemService(Context.USER_SERVICE); mIpm = AppGlobals.getPackageManager(); mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE); - mPackage = context.getPackageName(); + mRecentsPackage = context.getPackageName(); // Resolve the assist intent Intent assist = mSm.getAssistIntent(context, false); @@ -83,14 +83,14 @@ public class SystemServicesProxy { } /** Returns a list of the recents tasks */ - public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) { + public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId) { if (mAm == null) return null; // If we are mocking, then create some recent tasks if (Constants.DebugFlags.App.EnableSystemServicesProxy) { ArrayList<ActivityManager.RecentTaskInfo> tasks = new ArrayList<ActivityManager.RecentTaskInfo>(); - int count = Math.min(numTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount); + int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount); for (int i = 0; i < count; i++) { // Create a dummy component name int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount; @@ -114,9 +114,43 @@ public class SystemServicesProxy { return tasks; } - return mAm.getRecentTasksForUser(numTasks, + // Remove home/recents/excluded tasks + int minNumTasksToQuery = 10; + int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); + List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, ActivityManager.RECENT_IGNORE_UNAVAILABLE | - ActivityManager.RECENT_INCLUDE_PROFILES, userId); + ActivityManager.RECENT_INCLUDE_PROFILES | + ActivityManager.RECENT_WITH_EXCLUDED, userId); + boolean isFirstValidTask = true; + Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); + while (iter.hasNext()) { + ActivityManager.RecentTaskInfo t = iter.next(); + + // NOTE: The order of these checks happens in the expected order of the traversal of the + // tasks + + // Skip tasks from this Recents package + if (t.baseIntent.getComponent().getPackageName().equals(mRecentsPackage)) { + iter.remove(); + continue; + } + // Check the first non-recents task, include this task even if it is marked as excluded + // from recents. In other words, only remove excluded tasks if it is not the first task + boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; + if (isExcluded && !isFirstValidTask) { + iter.remove(); + continue; + } + isFirstValidTask = false; + // Skip tasks in the home stack + if (isInHomeStack(t.persistentId)) { + iter.remove(); + continue; + } + } + + return tasks.subList(0, Math.min(tasks.size(), numLatestTasks)); } /** Returns a list of the running tasks */ @@ -165,11 +199,12 @@ public class SystemServicesProxy { } /** Removes the task and kills the process */ - public void removeTask(int taskId) { + public void removeTask(int taskId, boolean isDocument) { if (mAm == null) return; if (Constants.DebugFlags.App.EnableSystemServicesProxy) return; - mAm.removeTask(taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS); + // Remove the task, and only kill the process if it is not a document + mAm.removeTask(taskId, isDocument ? 0 : ActivityManager.REMOVE_TASK_KILL_PROCESS); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java index b602f84..46e6ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java @@ -18,6 +18,7 @@ package com.android.systemui.recents; import android.graphics.Color; import android.graphics.Rect; +import android.graphics.drawable.Drawable; /* Common code */ public class Utilities { @@ -54,12 +55,15 @@ public class Utilities { 0.0722f * Color.blue(color)); } - /** Returns the ideal text color to draw on top of a specified background color. */ - public static int getIdealTextColorForBackgroundColor(int color) { - RecentsConfiguration configuration = RecentsConfiguration.getInstance(); + /** Returns the ideal color to draw on top of a specified background color. */ + public static int getIdealColorForBackgroundColor(int color, int lightRes, int darkRes) { int greyscale = colorToGreyscale(color); - return (greyscale < 128) ? configuration.taskBarViewLightTextColor : - configuration.taskBarViewDarkTextColor; - + return (greyscale < 128) ? lightRes : darkRes; + } + /** Returns the ideal drawable to draw on top of a specified background color. */ + public static Drawable getIdealResourceForBackgroundColor(int color, Drawable lightRes, + Drawable darkRes) { + int greyscale = colorToGreyscale(color); + return (greyscale < 128) ? lightRes : darkRes; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 8168619..a6d7e67 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -346,7 +346,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV RecentsTaskLoader.getInstance().getSystemServicesProxy() .moveTaskToFront(task.key.id, opts); } else { - // Launch the activity with the desired animation + // Launch the activity anew with the desired animation Intent i = new Intent(task.key.baseIntent); i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_TASK_ON_HOME @@ -361,6 +361,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } catch (ActivityNotFoundException anfe) { Console.logError(getContext(), "Could not start Activity"); } + + // And clean up the old task + onTaskRemoved(task); } Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask, @@ -390,6 +393,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV .addNextIntentWithParentStack(intent).startActivities(); } + @Override + public void onTaskRemoved(Task t) { + // Remove any stored data from the loader. We currently don't bother notifying the views + // that the data has been unloaded because at the point we call onTaskRemoved(), the views + // either don't need to be updated, or have already been removed. + RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); + loader.deleteTaskData(t, false); + + // Remove the old task from activity manager + int flags = t.key.baseIntent.getFlags(); + boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) == + Intent.FLAG_ACTIVITY_NEW_DOCUMENT; + RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id, + isDocument); + } + /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/ @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index c6cb812..07caa1b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -16,7 +16,10 @@ package com.android.systemui.recents.views; +import android.animation.ValueAnimator; import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.FrameLayout; import android.widget.ImageView; @@ -32,9 +35,13 @@ import com.android.systemui.recents.model.Task; class TaskBarView extends FrameLayout { Task mTask; + ImageView mDismissButton; ImageView mApplicationIcon; TextView mActivityDescription; + Drawable mLightDismissDrawable; + Drawable mDarkDismissDrawable; + public TaskBarView(Context context) { this(context, null); } @@ -49,6 +56,9 @@ class TaskBarView extends FrameLayout { public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + Resources res = context.getResources(); + mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light); + mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark); } @Override @@ -56,6 +66,28 @@ class TaskBarView extends FrameLayout { // Initialize the icon and description views mApplicationIcon = (ImageView) findViewById(R.id.application_icon); mActivityDescription = (TextView) findViewById(R.id.activity_description); + mDismissButton = (ImageView) findViewById(R.id.dismiss_task); + } + + /** Synchronizes this bar view's properties with the task's transform */ + void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform, + TaskViewTransform toTransform, int duration) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); + if (duration > 0) { + if (animateFromTransform != null) { + mDismissButton.setAlpha(animateFromTransform.dismissAlpha); + } + mDismissButton.animate() + .alpha(toTransform.dismissAlpha) + .setStartDelay(0) + .setDuration(duration) + .setInterpolator(config.defaultBezierInterpolator) + .withLayer() + .start(); + } else { + mDismissButton.setAlpha(toTransform.dismissAlpha); + } + mDismissButton.invalidate(); } /** Binds the bar view to the task */ @@ -74,7 +106,10 @@ class TaskBarView extends FrameLayout { int tint = t.colorPrimary; if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) { setBackgroundColor(tint); - mActivityDescription.setTextColor(Utilities.getIdealTextColorForBackgroundColor(tint)); + mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, + configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor)); + mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, + mLightDismissDrawable, mDarkDismissDrawable)); } else { setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor); mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java index c6c29a6..f1c362a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java @@ -30,7 +30,6 @@ import android.util.AttributeSet; import android.widget.Button; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.Utilities; @@ -111,7 +110,8 @@ class TaskInfoView extends FrameLayout { int duration = Utilities.calculateTranslationAnimationDuration((int) mMaxClipRadius); mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", toRadius); mCircularClipAnimator.setDuration(duration); - mCircularClipAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mCircularClipAnimator.setInterpolator( + RecentsConfiguration.getInstance().defaultBezierInterpolator); mCircularClipAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -143,7 +143,7 @@ class TaskInfoView extends FrameLayout { .scaleX(1f) .scaleY(1f) .setDuration(duration) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator) .withLayer() .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index ad0f2f82..b64225e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -23,6 +23,7 @@ import android.animation.ValueAnimator; import android.app.Activity; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.Region; @@ -36,7 +37,6 @@ import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.OverScroller; import com.android.systemui.R; -import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; @@ -60,6 +60,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal interface TaskStackViewCallbacks { public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t); public void onTaskAppInfoLaunched(Task t); + public void onTaskRemoved(Task t); } TaskStack mStack; @@ -168,6 +169,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset); } + // Set the alphas + transform.dismissAlpha = Math.max(-1f, Math.min(0f, t)) + 1f; + // Update the rect and visibility transform.rect.set(mTaskRect); if (t < -(numPeekCards + 1)) { @@ -336,7 +340,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll); mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll - curScroll, 250)); - mScrollAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE); + mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -1034,6 +1038,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + @Override + public void onTaskDismissed(TaskView tv) { + Task task = tv.getTask(); + // Remove the task from the view + mStack.removeTask(task); + // Notify the callback that we've removed the task and it can clean up after it + mCb.onTaskRemoved(task); + } + /**** View.OnClickListener Implementation ****/ @Override @@ -1093,6 +1106,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal @Override public void onComponentRemoved(Set<ComponentName> cns) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); // For other tasks, just remove them directly if they no longer exist ArrayList<Task> tasks = mStack.getTasks(); for (int i = tasks.size() - 1; i >= 0; i--) { @@ -1475,17 +1489,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { @Override public void onChildDismissed(View v) { TaskView tv = (TaskView) v; - Task task = tv.getTask(); - - // Remove the task from the view - mSv.mStack.removeTask(task); - - // Remove any stored data from the loader - RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); - loader.deleteTaskData(task); - - // Remove the task from activity manager - RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(tv.getTask().key.id); + mSv.onTaskDismissed(tv); // Disable HW layers mSv.decHwLayersRefCount("swipeComplete"); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index b03f389..5fad629 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -31,7 +31,6 @@ import android.view.View; import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; import com.android.systemui.R; -import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.model.Task; @@ -46,6 +45,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, public void onTaskInfoPanelShown(TaskView tv); public void onTaskInfoPanelHidden(TaskView tv); public void onTaskAppInfoClicked(TaskView tv); + public void onTaskDismissed(TaskView tv); // public void onTaskViewReboundToTask(TaskView tv, Task t); } @@ -143,6 +143,10 @@ public class TaskView extends FrameLayout implements View.OnClickListener, int minZ = config.taskViewTranslationZMinPx; int incZ = config.taskViewTranslationZIncrementPx; + // Update the bar view + mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration); + + // Update this task view if (duration > 0) { if (animateFromTransform != null) { setTranslationY(animateFromTransform.translationY); @@ -161,7 +165,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, .scaleY(toTransform.scale) .alpha(toTransform.alpha) .setDuration(duration) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .withLayer() .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override @@ -221,8 +225,8 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mBarView.setAlpha(0f); mBarView.animate() .alpha(1f) - .setStartDelay(235) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setStartDelay(250) + .setInterpolator(config.defaultBezierInterpolator) .setDuration(config.taskBarEnterAnimDuration) .withLayer() .start(); @@ -234,7 +238,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mBarView.animate() .alpha(0f) .setStartDelay(0) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .setDuration(config.taskBarExitAnimDuration) .withLayer() .withEndAction(new Runnable() { @@ -252,7 +256,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, animate().translationX(config.taskViewRemoveAnimTranslationXPx) .alpha(0f) .setStartDelay(0) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .setDuration(config.taskViewRemoveAnimDuration) .withLayer() .withEndAction(new Runnable() { @@ -310,7 +314,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mInfoView.animate() .alpha(0f) .setDuration(config.taskViewInfoPaneAnimDuration) - .setInterpolator(BakedBezierInterpolator.INSTANCE) + .setInterpolator(config.defaultBezierInterpolator) .withLayer() .withEndAction(new Runnable() { @Override @@ -380,6 +384,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mInfoView.rebindToTask(mTask, reloadingTaskData); // Rebind any listeners mBarView.mApplicationIcon.setOnClickListener(this); + mBarView.mDismissButton.setOnClickListener(this); mInfoView.mAppInfoButton.setOnClickListener(this); } mTaskDataLoaded = true; @@ -405,6 +410,15 @@ public class TaskView extends FrameLayout implements View.OnClickListener, hideInfoPane(); } else if (v == mBarView.mApplicationIcon) { mCb.onTaskIconClicked(this); + } else if (v == mBarView.mDismissButton) { + // Animate out the view and call the callback + final TaskView tv = this; + animateRemoval(new Runnable() { + @Override + public void run() { + mCb.onTaskDismissed(tv); + } + }); } else if (v == mInfoView.mAppInfoButton) { mCb.onTaskAppInfoClicked(this); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java index 0748bbb..e6391a8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -24,6 +24,7 @@ public class TaskViewTransform { public int translationY = 0; public float scale = 1f; public float alpha = 1f; + public float dismissAlpha = 1f; public boolean visible = false; public Rect rect = new Rect(); float t; @@ -36,6 +37,7 @@ public class TaskViewTransform { translationY = o.translationY; scale = o.scale; alpha = o.alpha; + dismissAlpha = o.dismissAlpha; visible = o.visible; rect.set(o.rect); t = o.t; @@ -44,6 +46,6 @@ public class TaskViewTransform { @Override public String toString() { return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha + - " visible: " + visible + " rect: " + rect; + " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha; } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 327e715..1747e6e 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -119,7 +119,6 @@ public class BrightnessController implements ToggleSlider.Listener { } }; mBrightnessObserver = new BrightnessObserver(mHandler); - mBrightnessObserver.startObserving(); PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mMinimumBacklight = pm.getMinimumScreenBrightnessSetting(); @@ -128,13 +127,6 @@ public class BrightnessController implements ToggleSlider.Listener { mAutomaticAvailable = context.getResources().getBoolean( com.android.internal.R.bool.config_automatic_brightness_available); mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power")); - - // Update the slider and mode before attaching the listener so we don't receive the - // onChanged notifications for the initial values. - updateMode(); - updateSlider(); - - control.setOnChangedListener(this); } public void addStateChangedCallback(BrightnessStateChangeCallback cb) { @@ -150,11 +142,24 @@ public class BrightnessController implements ToggleSlider.Listener { // Do nothing } + public void registerCallbacks() { + mBrightnessObserver.startObserving(); + mUserTracker.startTracking(); + + // Update the slider and mode before attaching the listener so we don't receive the + // onChanged notifications for the initial values. + updateMode(); + updateSlider(); + + mControl.setOnChangedListener(this); + } + /** Unregister all call backs, both to and from the controller */ public void unregisterCallbacks() { mBrightnessObserver.stopObserving(); mChangeCallbacks.clear(); mUserTracker.stopTracking(); + mControl.setOnChangedListener(null); } public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) { diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java index bd5e5e8..27881c4 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java @@ -92,6 +92,7 @@ public class BrightnessDialog extends Dialog implements mBrightnessController = new BrightnessController(getContext(), (ImageView) findViewById(R.id.brightness_icon), (ToggleSlider) findViewById(R.id.brightness_slider)); + mBrightnessController.registerCallbacks(); dismissBrightnessDialog(mBrightnessDialogLongTimeout); mBrightnessController.addStateChangedCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java index 036bd4f..f8ff616 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java +++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java @@ -29,9 +29,6 @@ public abstract class CurrentUserTracker extends BroadcastReceiver { private int mCurrentUserId; public CurrentUserTracker(Context context) { - IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); - context.registerReceiver(this, filter); - mCurrentUserId = ActivityManager.getCurrentUser(); mContext = context; } @@ -50,6 +47,12 @@ public abstract class CurrentUserTracker extends BroadcastReceiver { } } + public void startTracking() { + mCurrentUserId = ActivityManager.getCurrentUser(); + IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED); + mContext.registerReceiver(this, filter); + } + public void stopTracking() { mContext.unregisterReceiver(this); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java index 7d38058..4b78072 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java @@ -62,7 +62,6 @@ public class ToggleSlider extends RelativeLayout { mToggle = (CompoundButton) findViewById(R.id.toggle); mToggle.setOnCheckedChangeListener(mCheckListener); - mToggle.setBackground(res.getDrawable(R.drawable.status_bar_toggle_button)); mSlider = (SeekBar) findViewById(R.id.slider); mSlider.setOnSeekBarChangeListener(mSeekListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index ecefc39..898f06e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1029,6 +1029,9 @@ public abstract class BaseStatusBar extends SystemUI implements } protected void addNotificationViews(NotificationData.Entry entry) { + if (entry == null) { + return; + } // Add the expanded view and icon. int pos = mNotificationData.add(entry); if (DEBUG) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 281bd2d..4bd0e1c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -112,7 +112,7 @@ public abstract class ExpandableView extends FrameLayout { * @return The desired notification height. */ public int getIntrinsicHeight() { - return mActualHeight; + return getHeight(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java index 864c597..451c5c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java @@ -34,21 +34,6 @@ public class NotificationOverflowContainer extends ActivatableNotificationView { } @Override - public void setActualHeight(int currentHeight, boolean notifyListeners) { - // noop - } - - @Override - public int getActualHeight() { - return getHeight(); - } - - @Override - public void setClipTopAmount(int clipTopAmount) { - // noop - } - - @Override protected void onFinishInflate() { super.onFinishInflate(); mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java new file mode 100644 index 0000000..c26f15e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -0,0 +1,90 @@ +/* + * 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.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Path; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.ContactsContract; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; + +import com.android.systemui.R; +import com.android.systemui.settings.UserSwitcherHostView; +import com.android.systemui.statusbar.policy.UserInfoController; + +/** + * Image button for the multi user switcher. + */ +public class MultiUserSwitch extends ImageButton implements View.OnClickListener, + UserInfoController.OnUserInfoChangedListener { + + private ViewGroup mOverlayParent; + + public MultiUserSwitch(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setOnClickListener(this); + } + + public void setOverlayParent(ViewGroup parent) { + mOverlayParent = parent; + } + + @Override + public void onClick(View v) { + final UserManager um = UserManager.get(getContext()); + if (um.isUserSwitcherEnabled()) { + final UserSwitcherHostView switcher = + (UserSwitcherHostView) LayoutInflater.from(getContext()).inflate( + R.layout.user_switcher_host, mOverlayParent, false); + switcher.setFinishRunnable(new Runnable() { + @Override + public void run() { + mOverlayParent.removeView(switcher); + } + }); + switcher.refreshUsers(); + mOverlayParent.addView(switcher); + } else { + Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent( + getContext(), v, ContactsContract.Profile.CONTENT_URI, + ContactsContract.QuickContact.MODE_LARGE, null); + getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } + } + + public void setUserInfoController(UserInfoController userInfoController) { + userInfoController.addListener(this); + } + + @Override + public void onUserInfoChanged(String name, Drawable picture) { + setImageDrawable(picture); + } +} 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 f63ba9c..6132ed2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -24,9 +24,11 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import android.widget.LinearLayout; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; @@ -56,10 +58,18 @@ public class NotificationPanelView extends PanelView implements private int mTrackingPointer; private VelocityTracker mVelocityTracker; private boolean mTracking; + + /** + * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't + * intercepted yet. + */ + private boolean mIntercepting; private boolean mQsExpanded; private float mInitialHeightOnTouch; private float mInitialTouchX; private float mInitialTouchY; + private float mLastTouchX; + private float mLastTouchY; private float mQsExpansionHeight; private int mQsMinExpansionHeight; private int mQsMaxExpansionHeight; @@ -93,6 +103,7 @@ public class NotificationPanelView extends PanelView implements super.onFinishInflate(); mHeader = (StatusBarHeaderView) findViewById(R.id.header); mHeader.getBackgroundView().setOnClickListener(this); + mHeader.setOverlayParent(this); mKeyguardStatusView = findViewById(R.id.keyguard_status_view); mStackScrollerContainer = findViewById(R.id.notification_container_parent); mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container); @@ -144,7 +155,6 @@ public class NotificationPanelView extends PanelView implements public void setQsExpansionEnabled(boolean qsExpansionEnabled) { mQsExpansionEnabled = qsExpansionEnabled; - mHeader.setExpansionEnabled(qsExpansionEnabled); } public void closeQs() { @@ -193,6 +203,7 @@ public class NotificationPanelView extends PanelView implements switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + mIntercepting = true; mInitialTouchY = y; mInitialTouchX = x; initVelocityTracker(); @@ -215,6 +226,16 @@ public class NotificationPanelView extends PanelView implements case MotionEvent.ACTION_MOVE: final float h = y - mInitialTouchY; trackMovement(event); + if (mTracking) { + + // Already tracking because onOverscrolled was called. We need to update here + // so we don't stop for a frame until the next touch event gets handled in + // onTouchEvent. + setQsExpansion(h + mInitialHeightOnTouch); + trackMovement(event); + mIntercepting = false; + return true; + } if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) { onQsExpansionStarted(); @@ -222,14 +243,29 @@ public class NotificationPanelView extends PanelView implements mInitialTouchY = y; mInitialTouchX = x; mTracking = true; + mIntercepting = false; return true; } break; + + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + mIntercepting = false; + break; } return !mQsExpanded && super.onInterceptTouchEvent(event); } @Override + public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { + + // Block request so we can still intercept the scrolling when QS is expanded. + if (!mQsExpanded) { + super.requestDisallowInterceptTouchEvent(disallowIntercept); + } + } + + @Override public boolean onTouchEvent(MotionEvent event) { // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference // implementation. @@ -297,11 +333,26 @@ public class NotificationPanelView extends PanelView implements return mQsExpanded || super.onTouchEvent(event); } + @Override + public void onOverscrolled(int amount) { + if (mIntercepting) { + onQsExpansionStarted(amount); + mInitialHeightOnTouch = mQsExpansionHeight; + mInitialTouchY = mLastTouchY; + mInitialTouchX = mLastTouchX; + mTracking = true; + } + } + private void onQsExpansionStarted() { + onQsExpansionStarted(0); + } + + private void onQsExpansionStarted(int overscrollAmount) { cancelAnimation(); // Reset scroll position and apply that position to the expanded height. - float height = mQsExpansionHeight - mScrollView.getScrollY(); + float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount; mScrollView.scrollTo(0, 0); setQsExpansion(height); } @@ -359,6 +410,8 @@ public class NotificationPanelView extends PanelView implements private void trackMovement(MotionEvent event) { if (mVelocityTracker != null) mVelocityTracker.addMovement(event); + mLastTouchX = event.getX(); + mLastTouchY = event.getY(); } private void initVelocityTracker() { @@ -412,11 +465,8 @@ public class NotificationPanelView extends PanelView implements if (!mQsExpansionEnabled) { return false; } - View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded - ? mKeyguardStatusView - : mHeader; - boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight() - && y >= headerView.getTop() && y <= headerView.getBottom(); + boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight() + && y >= mHeader.getTop() && y <= mHeader.getBottom(); if (mQsExpanded) { return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0); } else { @@ -425,6 +475,35 @@ public class NotificationPanelView extends PanelView implements } @Override + public void setVisibility(int visibility) { + int oldVisibility = getVisibility(); + super.setVisibility(visibility); + if (visibility != oldVisibility) { + reparentStatusIcons(visibility == VISIBLE); + } + } + + /** + * When the notification panel gets expanded, we need to move the status icons in the header + * card. + */ + private void reparentStatusIcons(boolean toHeader) { + if (mStatusBar == null) { + return; + } + LinearLayout systemIcons = mStatusBar.getSystemIcons(); + if (systemIcons.getParent() != null) { + ((ViewGroup) systemIcons.getParent()).removeView(systemIcons); + } + if (toHeader) { + mHeader.attachSystemIcons(systemIcons); + } else { + mHeader.onSystemIconsDetached(); + mStatusBar.reattachSystemIcons(); + } + } + + @Override protected boolean isScrolledToBottom() { if (!isInSettings()) { return mNotificationStackScroller.isScrolledToBottom(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java index 46484f3..ba0b66e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java @@ -27,6 +27,7 @@ import android.widget.ScrollView; public class ObservableScrollView extends ScrollView { private Listener mListener; + private int mLastOverscrollAmount; public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -58,7 +59,25 @@ public class ObservableScrollView extends ScrollView { } } + @Override + protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, + int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, + boolean isTouchEvent) { + mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY()); + return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, + maxOverScrollX, maxOverScrollY, isTouchEvent); + } + + @Override + protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { + super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); + if (mListener != null && mLastOverscrollAmount > 0) { + mListener.onOverscrolled(mLastOverscrollAmount); + } + } + public interface Listener { void onScrollChanged(); + void onOverscrolled(int amount); } } 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 92eee4e..4d09d6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -29,7 +29,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCE import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.app.ActivityManager; import android.app.ActivityManagerNative; @@ -112,6 +111,7 @@ import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; +import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; @@ -185,6 +185,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, LocationController mLocationController; NetworkController mNetworkController; RotationLockController mRotationLockController; + UserInfoController mUserInfoController; int mNaturalBarHeight = -1; int mIconSize = -1; @@ -205,6 +206,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, // right-hand icons LinearLayout mSystemIconArea; + LinearLayout mSystemIcons; // left-hand icons LinearLayout mStatusIcons; @@ -221,7 +223,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mNotificationPanelGravity; int mNotificationPanelMarginBottomPx; float mNotificationPanelMinHeightFrac; - boolean mNotificationPanelIsFullScreenWidth; TextView mNotificationPanelDebugText; // settings @@ -230,7 +231,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, QuickSettingsContainerView mSettingsContainer; // top bar - View mNotificationPanelHeader; + StatusBarHeaderView mHeader; View mKeyguardStatusView; KeyguardBottomAreaView mKeyguardBottomArea; boolean mLeaveOpenOnKeyguardHide; @@ -240,8 +241,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, String mKeyguardHotwordPhrase = ""; int mKeyguardMaxNotificationCount; View mDateTimeView; - View mClearButton; - ImageView mHeaderFlipper; // carrier/wifi label private TextView mCarrierLabel; @@ -249,7 +248,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private int mCarrierLabelHeight; private TextView mEmergencyCallLabel; private int mStatusBarHeaderHeight; - private View mKeyguardCarrierLabel; private boolean mShowCarrierInPanel = false; @@ -554,8 +552,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( R.id.notification_panel); mNotificationPanel.setStatusBar(this); - mNotificationPanelIsFullScreenWidth = - (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT); // make the header non-responsive to clicks mNotificationPanel.findViewById(R.id.header).setOnTouchListener( @@ -609,6 +605,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mPixelFormat = PixelFormat.OPAQUE; mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area); + mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons); mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons); mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons); mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon); @@ -627,39 +624,30 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); mKeyguardIconOverflowContainer.setOnActivatedListener(this); - mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text); mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.addView(mKeyguardIconOverflowContainer); mExpandedContents = mStackScroller; - mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header); + mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); + mHeader.setActivityStarter(this); mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); mKeyguardBottomArea = (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea.setActivityStarter(this); mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById( R.id.keyguard_indication_text); - mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button); - mClearButton.setOnClickListener(mClearButtonListener); - mClearButton.setAlpha(0f); - mClearButton.setVisibility(View.INVISIBLE); - mClearButton.setEnabled(false); mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date); - mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime); + mDateTimeView = mHeader.findViewById(R.id.datetime); if (mDateTimeView != null) { mDateTimeView.setOnClickListener(mClockClickListener); mDateTimeView.setEnabled(true); } - mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper); - - if (!mNotificationPanelIsFullScreenWidth) { - mNotificationPanel.setSystemUiVisibility( - View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS | - View.STATUS_BAR_DISABLE_CLOCK); - } + mNotificationPanel.setSystemUiVisibility( + View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS | + View.STATUS_BAR_DISABLE_CLOCK); mTicker = new MyTicker(context, mStatusBarView); @@ -680,6 +668,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, || QuickSettings.DEBUG_GONE_TILES) { mRotationLockController = new RotationLockController(mContext); } + mUserInfoController = new UserInfoController(mContext); final SignalClusterView signalCluster = (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster); @@ -734,11 +723,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mFlipSettingsView = mSettingsContainer; if (mSettingsContainer != null) { mQS = new QuickSettings(mContext, mSettingsContainer); - if (!mNotificationPanelIsFullScreenWidth) { - mSettingsContainer.setSystemUiVisibility( - View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS - | View.STATUS_BAR_DISABLE_SYSTEM_INFO); - } mQS.setService(this); mQS.setBar(mStatusBarView); mQS.setup(mNetworkController, mBluetoothController, mBatteryController, @@ -747,6 +731,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mQS = null; // fly away, be free } + // User info. Trigger first load. + mHeader.setUserInfoController(mUserInfoController); + mUserInfoController.reloadUserInfo(); + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mBroadcastReceiver.onReceive(mContext, new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); @@ -1118,19 +1106,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, if (mNavigationBarView != null) { mNavigationBarView.setLayoutDirection(layoutDirection); } - - if (mClearButton != null && mClearButton instanceof ImageView) { - // Force asset reloading - ((ImageView)mClearButton).setImageDrawable(null); - ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear); - } - - if (mHeaderFlipper != null) { - // Force asset reloading - mHeaderFlipper.setImageDrawable(null); - mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings); - } - refreshAllStatusBarIcons(); } @@ -1301,38 +1276,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, + " any=" + any + " clearable=" + clearable); } - if (mFlipSettingsView != null - && mFlipSettingsView.getVisibility() == View.VISIBLE - && mStackScroller.getVisibility() != View.VISIBLE) { - // the flip settings panel is unequivocally showing; we should not be shown - mClearButton.setVisibility(View.INVISIBLE); - } else if (mClearButton.isShown()) { - if (clearable != (mClearButton.getAlpha() == 1.0f)) { - ObjectAnimator clearAnimation = ObjectAnimator.ofFloat( - mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250); - clearAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (mClearButton.getAlpha() <= 0.0f) { - mClearButton.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onAnimationStart(Animator animation) { - if (mClearButton.getAlpha() <= 0.0f) { - mClearButton.setVisibility(View.VISIBLE); - } - } - }); - clearAnimation.start(); - } - } else { - mClearButton.setAlpha(clearable ? 1.0f : 0.0f); - mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE); - } - mClearButton.setEnabled(clearable); - final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out); final boolean showDot = (any&&!areLightsOn()); if (showDot != (nlo.getAlpha() == 1.0f)) { @@ -1917,13 +1860,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, private void checkBarModes() { if (mDemoMode) return; - int sbMode = mStatusBarMode; - if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0 - && mState != StatusBarState.KEYGUARD) { - // if panels are expandable, force the status bar opaque on any interaction - sbMode = MODE_OPAQUE; - } - checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); + checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions()); if (mNavigationBarView != null) { checkBarMode(mNavigationBarMode, mNavigationBarWindowState, mNavigationBarView.getBarTransitions()); @@ -2449,10 +2386,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, final Context context = mContext; final Resources res = context.getResources(); - if (mClearButton instanceof TextView) { - ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button)); - } - // Update the QuickSettings container if (mQS != null) mQS.updateResources(); @@ -2774,20 +2707,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mKeyguardBottomArea.setVisibility(View.VISIBLE); mKeyguardIndicationTextView.setVisibility(View.VISIBLE); mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase); - mKeyguardCarrierLabel.setVisibility(View.VISIBLE); - mNotificationPanelHeader.setVisibility(View.GONE); mNotificationPanel.closeQs(); - mSettingsContainer.setKeyguardShowing(true); } else { mKeyguardStatusView.setVisibility(View.GONE); mKeyguardBottomArea.setVisibility(View.GONE); mKeyguardIndicationTextView.setVisibility(View.GONE); - mKeyguardCarrierLabel.setVisibility(View.GONE); - mNotificationPanelHeader.setVisibility(View.VISIBLE); - - mSettingsContainer.setKeyguardShowing(false); } + mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD); + mHeader.setKeyguardShowing(mState == StatusBarState.KEYGUARD); updateStackScrollerState(); updatePublicMode(); @@ -2943,4 +2871,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, public ViewGroup getQuickSettingsOverlayParent() { return mNotificationPanel; } + + public LinearLayout getSystemIcons() { + return mSystemIcons; + } + + /** + * Reattaches the system icons to its normal parent in collapsed status bar. + */ + public void reattachSystemIcons() { + mSystemIconArea.addView(mSystemIcons, 0); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index e6de057..084bfcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -63,7 +63,7 @@ public class PhoneStatusBarView extends PanelBar { } @Override - public void onAttachedToWindow() { + public void onFinishInflate() { mBarTransitions.init(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java index e1ef83a..005b0d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java @@ -460,6 +460,7 @@ class QuickSettingsModel implements BluetoothStateChangeCallback, rebindMediaRouterAsCurrentUser(); } }; + mUserTracker.startTracking(); mNextAlarmObserver = new NextAlarmObserver(mHandler); mNextAlarmObserver.startObserving(); 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 9d33930..5527473 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -17,25 +17,52 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.content.Intent; +import android.graphics.Outline; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.android.systemui.R; +import com.android.systemui.settings.BrightnessController; +import com.android.systemui.settings.ToggleSlider; +import com.android.systemui.statusbar.policy.UserInfoController; /** * The view to manage the header area in the expanded status bar. */ -public class StatusBarHeaderView extends RelativeLayout { +public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener { private boolean mExpanded; + private boolean mKeyguardShowing; + private View mBackground; - private View mFlipper; + private ViewGroup mSystemIconsContainer; + private View mDateTime; + private View mKeyguardCarrierText; + private MultiUserSwitch mMultiUserSwitch; + private View mDate; + private View mStatusIcons; + private View mSignalCluster; + private View mSettingsButton; + private View mBrightnessContainer; private int mCollapsedHeight; private int mExpandedHeight; + private int mKeyguardHeight; + + private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT; + private int mNormalWidth; + + private ActivityStarter mActivityStarter; + private BrightnessController mBrightnessController; + + private final Rect mClipBounds = new Rect(); + private final Outline mOutline = new Outline(); public StatusBarHeaderView(Context context, AttributeSet attrs) { super(context, attrs); @@ -45,19 +72,35 @@ public class StatusBarHeaderView extends RelativeLayout { protected void onFinishInflate() { super.onFinishInflate(); mBackground = findViewById(R.id.background); - mFlipper = findViewById(R.id.header_flipper); + mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container); + mDateTime = findViewById(R.id.datetime); + mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text); + mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch); + mDate = findViewById(R.id.date); + mSettingsButton = findViewById(R.id.settings_button); + mSettingsButton.setOnClickListener(this); + mBrightnessContainer = findViewById(R.id.brightness_container); + mBrightnessController = new BrightnessController(getContext(), + (ImageView) findViewById(R.id.brightness_icon), + (ToggleSlider) findViewById(R.id.brightness_slider)); loadDimens(); } private void loadDimens() { - mCollapsedHeight = getResources().getDimensionPixelSize( - R.dimen.status_bar_header_height); + mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height); mExpandedHeight = getResources().getDimensionPixelSize( R.dimen.status_bar_header_height_expanded); + mKeyguardHeight = getResources().getDimensionPixelSize( + R.dimen.status_bar_header_height_keyguard); + mNormalWidth = getLayoutParams().width; + } + + public void setActivityStarter(ActivityStarter activityStarter) { + mActivityStarter = activityStarter; } public int getCollapsedHeight() { - return mCollapsedHeight; + return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight; } public int getExpandedHeight() { @@ -65,16 +108,82 @@ public class StatusBarHeaderView extends RelativeLayout { } public void setExpanded(boolean expanded) { - if (expanded != mExpanded) { - ViewGroup.LayoutParams lp = getLayoutParams(); - lp.height = expanded ? mExpandedHeight : mCollapsedHeight; + boolean changed = expanded != mExpanded; + mExpanded = expanded; + if (changed) { + updateHeights(); + updateVisibilities(); + updateSystemIconsLayoutParams(); + updateBrightnessControllerState(); + } + } + + private void updateHeights() { + boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; + int height; + if (mExpanded) { + height = mExpandedHeight; + } else if (onKeyguardAndCollapsed) { + height = mKeyguardHeight; + } else { + height = mCollapsedHeight; + } + ViewGroup.LayoutParams lp = getLayoutParams(); + if (lp.height != height) { + lp.height = height; setLayoutParams(lp); - mExpanded = expanded; + } + int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight; + lp = mSystemIconsContainer.getLayoutParams(); + if (lp.height != systemIconsContainerHeight) { + lp.height = systemIconsContainerHeight; + mSystemIconsContainer.setLayoutParams(lp); + } + lp = mMultiUserSwitch.getLayoutParams(); + if (lp.height != systemIconsContainerHeight) { + lp.height = systemIconsContainerHeight; + mMultiUserSwitch.setLayoutParams(lp); } } - public void setExpansionEnabled(boolean enabled) { - mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE); + private void updateWidth() { + int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth; + ViewGroup.LayoutParams lp = getLayoutParams(); + if (width != lp.width) { + lp.width = width; + setLayoutParams(lp); + } + } + + private void updateVisibilities() { + boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded; + mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); + mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE); + mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE); + mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE); + if (mStatusIcons != null) { + mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE); + } + if (mSignalCluster != null) { + mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE); + } + } + + private void updateSystemIconsLayoutParams() { + RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams(); + lp.addRule(RelativeLayout.START_OF, mExpanded + ? mSettingsButton.getId() + : mMultiUserSwitch.getId()); + } + + private void updateBrightnessControllerState() { + if (mExpanded) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); + } } public void setExpansion(float height) { @@ -89,9 +198,65 @@ public class StatusBarHeaderView extends RelativeLayout { } else { mBackground.setTranslationY(0); } + setClipping(height); + } + + private void setClipping(float height) { + mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height); + setClipBounds(mClipBounds); + mOutline.setRect(mClipBounds); + setOutline(mOutline); } public View getBackgroundView() { return mBackground; } + + public void attachSystemIcons(LinearLayout systemIcons) { + mSystemIconsContainer.addView(systemIcons); + mStatusIcons = systemIcons.findViewById(R.id.statusIcons); + mSignalCluster = systemIcons.findViewById(R.id.signal_cluster); + } + + public void onSystemIconsDetached() { + if (mStatusIcons != null) { + mStatusIcons.setVisibility(View.VISIBLE); + } + if (mSignalCluster != null) { + mSignalCluster.setVisibility(View.VISIBLE); + } + mStatusIcons = null; + mSignalCluster = null; + } + + public void setKeyguardShowing(boolean keyguardShowing) { + mKeyguardShowing = keyguardShowing; + if (keyguardShowing) { + setZ(0); + } else { + setTranslationZ(0); + } + updateHeights(); + updateWidth(); + updateVisibilities(); + } + + public void setUserInfoController(UserInfoController userInfoController) { + mMultiUserSwitch.setUserInfoController(userInfoController); + } + + public void setOverlayParent(ViewGroup parent) { + mMultiUserSwitch.setOverlayParent(parent); + } + + @Override + public void onClick(View v) { + if (v == mSettingsButton) { + startSettingsActivity(); + } + } + + private void startSettingsActivity() { + mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)); + } } 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 f24c1b6..48c54fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -73,8 +73,7 @@ public class StatusBarKeyguardViewManager { public void show(Bundle options) { mShowing = true; mStatusBarWindowManager.setKeyguardShowing(true); - showBouncerOrKeyguard(); - updateStates(); + reset(); } /** @@ -105,13 +104,15 @@ public class StatusBarKeyguardViewManager { * Reset the state of the view. */ public void reset() { - if (mOccluded) { - mPhoneStatusBar.hideKeyguard(); - mBouncer.hide(); - } else { - showBouncerOrKeyguard(); + if (mShowing) { + if (mOccluded) { + mPhoneStatusBar.hideKeyguard(); + mBouncer.hide(); + } else { + showBouncerOrKeyguard(); + } + updateStates(); } - updateStates(); } public void onScreenTurnedOff() { @@ -121,7 +122,6 @@ public class StatusBarKeyguardViewManager { public void onScreenTurnedOn(final IKeyguardShowCallback callback) { mScreenOn = true; - reset(); if (callback != null) { callbackAfterDraw(callback); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java index a4c9df5..8809d18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java @@ -78,9 +78,8 @@ public class StatusBarWindowManager { | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, PixelFormat.TRANSLUCENT); - mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; - mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 8ced1c9..55a0bba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -20,6 +20,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.TypedArray; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -30,6 +31,7 @@ import android.util.AttributeSet; import android.widget.TextView; import com.android.systemui.DemoMode; +import com.android.systemui.R; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -52,7 +54,7 @@ public class Clock extends TextView implements DemoMode { private static final int AM_PM_STYLE_SMALL = 1; private static final int AM_PM_STYLE_GONE = 2; - private static final int AM_PM_STYLE = AM_PM_STYLE_GONE; + private final int mAmPmStyle; public Clock(Context context) { this(context, null); @@ -64,6 +66,15 @@ public class Clock extends TextView implements DemoMode { public Clock(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + TypedArray a = context.getTheme().obtainStyledAttributes( + attrs, + R.styleable.Clock, + 0, 0); + try { + mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE); + } finally { + a.recycle(); + } } @Override @@ -145,7 +156,7 @@ public class Clock extends TextView implements DemoMode { * add dummy characters around it to let us find it again after * formatting and change its size. */ - if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) { + if (mAmPmStyle != AM_PM_STYLE_NORMAL) { int a = -1; boolean quoted = false; for (int i = 0; i < format.length(); i++) { @@ -177,15 +188,15 @@ public class Clock extends TextView implements DemoMode { } String result = sdf.format(mCalendar.getTime()); - if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) { + if (mAmPmStyle != AM_PM_STYLE_NORMAL) { int magic1 = result.indexOf(MAGIC1); int magic2 = result.indexOf(MAGIC2); if (magic1 >= 0 && magic2 > magic1) { SpannableStringBuilder formatted = new SpannableStringBuilder(result); - if (AM_PM_STYLE == AM_PM_STYLE_GONE) { + if (mAmPmStyle == AM_PM_STYLE_GONE) { formatted.delete(magic1, magic2+1); } else { - if (AM_PM_STYLE == AM_PM_STYLE_SMALL) { + if (mAmPmStyle == AM_PM_STYLE_SMALL) { CharacterStyle style = new RelativeSizeSpan(0.7f); formatted.setSpan(style, magic1, magic2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java new file mode 100644 index 0000000..173af40 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java @@ -0,0 +1,215 @@ +/* + * 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.policy; + +import android.app.ActivityManagerNative; +import android.bluetooth.BluetoothAdapter; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.UserInfo; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.Shader; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; +import android.os.AsyncTask; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.ContactsContract; +import android.security.KeyChain; +import android.util.Log; +import android.util.Pair; + +import com.android.internal.view.RotationPolicy; +import com.android.systemui.R; + +import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; + +public final class UserInfoController { + + private static final String TAG = "UserInfoController"; + + private final Context mContext; + private final ArrayList<OnUserInfoChangedListener> mCallbacks = + new ArrayList<OnUserInfoChangedListener>(); + private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask; + + private boolean mUseDefaultAvatar; + private String mUserName; + private Drawable mUserDrawable; + + public UserInfoController(Context context) { + mContext = context; + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); + mContext.registerReceiver(mReceiver, filter); + + IntentFilter profileFilter = new IntentFilter(); + profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED); + profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); + mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter, + null, null); + } + + public void addListener(OnUserInfoChangedListener callback) { + mCallbacks.add(callback); + } + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (Intent.ACTION_USER_SWITCHED.equals(action)) { + reloadUserInfo(); + } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { + if (mUseDefaultAvatar) { + reloadUserInfo(); + } + } + } + }; + + private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) || + Intent.ACTION_USER_INFO_CHANGED.equals(action)) { + try { + final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id; + final int changedUser = + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); + if (changedUser == currentUser) { + reloadUserInfo(); + } + } catch (RemoteException e) { + Log.e(TAG, "Couldn't get current user id for profile change", e); + } + } + } + }; + + public void reloadUserInfo() { + if (mUserInfoTask != null) { + mUserInfoTask.cancel(false); + mUserInfoTask = null; + } + queryForUserInformation(); + } + + private Bitmap circularClip(Bitmap input) { + Bitmap output = Bitmap.createBitmap(input.getWidth(), + input.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + final Paint paint = new Paint(); + paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + paint.setAntiAlias(true); + canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, paint); + return output; + } + + private void queryForUserInformation() { + Context currentUserContext; + UserInfo userInfo; + try { + userInfo = ActivityManagerNative.getDefault().getCurrentUser(); + currentUserContext = mContext.createPackageContextAsUser("android", 0, + new UserHandle(userInfo.id)); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Couldn't create user context", e); + throw new RuntimeException(e); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't get user info", e); + throw new RuntimeException(e); + } + final int userId = userInfo.id; + final String userName = userInfo.name; + + final Context context = currentUserContext; + mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() { + @Override + protected Pair<String, Drawable> doInBackground(Void... params) { + final UserManager um = UserManager.get(mContext); + + // Fall back to the UserManager nickname if we can't read the name from the local + // profile below. + String name = userName; + Drawable avatar = null; + Bitmap rawAvatar = um.getUserIcon(userId); + if (rawAvatar != null) { + avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar)); + } else { + avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user); + mUseDefaultAvatar = true; + } + + // If it's a single-user device, get the profile name, since the nickname is not + // usually valid + if (um.getUsers().size() <= 1) { + // Try and read the display name from the local profile + final Cursor cursor = context.getContentResolver().query( + ContactsContract.Profile.CONTENT_URI, new String[] { + ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME}, + null, null, null); + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + name = cursor.getString(cursor.getColumnIndex( + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); + } + } finally { + cursor.close(); + } + } + } + return new Pair<String, Drawable>(name, avatar); + } + + @Override + protected void onPostExecute(Pair<String, Drawable> result) { + mUserName = result.first; + mUserDrawable = result.second; + mUserInfoTask = null; + notifyChanged(); + } + }; + mUserInfoTask.execute(); + } + + private void notifyChanged() { + for (OnUserInfoChangedListener listener : mCallbacks) { + listener.onUserInfoChanged(mUserName, mUserDrawable); + } + } + + public interface OnUserInfoChangedListener { + public void onUserInfoChanged(String name, Drawable picture); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index afd5068..5849afb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -80,9 +80,12 @@ public class NotificationStackScrollLayout extends ViewGroup private Paint mDebugPaint; private int mContentHeight; private int mCollapsedSize; + private int mBottomStackSlowDownHeight; private int mBottomStackPeekSize; private int mEmptyMarginBottom; private int mPaddingBetweenElements; + private int mPaddingBetweenElementsDimmed; + private int mPaddingBetweenElementsNormal; private int mTopPadding; /** @@ -153,7 +156,10 @@ public class NotificationStackScrollLayout extends ViewGroup if (DEBUG) { int y = mCollapsedSize; canvas.drawLine(0, y, getWidth(), y, mDebugPaint); - y = (int) (getLayoutHeight() - mBottomStackPeekSize - mCollapsedSize); + y = (int) (getLayoutHeight() - mBottomStackPeekSize + - mBottomStackSlowDownHeight); + canvas.drawLine(0, y, getWidth(), y, mDebugPaint); + y = (int) (getLayoutHeight() - mBottomStackPeekSize); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); y = (int) getLayoutHeight(); canvas.drawLine(0, y, getWidth(), y, mDebugPaint); @@ -183,9 +189,20 @@ public class NotificationStackScrollLayout extends ViewGroup .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount); mEmptyMarginBottom = context.getResources().getDimensionPixelSize( R.dimen.notification_stack_margin_bottom); - mPaddingBetweenElements = context.getResources() - .getDimensionPixelSize(R.dimen.notification_padding); mStackScrollAlgorithm = new StackScrollAlgorithm(context); + mPaddingBetweenElementsDimmed = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding_dimmed); + mPaddingBetweenElementsNormal = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding); + updatePadding(false); + } + + private void updatePadding(boolean dimmed) { + mPaddingBetweenElements = dimmed + ? mPaddingBetweenElementsDimmed + : mPaddingBetweenElementsNormal; + mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength(); + updateContentHeight(); } @Override @@ -739,15 +756,10 @@ public class NotificationStackScrollLayout extends ViewGroup if (firstChild != null) { int contentHeight = getContentHeight(); int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild); - - scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize); + scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize + + mBottomStackSlowDownHeight); if (scrollRange > 0) { View lastChild = getLastChildNotGone(); - if (isViewExpanded(lastChild)) { - // last child is expanded, so we have to ensure that it can exit the - // bottom stack - scrollRange += mCollapsedSize + mPaddingBetweenElements; - } // We want to at least be able collapse the first item and not ending in a weird // end state. scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize); @@ -1181,7 +1193,7 @@ public class NotificationStackScrollLayout extends ViewGroup public int getEmptyBottomMargin() { int emptyMargin = mMaxLayoutHeight - mContentHeight; if (needsHeightAdaption()) { - emptyMargin = emptyMargin - mCollapsedSize - mBottomStackPeekSize; + emptyMargin = emptyMargin - mBottomStackSlowDownHeight - mBottomStackPeekSize; } return Math.max(emptyMargin, 0); } @@ -1226,7 +1238,9 @@ public class NotificationStackScrollLayout extends ViewGroup * See {@link AmbientState#setDimmed}. */ public void setDimmed(boolean dimmed, boolean animate) { + mStackScrollAlgorithm.setDimmed(dimmed); mAmbientState.setDimmed(dimmed); + updatePadding(dimmed); if (animate) { mDimmedNeedsAnimation = true; mNeedsAnimation = true; @@ -1311,6 +1325,7 @@ public class NotificationStackScrollLayout extends ViewGroup // ANIMATION_TYPE_DIMMED new AnimationFilter() + .animateY() .animateScale() .animateDimmed() }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java index 38b544f..1c37c35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java @@ -38,27 +38,26 @@ public class PiecewiseLinearIndentationFunctor extends StackIndentationFunctor { * the actual visual distance below the top card but is a maximum, * achieved when the next card just starts transitioning into the stack and * the stack is full. - * If totalTransitionDistance is equal to this, we directly start at the peek, - * otherwise the first element transitions between 0 and - * totalTransitionDistance - peekSize. + * If distanceToPeekStart is 0, we directly start at the peek, otherwise the + * first element transitions between 0 and distanceToPeekStart. * Visualization: * --------------------------------------------------- --- * | | | - * | FIRST ITEM | | <- totalTransitionDistance + * | FIRST ITEM | | <- distanceToPeekStart * | | | - * |---------------------------------------------------| | --- - * |__________________SECOND ITEM______________________| | | <- peekSize - * |===================================================| _|_ _|_ + * |---------------------------------------------------| --- --- + * |__________________SECOND ITEM______________________| | <- peekSize + * |===================================================| _|_ * - * @param totalTransitionDistance The total transition distance an element has to go through + * @param distanceToPeekStart The distance to the start of the peak. * @param linearPart The interpolation factor between the linear and the quadratic amount taken. * This factor must be somewhere in [0 , 1] */ PiecewiseLinearIndentationFunctor(int maxItemsInStack, int peekSize, - int totalTransitionDistance, + int distanceToPeekStart, float linearPart) { - super(maxItemsInStack, peekSize, totalTransitionDistance); + super(maxItemsInStack, peekSize, distanceToPeekStart); mBaseValues = new ArrayList<Float>(maxItemsInStack+1); initBaseValues(); mLinearPart = linearPart; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java index f72947a..034eba6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java @@ -21,8 +21,8 @@ package com.android.systemui.statusbar.stack; */ public abstract class StackIndentationFunctor { - protected final int mTotalTransitionDistance; - protected final int mDistanceToPeekStart; + protected int mTotalTransitionDistance; + protected int mDistanceToPeekStart; protected int mMaxItemsInStack; protected int mPeekSize; protected boolean mStackStartsAtPeek; @@ -37,31 +37,41 @@ public abstract class StackIndentationFunctor { * the actual visual distance below the top card but is a maximum, * achieved when the next card just starts transitioning into the stack and * the stack is full. - * If totalTransitionDistance is equal to this, we directly start at the peek, - * otherwise the first element transitions between 0 and - * totalTransitionDistance - peekSize. + * If distanceToPeekStart is 0, we directly start at the peek, otherwise the + * first element transitions between 0 and distanceToPeekStart. * Visualization: * --------------------------------------------------- --- * | | | - * | FIRST ITEM | | <- totalTransitionDistance + * | FIRST ITEM | | <- distanceToPeekStart * | | | - * |---------------------------------------------------| | --- - * |__________________SECOND ITEM______________________| | | <- peekSize - * |===================================================| _|_ _|_ + * |---------------------------------------------------| --- --- + * |__________________SECOND ITEM______________________| | <- peekSize + * |===================================================| _|_ * - * @param totalTransitionDistance The total transition distance an element has to go through + * @param distanceToPeekStart The distance to the start of the peak. */ - StackIndentationFunctor(int maxItemsInStack, int peekSize, int totalTransitionDistance) { - mTotalTransitionDistance = totalTransitionDistance; - mDistanceToPeekStart = mTotalTransitionDistance - peekSize; + StackIndentationFunctor(int maxItemsInStack, int peekSize, int distanceToPeekStart) { + mDistanceToPeekStart = distanceToPeekStart; mStackStartsAtPeek = mDistanceToPeekStart == 0; mMaxItemsInStack = maxItemsInStack; mPeekSize = peekSize; + updateTotalTransitionDistance(); } + private void updateTotalTransitionDistance() { + mTotalTransitionDistance = mDistanceToPeekStart + mPeekSize; + } + public void setPeekSize(int mPeekSize) { this.mPeekSize = mPeekSize; + updateTotalTransitionDistance(); + } + + public void setDistanceToPeekStart(int distanceToPeekStart) { + mDistanceToPeekStart = distanceToPeekStart; + mStackStartsAtPeek = mDistanceToPeekStart == 0; + updateTotalTransitionDistance(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 5e4d496..bd9de82 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -65,13 +65,40 @@ public class StackScrollAlgorithm { private ExpandableView mFirstChildWhileExpanding; private boolean mExpandedOnStart; private int mTopStackTotalSize; + private int mPaddingBetweenElementsDimmed; + private int mPaddingBetweenElementsNormal; + private int mBottomStackSlowDownLength; public StackScrollAlgorithm(Context context) { initConstants(context); + updatePadding(false); + } + + private void updatePadding(boolean dimmed) { + mPaddingBetweenElements = dimmed + ? mPaddingBetweenElementsDimmed + : mPaddingBetweenElementsNormal; + mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements; + mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( + MAX_ITEMS_IN_TOP_STACK, + mTopStackPeekSize, + mTopStackTotalSize - mTopStackPeekSize, + 0.5f); + mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( + MAX_ITEMS_IN_BOTTOM_STACK, + mBottomStackPeekSize, + getBottomStackSlowDownLength(), + 0.5f); + } + + public int getBottomStackSlowDownLength() { + return mBottomStackSlowDownLength + mPaddingBetweenElements; } private void initConstants(Context context) { - mPaddingBetweenElements = context.getResources() + mPaddingBetweenElementsDimmed = context.getResources() + .getDimensionPixelSize(R.dimen.notification_padding_dimmed); + mPaddingBetweenElementsNormal = context.getResources() .getDimensionPixelSize(R.dimen.notification_padding); mCollapsedSize = context.getResources() .getDimensionPixelSize(R.dimen.notification_min_height); @@ -82,17 +109,8 @@ public class StackScrollAlgorithm { mZDistanceBetweenElements = context.getResources() .getDimensionPixelSize(R.dimen.z_distance_between_notifications); mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements; - mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements; - mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( - MAX_ITEMS_IN_TOP_STACK, - mTopStackPeekSize, - mTopStackTotalSize, - 0.5f); - mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor( - MAX_ITEMS_IN_BOTTOM_STACK, - mBottomStackPeekSize, - mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements, - 0.5f); + mBottomStackSlowDownLength = context.getResources() + .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length); } @@ -206,7 +224,7 @@ public class StackScrollAlgorithm { float bottomPeekStart = mInnerHeight - mBottomStackPeekSize; // The position where the bottom stack starts. - float bottomStackStart = bottomPeekStart - mCollapsedSize; + float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength; // The y coordinate of the current child. float currentYPosition = 0.0f; @@ -352,7 +370,7 @@ public class StackScrollAlgorithm { algorithmState.itemsInBottomStack += algorithmState.partialInBottom; childViewState.yTranslation = transitioningPositionStart + offset - childHeight - mPaddingBetweenElements; - + // We want at least to be at the end of the top stack when collapsing clampPositionToTopStackEnd(childViewState, childHeight); childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA; @@ -621,6 +639,10 @@ public class StackScrollAlgorithm { } } + public void setDimmed(boolean dimmed) { + updatePadding(dimmed); + } + class StackScrollAlgorithmState { /** |
