summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Onorato <joeo@android.com>2010-04-05 16:15:37 -0500
committerJoe Onorato <joeo@android.com>2010-04-05 16:16:40 -0500
commit7d8866f9ef026870d4735d6f2ede49525150e53e (patch)
tree94d68b33618b3243ab655637c697e16d01e9f7cc
parent876b0644770d1790b21e4049bc7f40851045dc86 (diff)
downloadbase-7d8866f9ef026870d4735d6f2ede49525150e53e.tar.gz
Redo the look of the recent apps switcher.
Change-Id: Icb523e2f5949c2b502aa8003aa14b7ac98a616f2
-rw-r--r--phone/com/android/internal/policy/impl/IconUtilities.java192
-rw-r--r--phone/com/android/internal/policy/impl/RecentApplicationsBackground.java152
-rw-r--r--phone/com/android/internal/policy/impl/RecentApplicationsDialog.java89
3 files changed, 387 insertions, 46 deletions
diff --git a/phone/com/android/internal/policy/impl/IconUtilities.java b/phone/com/android/internal/policy/impl/IconUtilities.java
new file mode 100644
index 0000000..99055cf
--- /dev/null
+++ b/phone/com/android/internal/policy/impl/IconUtilities.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.PaintDrawable;
+import android.graphics.drawable.StateListDrawable;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.TableMaskFilter;
+import android.graphics.Typeface;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.content.res.Resources;
+import android.content.Context;
+
+/**
+ * Various utilities shared amongst the Launcher's classes.
+ */
+final class IconUtilities {
+ private static final String TAG = "IconUtilities";
+
+ private static final int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
+
+ private int mIconWidth = -1;
+ private int mIconHeight = -1;
+ private int mIconTextureWidth = -1;
+ private int mIconTextureHeight = -1;
+
+ private final Paint mPaint = new Paint();
+ private final Paint mBlurPaint = new Paint();
+ private final Paint mGlowColorPressedPaint = new Paint();
+ private final Paint mGlowColorFocusedPaint = new Paint();
+ private final Rect mOldBounds = new Rect();
+ private final Canvas mCanvas = new Canvas();
+ private final DisplayMetrics mDisplayMetrics;
+
+ private int mColorIndex = 0;
+
+ public IconUtilities(Context context) {
+ final Resources resources = context.getResources();
+ DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
+ final float density = metrics.density;
+ final float blurPx = 5 * density;
+
+ mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
+ mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
+
+ mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL));
+ mGlowColorPressedPaint.setColor(0xffffc300);
+ mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
+ mGlowColorFocusedPaint.setColor(0xffff8e00);
+ mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
+
+ ColorMatrix cm = new ColorMatrix();
+ cm.setSaturation(0.2f);
+
+ mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
+ Paint.FILTER_BITMAP_FLAG));
+ }
+
+ public Drawable createIconDrawable(Drawable src) {
+ Bitmap scaled = createIconBitmap(src);
+
+ StateListDrawable result = new StateListDrawable();
+
+ result.addState(new int[] { android.R.attr.state_focused },
+ new BitmapDrawable(createSelectedBitmap(scaled, false)));
+ result.addState(new int[] { android.R.attr.state_pressed },
+ new BitmapDrawable(createSelectedBitmap(scaled, true)));
+ result.addState(new int[0], new BitmapDrawable(scaled));
+
+ result.setBounds(0, 0, mIconTextureWidth, mIconTextureHeight);
+ return result;
+ }
+
+ /**
+ * Returns a bitmap suitable for the all apps view. The bitmap will be a power
+ * of two sized ARGB_8888 bitmap that can be used as a gl texture.
+ */
+ private Bitmap createIconBitmap(Drawable icon) {
+ int width = mIconWidth;
+ int height = mIconHeight;
+
+ if (icon instanceof PaintDrawable) {
+ PaintDrawable painter = (PaintDrawable) icon;
+ painter.setIntrinsicWidth(width);
+ painter.setIntrinsicHeight(height);
+ } else if (icon instanceof BitmapDrawable) {
+ // Ensure the bitmap has a density.
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
+ Bitmap bitmap = bitmapDrawable.getBitmap();
+ if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
+ bitmapDrawable.setTargetDensity(mDisplayMetrics);
+ }
+ }
+ int sourceWidth = icon.getIntrinsicWidth();
+ int sourceHeight = icon.getIntrinsicHeight();
+
+ if (sourceWidth > 0 && sourceWidth > 0) {
+ // There are intrinsic sizes.
+ if (width < sourceWidth || height < sourceHeight) {
+ // It's too big, scale it down.
+ final float ratio = (float) sourceWidth / sourceHeight;
+ if (sourceWidth > sourceHeight) {
+ height = (int) (width / ratio);
+ } else if (sourceHeight > sourceWidth) {
+ width = (int) (height * ratio);
+ }
+ } else if (sourceWidth < width && sourceHeight < height) {
+ // It's small, use the size they gave us.
+ width = sourceWidth;
+ height = sourceHeight;
+ }
+ }
+
+ // no intrinsic size --> use default size
+ int textureWidth = mIconTextureWidth;
+ int textureHeight = mIconTextureHeight;
+
+ final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
+ Bitmap.Config.ARGB_8888);
+ final Canvas canvas = mCanvas;
+ canvas.setBitmap(bitmap);
+
+ final int left = (textureWidth-width) / 2;
+ final int top = (textureHeight-height) / 2;
+
+ if (false) {
+ // draw a big box for the icon for debugging
+ canvas.drawColor(sColors[mColorIndex]);
+ if (++mColorIndex >= sColors.length) mColorIndex = 0;
+ Paint debugPaint = new Paint();
+ debugPaint.setColor(0xffcccc00);
+ canvas.drawRect(left, top, left+width, top+height, debugPaint);
+ }
+
+ mOldBounds.set(icon.getBounds());
+ icon.setBounds(left, top, left+width, top+height);
+ icon.draw(canvas);
+ icon.setBounds(mOldBounds);
+
+ return bitmap;
+ }
+
+ private Bitmap createSelectedBitmap(Bitmap src, boolean pressed) {
+ final Bitmap result = Bitmap.createBitmap(mIconTextureWidth, mIconTextureHeight,
+ Bitmap.Config.ARGB_8888);
+ final Canvas dest = new Canvas(result);
+
+ dest.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ int[] xy = new int[2];
+ Bitmap mask = src.extractAlpha(mBlurPaint, xy);
+
+ dest.drawBitmap(mask, xy[0], xy[1],
+ pressed ? mGlowColorPressedPaint : mGlowColorFocusedPaint);
+
+ mask.recycle();
+
+ dest.drawBitmap(src, 0, 0, mPaint);
+
+ return result;
+ }
+}
diff --git a/phone/com/android/internal/policy/impl/RecentApplicationsBackground.java b/phone/com/android/internal/policy/impl/RecentApplicationsBackground.java
new file mode 100644
index 0000000..7c99e87
--- /dev/null
+++ b/phone/com/android/internal/policy/impl/RecentApplicationsBackground.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A vertical linear layout. However, instead of drawing the background
+ * behnd the items, it draws the background outside the items based on the
+ * padding. If there isn't enough room to draw both, it clips the background
+ * instead of the contents.
+ */
+public class RecentApplicationsBackground extends LinearLayout {
+ private static final String TAG = "RecentApplicationsBackground";
+
+ private boolean mBackgroundSizeChanged;
+ private Drawable mBackground;
+ private Rect mTmp0 = new Rect();
+ private Rect mTmp1 = new Rect();
+
+ public RecentApplicationsBackground(Context context) {
+ this(context, null);
+ init();
+ }
+
+ public RecentApplicationsBackground(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ mBackground = getBackground();
+ setBackgroundDrawable(null);
+ setPadding(0, 0, 0, 0);
+ setGravity(Gravity.CENTER);
+ }
+
+ @Override
+ protected boolean setFrame(int left, int top, int right, int bottom) {
+ setWillNotDraw(false);
+ if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
+ mBackgroundSizeChanged = true;
+ }
+ return super.setFrame(left, top, right, bottom);
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mBackground || super.verifyDrawable(who);
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ Drawable d = mBackground;
+ if (d != null && d.isStateful()) {
+ d.setState(getDrawableState());
+ }
+ super.drawableStateChanged();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final Drawable background = mBackground;
+ if (background != null) {
+ if (mBackgroundSizeChanged) {
+ mBackgroundSizeChanged = false;
+ Rect chld = mTmp0;
+ Rect bkg = mTmp1;
+ mBackground.getPadding(bkg);
+ getChildBounds(chld);
+ // This doesn't clamp to this view's bounds, which is what we want,
+ // so that the drawing is clipped.
+ final int top = chld.top - bkg.top;
+ final int bottom = chld.bottom + bkg.bottom;
+ // The background here is a gradient that wants to
+ // extend the full width of the screen (whatever that
+ // may be).
+ int left, right;
+ if (false) {
+ // This limits the width of the drawable.
+ left = chld.left - bkg.left;
+ right = chld.right + bkg.right;
+ } else {
+ // This expands it to full width.
+ left = 0;
+ right = getRight();
+ }
+ background.setBounds(left, top, right, bottom);
+ }
+ }
+ mBackground.draw(canvas);
+
+ if (false) {
+ android.graphics.Paint p = new android.graphics.Paint();
+ p.setColor(0x88ffff00);
+ canvas.drawRect(background.getBounds(), p);
+ }
+ canvas.drawARGB((int)(0.75*0xff), 0, 0, 0);
+
+ super.draw(canvas);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mBackground.setCallback(this);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mBackground.setCallback(null);
+ }
+
+ private void getChildBounds(Rect r) {
+ r.left = r.top = Integer.MAX_VALUE;
+ r.bottom = r.right = Integer.MIN_VALUE;
+ final int N = getChildCount();
+ for (int i=0; i<N; i++) {
+ View v = getChildAt(i);
+ if (v.getVisibility() == View.VISIBLE) {
+ r.left = Math.min(r.left, v.getLeft());
+ r.top = Math.min(r.top, v.getTop());
+ r.right = Math.max(r.right, v.getRight());
+ r.bottom = Math.max(r.bottom, v.getBottom());
+ }
+ }
+ }
+}
diff --git a/phone/com/android/internal/policy/impl/RecentApplicationsDialog.java b/phone/com/android/internal/policy/impl/RecentApplicationsDialog.java
index c5aafa5..8287253 100644
--- a/phone/com/android/internal/policy/impl/RecentApplicationsDialog.java
+++ b/phone/com/android/internal/policy/impl/RecentApplicationsDialog.java
@@ -44,14 +44,13 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
static private StatusBarManager sStatusBar;
- private static final int NUM_BUTTONS = 6;
+ private static final int NUM_BUTTONS = 8;
private static final int MAX_RECENT_TASKS = NUM_BUTTONS * 2; // allow for some discards
- final View[] mButtons = new View[NUM_BUTTONS];
+ final TextView[] mIcons = new TextView[NUM_BUTTONS];
View mNoAppsText;
IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-
private int mIconSize;
public RecentApplicationsDialog(Context context) {
@@ -77,26 +76,32 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
}
- Window theWindow = getWindow();
- theWindow.requestFeature(Window.FEATURE_NO_TITLE);
- theWindow.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
- theWindow.setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND,
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- theWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ Window window = getWindow();
+ window.requestFeature(Window.FEATURE_NO_TITLE);
+ window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+ window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
- theWindow.setTitle("Recents");
+ window.setTitle("Recents");
setContentView(com.android.internal.R.layout.recent_apps_dialog);
- mButtons[0] = findViewById(com.android.internal.R.id.button1);
- mButtons[1] = findViewById(com.android.internal.R.id.button2);
- mButtons[2] = findViewById(com.android.internal.R.id.button3);
- mButtons[3] = findViewById(com.android.internal.R.id.button4);
- mButtons[4] = findViewById(com.android.internal.R.id.button5);
- mButtons[5] = findViewById(com.android.internal.R.id.button6);
+ final WindowManager.LayoutParams params = window.getAttributes();
+ params.width = WindowManager.LayoutParams.MATCH_PARENT;
+ params.height = WindowManager.LayoutParams.MATCH_PARENT;
+ window.setAttributes(params);
+ window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+
+ mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0);
+ mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1);
+ mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2);
+ mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3);
+ mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4);
+ mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5);
+ mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6);
+ mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7);
mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);
- for (View b : mButtons) {
+ for (TextView b: mIcons) {
b.setOnClickListener(this);
}
}
@@ -106,7 +111,7 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
*/
public void onClick(View v) {
- for (View b : mButtons) {
+ for (TextView b: mIcons) {
if (b == v) {
// prepare a launch intent and send it
Intent intent = (Intent)b.getTag();
@@ -143,9 +148,9 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
super.onStop();
// dump extra memory we're hanging on to
- for (View b : mButtons) {
- setButtonAppearance(b, null, null);
- b.setTag(null);
+ for (TextView icon: mIcons) {
+ icon.setCompoundDrawables(null, null, null, null);
+ icon.setTag(null);
}
if (sStatusBar != null) {
@@ -172,12 +177,14 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME),
0);
+ IconUtilities iconUtilities = new IconUtilities(getContext());
+
// Performance note: Our android performance guide says to prefer Iterator when
// using a List class, but because we know that getRecentTasks() always returns
// an ArrayList<>, we'll use a simple index instead.
- int button = 0;
+ int index = 0;
int numTasks = recentTasks.size();
- for (int i = 0; i < numTasks && (button < NUM_BUTTONS); ++i) {
+ for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
final ActivityManager.RecentTaskInfo info = recentTasks.get(i);
// for debug purposes only, disallow first result to create empty lists
@@ -204,39 +211,29 @@ public class RecentApplicationsDialog extends Dialog implements OnClickListener
if (resolveInfo != null) {
final ActivityInfo activityInfo = resolveInfo.activityInfo;
final String title = activityInfo.loadLabel(pm).toString();
- final Drawable icon = activityInfo.loadIcon(pm);
+ Drawable icon = activityInfo.loadIcon(pm);
if (title != null && title.length() > 0 && icon != null) {
- final View b = mButtons[button];
- setButtonAppearance(b, title, icon);
- b.setTag(intent);
- b.setVisibility(View.VISIBLE);
- b.setPressed(false);
- b.clearFocus();
- ++button;
+ final TextView tv = mIcons[index];
+ tv.setText(title);
+ icon = iconUtilities.createIconDrawable(icon);
+ tv.setCompoundDrawables(null, icon, null, null);
+ tv.setTag(intent);
+ tv.setVisibility(View.VISIBLE);
+ tv.setPressed(false);
+ tv.clearFocus();
+ ++index;
}
}
}
// handle the case of "no icons to show"
- mNoAppsText.setVisibility((button == 0) ? View.VISIBLE : View.GONE);
+ mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);
// hide the rest
- for ( ; button < NUM_BUTTONS; ++button) {
- mButtons[button].setVisibility(View.GONE);
- }
- }
-
- /**
- * Adjust appearance of each icon-button
- */
- private void setButtonAppearance(View theButton, final String theTitle, final Drawable icon) {
- TextView tv = (TextView) theButton;
- tv.setText(theTitle);
- if (icon != null) {
- icon.setBounds(0, 0, mIconSize, mIconSize);
+ for (; index < NUM_BUTTONS; ++index) {
+ mIcons[index].setVisibility(View.GONE);
}
- tv.setCompoundDrawables(null, icon, null, null);
}
/**