summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI/src
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI/src')
-rw-r--r--packages/SystemUI/src/com/android/systemui/DessertCaseView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/RecentsComponent.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/Recents.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java242
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java178
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java129
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsService.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java92
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java233
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java87
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java75
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java216
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java185
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java110
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java18
26 files changed, 1448 insertions, 522 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
index 4147155..14392b4 100644
--- a/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/DessertCaseView.java
@@ -507,7 +507,6 @@ public class DessertCaseView extends FrameLayout {
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
index 7c85712..9650435 100644
--- a/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
@@ -20,9 +20,14 @@ import android.view.Display;
import android.view.View;
public interface RecentsComponent {
+ public interface Callbacks {
+ public void onVisibilityChanged(boolean visible);
+ }
+
void showRecents(boolean triggeredFromAltTab, View statusBarView);
void hideRecents(boolean triggeredFromAltTab);
void toggleRecents(Display display, int layoutDirection, View statusBarView);
void preloadRecents();
void cancelPreloadingRecents();
+ void setCallback(Callbacks cb);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c006c88..ffd76a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -59,8 +59,6 @@ import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.MultiUserAvatarCache;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.keyguard.analytics.KeyguardAnalytics;
-import com.android.keyguard.analytics.Session;
import com.android.systemui.SystemUI;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -70,7 +68,6 @@ import com.android.systemui.statusbar.phone.StatusBarWindowManager;
import java.io.File;
import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
-import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter;
/**
@@ -117,7 +114,6 @@ import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapte
public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
final static boolean DEBUG = false;
- private static final boolean ENABLE_ANALYTICS = false;
private final static boolean DBG_WAKE = false;
private final static String TAG = "KeyguardViewMediator";
@@ -199,8 +195,6 @@ public class KeyguardViewMediator extends SystemUI {
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private KeyguardAnalytics mKeyguardAnalytics;
-
// these are protected by synchronized (this)
/**
@@ -469,22 +463,6 @@ public class KeyguardViewMediator extends SystemUI {
mViewMediatorCallback, mLockPatternUtils);
final ContentResolver cr = mContext.getContentResolver();
- if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() &&
- Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) {
- mKeyguardAnalytics = new KeyguardAnalytics(mContext, new SessionTypeAdapter() {
-
- @Override
- public int getSessionType() {
- return mLockPatternUtils.isSecure() && !mUpdateMonitor.getUserHasTrust(
- mLockPatternUtils.getCurrentUser())
- ? Session.TYPE_KEYGUARD_SECURE
- : Session.TYPE_KEYGUARD_INSECURE;
- }
- }, new File(mContext.getCacheDir(), "keyguard_analytics.bin"));
- } else {
- mKeyguardAnalytics = null;
- }
-
mScreenOn = mPM.isScreenOn();
mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
@@ -585,9 +563,6 @@ public class KeyguardViewMediator extends SystemUI {
} else {
doKeyguardLocked(null);
}
- if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
- mKeyguardAnalytics.getCallback().onScreenOff();
- }
}
KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
}
@@ -830,9 +805,6 @@ public class KeyguardViewMediator extends SystemUI {
updateActivityLockScreenState();
adjustStatusBarLocked();
}
- if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
- mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index 116d755..e03c01c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -273,6 +273,13 @@ public class Recents extends SystemUI implements RecentsComponent {
}
}
+ @Override
+ public void setCallback(Callbacks cb) {
+ if (mUseAlternateRecents) {
+ mAlternateRecents.setRecentsComponentCallback(cb);
+ }
+ }
+
/**
* Send broadcast only if BOOT_COMPLETED
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 2f6d58f..8861752 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -27,7 +27,6 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -35,16 +34,13 @@ import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
-import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.Surface;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import com.android.systemui.R;
+import com.android.systemui.RecentsComponent;
+import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -129,8 +125,10 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
final public static int MSG_TOGGLE_RECENTS = 6;
final public static int MSG_START_ENTER_ANIMATION = 7;
- final public static String EXTRA_ANIMATING_WITH_THUMBNAIL = "recents.animatingWithThumbnail";
- final public static String EXTRA_FROM_ALT_TAB = "recents.triggeredFromAltTab";
+ final public static String EXTRA_FROM_HOME = "recents.triggeredOverHome";
+ final public static String EXTRA_FROM_APP_THUMBNAIL = "recents.animatingWithThumbnail";
+ final public static String EXTRA_FROM_APP_FULL_SCREENSHOT = "recents.thumbnail";
+ final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "recents.triggeredFromAltTab";
final public static String KEY_CONFIGURATION_DATA = "recents.data.updateForConfiguration";
final public static String KEY_WINDOW_RECT = "recents.windowRect";
final public static String KEY_SYSTEM_INSETS = "recents.systemInsets";
@@ -138,7 +136,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
final public static String KEY_TWO_TASK_STACK_RECT = "recents.twoCountTaskRect";
final public static String KEY_MULTIPLE_TASK_STACK_RECT = "recents.multipleCountTaskRect";
-
final static int sMinToggleDelay = 425;
final static String sToggleRecentsAction = "com.android.systemui.recents.SHOW_RECENTS";
@@ -146,6 +143,9 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
final static String sRecentsActivity = "com.android.systemui.recents.RecentsActivity";
final static String sRecentsService = "com.android.systemui.recents.RecentsService";
+ static Bitmap sLastScreenshot;
+ static RecentsComponent.Callbacks sRecentsComponentCallbacks;
+
Context mContext;
SystemServicesProxy mSystemServicesProxy;
@@ -213,15 +213,19 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
if (Console.Enabled) {
Console.log(Constants.Log.App.RecentsComponent, "[RecentsComponent|hideRecents]");
}
+
if (mServiceIsBound && mBootCompleted) {
- // Notify recents to close it
- try {
- Bundle data = new Bundle();
- Message msg = Message.obtain(null, MSG_HIDE_RECENTS, triggeredFromAltTab ? 1 : 0, 0);
- msg.setData(data);
- mService.send(msg);
- } catch (RemoteException re) {
- re.printStackTrace();
+ if (isRecentsTopMost(null)) {
+ // Notify recents to close it
+ try {
+ Bundle data = new Bundle();
+ Message msg = Message.obtain(null, MSG_HIDE_RECENTS,
+ triggeredFromAltTab ? 1 : 0, 0);
+ msg.setData(data);
+ mService.send(msg);
+ } catch (RemoteException re) {
+ re.printStackTrace();
+ }
}
}
}
@@ -343,80 +347,6 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
}
- /** Converts from the device rotation to the degree */
- float getDegreesForRotation(int value) {
- switch (value) {
- case Surface.ROTATION_90:
- return 360f - 90f;
- case Surface.ROTATION_180:
- return 360f - 180f;
- case Surface.ROTATION_270:
- return 360f - 270f;
- }
- return 0f;
- }
-
- /** Takes a screenshot of the surface */
- Bitmap takeScreenshot(Display display) {
- DisplayMetrics dm = new DisplayMetrics();
- display.getRealMetrics(dm);
- float[] dims = {dm.widthPixels, dm.heightPixels};
- float degrees = getDegreesForRotation(display.getRotation());
- boolean requiresRotation = (degrees > 0);
- if (requiresRotation) {
- // Get the dimensions of the device in its native orientation
- Matrix m = new Matrix();
- m.preRotate(-degrees);
- m.mapPoints(dims);
- dims[0] = Math.abs(dims[0]);
- dims[1] = Math.abs(dims[1]);
- }
- return SurfaceControl.screenshot((int) dims[0], (int) dims[1]);
- }
-
- /** Creates the activity options for a thumbnail transition. */
- ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
- // Loading from thumbnail
- Bitmap thumbnail;
- Bitmap firstThumbnail = loadFirstTaskThumbnail();
- if (firstThumbnail != null) {
- // Create the thumbnail
- thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
- Bitmap.Config.ARGB_8888);
- int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
- Canvas c = new Canvas(thumbnail);
- c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
- new Rect(0, 0, taskRect.width(), taskRect.height()), null);
- c.setBitmap(null);
- // Recycle the old thumbnail
- firstThumbnail.recycle();
- } else {
- // Load the thumbnail from the screenshot if can't get one from the system
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Bitmap screenshot = takeScreenshot(display);
- if (screenshot != null) {
- Resources res = mContext.getResources();
- int size = Math.min(screenshot.getWidth(), screenshot.getHeight());
- int statusBarHeight = res.getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height);
- thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(thumbnail);
- c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight +
- size), new Rect(0, 0, taskRect.width(), taskRect.height()), null);
- c.setBitmap(null);
- // Recycle the temporary screenshot
- screenshot.recycle();
- } else {
- return null;
- }
- }
-
- return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail,
- taskRect.left, taskRect.top, this);
- }
-
/** Returns whether the recents is currently running */
boolean isRecentsTopMost(AtomicBoolean isHomeTopMost) {
SystemServicesProxy ssp = mSystemServicesProxy;
@@ -462,10 +392,12 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
mService.send(msg);
// Time this path
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "sendToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "sendToggleRecents");
+ }
} catch (RemoteException re) {
re.printStackTrace();
}
@@ -486,6 +418,68 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
}
+ /**
+ * Creates the activity options for a unknown state->recents transition.
+ */
+ ActivityOptions getUnknownTransitionActivityOptions() {
+ // Reset the last screenshot
+ consumeLastScreenshot();
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_unknown_enter,
+ R.anim.recents_from_unknown_exit, mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for a home->recents transition.
+ */
+ ActivityOptions getHomeTransitionActivityOptions() {
+ // Reset the last screenshot
+ consumeLastScreenshot();
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_launcher_enter,
+ R.anim.recents_from_launcher_exit, mHandler, this);
+ }
+
+ /**
+ * Creates the activity options for an app->recents transition. If this method sets the static
+ * screenshot, then we will use that for the transition.
+ */
+ ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) {
+ // Recycle the last screenshot
+ consumeLastScreenshot();
+
+ // Take the full screenshot
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ sLastScreenshot = mSystemServicesProxy.takeScreenshot();
+ if (sLastScreenshot != null) {
+ return ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.recents_from_app_enter,
+ R.anim.recents_from_app_exit, mHandler, this);
+ }
+ }
+
+ // If the screenshot fails, then load the first task thumbnail and use that
+ Bitmap firstThumbnail = loadFirstTaskThumbnail();
+ if (firstThumbnail != null) {
+ // Create the new thumbnail for the animation down
+ // XXX: We should find a way to optimize this so we don't need to create a new bitmap
+ Bitmap thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(),
+ Bitmap.Config.ARGB_8888);
+ int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight());
+ Canvas c = new Canvas(thumbnail);
+ c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size),
+ new Rect(0, 0, taskRect.width(), taskRect.height()), null);
+ c.setBitmap(null);
+ // Recycle the old thumbnail
+ firstThumbnail.recycle();
+ return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView,
+ thumbnail, taskRect.left, taskRect.top, this);
+ }
+
+ // If both the screenshot and thumbnail fails, then just fall back to the default transition
+ return getUnknownTransitionActivityOptions();
+ }
+
/** Starts the recents activity */
void startRecentsActivity(boolean isTopTaskHome) {
// If Recents is not the front-most activity and we should animate into it. If
@@ -503,7 +497,11 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
// Try starting with a thumbnail transition
ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect);
if (opts != null) {
- startAlternateRecentsActivity(opts, true);
+ if (sLastScreenshot != null) {
+ startAlternateRecentsActivity(opts, EXTRA_FROM_APP_FULL_SCREENSHOT);
+ } else {
+ startAlternateRecentsActivity(opts, EXTRA_FROM_APP_THUMBNAIL);
+ }
} else {
// Fall through below to the non-thumbnail transition
useThumbnailTransition = false;
@@ -512,25 +510,33 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
// If there is no thumbnail transition, then just use a generic transition
if (!useThumbnailTransition) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.recents_from_launcher_enter,
- R.anim.recents_from_launcher_exit, mHandler, this);
- startAlternateRecentsActivity(opts, false);
+ if (Constants.DebugFlags.App.EnableHomeTransition) {
+ ActivityOptions opts = getHomeTransitionActivityOptions();
+ startAlternateRecentsActivity(opts, EXTRA_FROM_HOME);
+ } else {
+ ActivityOptions opts = getUnknownTransitionActivityOptions();
+ startAlternateRecentsActivity(opts, null);
+ }
}
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "startRecentsActivity");
+ }
mLastToggleTime = System.currentTimeMillis();
}
/** Starts the recents activity */
- void startAlternateRecentsActivity(ActivityOptions opts, boolean animatingWithThumbnail) {
+ void startAlternateRecentsActivity(ActivityOptions opts, String extraFlag) {
Intent intent = new Intent(sToggleRecentsAction);
intent.setClassName(sRecentsPackage, sRecentsActivity);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(EXTRA_ANIMATING_WITH_THUMBNAIL, animatingWithThumbnail);
- intent.putExtra(EXTRA_FROM_ALT_TAB, mTriggeredFromAltTab);
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+ if (extraFlag != null) {
+ intent.putExtra(extraFlag, true);
+ }
+ intent.putExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, mTriggeredFromAltTab);
if (opts != null) {
mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle(
UserHandle.USER_CURRENT));
@@ -539,6 +545,30 @@ public class AlternateRecentsComponent implements ActivityOptions.OnAnimationSta
}
}
+ /** Returns the last screenshot taken, this will be called by the RecentsActivity. */
+ public static Bitmap getLastScreenshot() {
+ return sLastScreenshot;
+ }
+
+ /** Recycles the last screenshot taken, this will be called by the RecentsActivity. */
+ public static void consumeLastScreenshot() {
+ if (sLastScreenshot != null) {
+ sLastScreenshot.recycle();
+ sLastScreenshot = null;
+ }
+ }
+
+ /** Sets the RecentsComponent callbacks. */
+ public void setRecentsComponentCallback(RecentsComponent.Callbacks cb) {
+ sRecentsComponentCallbacks = cb;
+ }
+
+ /** Notifies the callbacks that the visibility of Recents has changed. */
+ public static void notifyVisibilityChanged(boolean visible) {
+ if (sRecentsComponentCallbacks != null) {
+ sRecentsComponentCallbacks.onVisibilityChanged(visible);
+ }
+ }
/**** OnAnimationStartedListener Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 147ff62..cd4d206 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -25,6 +25,10 @@ public class Constants {
public static final boolean Verbose = false;
public static class App {
+ // Enables the home->Recents transition
+ public static final boolean EnableHomeTransition = false;
+ // Enables the screenshot app->Recents transition
+ public static final boolean EnableScreenshotAppTransition = false;
// Enables the filtering of tasks according to their grouping
public static final boolean EnableTaskFiltering = false;
// Enables clipping of tasks against each other
@@ -52,8 +56,11 @@ public class Constants {
public static class App {
public static final String TimeRecentsStartupKey = "startup";
public static final String TimeRecentsLaunchKey = "launchTask";
- public static final boolean TimeRecentsStartup = false;
- public static final boolean TimeRecentsLaunchTask = false;
+ public static final String TimeRecentsScreenshotTransitionKey = "screenshot";
+ public static final boolean TimeRecentsStartup = true;
+ public static final boolean TimeRecentsLaunchTask = true;
+ public static final boolean TimeRecentsScreenshotTransition = true;
+
public static final boolean RecentsComponent = false;
public static final boolean TaskDataLoader = false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 96344d5..f9c219b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -22,11 +22,9 @@ import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Pair;
import android.view.Gravity;
@@ -34,17 +32,17 @@ import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.widget.FrameLayout;
import com.android.systemui.R;
import com.android.systemui.recents.model.SpaceNode;
import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.FullScreenTransitionView;
import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.recents.views.ViewAnimation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.Set;
/** Our special app widget host */
class RecentsAppWidgetHost extends AppWidgetHost {
@@ -68,11 +66,16 @@ class RecentsAppWidgetHost extends AppWidgetHost {
/* Activity */
public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+ RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
+ FullScreenTransitionView.FullScreenTransitionViewCallbacks {
+
FrameLayout mContainerView;
RecentsView mRecentsView;
View mEmptyView;
View mNavBarScrimView;
+ FullScreenTransitionView mFullScreenshotView;
+
+ RecentsConfiguration mConfig;
AppWidgetHost mAppWidgetHost;
AppWidgetProviderInfo mSearchAppWidgetInfo;
@@ -108,8 +111,15 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Dismiss recents, launching the focused task
dismissRecentsIfVisible();
} else {
- // Otherwise, just finish the activity without launching any other activities
- finish();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // Otherwise, just finish the activity without launching any other activities
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
}
} else if (action.equals(RecentsService.ACTION_TOGGLE_RECENTS_ACTIVITY)) {
// Try and unfilter and filtered stacks
@@ -119,7 +129,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
} else if (action.equals(RecentsService.ACTION_START_ENTER_ANIMATION)) {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation();
+ mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ // Call our callback
+ onEnterAnimationTriggered();
}
}
};
@@ -128,18 +140,37 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // Mark recents as no longer visible
+ AlternateRecentsComponent.notifyVisibilityChanged(false);
+ // Finish without an animations
finish();
}
};
+ // A runnable to finish the Recents activity
+ Runnable mFinishRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Mark recents as no longer visible
+ AlternateRecentsComponent.notifyVisibilityChanged(false);
+ // Finish with an animations
+ finish();
+ overridePendingTransition(R.anim.recents_to_launcher_enter,
+ R.anim.recents_to_launcher_exit);
+ }
+ };
+
/** Updates the set of recent tasks */
void updateRecentsTasks(Intent launchIntent) {
// Update the configuration based on the launch intent
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- config.launchedWithThumbnailAnimation = launchIntent.getBooleanExtra(
- AlternateRecentsComponent.EXTRA_ANIMATING_WITH_THUMBNAIL, false);
- config.launchedFromAltTab = launchIntent.getBooleanExtra(
- AlternateRecentsComponent.EXTRA_FROM_ALT_TAB, false);
+ mConfig.launchedFromHome = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_HOME, false);
+ mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false);
+ mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false);
+ mConfig.launchedWithAltTab = launchIntent.getBooleanExtra(
+ AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount);
@@ -165,7 +196,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/** Attempts to allocate and bind the search bar app widget */
void bindSearchBarAppWidget() {
if (Constants.DebugFlags.App.EnableSearchLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
// Reset the host view and widget info
@@ -173,7 +203,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mSearchAppWidgetInfo = null;
// Try and load the app widget id from the settings
- int appWidgetId = config.searchBarAppWidgetId;
+ int appWidgetId = mConfig.searchBarAppWidgetId;
if (appWidgetId >= 0) {
mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId);
if (mSearchAppWidgetInfo == null) {
@@ -203,7 +233,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
// Save the app widget id into the settings
- config.updateSearchBarAppWidgetId(this, widgetInfo.first);
+ mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first);
mSearchAppWidgetInfo = widgetInfo.second;
}
}
@@ -213,8 +243,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/** Creates the search bar app widget view */
void addSearchBarAppWidgetView() {
if (Constants.DebugFlags.App.EnableSearchLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- int appWidgetId = config.searchBarAppWidgetId;
+ int appWidgetId = mConfig.searchBarAppWidgetId;
if (appWidgetId >= 0) {
if (Console.Enabled) {
Console.log(Constants.Log.App.SystemUIHandshake,
@@ -240,9 +269,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
/** Dismisses recents if we are already visible and the intent is to toggle the recents view */
boolean dismissRecentsIfVisible() {
if (mVisible) {
- if (!mRecentsView.launchFocusedTask()) {
- if (!mRecentsView.launchFirstTask()) {
- finish();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // If we have a focused task, then launch that task
+ if (!mRecentsView.launchFocusedTask()) {
+ // If there are any tasks, then launch the first task
+ if (!mRecentsView.launchFirstTask()) {
+ // We really shouldn't hit this, but if we do, just animate out (aka. finish)
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
}
}
return true;
@@ -264,7 +303,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Initialize the loader and the configuration
RecentsTaskLoader.initialize(this);
- RecentsConfiguration.reinitialize(this);
+ mConfig = RecentsConfiguration.reinitialize(this);
// Initialize the widget host (the host id is static and does not change)
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
@@ -286,16 +325,29 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView = new FullScreenTransitionView(this, this);
+ mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ }
mContainerView = new FrameLayout(this);
mContainerView.addView(mRecentsView);
mContainerView.addView(mEmptyView);
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mContainerView.addView(mFullScreenshotView);
+ }
mContainerView.addView(mNavBarScrimView);
setContentView(mContainerView);
// Update the recent tasks
updateRecentsTasks(getIntent());
+ // Prepare the screenshot transition if necessary
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
+ }
+
// Bind the search app widget when we first start up
bindSearchBarAppWidget();
// Add the search bar layout
@@ -319,7 +371,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
void onConfigurationChange() {
// Try and start the enter animation (or restart it on configuration changed)
- mRecentsView.startOnEnterAnimation();
+ mRecentsView.startOnEnterAnimation(new ViewAnimation.TaskViewEnterContext(mFullScreenshotView));
+ // Call our callback
+ onEnterAnimationTriggered();
}
@Override
@@ -338,11 +392,16 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Initialize the loader and the configuration
RecentsTaskLoader.initialize(this);
- RecentsConfiguration.reinitialize(this);
+ mConfig = RecentsConfiguration.reinitialize(this);
// Update the recent tasks
updateRecentsTasks(intent);
+ // Prepare the screenshot transition if necessary
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot());
+ }
+
// Don't attempt to rebind the search bar widget, but just add the search bar layout
addSearchBarAppWidgetView();
}
@@ -356,8 +415,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
super.onStart();
// Start listening for widget package changes if there is one bound
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.searchBarAppWidgetId >= 0) {
+ if (mConfig.searchBarAppWidgetId >= 0) {
mAppWidgetHost.startListening();
}
@@ -431,8 +489,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
super.onStop();
// Stop listening for widget package changes if there was one bound
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.searchBarAppWidgetId >= 0) {
+ if (mConfig.searchBarAppWidgetId >= 0) {
mAppWidgetHost.stopListening();
}
@@ -471,54 +528,85 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
public void onBackPressed() {
- // Unfilter any stacks
- if (!mRecentsView.unfilterFilteredStacks()) {
- if (!mRecentsView.launchFirstTask()) {
- super.onBackPressed();
+ // If we are mid-animation into Recents, then reverse it and finish
+ if (mFullScreenshotView == null ||
+ !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
+ // If we are currently filtering in any stacks, unfilter them first
+ if (!mRecentsView.unfilterFilteredStacks()) {
+ if (mConfig.launchedFromHome) {
+ // Just start the animation out of recents
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ } else {
+ // Otherwise, try and launch the first task
+ if (!mRecentsView.launchFirstTask()) {
+ // If there are no tasks, then just finish recents
+ ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
+ null, mFinishRunnable, null);
+ mRecentsView.startOnExitAnimation(
+ new ViewAnimation.TaskViewExitContext(exitTrigger));
+ }
+ }
}
}
}
- @Override
public void onEnterAnimationTriggered() {
// Fade in the scrim
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.hasNavBarScrim()) {
+ if (mConfig.hasNavBarScrim()) {
mNavBarScrimView.setVisibility(View.VISIBLE);
mNavBarScrimView.setAlpha(0f);
mNavBarScrimView.animate().alpha(1f)
- .setStartDelay(config.taskBarEnterAnimDelay)
- .setDuration(config.navBarScrimEnterDuration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setStartDelay(mConfig.taskBarEnterAnimDelay)
+ .setDuration(mConfig.navBarScrimEnterDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
}
}
@Override
+ public void onEnterAnimationComplete(boolean canceled) {
+ if (!canceled) {
+ // Reset the full screenshot transition view
+ if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
+ mFullScreenshotView.reset();
+ }
+
+ // XXX: We should clean up the screenshot in this case as well, but it needs to happen
+ // after to animate up
+ }
+ // Recycle the full screen screenshot
+ AlternateRecentsComponent.consumeLastScreenshot();
+ }
+
+ @Override
public void onTaskLaunching(boolean isTaskInStackBounds) {
mTaskLaunched = true;
// Fade out the scrim
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (!isTaskInStackBounds && config.hasNavBarScrim()) {
+ if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
mNavBarScrimView.animate().alpha(0f)
.setStartDelay(0)
- .setDuration(config.taskBarExitAnimDuration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
}
+
+ // Mark recents as no longer visible
+ AlternateRecentsComponent.notifyVisibilityChanged(false);
}
@Override
public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
- if (appWidgetId > -1 && appWidgetId == config.searchBarAppWidgetId) {
+ if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
// The search provider may have changed, so just delete the old widget and bind it again
ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
- config.updateSearchBarAppWidgetId(this, -1);
+ mConfig.updateSearchBarAppWidgetId(this, -1);
// Load the widget again
bindSearchBarAppWidget();
addSearchBarAppWidgetView();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 0cf6ee6..a0c5253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -37,27 +37,39 @@ public class RecentsConfiguration {
DisplayMetrics mDisplayMetrics;
- public Rect systemInsets = new Rect();
- public Rect displayRect = new Rect();
-
- boolean isLandscape;
- boolean transposeRecentsLayoutWithOrientation;
- int searchBarAppWidgetId = -1;
-
+ /** Animations */
public float animationPxMovementPerSecond;
+ /** Interpolators */
public Interpolator fastOutSlowInInterpolator;
public Interpolator fastOutLinearInInterpolator;
public Interpolator linearOutSlowInInterpolator;
+ public Interpolator quintOutInterpolator;
+ /** Filtering */
public int filteringCurrentViewsMinAnimDuration;
public int filteringNewViewsMinAnimDuration;
- public int taskStackScrollDismissInfoPaneDistance;
+ /** Insets */
+ public Rect systemInsets = new Rect();
+ public Rect displayRect = new Rect();
+
+ /** Layout */
+ boolean isLandscape;
+ boolean transposeRecentsLayoutWithOrientation;
+
+ /** Search bar */
+ int searchBarAppWidgetId = -1;
+ public int searchBarSpaceHeightPx;
+
+ /** Task stack */
public int taskStackMaxDim;
- public float taskStackWidthPaddingPct;
public int taskStackTopPaddingPx;
+ public float taskStackWidthPaddingPct;
+ /** Task view animation and styles */
+ public int taskViewEnterFromHomeDuration;
+ public int taskViewEnterFromHomeDelay;
public int taskViewRemoveAnimDuration;
public int taskViewRemoveAnimTranslationXPx;
public int taskViewTranslationZMinPx;
@@ -66,23 +78,28 @@ public class RecentsConfiguration {
public int taskViewRoundedCornerRadiusPx;
public int taskViewHighlightPx;
- public int searchBarSpaceHeightPx;
-
+ /** Task bar colors */
public int taskBarViewDefaultBackgroundColor;
public int taskBarViewDefaultTextColor;
public int taskBarViewLightTextColor;
public int taskBarViewDarkTextColor;
public int taskBarViewHighlightColor;
+ /** Task bar animations */
public int taskBarEnterAnimDuration;
public int taskBarEnterAnimDelay;
public int taskBarExitAnimDuration;
+ /** Nav bar scrim */
public int navBarScrimEnterDuration;
- public boolean launchedFromAltTab;
- public boolean launchedWithThumbnailAnimation;
+ /** Launch states */
+ public boolean launchedWithAltTab;
+ public boolean launchedFromAppWithThumbnail;
+ public boolean launchedFromAppWithScreenshot;
+ public boolean launchedFromHome;
+ /** Dev options */
public boolean developerOptionsEnabled;
/** Private constructor */
@@ -108,33 +125,54 @@ public class RecentsConfiguration {
DisplayMetrics dm = res.getDisplayMetrics();
mDisplayMetrics = dm;
- isLandscape = res.getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE;
- transposeRecentsLayoutWithOrientation =
- res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
- if (Console.Enabled) {
- Console.log(Constants.Log.UI.MeasureAndLayout,
- "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
- Console.AnsiGreen);
- }
-
- displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
+ // Animations
animationPxMovementPerSecond =
res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
+
+ // Interpolators
+ fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+ fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_linear_in);
+ linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.linear_out_slow_in);
+ quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.decelerate_quint);
+
+ // Filtering
filteringCurrentViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
filteringNewViewsMinAnimDuration =
res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
- taskStackScrollDismissInfoPaneDistance = res.getDimensionPixelSize(
- R.dimen.recents_task_stack_scroll_dismiss_info_pane_distance);
- taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
+ // Insets
+ displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
+
+ // Layout
+ isLandscape = res.getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ transposeRecentsLayoutWithOrientation =
+ res.getBoolean(R.bool.recents_transpose_layout_with_orientation);
+ // Search bar
+ searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
+
+ // Update the search widget id
+ SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
+ searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
+
+ // Task stack
TypedValue widthPaddingPctValue = new TypedValue();
res.getValue(R.dimen.recents_stack_width_padding_percentage, widthPaddingPctValue, true);
taskStackWidthPaddingPct = widthPaddingPctValue.getFloat();
+ taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim);
taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding);
+ // Task view animation and styles
+ taskViewEnterFromHomeDuration =
+ res.getInteger(R.integer.recents_animate_task_enter_from_home_duration);
+ taskViewEnterFromHomeDelay =
+ res.getInteger(R.integer.recents_animate_task_enter_from_home_delay);
taskViewRemoveAnimDuration =
res.getInteger(R.integer.recents_animate_task_view_remove_duration);
taskViewRemoveAnimTranslationXPx =
@@ -148,8 +186,7 @@ public class RecentsConfiguration {
taskViewShadowOutlineBottomInsetPx =
res.getDimensionPixelSize(R.dimen.recents_task_view_shadow_outline_bottom_inset);
- searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
+ // Task bar colors
taskBarViewDefaultBackgroundColor =
res.getColor(R.color.recents_task_bar_default_background_color);
taskBarViewDefaultTextColor =
@@ -161,6 +198,7 @@ public class RecentsConfiguration {
taskBarViewHighlightColor =
res.getColor(R.color.recents_task_bar_highlight_color);
+ // Task bar animations
taskBarEnterAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_enter_duration);
taskBarEnterAnimDelay =
@@ -168,24 +206,20 @@ public class RecentsConfiguration {
taskBarExitAnimDuration =
res.getInteger(R.integer.recents_animate_task_bar_exit_duration);
+ // Nav bar scrim
navBarScrimEnterDuration =
res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
- fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_slow_in);
- fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.fast_out_linear_in);
- linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
- com.android.internal.R.interpolator.linear_out_slow_in);
-
// Check if the developer options are enabled
ContentResolver cr = context.getContentResolver();
developerOptionsEnabled = Settings.Global.getInt(cr,
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
- // Update the search widget id
- SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
- searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.MeasureAndLayout,
+ "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
+ Console.AnsiGreen);
+ }
}
/** Updates the system insets */
@@ -204,8 +238,10 @@ public class RecentsConfiguration {
/** Called when the configuration has changed, and we want to reset any configuration specific
* members. */
public void updateOnConfigurationChange() {
- launchedFromAltTab = false;
- launchedWithThumbnailAnimation = false;
+ launchedWithAltTab = false;
+ launchedFromAppWithThumbnail = false;
+ launchedFromAppWithScreenshot = false;
+ launchedFromHome = false;
}
/** Returns whether the search bar app widget exists. */
@@ -257,15 +293,4 @@ public class RecentsConfiguration {
searchBarSpaceBounds.set(0, 0, width, searchBarSpaceHeightPx);
}
}
-
- /** Converts from DPs to PXs */
- public int pxFromDp(float size) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- size, mDisplayMetrics));
- }
- /** Converts from SPs to PXs */
- public int pxFromSp(float size) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- size, mDisplayMetrics));
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
index 113efe3..e554af7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsService.java
@@ -134,10 +134,12 @@ class SystemUIMessageHandler extends Handler {
context.sendBroadcast(intent);
// Time this path
- Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
- Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsStartup,
+ Constants.Log.App.TimeRecentsStartupKey, "receivedToggleRecents");
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "receivedToggleRecents");
+ }
} else if (msg.what == AlternateRecentsComponent.MSG_START_ENTER_ANIMATION) {
// Send a broadcast to start the enter animation
Intent intent = new Intent(RecentsService.ACTION_START_ENTER_ANIMATION);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index 4685186..dbcdb94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -478,7 +478,7 @@ public class RecentsTaskLoader {
// Load the thumbnail (if possible and not the foremost task, from the cache)
if (!isForemostTask) {
task.thumbnail = mThumbnailCache.get(task.key);
- if (task.thumbnail != null) {
+ if (task.thumbnail != null && !tasksToForceLoad.contains(task)) {
// Even though we get things from the cache, we should update them if
// they've changed in the bg
tasksToForceLoad.add(task);
@@ -489,6 +489,7 @@ public class RecentsTaskLoader {
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|loadingTaskThumbnail]");
}
+
task.thumbnail = ssp.getTaskThumbnail(task.key.id);
if (task.thumbnail != null) {
task.thumbnail.setHasAlpha(false);
@@ -512,20 +513,6 @@ public class RecentsTaskLoader {
"" + (System.currentTimeMillis() - t1) + "ms");
}
- /*
- // Get all the stacks
- t1 = System.currentTimeMillis();
- List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos();
- Console.log(Constants.Log.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms");
- Console.log(Constants.Log.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size());
- for (ActivityManager.StackInfo s : stackInfos) {
- Console.log(Constants.Log.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString());
- if (stacks.containsKey(s.stackId)) {
- stacks.get(s.stackId).setRect(s.bounds);
- }
- }
- */
-
// Start the task loader
mLoader.start(context);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
new file mode 100644
index 0000000..2f89e6d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
@@ -0,0 +1,92 @@
+/*
+ * 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.recents;
+
+import android.content.Context;
+
+/**
+ * A ref counted trigger that does some logic when the count is first incremented, or last
+ * decremented. Not thread safe as it's not currently needed.
+ */
+public class ReferenceCountedTrigger {
+
+ Context mContext;
+ int mCount;
+ Runnable mFirstIncRunnable;
+ Runnable mLastDecRunnable;
+ Runnable mErrorRunnable;
+
+ // Convenience runnables
+ Runnable mIncrementRunnable = new Runnable() {
+ @Override
+ public void run() {
+ increment();
+ }
+ };
+ Runnable mDecrementRunnable = new Runnable() {
+ @Override
+ public void run() {
+ decrement();
+ }
+ };
+
+ public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
+ Runnable lastDecRunnable, Runnable errorRunanable) {
+ mContext = context;
+ mFirstIncRunnable = firstIncRunnable;
+ mLastDecRunnable = lastDecRunnable;
+ mErrorRunnable = errorRunanable;
+ }
+
+ /** Increments the ref count */
+ public void increment() {
+ if (mCount == 0 && mFirstIncRunnable != null) {
+ mFirstIncRunnable.run();
+ }
+ mCount++;
+ }
+
+ /** Convenience method to increment this trigger as a runnable */
+ public Runnable incrementAsRunnable() {
+ return mIncrementRunnable;
+ }
+
+ /** Decrements the ref count */
+ public void decrement() {
+ mCount--;
+ if (mCount == 0 && mLastDecRunnable != null) {
+ mLastDecRunnable.run();
+ } else if (mCount < 0) {
+ if (mErrorRunnable != null) {
+ mErrorRunnable.run();
+ } else {
+ new Throwable("Invalid ref count").printStackTrace();
+ Console.logError(mContext, "Invalid ref count");
+ }
+ }
+ }
+
+ /** Convenience method to decrement this trigger as a runnable */
+ public Runnable decrementAsRunnable() {
+ return mDecrementRunnable;
+ }
+
+ /** Returns the current ref count */
+ public int getCount() {
+ return mCount;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 7a3ffb8..f532aa0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,13 +30,20 @@ import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Matrix;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.DisplayMetrics;
import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
import java.util.ArrayList;
import java.util.Iterator;
@@ -54,6 +61,8 @@ public class SystemServicesProxy {
IPackageManager mIpm;
UserManager mUm;
SearchManager mSm;
+ WindowManager mWm;
+ Display mDisplay;
String mRecentsPackage;
ComponentName mAssistComponent;
@@ -67,6 +76,8 @@ public class SystemServicesProxy {
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mIpm = AppGlobals.getPackageManager();
mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+ mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mDisplay = mWm.getDefaultDisplay();
mRecentsPackage = context.getPackageName();
// Resolve the assist intent
@@ -325,4 +336,13 @@ public class SystemServicesProxy {
// Delete the app widget
host.deleteAppWidgetId(appWidgetId);
}
+
+ /**
+ * Takes a screenshot of the current surface.
+ */
+ public Bitmap takeScreenshot() {
+ DisplayInfo di = new DisplayInfo();
+ mDisplay.getDisplayInfo(di);
+ return SurfaceControl.screenshot(di.getNaturalWidth(), di.getNaturalHeight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
new file mode 100644
index 0000000..ad2fa8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -0,0 +1,233 @@
+/*
+ * 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.recents.views;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsConfiguration;
+
+
+/**
+ * The full screen transition view that gets animated down from the full screen into a task
+ * thumbnail view.
+ */
+public class FullScreenTransitionView extends FrameLayout {
+
+ /** The FullScreenTransitionView callbacks */
+ public interface FullScreenTransitionViewCallbacks {
+ void onEnterAnimationComplete(boolean canceled);
+ }
+
+ RecentsConfiguration mConfig;
+
+ FullScreenTransitionViewCallbacks mCb;
+
+ ImageView mScreenshotView;
+
+ Rect mClipRect = new Rect();
+
+ boolean mIsAnimating;
+ AnimatorSet mEnterAnimation;
+
+ public FullScreenTransitionView(Context context, FullScreenTransitionViewCallbacks cb) {
+ super(context);
+ mConfig = RecentsConfiguration.getInstance();
+ mCb = cb;
+ mScreenshotView = new ImageView(context);
+ mScreenshotView.setScaleType(ImageView.ScaleType.FIT_XY);
+ mScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ addView(mScreenshotView);
+ setClipTop(getClipTop());
+ setClipBottom(getClipBottom());
+ setWillNotDraw(false);
+ }
+
+ /** Sets the top clip */
+ public void setClipTop(int clip) {
+ mClipRect.top = clip;
+ postInvalidateOnAnimation();
+ }
+
+ /** Gets the top clip */
+ public int getClipTop() {
+ return mClipRect.top;
+ }
+
+ /** Sets the bottom clip */
+ public void setClipBottom(int clip) {
+ mClipRect.bottom = clip;
+ postInvalidateOnAnimation();
+ }
+
+ /** Gets the top clip */
+ public int getClipBottom() {
+ return mClipRect.bottom;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mClipRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int restoreCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(mClipRect);
+ super.draw(canvas);
+ canvas.restoreToCount(restoreCount);
+ }
+
+ /** Prepares the screenshot view for the transition into Recents */
+ public void prepareAnimateOnEnterRecents(Bitmap screenshot) {
+ if (!mConfig.launchedFromAppWithScreenshot) return;
+
+ if (Console.Enabled) {
+ Console.logStartTracingTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey);
+ }
+
+ setClipTop(0);
+ setClipBottom(getMeasuredHeight());
+ setTranslationY(0f);
+ setScaleX(1f);
+ setScaleY(1f);
+ setVisibility(mConfig.launchedFromAppWithScreenshot ? View.VISIBLE : View.INVISIBLE);
+ if (screenshot != null) {
+ mScreenshotView.setImageBitmap(screenshot);
+ } else {
+ mScreenshotView.setImageDrawable(null);
+ }
+ }
+
+ /** Resets the transition view */
+ public void reset() {
+ setVisibility(View.INVISIBLE);
+ mScreenshotView.setImageDrawable(null);
+ }
+
+ /** Animates this view as it enters recents */
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx,
+ final Runnable postAnimRunnable) {
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Starting");
+ }
+
+ // Cancel the current animation
+ if (mEnterAnimation != null) {
+ mEnterAnimation.removeAllListeners();
+ mEnterAnimation.cancel();
+ }
+
+ // Calculate the bottom clip
+ float scale = (float) ctx.taskRect.width() / getMeasuredWidth();
+ int translationY = -mConfig.systemInsets.top + ctx.stackRectSansPeek.top +
+ ctx.transform.translationY;
+ int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
+
+ // Enable the HW Layers on the screenshot view
+ mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ // Compose the animation
+ mEnterAnimation = new AnimatorSet();
+ mEnterAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Notify any callbacks
+ mCb.onEnterAnimationComplete(false);
+ // Run the given post-anim runnable
+ postAnimRunnable.run();
+ // Mark that we are no longer animating
+ mIsAnimating = false;
+ // Disable the HW Layers on this view
+ setLayerType(View.LAYER_TYPE_NONE, null);
+
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
+ Constants.Log.App.TimeRecentsScreenshotTransitionKey, "Completed");
+ }
+ }
+ });
+ mEnterAnimation.setStartDelay(0);
+ mEnterAnimation.setDuration(475);
+ mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mEnterAnimation.playTogether(
+ ObjectAnimator.ofInt(this, "clipTop", mConfig.systemInsets.top),
+ ObjectAnimator.ofInt(this, "clipBottom", clipBottom),
+ ObjectAnimator.ofFloat(this, "translationY", translationY),
+ ObjectAnimator.ofFloat(this, "scaleX", scale),
+ ObjectAnimator.ofFloat(this, "scaleY", scale)
+ );
+ mEnterAnimation.start();
+
+ mIsAnimating = true;
+ }
+
+ /** Animates this view back out of Recents if we were in the process of animating in. */
+ public boolean cancelAnimateOnEnterRecents(final Runnable postAnimRunnable) {
+ if (mIsAnimating) {
+ // Cancel the current animation
+ if (mEnterAnimation != null) {
+ mEnterAnimation.removeAllListeners();
+ mEnterAnimation.cancel();
+ }
+
+ // Compose the animation
+ mEnterAnimation = new AnimatorSet();
+ mEnterAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Notify any callbacks
+ mCb.onEnterAnimationComplete(true);
+ // Run the given post-anim runnable
+ postAnimRunnable.run();
+ // Mark that we are no longer animating
+ mIsAnimating = false;
+ // Disable the HW Layers on the screenshot view
+ mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
+ mEnterAnimation.setDuration(475);
+ mEnterAnimation.setInterpolator(mConfig.fastOutSlowInInterpolator);
+ mEnterAnimation.playTogether(
+ ObjectAnimator.ofInt(this, "clipTop", 0),
+ ObjectAnimator.ofInt(this, "clipBottom", getMeasuredHeight()),
+ ObjectAnimator.ofFloat(this, "translationY", 0f),
+ ObjectAnimator.ofFloat(this, "scaleX", 1f),
+ ObjectAnimator.ofFloat(this, "scaleY", 1f)
+ );
+ mEnterAnimation.start();
+
+ return true;
+ }
+ return false;
+ }
+}
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 db398b1..a2c250c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -55,9 +55,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
/** The RecentsView callbacks */
public interface RecentsViewCallbacks {
public void onTaskLaunching(boolean isTaskInStackBounds);
- public void onEnterAnimationTriggered();
}
+ RecentsConfiguration mConfig;
+ LayoutInflater mInflater;
+
// The space partitioning root of this container
SpaceNode mBSP;
// Whether there are any tasks
@@ -67,10 +69,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// Recents view callbacks
RecentsViewCallbacks mCb;
- LayoutInflater mInflater;
-
public RecentsView(Context context) {
super(context);
+ mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
setWillNotDraw(false);
}
@@ -160,18 +161,37 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
/** Requests all task stacks to start their enter-recents animation */
- public void startOnEnterAnimation() {
- // Notify callbacks that we are starting the enter animation
- mCb.onEnterAnimationTriggered();
-
+ public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child instanceof TaskStackView) {
TaskStackView stackView = (TaskStackView) child;
- stackView.startOnEnterAnimation();
+ stackView.startOnEnterAnimation(ctx);
+ }
+ }
+ }
+
+ /** Requests all task stacks to start their exit-recents animation */
+ public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.increment();
+
+ if (Constants.DebugFlags.App.EnableHomeTransition) {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof TaskStackView) {
+ TaskStackView stackView = (TaskStackView) child;
+ stackView.startOnExitAnimation(ctx);
+ }
}
}
+
+ // Handle the case when there are no views by incrementing and decrementing after all
+ // animations are started.
+ ctx.postAnimationTrigger.decrement();
}
/** Adds the search bar */
@@ -215,10 +235,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
// Get the search bar bounds and measure the search bar layout
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- config.getSearchBarBounds(width, height - config.systemInsets.top, searchBarSpaceBounds);
+ mConfig.getSearchBarBounds(width, height - mConfig.systemInsets.top, searchBarSpaceBounds);
mSearchBar.measure(
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.height(), MeasureSpec.EXACTLY));
@@ -229,9 +248,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// In addition, we give it the full height, not including the top inset or search bar space,
// since we want the tasks to render under the navigation buttons in portrait.
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(width, height, taskStackBounds);
- int childWidth = width - config.systemInsets.right;
- int childHeight = taskStackBounds.height() - config.systemInsets.top;
+ mConfig.getTaskStackBounds(width, height, taskStackBounds);
+ int childWidth = width - mConfig.systemInsets.right;
+ int childHeight = taskStackBounds.height() - mConfig.systemInsets.top;
// Measure each TaskStackView
int childCount = getChildCount();
@@ -259,23 +278,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
// Get the search bar bounds so that we lay it out
- RecentsConfiguration config = RecentsConfiguration.getInstance();
if (mSearchBar != null) {
Rect searchBarSpaceBounds = new Rect();
- config.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
- mSearchBar.layout(config.systemInsets.left + searchBarSpaceBounds.left,
- config.systemInsets.top + searchBarSpaceBounds.top,
- config.systemInsets.left + mSearchBar.getMeasuredWidth(),
- config.systemInsets.top + mSearchBar.getMeasuredHeight());
+ mConfig.getSearchBarBounds(getMeasuredWidth(), getMeasuredHeight(), searchBarSpaceBounds);
+ mSearchBar.layout(mConfig.systemInsets.left + searchBarSpaceBounds.left,
+ mConfig.systemInsets.top + searchBarSpaceBounds.top,
+ mConfig.systemInsets.left + mSearchBar.getMeasuredWidth(),
+ mConfig.systemInsets.top + mSearchBar.getMeasuredHeight());
}
// We offset the stack view by the left inset (if any), but lay it out under the search bar.
// In addition, we offset our stack views by the top inset and search bar height, but not
// the bottom insets because we want it to render under the navigation buttons.
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
- left += config.systemInsets.left;
- top += config.systemInsets.top + taskStackBounds.top;
+ mConfig.getTaskStackBounds(getMeasuredWidth(), getMeasuredHeight(), taskStackBounds);
+ left += mConfig.systemInsets.left;
+ top += mConfig.systemInsets.top + taskStackBounds.top;
// Layout each child
// XXX: Based on the space node for that task view
@@ -324,8 +342,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
// Update the configuration with the latest system insets and trigger a relayout
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- config.updateSystemInsets(insets.getSystemWindowInsets());
+ mConfig.updateSystemInsets(insets.getSystemWindowInsets());
requestLayout();
return insets.consumeSystemWindowInsets(false, false, false, true);
@@ -365,6 +382,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
final Runnable launchRunnable = new Runnable() {
@Override
public void run() {
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
+ }
+
TaskViewTransform transform;
View sourceView = tv;
int offsetX = 0;
@@ -374,11 +396,10 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
// If there is no actual task view, then use the stack view as the source view
// and then offset to the expected transform rect, but bound this to just
// outside the display rect (to ensure we don't animate from too far away)
- RecentsConfiguration config = RecentsConfiguration.getInstance();
sourceView = stackView;
transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
offsetX = transform.rect.left;
- offsetY = Math.min(transform.rect.top, config.displayRect.height());
+ offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
} else {
transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
}
@@ -426,19 +447,23 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
onTaskRemoved(task);
}
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "startActivity");
+ }
}
};
- Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
- Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
+ if (Console.Enabled) {
+ Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
+ Constants.Log.App.TimeRecentsLaunchKey, "onTaskLaunched");
+ }
// Launch the app right away if there is no task view, otherwise, animate the icon out first
if (tv == null) {
post(launchRunnable);
} else {
- tv.animateOnLeavingRecents(launchRunnable);
+ tv.animateOnLaunchingTask(launchRunnable);
}
}
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 c10ddd1..2c637a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -22,8 +22,10 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@@ -36,6 +38,9 @@ import com.android.systemui.recents.model.Task;
/* The task bar view */
class TaskBarView extends FrameLayout {
+
+ RecentsConfiguration mConfig;
+
Task mTask;
ImageView mDismissButton;
@@ -61,6 +66,7 @@ class TaskBarView extends FrameLayout {
public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
// Load the dismiss resources
@@ -70,11 +76,10 @@ class TaskBarView extends FrameLayout {
// Configure the highlight paint
if (sHighlightPaint == null) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
sHighlightPaint = new Paint();
sHighlightPaint.setStyle(Paint.Style.STROKE);
- sHighlightPaint.setStrokeWidth(config.taskViewHighlightPx);
- sHighlightPaint.setColor(config.taskBarViewHighlightColor);
+ sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx);
+ sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor);
sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
sHighlightPaint.setAntiAlias(true);
}
@@ -90,11 +95,9 @@ class TaskBarView extends FrameLayout {
@Override
protected void onDraw(Canvas canvas) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
// Draw the highlight at the top edge (but put the bottom edge just out of view)
- float offset = config.taskViewHighlightPx / 2f;
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float offset = mConfig.taskViewHighlightPx / 2f;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset,
getMeasuredHeight() + radius, radius, radius, sHighlightPaint);
}
@@ -102,7 +105,6 @@ class TaskBarView extends FrameLayout {
/** 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);
@@ -111,18 +113,16 @@ class TaskBarView extends FrameLayout {
.alpha(toTransform.dismissAlpha)
.setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
.withLayer()
.start();
} else {
mDismissButton.setAlpha(toTransform.dismissAlpha);
}
- mDismissButton.invalidate();
}
/** Binds the bar view to the task */
void rebindToTask(Task t, boolean animate) {
- RecentsConfiguration configuration = RecentsConfiguration.getInstance();
mTask = t;
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
@@ -137,12 +137,12 @@ class TaskBarView extends FrameLayout {
if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
setBackgroundColor(tint);
mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
- configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor));
+ mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
mLightDismissDrawable, mDarkDismissDrawable));
} else {
- setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
- mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
+ setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor);
+ mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor);
}
if (animate) {
// XXX: Investigate how expensive it will be to create a second bitmap and crossfade
@@ -155,4 +155,51 @@ class TaskBarView extends FrameLayout {
mApplicationIcon.setImageDrawable(null);
mActivityDescription.setText("");
}
+
+ /** Prepares this task view for the enter-recents animations. This is called earlier in the
+ * first layout because the actual animation into recents may take a long time. */
+ public void prepareAnimateEnterRecents() {
+ setVisibility(View.INVISIBLE);
+ }
+
+ /** Animates this task bar as it enters recents */
+ public void animateOnEnterRecents(int delay) {
+ // Animate the task bar of the first task view
+ setVisibility(View.VISIBLE);
+ setTranslationY(-getMeasuredHeight());
+ animate()
+ .translationY(0)
+ .setStartDelay(delay > -1 ? delay : mConfig.taskBarEnterAnimDelay)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskBarEnterAnimDuration)
+ .withLayer()
+ .start();
+ }
+
+ /** Animates this task bar as it exits recents */
+ public void animateOnLaunchingTask(final Runnable r) {
+ animate()
+ .translationY(-getMeasuredHeight())
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutLinearInInterpolator)
+ .setDuration(mConfig.taskBarExitAnimDuration)
+ .withLayer()
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ post(r);
+ }
+ })
+ .start();
+ }
+
+ /** Enable the hw layers on this task view */
+ void enableHwLayers() {
+ mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ }
+
+ /** Disable the hw layers on this task view */
+ void disableHwLayers() {
+ mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
}
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 8d9f8be..186565b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -41,6 +41,7 @@ import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.RecentsPackageMonitor;
import com.android.systemui.recents.RecentsTaskLoader;
+import com.android.systemui.recents.ReferenceCountedTrigger;
import com.android.systemui.recents.Utilities;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -62,10 +63,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public void onTaskRemoved(Task t);
}
+ RecentsConfiguration mConfig;
+
TaskStack mStack;
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
ViewPool<TaskView, Task> mViewPool;
+ ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>();
// The various rects that define the stack view
Rect mRect = new Rect();
@@ -83,11 +87,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ObjectAnimator mScrollAnimator;
// Optimizations
- int mHwLayersRefCount;
+ ReferenceCountedTrigger mHwLayersTrigger;
int mStackViewsAnimationDuration;
boolean mStackViewsDirty = true;
boolean mAwaitingFirstLayout = true;
boolean mStartEnterAnimationRequestedAfterLayout;
+ ViewAnimation.TaskViewEnterContext mStartEnterAnimationContext;
int[] mTmpVisibleRange = new int[2];
Rect mTmpRect = new Rect();
Rect mTmpRect2 = new Rect();
@@ -95,12 +100,40 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
public TaskStackView(Context context, TaskStack stack) {
super(context);
+ mConfig = RecentsConfiguration.getInstance();
mStack = stack;
mStack.setCallbacks(this);
mScroller = new OverScroller(context);
mTouchHandler = new TaskStackViewTouchHandler(context, this);
mViewPool = new ViewPool<TaskView, Task>(context, this);
mInflater = LayoutInflater.from(context);
+ mHwLayersTrigger = new ReferenceCountedTrigger(getContext(), new Runnable() {
+ @Override
+ public void run() {
+ // Enable hw layers on each of the children
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.enableHwLayers();
+ }
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ // Disable hw layers on each of the children
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.disableHwLayers();
+ }
+ }
+ }, new Runnable() {
+ @Override
+ public void run() {
+ new Throwable("Invalid hw layers ref count").printStackTrace();
+ Console.logError(getContext(), "Invalid HW layers ref count");
+ }
+ });
}
/** Sets the callbacks */
@@ -118,7 +151,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
"[TaskStackView|requestSynchronize]", "" + duration + "ms", Console.AnsiYellow);
}
if (!mStackViewsDirty) {
- invalidate();
+ invalidate(mStackRect);
}
if (mAwaitingFirstLayout) {
// Skip the animation if we are awaiting first layout
@@ -165,7 +198,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
transform.scale = scale;
- // Set the translation
+ // Set the y translation
if (boundedT < 0f) {
transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
numPeekCards) * peekHeight - scaleYOffset);
@@ -174,9 +207,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Set the z translation
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- int minZ = config.taskViewTranslationZMinPx;
- int incZ = config.taskViewTranslationZIncrementPx;
+ int minZ = mConfig.taskViewTranslationZMinPx;
+ int incZ = mConfig.taskViewTranslationZIncrementPx;
transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
// Set the alphas
@@ -198,16 +230,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**
* Gets the stack transforms of a list of tasks, and returns the visible range of tasks.
*/
- private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
- int stackScroll,
- int[] visibleRangeOut,
- boolean boundTranslationsToRect) {
+ private void updateStackTransforms(ArrayList<TaskViewTransform> taskTransforms,
+ ArrayList<Task> tasks,
+ int stackScroll,
+ int[] visibleRangeOut,
+ boolean boundTranslationsToRect) {
// XXX: Optimization: Use binary search to find the visible range
- ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
int taskCount = tasks.size();
int firstVisibleIndex = -1;
int lastVisibleIndex = -1;
+ taskTransforms.clear();
+ taskTransforms.ensureCapacity(taskCount);
for (int i = 0; i < taskCount; i++) {
TaskViewTransform transform = getStackTransform(i, stackScroll);
taskTransforms.add(transform);
@@ -226,6 +260,19 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
visibleRangeOut[0] = firstVisibleIndex;
visibleRangeOut[1] = lastVisibleIndex;
}
+ }
+
+ /**
+ * Gets the stack transforms of a list of tasks, and returns the visible range of tasks. This
+ * call is less optimal than calling updateStackTransforms directly.
+ */
+ private ArrayList<TaskViewTransform> getStackTransforms(ArrayList<Task> tasks,
+ int stackScroll,
+ int[] visibleRangeOut,
+ boolean boundTranslationsToRect) {
+ ArrayList<TaskViewTransform> taskTransforms = new ArrayList<TaskViewTransform>();
+ updateStackTransforms(taskTransforms, tasks, stackScroll, visibleRangeOut,
+ boundTranslationsToRect);
return taskTransforms;
}
@@ -245,14 +292,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
int[] visibleRange = mTmpVisibleRange;
int stackScroll = getStackScroll();
ArrayList<Task> tasks = mStack.getTasks();
- ArrayList<TaskViewTransform> taskTransforms = getStackTransforms(tasks, stackScroll,
- visibleRange, false);
+ updateStackTransforms(mTaskTransforms, tasks, stackScroll, visibleRange, false);
// Update the visible state of all the tasks
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
- TaskViewTransform transform = taskTransforms.get(i);
+ TaskViewTransform transform = mTaskTransforms.get(i);
TaskView tv = getChildViewForTask(task);
if (transform.visible) {
@@ -281,10 +327,10 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
TaskView tv = (TaskView) getChildAt(i);
Task task = tv.getTask();
int taskIndex = mStack.indexOfTask(task);
- if (taskIndex < 0 || !taskTransforms.get(taskIndex).visible) {
+ if (taskIndex < 0 || !mTaskTransforms.get(taskIndex).visible) {
mViewPool.returnViewToPool(tv);
} else {
- tv.updateViewPropertiesToTaskTransform(null, taskTransforms.get(taskIndex),
+ tv.updateViewPropertiesToTaskTransform(null, mTaskTransforms.get(taskIndex),
mStackViewsAnimationDuration);
}
}
@@ -361,7 +407,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(RecentsConfiguration.getInstance().fastOutSlowInInterpolator);
+ mScrollAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -543,48 +589,31 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/** Enables the hw layers and increments the hw layer requirement ref count */
void addHwLayersRefCount(String reason) {
if (Console.Enabled) {
+ int refCount = mHwLayersTrigger.getCount();
Console.log(Constants.Log.UI.HwLayers,
"[TaskStackView|addHwLayersRefCount] refCount: " +
- mHwLayersRefCount + "->" + (mHwLayersRefCount + 1) + " " + reason);
+ refCount + "->" + (refCount + 1) + " " + reason);
}
- if (mHwLayersRefCount == 0) {
- // Enable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.enableHwLayers();
- }
- }
- mHwLayersRefCount++;
+ mHwLayersTrigger.increment();
}
/** Decrements the hw layer requirement ref count and disables the hw layers when we don't
need them anymore. */
void decHwLayersRefCount(String reason) {
if (Console.Enabled) {
+ int refCount = mHwLayersTrigger.getCount();
Console.log(Constants.Log.UI.HwLayers,
"[TaskStackView|decHwLayersRefCount] refCount: " +
- mHwLayersRefCount + "->" + (mHwLayersRefCount - 1) + " " + reason);
- }
- mHwLayersRefCount--;
- if (mHwLayersRefCount == 0) {
- // Disable hw layers on each of the children
- int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- TaskView tv = (TaskView) getChildAt(i);
- tv.disableHwLayers();
- }
- } else if (mHwLayersRefCount < 0) {
- new Throwable("Invalid hw layers ref count").printStackTrace();
- Console.logError(getContext(), "Invalid HW layers ref count");
+ refCount + "->" + (refCount - 1) + " " + reason);
}
+ mHwLayersTrigger.decrement();
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
setStackScroll(mScroller.getCurrY());
- invalidate();
+ invalidate(mStackRect);
// If we just finished scrolling, then disable the hw layers
if (mScroller.isFinished()) {
@@ -616,7 +645,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (Constants.DebugFlags.App.EnableTaskStackClipping) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
TaskView tv = (TaskView) child;
TaskView nextTv = null;
TaskView tmpTv = null;
@@ -632,13 +660,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Clip against the next view (if we aren't animating its alpha)
- if (nextTv != null && nextTv.getAlpha() == 1f) {
+ if (nextTv != null) {
Rect curRect = tv.getClippingRect(mTmpRect);
Rect nextRect = nextTv.getClippingRect(mTmpRect2);
// The hit rects are relative to the task view, which needs to be offset by
// the system bar height
- curRect.offset(0, config.systemInsets.top);
- nextRect.offset(0, config.systemInsets.top);
+ curRect.offset(0, mConfig.systemInsets.top);
+ nextRect.offset(0, mConfig.systemInsets.top);
// Compute the clip region
Region clipRegion = new Region();
clipRegion.op(curRect, Region.Op.UNION);
@@ -660,7 +688,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Note: We let the stack view be the full height because we want the cards to go under the
// navigation bar if possible. However, the stack rects which we use to calculate
// max scroll, etc. need to take the nav bar into account
- RecentsConfiguration config = RecentsConfiguration.getInstance();
// Compute the stack rects
mRect.set(0, 0, width, height);
@@ -668,8 +695,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mStackRect.left += insetLeft;
mStackRect.bottom -= insetBottom;
- int widthPadding = (int) (config.taskStackWidthPaddingPct * mStackRect.width());
- int heightPadding = config.taskStackTopPaddingPx;
+ int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width());
+ int heightPadding = mConfig.taskStackTopPaddingPx;
if (Constants.DebugFlags.App.EnableSearchLayout) {
mStackRect.top += heightPadding;
mStackRect.left += widthPadding;
@@ -707,10 +734,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Compute our stack/task rects
- RecentsConfiguration config = RecentsConfiguration.getInstance();
Rect taskStackBounds = new Rect();
- config.getTaskStackBounds(width, height, taskStackBounds);
- computeRects(width, height, taskStackBounds.left, config.systemInsets.bottom);
+ mConfig.getTaskStackBounds(width, height, taskStackBounds);
+ computeRects(width, height, taskStackBounds.left, mConfig.systemInsets.bottom);
// Debug logging
if (Constants.Log.UI.MeasureAndLayout) {
@@ -768,47 +794,66 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
if (mAwaitingFirstLayout) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
-
- // Update the focused task index to be the next item to the top task
- if (config.launchedFromAltTab) {
- focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
- }
+ // Mark that we have completely the first layout
+ mAwaitingFirstLayout = false;
// Prepare the first view for its enter animation
- if (config.launchedWithThumbnailAnimation) {
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- if (tv != null) {
- tv.prepareAnimateOnEnterRecents();
- }
+ int offsetTopAlign = -mTaskRect.top;
+ int offscreenY = mRect.bottom - (mTaskRect.top - mRect.top);
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.prepareAnimateEnterRecents((i == (getChildCount() - 1)), offsetTopAlign,
+ offscreenY, mTaskRect);
}
- // Mark that we have completely the first layout
- mAwaitingFirstLayout = false;
-
// If the enter animation started already and we haven't completed a layout yet, do the
// enter animation now
if (mStartEnterAnimationRequestedAfterLayout) {
- startOnEnterAnimation();
+ startOnEnterAnimation(mStartEnterAnimationContext);
+ mStartEnterAnimationRequestedAfterLayout = false;
+ mStartEnterAnimationContext = null;
+ }
+
+ // Update the focused task index to be the next item to the top task
+ if (mConfig.launchedWithAltTab) {
+ focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
}
}
}
/** Requests this task stacks to start it's enter-recents animation */
- public void startOnEnterAnimation() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (!config.launchedWithThumbnailAnimation) return;
-
+ public void startOnEnterAnimation(ViewAnimation.TaskViewEnterContext ctx) {
// If we are still waiting to layout, then just defer until then
if (mAwaitingFirstLayout) {
mStartEnterAnimationRequestedAfterLayout = true;
+ mStartEnterAnimationContext = ctx;
return;
}
- // Animate the task bar of the first task view
- TaskView tv = (TaskView) getChildAt(getChildCount() - 1);
- if (tv != null) {
- tv.animateOnEnterRecents();
+ // Animate all the task views into view
+ ctx.taskRect = mTaskRect;
+ ctx.stackRectSansPeek = mStackRectSansPeek;
+ int childCount = getChildCount();
+ for (int i = childCount - 1; i >= 0; i--) {
+ TaskView tv = (TaskView) getChildAt(i);
+ TaskViewTransform transform = getStackTransform(mStack.indexOfTask(tv.getTask()),
+ getStackScroll());
+ ctx.stackViewIndex = i;
+ ctx.stackViewCount = childCount;
+ ctx.isFrontMost = (i == (getChildCount() - 1));
+ ctx.transform = transform;
+ tv.animateOnEnterRecents(ctx);
+ }
+ }
+
+ /** Requests this task stacks to start it's exit-recents animation. */
+ public void startOnExitAnimation(ViewAnimation.TaskViewExitContext ctx) {
+ // Animate all the task views into view
+ ctx.offscreenTranslationY = mRect.bottom - (mTaskRect.top - mRect.top);
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ tv.animateOnExitRecents(ctx);
}
}
@@ -871,8 +916,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ArrayList<TaskViewTransform> curTaskTransforms,
ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
- ArrayList<TaskView> childrenToRemoveOut,
- RecentsConfiguration config) {
+ ArrayList<TaskView> childrenToRemoveOut) {
// Animate all of the existing views out of view (if they are not in the visible range in
// the new stack) or to their final positions in the new stack
int movement = 0;
@@ -902,7 +946,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
childViewTransformsOut.put(tv, new Pair(0, toTransform));
}
return Utilities.calculateTranslationAnimationDuration(movement,
- config.filteringCurrentViewsMinAnimDuration);
+ mConfig.filteringCurrentViewsMinAnimDuration);
}
/**
@@ -911,8 +955,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
*/
int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
ArrayList<TaskViewTransform> taskTransforms,
- HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
- RecentsConfiguration config) {
+ HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) {
int offset = 0;
int movement = 0;
int taskCount = tasks.size();
@@ -942,7 +985,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
return Utilities.calculateTranslationAnimationDuration(movement,
- config.filteringNewViewsMinAnimDuration);
+ mConfig.filteringNewViewsMinAnimDuration);
}
/** Orchestrates the animations of the current child views and any new views. */
@@ -950,22 +993,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
ArrayList<TaskViewTransform> curTaskTransforms,
final ArrayList<Task> tasks,
final ArrayList<TaskViewTransform> taskTransforms) {
- final RecentsConfiguration config = RecentsConfiguration.getInstance();
-
// Calculate the transforms to animate out all the existing views if they are not in the
// new visible range (or to their final positions in the stack if they are)
final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms =
new HashMap<TaskView, Pair<Integer, TaskViewTransform>>();
int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
- taskTransforms, childViewTransforms, childrenToRemove, config);
+ taskTransforms, childViewTransforms, childrenToRemove);
// If all the current views are in the visible range of the new stack, then don't wait for
// views to animate out and animate all the new views into their place
final boolean unifyNewViewAnimation = childrenToRemove.isEmpty();
if (unifyNewViewAnimation) {
int inDuration = getEnterTransformsForFilterAnimation(tasks, taskTransforms,
- childViewTransforms, config);
+ childViewTransforms);
duration = Math.max(duration, inDuration);
}
@@ -989,7 +1030,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// For views that are not already visible, animate them in
childViewTransforms.clear();
int duration = getEnterTransformsForFilterAnimation(tasks,
- taskTransforms, childViewTransforms, config);
+ taskTransforms, childViewTransforms);
for (final TaskView tv : childViewTransforms.keySet()) {
Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
tv.animate().setStartDelay(t.first);
@@ -1127,7 +1168,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
// Enable hw layers on this view if hw layers are enabled on the stack
- if (mHwLayersRefCount > 0) {
+ if (mHwLayersTrigger.getCount() > 0) {
tv.enableHwLayers();
}
}
@@ -1196,7 +1237,6 @@ 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--) {
@@ -1502,7 +1542,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
mSv.mMinScroll, mSv.mMaxScroll,
0, overscrollRange);
// Invalidate to kick off computeScroll
- mSv.invalidate();
+ mSv.invalidate(mSv.mStackRect);
} else if (mSv.isScrollOutOfBounds()) {
// Animate the scroll back into bounds
// XXX: Make this animation a function of the velocity OR distance
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 5df5e4d..9e4386f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -28,9 +28,11 @@ import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewParent;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
+import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -45,10 +47,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
public void onTaskAppInfoClicked(TaskView tv);
public void onTaskFocused(TaskView tv);
public void onTaskDismissed(TaskView tv);
-
- // public void onTaskViewReboundToTask(TaskView tv, Task t);
}
+ RecentsConfiguration mConfig;
+
int mDim;
int mMaxDim;
TimeInterpolator mDimInterpolator = new AccelerateInterpolator();
@@ -59,11 +61,21 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
boolean mClipViewInStack;
Point mLastTouchDown = new Point();
Path mRoundedRectClipPath = new Path();
+ Rect mTmpRect = new Rect();
TaskThumbnailView mThumbnailView;
TaskBarView mBarView;
TaskViewCallbacks mCb;
+ // Optimizations
+ ValueAnimator.AnimatorUpdateListener mUpdateDimListener =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateDimOverlayFromScale();
+ }
+ };
+
public TaskView(Context context) {
this(context, null);
@@ -79,13 +91,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ mConfig = RecentsConfiguration.getInstance();
setWillNotDraw(false);
}
@Override
protected void onFinishInflate() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mMaxDim = config.taskStackMaxDim;
+ mMaxDim = mConfig.taskStackMaxDim;
// By default, all views are clipped to other views in their stack
mClipViewInStack = true;
@@ -104,8 +116,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Update the rounded rect clip path
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- float radius = config.taskViewRoundedCornerRadiusPx;
+ float radius = mConfig.taskViewRoundedCornerRadiusPx;
mRoundedRectClipPath.reset();
mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()),
radius, radius, Path.Direction.CW);
@@ -113,7 +124,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
// Update the outline
Outline o = new Outline();
o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight() -
- config.taskViewShadowOutlineBottomInsetPx, radius);
+ mConfig.taskViewShadowOutlineBottomInsetPx, radius);
setOutline(o);
}
@@ -141,7 +152,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Synchronizes this view's properties with the task's transform */
void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
TaskViewTransform toTransform, int duration) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
+ if (Console.Enabled) {
+ Console.log(Constants.Log.UI.Draw, "[TaskView|updateViewPropertiesToTaskTransform]",
+ "duration: " + duration, Console.AnsiPurple);
+ }
// Update the bar view
mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
@@ -164,15 +178,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
.scaleX(toTransform.scale)
.scaleY(toTransform.scale)
.alpha(toTransform.alpha)
+ .setStartDelay(0)
.setDuration(duration)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .withLayer()
- .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateDimOverlayFromScale();
- }
- })
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setUpdateListener(mUpdateDimListener)
.start();
} else {
setTranslationY(toTransform.translationY);
@@ -197,6 +206,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
setScaleX(1f);
setScaleY(1f);
setAlpha(1f);
+ mDim = 0;
invalidate();
}
@@ -221,40 +231,103 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- public void prepareAnimateOnEnterRecents() {
- mBarView.setVisibility(View.INVISIBLE);
+ public void prepareAnimateEnterRecents(boolean isTaskViewFrontMost, int offsetY, int offscreenY,
+ Rect taskRect) {
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (isTaskViewFrontMost) {
+ // Hide the task view as we are going to animate the full screenshot into view
+ // and then replace it with this view once we are done
+ setVisibility(View.INVISIBLE);
+ // Also hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ } else {
+ // Top align the task views
+ setTranslationY(offsetY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (isTaskViewFrontMost) {
+ // Hide the front most task bar view so we can animate it in
+ mBarView.prepareAnimateEnterRecents();
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Move the task view off screen (below) so we can animate it in
+ setTranslationY(offscreenY);
+ setScaleX(1f);
+ setScaleY(1f);
+ }
}
/** Animates this task view as it enters recents */
- public void animateOnEnterRecents() {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.setVisibility(View.VISIBLE);
- mBarView.setTranslationY(-mBarView.getMeasuredHeight());
- mBarView.animate()
- .translationY(0)
- .setStartDelay(config.taskBarEnterAnimDelay)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskBarEnterAnimDuration)
+ public void animateOnEnterRecents(ViewAnimation.TaskViewEnterContext ctx) {
+ TaskViewTransform transform = ctx.transform;
+
+ if (mConfig.launchedFromAppWithScreenshot) {
+ if (ctx.isFrontMost) {
+ // Animate the full screenshot down first, before swapping with this task view
+ ctx.fullScreenshot.animateOnEnterRecents(ctx, new Runnable() {
+ @Override
+ public void run() {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(0);
+ setVisibility(View.VISIBLE);
+ }
+ });
+ } else {
+ // Animate the tasks down behind the full screenshot
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.linearOutSlowInInterpolator)
+ .setDuration(475)
+ .withLayer()
+ .start();
+ }
+
+ } else if (mConfig.launchedFromAppWithThumbnail) {
+ if (ctx.isFrontMost) {
+ // Animate the task bar of the first task view
+ mBarView.animateOnEnterRecents(-1);
+ }
+
+ } else if (mConfig.launchedFromHome) {
+ // Animate the tasks up
+ int frontIndex = (ctx.stackViewCount - ctx.stackViewIndex - 1);
+ int delay = mConfig.taskBarEnterAnimDelay +
+ frontIndex * mConfig.taskViewEnterFromHomeDelay;
+ animate()
+ .scaleX(transform.scale)
+ .scaleY(transform.scale)
+ .translationY(transform.translationY)
+ .setStartDelay(delay)
+ .setInterpolator(mConfig.quintOutInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
+ .withLayer()
+ .start();
+ }
+ }
+
+ /** Animates this task view as it leaves recents */
+ public void animateOnExitRecents(ViewAnimation.TaskViewExitContext ctx) {
+ animate()
+ .translationY(ctx.offscreenTranslationY)
+ .setStartDelay(0)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewEnterFromHomeDuration)
.withLayer()
+ .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable())
.start();
+ ctx.postAnimationTrigger.increment();
}
/** Animates this task view as it exits recents */
- public void animateOnLeavingRecents(final Runnable r) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- mBarView.animate()
- .translationY(-mBarView.getMeasuredHeight())
- .setStartDelay(0)
- .setInterpolator(config.fastOutLinearInInterpolator)
- .setDuration(config.taskBarExitAnimDuration)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- post(r);
- }
- })
- .start();
+ public void animateOnLaunchingTask(final Runnable r) {
+ mBarView.animateOnLaunchingTask(r);
}
/** Animates the deletion of this task view */
@@ -262,20 +335,24 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
// Disabling clipping with the stack while the view is animating away
setClipViewInStack(false);
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- animate().translationX(config.taskViewRemoveAnimTranslationXPx)
+ animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(config.fastOutSlowInInterpolator)
- .setDuration(config.taskViewRemoveAnimDuration)
+ .setInterpolator(mConfig.fastOutSlowInInterpolator)
+ .setDuration(mConfig.taskViewRemoveAnimDuration)
.withLayer()
.withEndAction(new Runnable() {
@Override
public void run() {
- post(r);
+ // We just throw this into a runnable because starting a view property
+ // animation using layers can cause inconsisten results if we try and
+ // update the layers while the animation is running. In some cases,
+ // the runnabled passed in may start an animation which also uses layers
+ // so we defer all this by posting this.
+ r.run();
// Re-enable clipping with the stack (we will reuse this view)
- setClipViewInStack(false);
+ setClipViewInStack(true);
}
})
.start();
@@ -293,11 +370,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
/** Enable the hw layers on this task view */
void enableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mBarView.enableHwLayers();
}
/** Disable the hw layers on this task view */
void disableHwLayers() {
mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+ mBarView.disableHwLayers();
}
/**
@@ -305,7 +384,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
* view.
*/
boolean shouldClipViewInStack() {
- return mClipViewInStack;
+ return mClipViewInStack && (getVisibility() == View.VISIBLE);
}
/** Sets whether this view should be clipped, or clipped against. */
@@ -313,9 +392,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
if (clip != mClipViewInStack) {
mClipViewInStack = clip;
if (getParent() instanceof View) {
- Rect r = new Rect();
- getHitRect(r);
- ((View) getParent()).invalidate(r);
+ getHitRect(mTmpRect);
+ ((View) getParent()).invalidate(mTmpRect);
}
}
}
@@ -391,8 +469,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On
mBarView.mApplicationIcon.setOnClickListener(this);
mBarView.mDismissButton.setOnClickListener(this);
if (Constants.DebugFlags.App.EnableDevAppInfoOnLongPress) {
- RecentsConfiguration config = RecentsConfiguration.getInstance();
- if (config.developerOptionsEnabled) {
+ if (mConfig.developerOptionsEnabled) {
mBarView.mApplicationIcon.setOnLongClickListener(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 3c3ebd7..4a76872 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -47,7 +47,8 @@ public class TaskViewTransform {
@Override
public String toString() {
- return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
- " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha;
+ return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale +
+ " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
+ " dismissAlpha: " + dismissAlpha;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
new file mode 100644
index 0000000..b5e8ffd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -0,0 +1,60 @@
+/*
+ * 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.recents.views;
+
+import android.graphics.Rect;
+import com.android.systemui.recents.ReferenceCountedTrigger;
+
+/* Common code related to view animations */
+public class ViewAnimation {
+
+ /* The animation context for a task view animation into Recents */
+ public static class TaskViewEnterContext {
+ // The full screenshot view that we are animating down
+ FullScreenTransitionView fullScreenshot;
+ // The transform of the current task view
+ TaskViewTransform transform;
+ // The stack rect that the transform is relative to
+ Rect stackRectSansPeek;
+ // The task rect
+ Rect taskRect;
+ // The view index of the current task view
+ int stackViewIndex;
+ // The total number of task views
+ int stackViewCount;
+ // Whether this is the front most task view
+ boolean isFrontMost;
+
+ public TaskViewEnterContext(FullScreenTransitionView fss) {
+ fullScreenshot = fss;
+ }
+ }
+
+ /* The animation context for a task view animation out of Recents */
+ public static class TaskViewExitContext {
+ // A trigger to run some logic when all the animations complete. This works around the fact
+ // that it is difficult to coordinate ViewPropertyAnimators
+ ReferenceCountedTrigger postAnimationTrigger;
+ // The translationY to apply to a TaskView to move it off screen
+ int offscreenTranslationY;
+
+ public TaskViewExitContext(ReferenceCountedTrigger t) {
+ postAnimationTrigger = t;
+ }
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
index d67e7cb..a3b10f2 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
@@ -21,8 +21,16 @@ import com.android.systemui.R;
import android.app.ActivityManagerNative;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
@@ -43,15 +51,18 @@ import java.util.List;
/**
* A quick and dirty view to show a user switcher.
*/
-public class UserSwitcherHostView extends FrameLayout implements ListView.OnItemClickListener {
+public class UserSwitcherHostView extends FrameLayout
+ implements ListView.OnItemClickListener, View.OnClickListener {
private static final String TAG = "UserSwitcherDialog";
private ArrayList<UserInfo> mUserInfo = new ArrayList<UserInfo>();
+ private UserInfo mGuestUser;
private Adapter mAdapter = new Adapter();
private UserManager mUserManager;
private Runnable mFinishRunnable;
private ListView mListView;
+ private boolean mGuestUserEnabled;
public UserSwitcherHostView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -60,6 +71,9 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem
return;
}
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
+ mGuestUserEnabled = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.GUEST_USER_ENABLED, 0) == 1;
}
public UserSwitcherHostView(Context context, AttributeSet attrs) {
@@ -80,7 +94,39 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem
@Override
public void onItemClick(AdapterView<?> l, View v, int position, long id) {
- int userId = mAdapter.getItem(position).id;
+ // Last item is the guest
+ if (position == mUserInfo.size()) {
+ postDelayed(new Runnable() {
+ public void run() {
+ switchToGuestUser();
+ }
+ }, 100);
+ } else {
+ final int userId = mAdapter.getItem(position).id;
+ postDelayed(new Runnable() {
+ public void run() {
+ switchUser(userId);
+ }
+ }, 100);
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ // Delete was clicked
+ postDelayed(new Runnable() {
+ public void run() {
+ if (mGuestUser != null) {
+ switchUser(0);
+ mUserManager.removeUser(mGuestUser.id);
+ mGuestUser = null;
+ refreshUsers();
+ }
+ }
+ }, 100);
+ }
+
+ private void switchUser(int userId) {
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
ActivityManagerNative.getDefault().switchUser(userId);
@@ -90,6 +136,15 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem
}
}
+ private void switchToGuestUser() {
+ if (mGuestUser == null) {
+ // No guest user. Create one.
+ mGuestUser = mUserManager.createGuest(mContext,
+ mContext.getResources().getString(R.string.guest_nickname));
+ }
+ switchUser(mGuestUser.id);
+ }
+
private void finish() {
if (mFinishRunnable != null) {
mFinishRunnable.run();
@@ -119,9 +174,12 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem
public void refreshUsers() {
mUserInfo.clear();
+ mGuestUser = null;
List<UserInfo> users = mUserManager.getUsers(true);
for (UserInfo user : users) {
- if (!user.isManagedProfile()) {
+ if (user.isGuest()) {
+ mGuestUser = user;
+ } else if (!user.isManagedProfile()) {
mUserInfo.add(user);
}
}
@@ -132,17 +190,25 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem
@Override
public int getCount() {
- return mUserInfo.size();
+ return mUserInfo.size() + (mGuestUserEnabled ? 1 : 0);
}
@Override
public UserInfo getItem(int position) {
- return mUserInfo.get(position);
+ if (position < mUserInfo.size()) {
+ return mUserInfo.get(position);
+ } else {
+ return mGuestUser;
+ }
}
@Override
public long getItemId(int position) {
- return getItem(position).serialNumber;
+ if (position < mUserInfo.size()) {
+ return getItem(position).serialNumber;
+ } else {
+ return mGuestUser != null ? mGuestUser.serialNumber : -1;
+ }
}
@Override
@@ -161,18 +227,46 @@ public class UserSwitcherHostView extends FrameLayout implements ListView.OnItem
ViewHolder h = new ViewHolder();
h.name = (TextView) v.findViewById(R.id.user_name);
h.picture = (ImageView) v.findViewById(R.id.user_picture);
+ h.delete = (ImageView) v.findViewById(R.id.user_delete);
v.setTag(h);
return v;
}
private void bindView(ViewHolder h, UserInfo item) {
- h.name.setText(item.name);
- h.picture.setImageBitmap(mUserManager.getUserIcon(item.id));
+ if (item != null) {
+ h.name.setText(item.name);
+ h.picture.setImageBitmap(circularClip(mUserManager.getUserIcon(item.id)));
+ h.delete.setVisibility(item.isGuest() ? View.VISIBLE : View.GONE);
+ h.delete.setOnClickListener(UserSwitcherHostView.this);
+ if (item.isGuest()) {
+ h.picture.setImageResource(R.drawable.ic_account_circle);
+ }
+ } else {
+ h.name.setText(R.string.guest_new_guest);
+ h.picture.setImageResource(R.drawable.ic_account_circle);
+ h.delete.setVisibility(View.GONE);
+ }
+ }
+
+ private Bitmap circularClip(Bitmap input) {
+ if (input == null) {
+ return null;
+ }
+ 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;
}
class ViewHolder {
TextView name;
ImageView picture;
+ ImageView delete;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 97123fa..20684a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -89,7 +89,8 @@ import java.util.Locale;
import static com.android.keyguard.KeyguardHostView.OnDismissAction;
public abstract class BaseStatusBar extends SystemUI implements
- CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener {
+ CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
+ RecentsComponent.Callbacks {
public static final String TAG = "StatusBar";
public static final boolean DEBUG = false;
public static final boolean MULTIUSER_DEBUG = false;
@@ -385,6 +386,7 @@ public abstract class BaseStatusBar extends SystemUI implements
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mRecents = getComponent(RecentsComponent.class);
+ mRecents.setCallback(this);
mLocale = mContext.getResources().getConfiguration().locale;
mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(mLocale);
@@ -768,6 +770,11 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ // Do nothing
+ }
+
public abstract void resetHeadsUpDecayTimer();
public abstract void scheduleHeadsUpOpen();
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 8a57ce6..b23992d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -267,7 +267,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private TextView mCarrierLabel;
private boolean mCarrierLabelVisible = false;
private int mCarrierLabelHeight;
- private TextView mEmergencyCallLabel;
private int mStatusBarHeaderHeight;
private boolean mShowCarrierInPanel = false;
@@ -717,23 +716,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mNetworkController.addSignalCluster(signalCluster);
signalCluster.setNetworkController(mNetworkController);
-
final boolean isAPhone = mNetworkController.hasVoiceCallingFeature();
if (isAPhone) {
- mEmergencyCallLabel =
- (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only);
- // TODO: Uncomment when correctly positioned
-// if (mEmergencyCallLabel != null) {
-// mNetworkController.addEmergencyLabelView(mEmergencyCallLabel);
-// mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() {
-// public void onClick(View v) { }});
-// mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-// @Override
-// public void onLayoutChange(View v, int left, int top, int right, int bottom,
-// int oldLeft, int oldTop, int oldRight, int oldBottom) {
-// updateCarrierLabelVisibility(false);
-// }});
-// }
+ mNetworkController.addEmergencyLabelView(mHeader);
}
mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label);
@@ -759,6 +744,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// updateCarrierLabelVisibility(false);
}
+ mBatteryController.setStatusBarHeaderView(mHeader);
+
// Set up the quick settings tile panel
mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
if (mQSPanel != null) {
@@ -1387,7 +1374,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mCarrierLabelHeight));
}
- final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null;
+ // Emergency calls only is shown in the expanded header now.
+ final boolean emergencyCallsShownElsewhere = true;
final boolean makeVisible =
!(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
&& mStackScroller.getHeight() < (mNotificationPanel.getHeight()
@@ -1718,7 +1706,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
if (mStatusBarWindow != null) {
-
// release focus immediately to kick off focus change transition
mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -1960,6 +1947,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
if (diff != 0) {
+ // we never set the recents bit via this method, so save the prior state to prevent
+ // clobbering the bit below
+ final boolean wasRecentsVisible = (mSystemUiVisibility & View.RECENT_APPS_VISIBLE) > 0;
+
mSystemUiVisibility = newVal;
// update low profile
@@ -2014,6 +2005,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mSystemUiVisibility &= ~View.NAVIGATION_BAR_UNHIDE;
}
+ // restore the recents bit
+ if (wasRecentsVisible) {
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ }
+
// send updated sysui visibility to window manager
notifyUiVisibilityChanged(mSystemUiVisibility);
}
@@ -3144,4 +3140,41 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
}
}
};
+
+ // Recents
+
+ @Override
+ protected void showRecents(boolean triggeredFromAltTab) {
+ // Set the recents visibility flag
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.showRecents(triggeredFromAltTab);
+ }
+
+ @Override
+ protected void hideRecents(boolean triggeredFromAltTab) {
+ // Unset the recents visibility flag
+ mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.hideRecents(triggeredFromAltTab);
+ }
+
+ @Override
+ protected void toggleRecents() {
+ // Toggle the recents visibility flag
+ mSystemUiVisibility ^= View.RECENT_APPS_VISIBLE;
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ super.toggleRecents();
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ // Update the recents visibility flag
+ if (visible) {
+ mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+ } else {
+ mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
+ }
+ notifyUiVisibilityChanged(mSystemUiVisibility);
+ }
}
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 7837769..c8ab027 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -26,6 +26,7 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
@@ -57,6 +58,11 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
private View mSignalCluster;
private View mSettingsButton;
private View mBrightnessContainer;
+ private View mEmergencyCallsOnly;
+ private TextView mChargingInfo;
+
+ private boolean mShowEmergencyCallsOnly;
+ private boolean mShowChargingInfo;
private int mCollapsedHeight;
private int mExpandedHeight;
@@ -64,8 +70,6 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
private int mNormalWidth;
- private int mPadding;
- private int mMultiUserExpandedMargin;
private ActivityStarter mActivityStarter;
private BrightnessController mBrightnessController;
@@ -93,6 +97,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
mBrightnessController = new BrightnessController(getContext(),
(ImageView) findViewById(R.id.brightness_icon),
(ToggleSlider) findViewById(R.id.brightness_slider));
+ mEmergencyCallsOnly = findViewById(R.id.header_emergency_calls_only);
+ mChargingInfo = (TextView) findViewById(R.id.header_charging_info);
loadDimens();
updateVisibilities();
addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@@ -114,10 +120,6 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
mKeyguardHeight = getResources().getDimensionPixelSize(
R.dimen.status_bar_header_height_keyguard);
mNormalWidth = getLayoutParams().width;
- mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
- mMultiUserExpandedMargin =
- getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
-
}
public void setActivityStarter(ActivityStarter activityStarter) {
@@ -145,8 +147,6 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
updateZTranslation();
updateClickTargets();
updateWidth();
- updatePadding();
- updateMultiUserSwitch();
if (mQSPanel != null) {
mQSPanel.setExpanded(expanded && !overscrolled);
}
@@ -204,13 +204,32 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
if (mSignalCluster != null) {
mSignalCluster.setVisibility(!mExpanded || mOverscrolled ? View.VISIBLE : View.GONE);
}
+ mEmergencyCallsOnly.setVisibility(mExpanded && !mOverscrolled && mShowEmergencyCallsOnly
+ ? VISIBLE : GONE);
+ mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
+ && !mShowEmergencyCallsOnly ? VISIBLE : GONE);
}
private void updateSystemIconsLayoutParams() {
RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
- lp.addRule(RelativeLayout.START_OF, mExpanded
- ? mSettingsButton.getId()
- : mMultiUserSwitch.getId());
+ boolean systemIconsAboveClock = mExpanded && !mOverscrolled
+ && mShowChargingInfo && !mShowEmergencyCallsOnly;
+ if (systemIconsAboveClock) {
+ lp.addRule(ALIGN_PARENT_START);
+ lp.removeRule(START_OF);
+ } else {
+ lp.addRule(RelativeLayout.START_OF, mExpanded
+ ? mSettingsButton.getId()
+ : mMultiUserSwitch.getId());
+ lp.removeRule(ALIGN_PARENT_START);
+ }
+
+ RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
+ if (systemIconsAboveClock) {
+ clockLp.addRule(BELOW, mChargingInfo.getId());
+ } else {
+ clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
+ }
}
private void updateBrightnessControllerState() {
@@ -237,21 +256,6 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
}
}
- private void updatePadding() {
- boolean padded = !mKeyguardShowing || mExpanded;
- int padding = padded ? mPadding : 0;
- setPaddingRelative(padding, 0, padding, 0);
- }
-
- private void updateMultiUserSwitch() {
- int marginEnd = !mKeyguardShowing || mExpanded ? mMultiUserExpandedMargin : 0;
- MarginLayoutParams lp = (MarginLayoutParams) mMultiUserSwitch.getLayoutParams();
- if (marginEnd != lp.getMarginEnd()) {
- lp.setMarginEnd(marginEnd);
- mMultiUserSwitch.setLayoutParams(lp);
- }
- }
-
public void setExpansion(float height) {
height = (height - mCollapsedHeight) * EXPANSION_RUBBERBAND_FACTOR + mCollapsedHeight;
if (height < mCollapsedHeight) {
@@ -297,8 +301,6 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
updateWidth();
updateVisibilities();
updateZTranslation();
- updatePadding();
- updateMultiUserSwitch();
}
public void setUserInfoController(UserInfoController userInfoController) {
@@ -338,4 +340,24 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
public boolean shouldDelayChildPressedState() {
return true;
}
+
+ public void setShowEmergencyCallsOnly(boolean show) {
+ mShowEmergencyCallsOnly = show;
+ if (mExpanded) {
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ }
+ }
+
+ public void setShowChargingInfo(boolean showChargingInfo) {
+ mShowChargingInfo = showChargingInfo;
+ if (mExpanded) {
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ }
+ }
+
+ public void setChargingInfo(CharSequence chargingInfo) {
+ mChargingInfo.setText(chargingInfo);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 6db9bc3..4cf66a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -16,26 +16,49 @@
package com.android.systemui.statusbar.policy;
+import com.android.internal.app.IBatteryStats;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.format.Formatter;
+import android.util.Log;
import java.util.ArrayList;
public class BatteryController extends BroadcastReceiver {
private static final String TAG = "StatusBar.BatteryController";
-
private ArrayList<BatteryStateChangeCallback> mChangeCallbacks =
new ArrayList<BatteryStateChangeCallback>();
+ private Context mContext;
+ private StatusBarHeaderView mStatusBarHeaderView;
+ private IBatteryStats mBatteryInfo;
+
+ private int mLevel;
+ private boolean mPluggedIn;
+ private boolean mCharging;
+ private boolean mCharged;
+
+
public interface BatteryStateChangeCallback {
- public void onBatteryLevelChanged(int level, boolean pluggedIn);
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
}
public BatteryController(Context context) {
+ mContext = context;
+
+ mBatteryInfo = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
context.registerReceiver(this, filter);
@@ -45,24 +68,59 @@ public class BatteryController extends BroadcastReceiver {
mChangeCallbacks.add(cb);
}
+ public void setStatusBarHeaderView(StatusBarHeaderView statusBarHeaderView) {
+ mStatusBarHeaderView = statusBarHeaderView;
+ updateStatusBarHeaderView();
+ }
+
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ mLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+ mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+
final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
+ mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
+ mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
- boolean plugged = false;
- switch (status) {
- case BatteryManager.BATTERY_STATUS_CHARGING:
- case BatteryManager.BATTERY_STATUS_FULL:
- plugged = true;
- break;
+ updateStatusBarHeaderView();
+ for (BatteryStateChangeCallback cb : mChangeCallbacks) {
+ cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
}
+ }
+ }
- for (BatteryStateChangeCallback cb : mChangeCallbacks) {
- cb.onBatteryLevelChanged(level, plugged);
+ private void updateStatusBarHeaderView() {
+ if (mStatusBarHeaderView != null) {
+ mStatusBarHeaderView.setShowChargingInfo(mPluggedIn);
+ mStatusBarHeaderView.setChargingInfo(computeChargingInfo());
+ }
+ }
+
+ private String computeChargingInfo() {
+ if (!mPluggedIn || !mCharged && !mCharging) {
+ return mContext.getResources().getString(R.string.expanded_header_battery_not_charging);
+ }
+
+ if (mCharged) {
+ return mContext.getResources().getString(R.string.expanded_header_battery_charged);
+ }
+
+ // Try fetching charging time from battery stats.
+ try {
+ long chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
+ if (chargingTimeRemaining > 0) {
+ String chargingTimeFormatted =
+ Formatter.formatShortElapsedTime(mContext, chargingTimeRemaining);
+ return mContext.getResources().getString(
+ R.string.expanded_header_battery_charging_with_time, chargingTimeFormatted);
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling IBatteryStats: ", e);
}
+
+ // Fall back to simple charging label.
+ return mContext.getResources().getString(R.string.expanded_header_battery_charging);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index cadb44a..f978833 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.widget.TextView;
@@ -29,8 +30,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
-import libcore.icu.ICU;
-
public class DateView extends TextView {
private static final String TAG = "DateView";
@@ -87,7 +86,7 @@ public class DateView extends TextView {
if (mDateFormat == null) {
final String dateFormat = getContext().getString(R.string.system_ui_date_pattern);
final Locale l = Locale.getDefault();
- final String fmt = ICU.getBestDateTimePattern(dateFormat, l.toString());
+ final String fmt = DateFormat.getBestDateTimePattern(l, dateFormat);
mDateFormat = new SimpleDateFormat(fmt, l);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 254a0e8..4e54e41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -47,6 +47,7 @@ import com.android.internal.telephony.cdma.EriInfo;
import com.android.internal.util.AsyncChannel;
import com.android.systemui.DemoMode;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -144,7 +145,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
ArrayList<TextView> mCombinedLabelViews = new ArrayList<TextView>();
ArrayList<TextView> mMobileLabelViews = new ArrayList<TextView>();
ArrayList<TextView> mWifiLabelViews = new ArrayList<TextView>();
- ArrayList<TextView> mEmergencyLabelViews = new ArrayList<TextView>();
+ ArrayList<StatusBarHeaderView> mEmergencyViews = new ArrayList<>();
ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
new ArrayList<NetworkSignalChangedCallback>();
@@ -262,8 +263,8 @@ public class NetworkControllerImpl extends BroadcastReceiver
mWifiLabelViews.add(v);
}
- public void addEmergencyLabelView(TextView v) {
- mEmergencyLabelViews.add(v);
+ public void addEmergencyLabelView(StatusBarHeaderView v) {
+ mEmergencyViews.add(v);
}
public void addSignalCluster(SignalCluster cluster) {
@@ -1254,15 +1255,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
}
// e-call label
- N = mEmergencyLabelViews.size();
+ N = mEmergencyViews.size();
for (int i=0; i<N; i++) {
- TextView v = mEmergencyLabelViews.get(i);
- if (!emergencyOnly) {
- v.setVisibility(View.GONE);
- } else {
- v.setText(mobileLabel); // comes from the telephony stack
- v.setVisibility(View.VISIBLE);
- }
+ StatusBarHeaderView v = mEmergencyViews.get(i);
+ v.setShowEmergencyCallsOnly(emergencyOnly);
}
}