summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/res/anim/recent_app_enter.xml30
-rw-r--r--packages/SystemUI/res/anim/recent_app_leave.xml30
-rw-r--r--packages/SystemUI/res/drawable/recent_overlay.pngbin0 -> 162325 bytes
-rw-r--r--packages/SystemUI/res/drawable/recent_rez_border.pngbin0 -> 21739 bytes
-rw-r--r--packages/SystemUI/res/layout/recents_detail_view.xml40
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java297
7 files changed, 330 insertions, 68 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f1f31cf..6057023 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -32,6 +32,7 @@
<activity android:name=".recent.RecentApplicationsActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:excludeFromRecents="true"
+ android:launchMode="singleInstance"
android:exported="true">
</activity>
diff --git a/packages/SystemUI/res/anim/recent_app_enter.xml b/packages/SystemUI/res/anim/recent_app_enter.xml
new file mode 100644
index 0000000..4947eee
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+ it starts at 200% and scales down. Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator">
+ <scale android:fromXScale="0.25" android:toXScale="1.0"
+ android:fromYScale="0.25" android:toYScale="1.0"
+ android:pivotX="0%p" android:pivotY="0%p"
+ android:duration="500" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/anim/recent_app_leave.xml b/packages/SystemUI/res/anim/recent_app_leave.xml
new file mode 100644
index 0000000..3d83988
--- /dev/null
+++ b/packages/SystemUI/res/anim/recent_app_leave.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<!-- Special window zoom animation: this is the element that enters the screen,
+ it starts at 200% and scales down. Goes with zoom_exit.xml. -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator">
+ <scale android:fromXScale="1.0" android:toXScale="0.25"
+ android:fromYScale="1.0" android:toYScale="0.25"
+ android:pivotX="0%p" android:pivotY="0%p"
+ android:duration="500" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="500"/>
+</set>
diff --git a/packages/SystemUI/res/drawable/recent_overlay.png b/packages/SystemUI/res/drawable/recent_overlay.png
new file mode 100644
index 0000000..4dfa3d9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_overlay.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/recent_rez_border.png b/packages/SystemUI/res/drawable/recent_rez_border.png
new file mode 100644
index 0000000..ad025f5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recent_rez_border.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/recents_detail_view.xml b/packages/SystemUI/res/layout/recents_detail_view.xml
new file mode 100644
index 0000000..879d0f2
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_detail_view.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <!-- Application Title -->
+ <TextView android:id="@+id/app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:singleLine="true"/>
+
+ <!-- Application Details -->
+ <TextView
+ android:id="@+id/app_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
index 9cc24be..bf24a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentApplicationsActivity.java
@@ -20,7 +20,9 @@ package com.android.systemui.recent;
import com.android.systemui.R;
import com.android.ex.carousel.CarouselView;
+import com.android.ex.carousel.CarouselViewHelper;
import com.android.ex.carousel.CarouselRS.CarouselCallback;
+import com.android.ex.carousel.CarouselViewHelper.DetailTextureParameters;
import java.util.ArrayList;
import java.util.List;
@@ -38,37 +40,104 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.PorterDuff;
import android.graphics.Bitmap.Config;
import android.graphics.drawable.Drawable;
import android.graphics.PixelFormat;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
+import android.view.View.MeasureSpec;
+import android.widget.TextView;
public class RecentApplicationsActivity extends Activity {
private static final String TAG = "RecentApplicationsActivity";
- private static boolean DBG = true;
+ private static boolean DBG = false;
private static final int CARD_SLOTS = 56;
private static final int VISIBLE_SLOTS = 7;
private static final int MAX_TASKS = VISIBLE_SLOTS * 2;
+
+ // TODO: these should be configurable
+ private static final int DETAIL_TEXTURE_MAX_WIDTH = 200;
+ private static final int DETAIL_TEXTURE_MAX_HEIGHT = 80;
+ private static final int TEXTURE_WIDTH = 256;
+ private static final int TEXTURE_HEIGHT = 256;
+
private ActivityManager mActivityManager;
private List<RunningTaskInfo> mRunningTaskList;
private boolean mPortraitMode = true;
private ArrayList<ActivityDescription> mActivityDescriptions
= new ArrayList<ActivityDescription>();
private CarouselView mCarouselView;
+ private LocalCarouselViewHelper mHelper;
private View mNoRecentsView;
- private Bitmap mBlankBitmap = Bitmap.createBitmap(
- new int[] {0xff808080, 0xffffffff, 0xff808080, 0xffffffff}, 2, 2, Config.RGB_565);
+ private Bitmap mLoadingBitmap;
+ private Bitmap mRecentOverlay;
+ private boolean mHidden = false;
+ private boolean mHiding = false;
+ private DetailInfo mDetailInfo;
+
+ /**
+ * This class is a container for all items associated with the DetailView we'll
+ * be drawing to a bitmap and sending to Carousel.
+ *
+ */
+ static final class DetailInfo {
+ public DetailInfo(View _view, TextView _title, TextView _desc) {
+ view = _view;
+ title = _title;
+ description = _desc;
+ }
+
+ /**
+ * Draws view into the given bitmap, if provided
+ * @param bitmap
+ */
+ public Bitmap draw(Bitmap bitmap) {
+ resizeView(view, DETAIL_TEXTURE_MAX_WIDTH, DETAIL_TEXTURE_MAX_HEIGHT);
+ int desiredWidth = view.getWidth();
+ int desiredHeight = view.getHeight();
+ if (bitmap == null || desiredWidth != bitmap.getWidth()
+ || desiredHeight != bitmap.getHeight()) {
+ bitmap = Bitmap.createBitmap(desiredWidth, desiredHeight, Config.ARGB_8888);
+ }
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ return bitmap;
+ }
+
+ /**
+ * Force a layout pass on the given view.
+ */
+ private void resizeView(View view, int maxWidth, int maxHeight) {
+ int widthSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+ | MeasureSpec.getSize(maxWidth);
+ int heightSpec = MeasureSpec.getMode(MeasureSpec.AT_MOST)
+ | MeasureSpec.getSize(maxHeight);
+ view.measure(widthSpec, heightSpec);
+ view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
+ Log.v(TAG, "RESIZED VIEW: " + view.getWidth() + ", " + view.getHeight());
+ }
+
+ public View view;
+ public TextView title;
+ public TextView description;
+ }
static class ActivityDescription {
int id;
Bitmap thumbnail; // generated by Activity.onCreateThumbnail()
Drawable icon; // application package icon
String label; // application package label
- String description; // generated by Activity.onCreateDescription()
+ CharSequence description; // generated by Activity.onCreateDescription()
Intent intent; // launch intent for application
Matrix matrix; // arbitrary rotation matrix to correct orientation
int position; // position in list
@@ -106,14 +175,17 @@ public class RecentApplicationsActivity extends Activity {
return null;
}
- final CarouselCallback mCarouselCallback = new CarouselCallback() {
-
- public void onAnimationFinished() {
+ private class LocalCarouselViewHelper extends CarouselViewHelper {
+ private Paint mPaint = new Paint();
+ private DetailTextureParameters mDetailParams = new DetailTextureParameters(10.0f, 20.0f);
+ public LocalCarouselViewHelper(Context context) {
+ super(context);
}
- public void onAnimationStarted() {
-
+ @Override
+ public DetailTextureParameters getDetailTextureParameters(int id) {
+ return mDetailParams;
}
public void onCardSelected(int n) {
@@ -125,7 +197,7 @@ public class RecentApplicationsActivity extends Activity {
try {
if (DBG) Log.v(TAG, "Starting intent " + item.intent);
startActivity(item.intent);
- //overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
+ overridePendingTransition(R.anim.recent_app_enter, R.anim.recent_app_leave);
} catch (ActivityNotFoundException e) {
if (DBG) Log.w("Recent", "Unable to launch recent task", e);
}
@@ -134,48 +206,88 @@ public class RecentApplicationsActivity extends Activity {
}
}
- public void onInvalidateTexture(int n) {
-
- }
-
- public void onRequestGeometry(int n) {
-
- }
-
- public void onInvalidateGeometry(int n) {
-
+ @Override
+ public Bitmap getTexture(final int id) {
+ if (DBG) Log.v(TAG, "onRequestTexture(" + id + ")");
+ ActivityDescription info;
+ synchronized(mActivityDescriptions) {
+ info = mActivityDescriptions.get(id);
+ }
+ Bitmap bitmap = null;
+ if (info != null) {
+ bitmap = compositeBitmap(info);
+ }
+ return bitmap;
}
- public void onRequestTexture(final int n) {
- if (DBG) Log.v(TAG, "onRequestTexture(" + n + ")");
+ @Override
+ public Bitmap getDetailTexture(int n) {
+ Bitmap bitmap = null;
if (n < mActivityDescriptions.size()) {
- mCarouselView.post(new Runnable() {
- public void run() {
- ActivityDescription info = mActivityDescriptions.get(n);
- if (info != null) {
- if (DBG) Log.v(TAG, "FOUND ACTIVITY THUMBNAIL " + info.thumbnail);
- Bitmap bitmap = info.thumbnail == null ? mBlankBitmap : info.thumbnail;
- mCarouselView.setTextureForItem(n, bitmap);
- } else {
- if (DBG) Log.v(TAG, "FAILED TO GET ACTIVITY THUMBNAIL FOR ITEM " + n);
- }
- }
- });
+ ActivityDescription item = mActivityDescriptions.get(n);
+ mDetailInfo.title.setText(item.label);
+ mDetailInfo.description.setText(item.description);
+ bitmap = mDetailInfo.draw(null);
}
+ return bitmap;
}
+ };
- public void onInvalidateDetailTexture(int n) {
-
+ private Bitmap compositeBitmap(ActivityDescription info) {
+ final int targetWidth = TEXTURE_WIDTH;
+ final int targetHeight = TEXTURE_HEIGHT;
+ final int border = 3; // inset along the edge for thumnnail content
+ final int overlap = 1; // how many pixels of overlap between border and thumbnail
+ final Resources res = getResources();
+ if (mRecentOverlay == null) {
+ mRecentOverlay = BitmapFactory.decodeResource(res, R.drawable.recent_overlay);
}
- public void onRequestDetailTexture(int n) {
-
+ // Create a bitmap of the proper size/format and set the canvas to draw to it
+ final Bitmap result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(result);
+ canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG));
+ Paint paint = new Paint();
+ paint.setFilterBitmap(false);
+
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ canvas.save();
+ if (info.thumbnail != null) {
+ // Draw the thumbnail
+ int sourceWidth = targetWidth - 2 * (border - overlap);
+ int sourceHeight = targetHeight - 2 * (border - overlap);
+ final float scaleX = (float) sourceWidth / info.thumbnail.getWidth();
+ final float scaleY = (float) sourceHeight / info.thumbnail.getHeight();
+ canvas.translate(border * 0.5f, border * 0.5f);
+ canvas.scale(scaleX, scaleY);
+ canvas.drawBitmap(info.thumbnail, 0, 0, paint);
+ } else {
+ // Draw the Loading bitmap placeholder, TODO: Remove when RS handles blending
+ final float scaleX = (float) targetWidth / mLoadingBitmap.getWidth();
+ final float scaleY = (float) targetHeight / mLoadingBitmap.getHeight();
+ canvas.scale(scaleX, scaleY);
+ canvas.drawBitmap(mLoadingBitmap, 0, 0, paint);
}
-
- public void onReportFirstCardPosition(int n) {
-
+ canvas.restore();
+
+ // Draw overlay
+ canvas.save();
+ final float scaleOverlayX = (float) targetWidth / mRecentOverlay.getWidth();
+ final float scaleOverlayY = (float) targetHeight / mRecentOverlay.getHeight();
+ canvas.scale(scaleOverlayX, scaleOverlayY);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+ canvas.drawBitmap(mRecentOverlay, 0, 0, paint);
+ canvas.restore();
+
+ // Draw icon
+ if (info.icon != null) {
+ canvas.save();
+ info.icon.draw(canvas);
+ canvas.restore();
}
- };
+
+ return result;
+ }
private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
@@ -192,6 +304,7 @@ public class RecentApplicationsActivity extends Activity {
ActivityDescription info = findActivityDescription(id);
if (info != null) {
info.thumbnail = bitmap;
+ info.description = description;
final int thumbWidth = bitmap.getWidth();
final int thumbHeight = bitmap.getHeight();
if ((mPortraitMode && thumbWidth > thumbHeight)
@@ -202,13 +315,34 @@ public class RecentApplicationsActivity extends Activity {
} else {
info.matrix = null;
}
- mCarouselView.setTextureForItem(info.position, info.thumbnail);
+ mCarouselView.setTextureForItem(info.position, compositeBitmap(info));
} else {
if (DBG) Log.v(TAG, "Can't find view for id " + id);
}
}
};
+ /**
+ * We never really finish() RecentApplicationsActivity, since we don't want to
+ * get destroyed and pay the start-up cost to restart it.
+ */
+ @Override
+ public void finish() {
+ moveTaskToBack(true);
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ mHidden = !mHidden;
+ if (mHidden) {
+ mHiding = true;
+ moveTaskToBack(true);
+ } else {
+ mHiding = false;
+ }
+ super.onNewIntent(intent);
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -218,25 +352,35 @@ public class RecentApplicationsActivity extends Activity {
getWindow().getDecorView().setBackgroundColor(0x80000000);
setContentView(R.layout.recent_apps_activity);
- mCarouselView = (CarouselView)findViewById(R.id.carousel);
- mNoRecentsView = (View) findViewById(R.id.no_applications_message);
- //mCarouselView = new CarouselView(this);
- //setContentView(mCarouselView);
- mCarouselView.setSlotCount(CARD_SLOTS);
- mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
- mCarouselView.createCards(1);
- mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
- mCarouselView.setDefaultBitmap(mBlankBitmap);
- mCarouselView.setLoadingBitmap(mBlankBitmap);
- mCarouselView.setCallback(mCarouselCallback);
- mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
-
- mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- mPortraitMode = decorView.getHeight() > decorView.getWidth();
- refresh();
+ if (mCarouselView == null) {
+ mLoadingBitmap = BitmapFactory.decodeResource(res, R.drawable.recent_rez_border);
+ mCarouselView = (CarouselView)findViewById(R.id.carousel);
+ mHelper = new LocalCarouselViewHelper(this);
+ mHelper.setCarouselView(mCarouselView);
+
+ mCarouselView.setSlotCount(CARD_SLOTS);
+ mCarouselView.setVisibleSlots(VISIBLE_SLOTS);
+ mCarouselView.createCards(0);
+ mCarouselView.setStartAngle((float) -(2.0f*Math.PI * 5 / CARD_SLOTS));
+ mCarouselView.setDefaultBitmap(mLoadingBitmap);
+ mCarouselView.setLoadingBitmap(mLoadingBitmap);
+ mCarouselView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
+
+ mNoRecentsView = (View) findViewById(R.id.no_applications_message);
+
+ mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
+ mPortraitMode = decorView.getHeight() > decorView.getWidth();
+
+ // Load detail view which will be used to render text
+ View detail = getLayoutInflater().inflate(R.layout.recents_detail_view, null);
+ TextView title = (TextView) detail.findViewById(R.id.app_title);
+ TextView description = (TextView) detail.findViewById(R.id.app_description);
+ mDetailInfo = new DetailInfo(detail, title, description);
+ refresh();
+ }
}
@Override
@@ -264,7 +408,7 @@ public class RecentApplicationsActivity extends Activity {
ActivityDescription desc = findActivityDescription(r.id);
if (desc != null) {
desc.thumbnail = r.thumbnail;
- desc.label = r.topActivity.flattenToShortString();
+ desc.description = r.description;
if ((mPortraitMode && thumbWidth > thumbHeight)
|| (!mPortraitMode && thumbWidth < thumbHeight)) {
Matrix matrix = new Matrix();
@@ -336,17 +480,34 @@ public class RecentApplicationsActivity extends Activity {
}
}
- private void refresh() {
- updateRecentTasks();
- updateRunningTasks();
- if (mActivityDescriptions.size() == 0) {
- // show "No Recent Takss"
- mNoRecentsView.setVisibility(View.VISIBLE);
- mCarouselView.setVisibility(View.GONE);
- } else {
+ private final Runnable mRefreshRunnable = new Runnable() {
+ public void run() {
+ updateRecentTasks();
+ updateRunningTasks();
+ showCarousel(mActivityDescriptions.size() > 0);
+ }
+ };
+
+ private void showCarousel(boolean show) {
+ if (show) {
+ // Make carousel visible
mNoRecentsView.setVisibility(View.GONE);
mCarouselView.setVisibility(View.VISIBLE);
mCarouselView.createCards(mActivityDescriptions.size());
+ } else {
+ // show "No Recent Tasks"
+ mNoRecentsView.setVisibility(View.VISIBLE);
+ mCarouselView.setVisibility(View.GONE);
+ }
+ }
+
+ private void refresh() {
+ if (!mHiding && mCarouselView != null) {
+ // Don't update the view now. Instead, post a request so it happens next time
+ // we reach the looper after a delay. This way we can fold multiple refreshes
+ // into just the latest.
+ mCarouselView.removeCallbacks(mRefreshRunnable);
+ mCarouselView.postDelayed(mRefreshRunnable, 50);
}
}
}