diff options
| author | Michael Jurka <mikejurka@google.com> | 2012-04-13 09:32:47 -0700 |
|---|---|---|
| committer | Michael Jurka <mikejurka@google.com> | 2012-08-23 04:53:53 -0700 |
| commit | cb2522c86d75fff277dc38ec7e444a5b5f5130ea (patch) | |
| tree | 0453cda9b18962343ed09084889963db65944e3a /packages | |
| parent | 257662efe2a5edd13601b4372b5c1ff0431ddda9 (diff) | |
| download | frameworks_base-cb2522c86d75fff277dc38ec7e444a5b5f5130ea.zip frameworks_base-cb2522c86d75fff277dc38ec7e444a5b5f5130ea.tar.gz frameworks_base-cb2522c86d75fff277dc38ec7e444a5b5f5130ea.tar.bz2 | |
Recents: apps scale down to thumbnails now
As a part of this change, Recents is now an
activity.
Known issues:
* Jank: jump-cut as app icon appears suddenly
after the aniamtion
* Preloading recents is broken on phones without
soft nav bar and on tablets
* Thumbnail window from animation lingers/flashes
sometimes
Change-Id: Ie6f991f3c2e1e67f9ed84eb6adba9174ed957248
Diffstat (limited to 'packages')
16 files changed, 635 insertions, 609 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2eee31d..c18f76c 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -53,6 +53,7 @@ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <application + android:name="com.android.systemui.SystemUIApplication" android:persistent="true" android:allowClearUserData="false" android:allowBackup="false" @@ -96,6 +97,16 @@ android:excludeFromRecents="true"> </activity> + <activity android:name=".recent.RecentsActivity" + android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" + android:excludeFromRecents="true" + android:launchMode="singleInstance" + android:exported="true"> + <intent-filter> + <action android:name="com.android.systemui.TOGGLE_RECENTS" /> + </intent-filter> + </activity> + <!-- started from UsbDeviceSettingsManager --> <activity android:name=".usb.UsbConfirmActivity" android:exported="true" diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml index 00e3e27..2df9f6c 100644 --- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml @@ -26,11 +26,6 @@ android:layout_width="match_parent" systemui:recentItemLayout="@layout/status_bar_recent_item" > - <View - android:id="@+id/recents_transition_background" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:visibility="invisible" /> <FrameLayout android:id="@+id/recents_bg_protect" android:background="@drawable/status_bar_recents_background" @@ -40,12 +35,6 @@ android:clipToPadding="false" android:clipChildren="false"> - <ImageView - android:id="@+id/recents_transition_placeholder_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="invisible" /> - <com.android.systemui.recent.RecentsHorizontalScrollView android:id="@+id/recents_container" android:layout_width="wrap_content" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/status_bar_recent_panel.xml b/packages/SystemUI/res/layout/status_bar_recent_panel.xml index a7e5db1..7335f86 100644 --- a/packages/SystemUI/res/layout/status_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout/status_bar_recent_panel.xml @@ -26,11 +26,6 @@ android:layout_width="match_parent" systemui:recentItemLayout="@layout/status_bar_recent_item" > - <View - android:id="@+id/recents_transition_background" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:visibility="invisible" /> <FrameLayout android:id="@+id/recents_bg_protect" android:background="@drawable/status_bar_recents_background" @@ -38,12 +33,6 @@ android:layout_height="match_parent" android:layout_alignParentBottom="true"> - <ImageView - android:id="@+id/recents_transition_placeholder_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="invisible" /> - <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/system_bar_recent_panel.xml b/packages/SystemUI/res/layout/system_bar_recent_panel.xml index 127551d..3951bba 100644 --- a/packages/SystemUI/res/layout/system_bar_recent_panel.xml +++ b/packages/SystemUI/res/layout/system_bar_recent_panel.xml @@ -28,11 +28,6 @@ android:clipChildren="false" systemui:recentItemLayout="@layout/system_bar_recent_item" > - <View - android:id="@+id/recents_transition_background" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:visibility="invisible" /> <FrameLayout android:id="@+id/recents_bg_protect" android:background="@drawable/recents_bg_protect_tile" @@ -42,11 +37,6 @@ android:layout_marginBottom="@*android:dimen/system_bar_height" android:clipToPadding="false" android:clipChildren="false"> - <ImageView - android:id="@+id/recents_transition_placeholder_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:visibility="invisible" /> <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9539373..b5f1a33 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -28,6 +28,7 @@ <!-- Size of application thumbnail --> <dimen name="status_bar_recents_thumbnail_width">164dp</dimen> <dimen name="status_bar_recents_thumbnail_height">145dp</dimen> + <dimen name="status_bar_recents_thumbnail_bg_padding">4dp</dimen> <!-- Size of application label text --> <dimen name="status_bar_recents_app_label_text_size">14dip</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java new file mode 100644 index 0000000..f97d4ff --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 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; + +import android.app.Application; + +import com.android.systemui.recent.RecentTasksLoader; + +public class SystemUIApplication extends Application { + private RecentTasksLoader mRecentTasksLoader; + + public RecentTasksLoader getRecentTasksLoader() { + if (mRecentTasksLoader == null) { + mRecentTasksLoader = new RecentTasksLoader(this); + } + return mRecentTasksLoader; + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java deleted file mode 100644 index 18ad682..0000000 --- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2011 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.recent; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.AnimatorSet.Builder; -import android.animation.ObjectAnimator; -import android.content.res.Resources; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.util.Slog; -import android.view.View; - -import com.android.systemui.R; - -/* package */ class Choreographer implements Animator.AnimatorListener { - // should group this into a multi-property animation - private static final int OPEN_DURATION = 136; - private static final int CLOSE_DURATION = 230; - private static final int SCRIM_DURATION = 400; - private static final String TAG = RecentsPanelView.TAG; - private static final boolean DEBUG = RecentsPanelView.DEBUG; - - boolean mVisible; - int mPanelHeight; - RecentsPanelView mRootView; - View mScrimView; - View mContentView; - View mNoRecentAppsView; - AnimatorSet mContentAnim; - Animator.AnimatorListener mListener; - - // the panel will start to appear this many px from the end - final int HYPERSPACE_OFFRAMP = 200; - - public Choreographer(RecentsPanelView root, View scrim, View content, - View noRecentApps, Animator.AnimatorListener listener) { - mRootView = root; - mScrimView = scrim; - mContentView = content; - mListener = listener; - mNoRecentAppsView = noRecentApps; - } - - void createAnimation(boolean appearing) { - float start, end; - - // 0: on-screen - // height: off-screen - float y = mContentView.getTranslationY(); - if (appearing) { - // we want to go from near-the-top to the top, unless we're half-open in the right - // general vicinity - start = (y < HYPERSPACE_OFFRAMP) ? y : HYPERSPACE_OFFRAMP; - end = 0; - } else { - start = y; - end = y; - } - - Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY", - start, end); - posAnim.setInterpolator(appearing - ? new android.view.animation.DecelerateInterpolator(2.5f) - : new android.view.animation.AccelerateInterpolator(2.5f)); - posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); - - Animator fadeAnim = ObjectAnimator.ofFloat(mContentView, "alpha", - mContentView.getAlpha(), appearing ? 1.0f : 0.0f); - fadeAnim.setInterpolator(appearing - ? new android.view.animation.AccelerateInterpolator(1.0f) - : new android.view.animation.AccelerateInterpolator(2.5f)); - fadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); - - Animator noRecentAppsFadeAnim = null; - if (mNoRecentAppsView != null && // doesn't exist on large devices - mNoRecentAppsView.getVisibility() == View.VISIBLE) { - noRecentAppsFadeAnim = ObjectAnimator.ofFloat(mNoRecentAppsView, "alpha", - mContentView.getAlpha(), appearing ? 1.0f : 0.0f); - noRecentAppsFadeAnim.setInterpolator(appearing - ? new android.view.animation.AccelerateInterpolator(1.0f) - : new android.view.animation.DecelerateInterpolator(1.0f)); - noRecentAppsFadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); - } - - mContentAnim = new AnimatorSet(); - final Builder builder = mContentAnim.play(fadeAnim).with(posAnim); - - if (noRecentAppsFadeAnim != null) { - builder.with(noRecentAppsFadeAnim); - } - - if (appearing) { - Drawable background = mScrimView.getBackground(); - if (background != null) { - Animator bgAnim = ObjectAnimator.ofInt(background, - "alpha", appearing ? 0 : 255, appearing ? 255 : 0); - bgAnim.setDuration(appearing ? SCRIM_DURATION : CLOSE_DURATION); - builder.with(bgAnim); - } - } else { - final Resources res = mRootView.getResources(); - boolean isTablet = res.getBoolean(R.bool.config_recents_interface_for_tablets); - if (!isTablet) { - View recentsTransitionBackground = - mRootView.findViewById(R.id.recents_transition_background); - recentsTransitionBackground.setVisibility(View.VISIBLE); - Drawable bgDrawable = new ColorDrawable(0xFF000000); - recentsTransitionBackground.setBackground(bgDrawable); - Animator bgAnim = ObjectAnimator.ofInt(bgDrawable, "alpha", 0, 255); - bgAnim.setDuration(CLOSE_DURATION); - bgAnim.setInterpolator(new android.view.animation.AccelerateInterpolator(1f)); - builder.with(bgAnim); - } - } - mContentAnim.addListener(this); - if (mListener != null) { - mContentAnim.addListener(mListener); - } - } - - void startAnimation(boolean appearing) { - if (DEBUG) Slog.d(TAG, "startAnimation(appearing=" + appearing + ")"); - - createAnimation(appearing); - - // isHardwareAccelerated() checks if we're attached to a window and if that - // window is HW accelerated-- we were sometimes not attached to a window - // and buildLayer was throwing an IllegalStateException - if (mContentView.isHardwareAccelerated()) { - mContentView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mContentView.buildLayer(); - } - mContentAnim.start(); - - mVisible = appearing; - } - - void jumpTo(boolean appearing) { - mContentView.setTranslationY(appearing ? 0 : mPanelHeight); - if (mScrimView.getBackground() != null) { - mScrimView.getBackground().setAlpha(appearing ? 255 : 0); - } - View recentsTransitionBackground = - mRootView.findViewById(R.id.recents_transition_background); - recentsTransitionBackground.setVisibility(View.INVISIBLE); - mRootView.requestLayout(); - } - - public void setPanelHeight(int h) { - if (DEBUG) Slog.d(TAG, "panelHeight=" + h); - mPanelHeight = h; - } - - public void onAnimationCancel(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationCancel"); - // force this to zero so we close the window - mVisible = false; - } - - public void onAnimationEnd(Animator animation) { - if (DEBUG) Slog.d(TAG, "onAnimationEnd"); - if (!mVisible) { - mRootView.hideWindow(); - } - mContentView.setLayerType(View.LAYER_TYPE_NONE, null); - mContentView.setAlpha(1f); - mContentAnim = null; - } - - public void onAnimationRepeat(Animator animation) { - } - - public void onAnimationStart(Animator animation) { - } -} diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java index 4d8c168..4281ccf 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentTasksLoader.java @@ -32,6 +32,8 @@ import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.util.Log; +import android.view.MotionEvent; +import android.view.View; import com.android.systemui.R; import com.android.systemui.statusbar.phone.PhoneStatusBar; @@ -42,7 +44,7 @@ import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -public class RecentTasksLoader { +public class RecentTasksLoader implements View.OnTouchListener { static final String TAG = "RecentTasksLoader"; static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; @@ -51,17 +53,43 @@ public class RecentTasksLoader { private Context mContext; private RecentsPanelView mRecentsPanel; + private TaskDescription mFirstTask; + private boolean mFirstTaskLoaded; private AsyncTask<Void, ArrayList<TaskDescription>, Void> mTaskLoader; private AsyncTask<Void, TaskDescription, Void> mThumbnailLoader; + private Handler mHandler; private int mIconDpi; private Bitmap mDefaultThumbnailBackground; private Bitmap mDefaultIconBackground; - private int mNumTasksInFirstScreenful; + private int mNumTasksInFirstScreenful = Integer.MAX_VALUE; + + private boolean mFirstScreenful; + private ArrayList<TaskDescription> mLoadedTasks; + + private enum State { LOADING, LOADED, CANCELLED }; + private State mState = State.CANCELLED; + + public TaskDescription getFirstTask() { + while (!mFirstTaskLoaded) { + if (mState == State.CANCELLED) { + loadTasksInBackground(); + } + try { + if (mState == State.LOADED) { + break; + } + Thread.sleep(5); + } catch (InterruptedException e) { + } + } + return mFirstTask; + } public RecentTasksLoader(Context context) { mContext = context; + mHandler = new Handler(); final Resources res = context.getResources(); @@ -91,16 +119,16 @@ public class RecentTasksLoader { Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(mDefaultThumbnailBackground); c.drawColor(color); - - // If we're using the cache, begin listening to the activity manager for - // updated thumbnails - final ActivityManager am = (ActivityManager) - mContext.getSystemService(Context.ACTIVITY_SERVICE); } - public void setRecentsPanel(RecentsPanelView recentsPanel) { - mRecentsPanel = recentsPanel; - mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful(); + public void setRecentsPanel(RecentsPanelView newRecentsPanel, RecentsPanelView caller) { + // Only allow clearing mRecentsPanel if the caller is the current recentsPanel + if (newRecentsPanel != null || mRecentsPanel == caller) { + mRecentsPanel = newRecentsPanel; + if (mRecentsPanel != null) { + mNumTasksInFirstScreenful = mRecentsPanel.numItemsInOneScreenful(); + } + } } public Bitmap getDefaultThumbnail() { @@ -111,26 +139,33 @@ public class RecentTasksLoader { return mDefaultIconBackground; } - // Create an TaskDescription, returning null if the title or icon is null, or if it's the - // home activity + public ArrayList<TaskDescription> getLoadedTasks() { + return mLoadedTasks; + } + + public boolean isFirstScreenful() { + return mFirstScreenful; + } + + private boolean isCurrentHomeActivity(ComponentName component, ActivityInfo homeInfo) { + if (homeInfo == null) { + final PackageManager pm = mContext.getPackageManager(); + homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) + .resolveActivityInfo(pm, 0); + } + return homeInfo != null + && homeInfo.packageName.equals(component.getPackageName()) + && homeInfo.name.equals(component.getClassName()); + } + + // Create an TaskDescription, returning null if the title or icon is null TaskDescription createTaskDescription(int taskId, int persistentTaskId, Intent baseIntent, - ComponentName origActivity, CharSequence description, ActivityInfo homeInfo) { + ComponentName origActivity, CharSequence description) { Intent intent = new Intent(baseIntent); if (origActivity != null) { intent.setComponent(origActivity); } final PackageManager pm = mContext.getPackageManager(); - if (homeInfo == null) { - homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME) - .resolveActivityInfo(pm, 0); - } - // Don't load the current home activity. - if (homeInfo != null - && homeInfo.packageName.equals(intent.getComponent().getPackageName()) - && homeInfo.name.equals(intent.getComponent().getClassName())) { - return null; - } - intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) | Intent.FLAG_ACTIVITY_NEW_TASK); final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); @@ -207,7 +242,43 @@ public class RecentTasksLoader { return getFullResDefaultActivityIcon(); } - public void cancelLoadingThumbnailsAndIcons() { + Runnable mPreloadTasksRunnable = new Runnable() { + public void run() { + loadTasksInBackground(); + } + }; + + // additional optimization when we have software system buttons - start loading the recent + // tasks on touch down + @Override + public boolean onTouch(View v, MotionEvent ev) { + int action = ev.getAction() & MotionEvent.ACTION_MASK; + if (action == MotionEvent.ACTION_DOWN) { + mHandler.post(mPreloadTasksRunnable); + } else if (action == MotionEvent.ACTION_CANCEL) { + cancelLoadingThumbnailsAndIcons(); + mHandler.removeCallbacks(mPreloadTasksRunnable); + } else if (action == MotionEvent.ACTION_UP) { + // Remove the preloader if we haven't called it yet + mHandler.removeCallbacks(mPreloadTasksRunnable); + if (!v.isPressed()) { + cancelLoadingThumbnailsAndIcons(); + } + + } + return false; + } + + public void cancelLoadingThumbnailsAndIcons(RecentsPanelView caller) { + // Only oblige this request if it comes from the current RecentsPanel + // (eg when you rotate, the old RecentsPanel request should be ignored) + if (mRecentsPanel == caller) { + cancelLoadingThumbnailsAndIcons(); + } + } + + + private void cancelLoadingThumbnailsAndIcons() { if (mTaskLoader != null) { mTaskLoader.cancel(false); mTaskLoader = null; @@ -216,11 +287,26 @@ public class RecentTasksLoader { mThumbnailLoader.cancel(false); mThumbnailLoader = null; } + mLoadedTasks = null; + mFirstTask = null; + mFirstTaskLoaded = false; + if (mRecentsPanel != null) { + mRecentsPanel.onTaskLoadingCancelled(); + } + mFirstScreenful = false; + mState = State.CANCELLED; } public void loadTasksInBackground() { - // cancel all previous loading of tasks and thumbnails - cancelLoadingThumbnailsAndIcons(); + loadTasksInBackground(false); + } + public void loadTasksInBackground(final boolean zeroeth) { + if (mState != State.CANCELLED) { + return; + } + mState = State.LOADING; + mFirstScreenful = true; + final LinkedBlockingQueue<TaskDescription> tasksWaitingForThumbnails = new LinkedBlockingQueue<TaskDescription>(); mTaskLoader = new AsyncTask<Void, ArrayList<TaskDescription>, Void>() { @@ -230,7 +316,14 @@ public class RecentTasksLoader { ArrayList<TaskDescription> newTasks = values[0]; // do a callback to RecentsPanelView to let it know we have more values // how do we let it know we're all done? just always call back twice - mRecentsPanel.onTasksLoaded(newTasks); + if (mRecentsPanel != null) { + mRecentsPanel.onTasksLoaded(newTasks, mFirstScreenful); + } + if (mLoadedTasks == null) { + mLoadedTasks = new ArrayList<TaskDescription>(); + } + mLoadedTasks.addAll(newTasks); + mFirstScreenful = false; } } @Override @@ -254,15 +347,34 @@ public class RecentTasksLoader { ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>(); // skip the first task - assume it's either the home screen or the current activity. - final int first = 1; + final int first = 0; for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) { if (isCancelled()) { break; } final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i); + + Intent intent = new Intent(recentInfo.baseIntent); + if (recentInfo.origActivity != null) { + intent.setComponent(recentInfo.origActivity); + } + + // Don't load the current home activity. + if (isCurrentHomeActivity(intent.getComponent(), homeInfo)) { + if (index == 0) { + mFirstTaskLoaded = true; + } + continue; + } + + // Don't load ourselves + if (intent.getComponent().getPackageName().equals(mContext.getPackageName())) { + continue; + } + TaskDescription item = createTaskDescription(recentInfo.id, recentInfo.persistentId, recentInfo.baseIntent, - recentInfo.origActivity, recentInfo.description, homeInfo); + recentInfo.origActivity, recentInfo.description); if (item != null) { while (true) { @@ -317,7 +429,13 @@ public class RecentTasksLoader { protected void onProgressUpdate(TaskDescription... values) { if (!isCancelled()) { TaskDescription td = values[0]; - mRecentsPanel.onTaskThumbnailLoaded(td); + if (td.isNull()) { // end sentinel + mState = State.LOADED; + } else { + if (mRecentsPanel != null) { + mRecentsPanel.onTaskThumbnailLoaded(td); + } + } } } @Override @@ -336,19 +454,25 @@ public class RecentTasksLoader { } catch (InterruptedException e) { } } - if (td.isNull()) { + if (td.isNull()) { // end sentinel + publishProgress(td); break; } loadThumbnailAndIcon(td); - synchronized(td) { - publishProgress(td); + + if (!mFirstTaskLoaded) { + mFirstTask = td; + mFirstTaskLoaded = true; } + publishProgress(td); } Process.setThreadPriority(origPri); return null; } }; + mFirstTask = null; + mFirstTaskLoaded = false; mThumbnailLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java new file mode 100644 index 0000000..a4c8e64 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsActivity.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2012 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.recent; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.UserHandle; +import android.view.MotionEvent; +import android.view.View; + +import com.android.systemui.R; +import com.android.systemui.SystemUIApplication; +import com.android.systemui.statusbar.tablet.StatusBarPanel; + +public class RecentsActivity extends Activity { + public static final String TOGGLE_RECENTS_INTENT = "com.android.systemui.TOGGLE_RECENTS"; + public static final String CLOSE_RECENTS_INTENT = "com.android.systemui.CLOSE_RECENTS"; + + private RecentsPanelView mRecentsPanel; + private IntentFilter mIntentFilter; + private boolean mShowing; + private boolean mForeground; + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (mRecentsPanel != null && mRecentsPanel.isShowing()) { + if (mShowing && !mForeground) { + // Captures the case right before we transition to another activity + mRecentsPanel.show(false); + } + } + } + }; + + public class TouchOutsideListener implements View.OnTouchListener { + private StatusBarPanel mPanel; + + public TouchOutsideListener(StatusBarPanel panel) { + mPanel = panel; + } + + public boolean onTouch(View v, MotionEvent ev) { + final int action = ev.getAction(); + if (action == MotionEvent.ACTION_OUTSIDE + || (action == MotionEvent.ACTION_DOWN + && !mPanel.isInContentArea((int) ev.getX(), (int) ev.getY()))) { + dismissAndGoHome(); + return true; + } + return false; + } + } + + @Override + public void onPause() { + mForeground = false; + super.onPause(); + } + + @Override + public void onStop() { + mShowing = false; + if (mRecentsPanel != null) { + mRecentsPanel.onUiHidden(); + } + super.onStop(); + } + + @Override + public void onStart() { + mShowing = true; + super.onStart(); + } + + @Override + public void onResume() { + mForeground = true; + super.onResume(); + } + + @Override + public void onBackPressed() { + dismissAndGoBack(); + } + + public void dismissAndGoHome() { + if (mRecentsPanel != null) { + Intent homeIntent = new Intent(Intent.ACTION_MAIN, null); + homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + startActivityAsUser(homeIntent, new UserHandle(UserHandle.USER_CURRENT)); + mRecentsPanel.show(false); + } + } + + public void dismissAndGoBack() { + if (mRecentsPanel != null) { + final SystemUIApplication app = (SystemUIApplication) getApplication(); + final RecentTasksLoader recentTasksLoader = app.getRecentTasksLoader(); + TaskDescription firstTask = recentTasksLoader.getFirstTask(); + if (firstTask != null && mRecentsPanel.simulateClick(firstTask)) { + // recents panel will take care of calling show(false); + return; + } + mRecentsPanel.show(false); + } + finish(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + final SystemUIApplication app = (SystemUIApplication) getApplication(); + final RecentTasksLoader recentTasksLoader = app.getRecentTasksLoader(); + + setContentView(R.layout.status_bar_recent_panel); + mRecentsPanel = (RecentsPanelView) findViewById(R.id.recents_root); + mRecentsPanel.setOnTouchListener(new TouchOutsideListener(mRecentsPanel)); + mRecentsPanel.setRecentTasksLoader(recentTasksLoader); + recentTasksLoader.setRecentsPanel(mRecentsPanel, mRecentsPanel); + + handleIntent(getIntent()); + mIntentFilter = new IntentFilter(); + mIntentFilter.addAction(CLOSE_RECENTS_INTENT); + registerReceiver(mIntentReceiver, mIntentFilter); + super.onCreate(savedInstanceState); + } + + @Override + protected void onDestroy() { + final SystemUIApplication app = (SystemUIApplication) getApplication(); + final RecentTasksLoader recentTasksLoader = app.getRecentTasksLoader(); + recentTasksLoader.setRecentsPanel(null, mRecentsPanel); + unregisterReceiver(mIntentReceiver); + super.onDestroy(); + } + + @Override + protected void onNewIntent(Intent intent) { + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + super.onNewIntent(intent); + + if (TOGGLE_RECENTS_INTENT.equals(intent.getAction())) { + if (mRecentsPanel != null && !mRecentsPanel.isShowing()) { + final SystemUIApplication app = (SystemUIApplication) getApplication(); + final RecentTasksLoader recentTasksLoader = app.getRecentTasksLoader(); + mRecentsPanel.show(true, recentTasksLoader.getLoadedTasks(), + recentTasksLoader.isFirstScreenful()); + } else if ((mRecentsPanel != null && mRecentsPanel.isShowing())) { + dismissAndGoBack(); + } + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index e9c2ecb7..4aa2095 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -76,6 +76,17 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView } } + public View findViewForTask(TaskDescription task) { + for (int i = 0; i < mLinearLayout.getChildCount(); i++) { + View v = mLinearLayout.getChildAt(i); + RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) v.getTag(); + if (holder.taskDescription == task) { + return v; + } + } + return null; + } + private void update() { for (int i = 0; i < mLinearLayout.getChildCount(); i++) { View v = mLinearLayout.getChildAt(i); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index b407078..c3ecdb5 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -29,20 +29,17 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Matrix; -import android.graphics.Rect; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.view.Display; import android.view.KeyEvent; -import android.view.IWindowManager; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.MotionEvent; @@ -71,7 +68,7 @@ import com.android.systemui.statusbar.tablet.TabletStatusBar; import java.util.ArrayList; public class RecentsPanelView extends FrameLayout implements OnItemClickListener, RecentsCallback, - StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener { + StatusBarPanel, Animator.AnimatorListener { static final String TAG = "RecentsPanelView"; static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false; private Context mContext; @@ -84,36 +81,22 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener private boolean mShowing; private boolean mWaitingToShow; - private boolean mWaitingToShowAnimated; - private boolean mReadyToShow; private int mNumItemsWaitingForThumbnailsAndIcons; - private Choreographer mChoreo; - OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener; - - ImageView mPlaceholderThumbnail; - View mTransitionBg; - boolean mHideRecentsAfterThumbnailScaleUpStarted; private RecentTasksLoader mRecentTasksLoader; private ArrayList<TaskDescription> mRecentTaskDescriptions; - private Runnable mPreloadTasksRunnable; - private boolean mRecentTasksDirty = true; private TaskDescriptionAdapter mListAdapter; private int mThumbnailWidth; private boolean mFitThumbnailToXY; private int mRecentItemLayoutId; - private boolean mFirstScreenful = true; private boolean mHighEndGfx; - public static interface OnRecentsPanelVisibilityChangedListener { - public void onRecentsPanelVisibilityChanged(boolean visible); - } - public static interface RecentsScrollView { public int numItemsInOneScreenful(); public void setAdapter(TaskDescriptionAdapter adapter); public void setCallback(RecentsCallback callback); public void setMinSwipeAlpha(float minAlpha); + public View findViewForTask(TaskDescription task); } private final class OnLongClickDelegate implements View.OnLongClickListener { @@ -252,15 +235,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_BACK && !event.isCanceled()) { - show(false, false); - return true; - } - return super.onKeyUp(keyCode, event); - } - private boolean pointInside(int x, int y, View v) { final int l = v.getLeft(); final int r = v.getRight(); @@ -280,22 +254,26 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } - public void show(boolean show, boolean animate) { + public void show(boolean show) { + show(show, null, false); + } + + public void show(boolean show, ArrayList<TaskDescription> recentTaskDescriptions, + boolean firstScreenful) { if (show) { - refreshRecentTasksList(null, true); mWaitingToShow = true; - mWaitingToShowAnimated = animate; + refreshRecentTasksList(recentTaskDescriptions, firstScreenful); showIfReady(); } else { - show(show, animate, null, false); + showImpl(false); } } private void showIfReady() { - // mWaitingToShow = there was a touch up on the recents button - // mReadyToShow = we've created views for the first screenful of items - if (mWaitingToShow && mReadyToShow) { // && mNumItemsWaitingForThumbnailsAndIcons <= 0 - show(true, mWaitingToShowAnimated, null, false); + // mWaitingToShow => there was a touch up on the recents button + // mRecentTaskDescriptions != null => we've created views for the first screenful of items + if (mWaitingToShow && mRecentTaskDescriptions != null) { + showImpl(true); } } @@ -308,79 +286,44 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } - public void show(boolean show, boolean animate, - ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) { + private void showImpl(boolean show) { sendCloseSystemWindows(mContext, BaseStatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS); + mShowing = show; + if (show) { - // Need to update list of recent apps before we set visibility so this view's - // content description is updated before it gets focus for TalkBack mode - refreshRecentTasksList(recentTaskDescriptions, firstScreenful); + // if there are no apps, bring up a "No recent apps" message + boolean noApps = mRecentTaskDescriptions != null + && (mRecentTaskDescriptions.size() == 0); + mRecentsNoApps.setAlpha(1f); + mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE); - // if there are no apps, either bring up a "No recent apps" message, or just - // quit early - boolean noApps = !mFirstScreenful && (mRecentTaskDescriptions.size() == 0); - if (mRecentsNoApps != null) { - mRecentsNoApps.setAlpha(1f); - mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE); - } else { - if (noApps) { - if (DEBUG) Log.v(TAG, "Nothing to show"); - // Need to set recent tasks to dirty so that next time we load, we - // refresh the list of tasks - mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(); - mRecentTasksDirty = true; - - mWaitingToShow = false; - mReadyToShow = false; - return; - } - } - } else { - // Need to set recent tasks to dirty so that next time we load, we - // refresh the list of tasks - mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(); - mRecentTasksDirty = true; - mWaitingToShow = false; - mReadyToShow = false; - } - if (animate) { - if (mShowing != show) { - mShowing = show; - if (show) { - setVisibility(View.VISIBLE); - } - mChoreo.startAnimation(show); - } - } else { - mShowing = show; - setVisibility(show ? View.VISIBLE : View.GONE); - mChoreo.jumpTo(show); onAnimationEnd(null); - } - if (show) { setFocusable(true); setFocusableInTouchMode(true); requestFocus(); } else { + mWaitingToShow = false; + // call onAnimationEnd() and clearRecentTasksList() in onUiHidden() if (mPopup != null) { mPopup.dismiss(); } } } + public void onUiHidden() { + if (!mShowing && mRecentTaskDescriptions != null) { + onAnimationEnd(null); + clearRecentTasksList(); + } + } + public void dismiss() { - hide(true); + ((RecentsActivity) mContext).dismissAndGoHome(); } - public void hide(boolean animate) { - if (!animate) { - setVisibility(View.GONE); - } - if (mBar != null) { - // This will indirectly cause show(false, ...) to get called - mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE); - } + public void dismissAndGoBack() { + ((RecentsActivity) mContext).dismissAndGoBack(); } public void onAnimationCancel(Animator animation) { @@ -393,7 +336,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener createCustomAnimations(transitioner); } else { ((ViewGroup)mRecentsContainer).setLayoutTransition(null); - clearRecentTasksList(); } } @@ -403,16 +345,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener public void onAnimationStart(Animator animation) { } - /** - * We need to be aligned at the bottom. LinearLayout can't do this, so instead, - * let LinearLayout do all the hard work, and then shift everything down to the bottom. - */ - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - super.onLayout(changed, l, t, r, b); - mChoreo.setPanelHeight(mRecentsContainer.getHeight()); - } - @Override public boolean dispatchHoverEvent(MotionEvent event) { // Ignore hover events outside of this panel bounds since such events @@ -449,18 +381,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener mRecentTasksLoader = loader; } - public void setOnVisibilityChangedListener(OnRecentsPanelVisibilityChangedListener l) { - mVisibilityChangedListener = l; - - } - - public void setVisibility(int visibility) { - if (mVisibilityChangedListener != null) { - mVisibilityChangedListener.onRecentsPanelVisibilityChanged(visibility == VISIBLE); - } - super.setVisibility(visibility); - } - public void updateValuesFromResources() { final Resources res = mContext.getResources(); mThumbnailWidth = Math.round(res.getDimension(R.dimen.status_bar_recents_thumbnail_width)); @@ -486,7 +406,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener mRecentsScrim = findViewById(R.id.recents_bg_protect); mRecentsNoApps = findViewById(R.id.recents_no_apps); - mChoreo = new Choreographer(this, mRecentsScrim, mRecentsContainer, mRecentsNoApps, this); if (mRecentsScrim != null) { mHighEndGfx = ActivityManager.isHighEndGfx(); @@ -497,18 +416,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener ((BitmapDrawable) mRecentsScrim.getBackground()).setTileModeY(TileMode.REPEAT); } } - - mPreloadTasksRunnable = new Runnable() { - public void run() { - // If we set our visibility to INVISIBLE here, we avoid an extra call to - // onLayout later when we become visible (because onLayout is always called - // when going from GONE) - if (!mShowing) { - setVisibility(INVISIBLE); - refreshRecentTasksList(); - } - } - }; } public void setMinSwipeAlpha(float minAlpha) { @@ -602,44 +509,19 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener showIfReady(); } - // additional optimization when we have software system buttons - start loading the recent - // tasks on touch down - @Override - public boolean onTouch(View v, MotionEvent ev) { - if (!mShowing) { - int action = ev.getAction() & MotionEvent.ACTION_MASK; - if (action == MotionEvent.ACTION_DOWN) { - post(mPreloadTasksRunnable); - } else if (action == MotionEvent.ACTION_CANCEL) { - setVisibility(GONE); - clearRecentTasksList(); - // Remove the preloader if we haven't called it yet - removeCallbacks(mPreloadTasksRunnable); - } else if (action == MotionEvent.ACTION_UP) { - // Remove the preloader if we haven't called it yet - removeCallbacks(mPreloadTasksRunnable); - if (!v.isPressed()) { - setVisibility(GONE); - clearRecentTasksList(); - } - } - } - return false; - } - - public void preloadRecentTasksList() { - if (!mShowing) { - mPreloadTasksRunnable.run(); + public void clearRecentTasksList() { + // Clear memory used by screenshots + if (mRecentTaskDescriptions != null) { + mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(this); + onTaskLoadingCancelled(); } } - public void clearRecentTasksList() { - // Clear memory used by screenshots - if (!mShowing && mRecentTaskDescriptions != null) { - mRecentTasksLoader.cancelLoadingThumbnailsAndIcons(); - mRecentTaskDescriptions.clear(); + public void onTaskLoadingCancelled() { + // Gets called by RecentTasksLoader when it's cancelled + if (mRecentTaskDescriptions != null) { + mRecentTaskDescriptions = null; mListAdapter.notifyDataSetInvalidated(); - mRecentTasksDirty = true; } } @@ -649,23 +531,15 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener private void refreshRecentTasksList( ArrayList<TaskDescription> recentTasksList, boolean firstScreenful) { - if (mRecentTasksDirty) { - if (recentTasksList != null) { - mFirstScreenful = true; - onTasksLoaded(recentTasksList); - } else { - mFirstScreenful = true; - mRecentTasksLoader.loadTasksInBackground(); - } - mRecentTasksDirty = false; + if (mRecentTaskDescriptions == null && recentTasksList != null) { + onTasksLoaded(recentTasksList, firstScreenful); + } else { + mRecentTasksLoader.loadTasksInBackground(); } } - public void onTasksLoaded(ArrayList<TaskDescription> tasks) { - if (!mFirstScreenful && tasks.size() == 0) { - return; - } - mNumItemsWaitingForThumbnailsAndIcons = mFirstScreenful + public void onTasksLoaded(ArrayList<TaskDescription> tasks, boolean firstScreenful) { + mNumItemsWaitingForThumbnailsAndIcons = firstScreenful ? tasks.size() : mRecentTaskDescriptions == null ? 0 : mRecentTaskDescriptions.size(); if (mRecentTaskDescriptions == null) { @@ -675,19 +549,9 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } mListAdapter.notifyDataSetInvalidated(); updateUiElements(getResources().getConfiguration()); - mReadyToShow = true; - mFirstScreenful = false; showIfReady(); } - public ArrayList<TaskDescription> getRecentTasksList() { - return mRecentTaskDescriptions; - } - - public boolean getFirstScreenful() { - return mFirstScreenful; - } - private void updateUiElements(Configuration config) { final int items = mRecentTaskDescriptions.size(); @@ -706,8 +570,19 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener setContentDescription(recentAppsAccessibilityDescription); } + public boolean simulateClick(TaskDescription task) { + if (mRecentsContainer instanceof RecentsScrollView){ + RecentsScrollView scrollView + = (RecentsScrollView) mRecentsContainer; + View v = scrollView.findViewForTask(task); + if (v != null) { + handleOnClick(v); + return true; + } + } + return false; + } - boolean mThumbnailScaleUpStarted; public void handleOnClick(View view) { ViewHolder holder = (ViewHolder)view.getTag(); TaskDescription ad = holder.taskDescription; @@ -725,59 +600,10 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener usingDrawingCache = true; } - if (mPlaceholderThumbnail == null) { - mPlaceholderThumbnail = - (ImageView) findViewById(R.id.recents_transition_placeholder_icon); - } - if (mTransitionBg == null) { - mTransitionBg = (View) findViewById(R.id.recents_transition_background); - - IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); - try { - if (!wm.hasSystemNavBar()) { - FrameLayout.LayoutParams lp = - (FrameLayout.LayoutParams) mTransitionBg.getLayoutParams(); - int statusBarHeight = getResources(). - getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); - lp.setMargins(0, statusBarHeight, 0, 0); - mTransitionBg.setLayoutParams(lp); - } - } catch (RemoteException e) { - Log.w(TAG, "Failing checking whether status bar is visible", e); - } - } - - final ImageView placeholderThumbnail = mPlaceholderThumbnail; - mHideRecentsAfterThumbnailScaleUpStarted = false; - placeholderThumbnail.setVisibility(VISIBLE); - if (!usingDrawingCache) { - placeholderThumbnail.setImageBitmap(bm); - } else { - Bitmap b2 = bm.copy(bm.getConfig(), true); - placeholderThumbnail.setImageBitmap(b2); - } - Rect r = new Rect(); - holder.thumbnailViewImage.getGlobalVisibleRect(r); - - placeholderThumbnail.setTranslationX(r.left); - placeholderThumbnail.setTranslationY(r.top); - - show(false, true); - - mThumbnailScaleUpStarted = false; ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation( - holder.thumbnailViewImage, bm, 0, 0, - new ActivityOptions.OnAnimationStartedListener() { - @Override public void onAnimationStarted() { - mThumbnailScaleUpStarted = true; - if (!mHighEndGfx) { - mPlaceholderThumbnail.setVisibility(INVISIBLE); - } - if (mHideRecentsAfterThumbnailScaleUpStarted) { - hideWindow(); - } - } - }); + holder.thumbnailViewImage, bm, 0, 0, null); + + show(false); if (ad.taskId >= 0) { // This is an active task; it should just go to the foreground. am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME, @@ -796,17 +622,6 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener } } - public void hideWindow() { - if (!mThumbnailScaleUpStarted) { - mHideRecentsAfterThumbnailScaleUpStarted = true; - } else { - setVisibility(GONE); - mTransitionBg.setVisibility(INVISIBLE); - mPlaceholderThumbnail.setVisibility(INVISIBLE); - mHideRecentsAfterThumbnailScaleUpStarted = false; - } - } - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { handleOnClick(view); } @@ -825,7 +640,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener // mListAdapter.notifyDataSetChanged(); if (mRecentTaskDescriptions.size() == 0) { - hide(false); + dismissAndGoBack(); } // Currently, either direction means the same thing, so ignore direction and remove @@ -875,7 +690,7 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener if (viewHolder != null) { final TaskDescription ad = viewHolder.taskDescription; startApplicationDetailsActivity(ad.packageName); - mBar.animateCollapse(CommandQueue.FLAG_EXCLUDE_NONE); + show(false); } else { throw new IllegalStateException("Oops, no tag on view " + selectedView); } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index ba08775..a0f197d 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -77,6 +77,17 @@ public class RecentsVerticalScrollView extends ScrollView } } + public View findViewForTask(TaskDescription task) { + for (int i = 0; i < mLinearLayout.getChildCount(); i++) { + View v = mLinearLayout.getChildAt(i); + RecentsPanelView.ViewHolder holder = (RecentsPanelView.ViewHolder) v.getTag(); + if (holder.taskDescription == task) { + return v; + } + } + return null; + } + private void update() { for (int i = 0; i < mLinearLayout.getChildCount(); i++) { View v = mLinearLayout.getChildAt(i); @@ -146,7 +157,9 @@ public class RecentsVerticalScrollView extends ScrollView appTitle.setContentDescription(" "); appTitle.setOnTouchListener(noOpListener); final View calloutLine = view.findViewById(R.id.recents_callout_line); - calloutLine.setOnTouchListener(noOpListener); + if (calloutLine != null) { + calloutLine.setOnTouchListener(noOpListener); + } mLinearLayout.addView(view); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index e9e043e..106ce7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1,3 +1,4 @@ + /* * Copyright (C) 2010 The Android Open Source Project * @@ -24,23 +25,31 @@ import com.android.internal.widget.SizeAdaptiveLayout; import com.android.systemui.R; import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; +import com.android.systemui.SystemUIApplication; import com.android.systemui.recent.RecentTasksLoader; -import com.android.systemui.recent.RecentsPanelView; +import com.android.systemui.recent.RecentsActivity; import com.android.systemui.recent.TaskDescription; import com.android.systemui.statusbar.policy.NotificationRowLayout; import com.android.systemui.statusbar.tablet.StatusBarPanel; import android.app.ActivityManagerNative; +import android.app.ActivityOptions; import android.app.KeyguardManager; import android.app.PendingIntent; +import android.app.Service; import android.app.TaskStackBuilder; +import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Configuration; +import android.content.res.Resources; import android.database.ContentObserver; +import android.graphics.Bitmap; +import android.graphics.Paint; import android.graphics.Rect; import android.net.Uri; import android.os.Build; @@ -52,6 +61,7 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.Slog; import android.view.Display; @@ -73,12 +83,12 @@ import android.widget.TextView; import java.util.ArrayList; public abstract class BaseStatusBar extends SystemUI implements - CommandQueue.Callbacks, RecentsPanelView.OnRecentsPanelVisibilityChangedListener { + CommandQueue.Callbacks { static final String TAG = "StatusBar"; private static final boolean DEBUG = false; public static final boolean MULTIUSER_DEBUG = false; - protected static final int MSG_OPEN_RECENTS_PANEL = 1020; + protected static final int MSG_TOGGLE_RECENTS_PANEL = 1020; protected static final int MSG_CLOSE_RECENTS_PANEL = 1021; protected static final int MSG_PRELOAD_RECENT_APPS = 1022; protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023; @@ -111,10 +121,6 @@ public abstract class BaseStatusBar extends SystemUI implements // Search panel protected SearchPanelView mSearchPanelView; - // Recent apps - protected RecentsPanelView mRecentsPanel; - protected RecentTasksLoader mRecentTasksLoader; - protected PopupMenu mNotificationBlamePopup; protected int mCurrentUserId = 0; @@ -377,8 +383,7 @@ public abstract class BaseStatusBar extends SystemUI implements @Override public void toggleRecentApps() { - int msg = (mRecentsPanel.getVisibility() == View.VISIBLE) - ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL; + int msg = MSG_TOGGLE_RECENTS_PANEL; mHandler.removeMessages(msg); mHandler.sendEmptyMessage(msg); } @@ -411,49 +416,15 @@ public abstract class BaseStatusBar extends SystemUI implements mHandler.sendEmptyMessage(msg); } - @Override - public void onRecentsPanelVisibilityChanged(boolean visible) { - } - protected abstract WindowManager.LayoutParams getRecentsLayoutParams( LayoutParams layoutParams); protected abstract WindowManager.LayoutParams getSearchLayoutParams( LayoutParams layoutParams); - protected void updateRecentsPanel(int recentsResId) { - // Recents Panel - boolean visible = false; - ArrayList<TaskDescription> recentTasksList = null; - boolean firstScreenful = false; - if (mRecentsPanel != null) { - visible = mRecentsPanel.isShowing(); - mWindowManager.removeView(mRecentsPanel); - if (visible) { - recentTasksList = mRecentsPanel.getRecentTasksList(); - firstScreenful = mRecentsPanel.getFirstScreenful(); - } - } - - // Provide RecentsPanelView with a temporary parent to allow layout params to work. - LinearLayout tmpRoot = new LinearLayout(mContext); - mRecentsPanel = (RecentsPanelView) LayoutInflater.from(mContext).inflate( - recentsResId, tmpRoot, false); - mRecentsPanel.setRecentTasksLoader(mRecentTasksLoader); - mRecentTasksLoader.setRecentsPanel(mRecentsPanel); - mRecentsPanel.setOnTouchListener( - new TouchOutsideListener(MSG_CLOSE_RECENTS_PANEL, mRecentsPanel)); - mRecentsPanel.setVisibility(View.GONE); - - - WindowManager.LayoutParams lp = getRecentsLayoutParams(mRecentsPanel.getLayoutParams()); - - mWindowManager.addView(mRecentsPanel, lp); - mRecentsPanel.setBar(this); - if (visible) { - mRecentsPanel.show(true, false, recentTasksList, firstScreenful); - } - + protected RecentTasksLoader getRecentTasksLoader() { + final SystemUIApplication app = (SystemUIApplication) ((Service) mContext).getApplication(); + return app.getRecentTasksLoader(); } protected void updateSearchPanel() { @@ -494,28 +465,148 @@ public abstract class BaseStatusBar extends SystemUI implements } } + protected abstract View getStatusBarView(); + + protected void toggleRecentsActivity() { + try { + final RecentTasksLoader recentTasksLoader = getRecentTasksLoader(); + TaskDescription firstTask = recentTasksLoader.getFirstTask(); + + Intent intent = new Intent(RecentsActivity.TOGGLE_RECENTS_INTENT); + intent.setClassName("com.android.systemui", + "com.android.systemui.recent.RecentsActivity"); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + + if (firstTask == null) { + mContext.startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + } else { + Bitmap first = firstTask.getThumbnail(); + final Resources res = mContext.getResources(); + + float thumbWidth = res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width); + float thumbHeight = res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height); + if (first.getWidth() != thumbWidth || first.getHeight() != thumbHeight) { + first = Bitmap.createScaledBitmap(first, (int) thumbWidth, (int) thumbHeight, + true); + } + + DisplayMetrics dm = new DisplayMetrics(); + mDisplay.getMetrics(dm); + // calculate it here, but consider moving it elsewhere + // first, determine which orientation you're in. + // todo: move the system_bar layouts to sw600dp ? + final Configuration config = res.getConfiguration(); + int x, y; + + if (config.orientation == Configuration.ORIENTATION_PORTRAIT) { + float appLabelLeftMargin = res + .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_left_margin); + float appLabelWidth = res + .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_width); + float thumbLeftMargin = res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_left_margin); + float thumbBgPadding = res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_bg_padding); + + float width = appLabelLeftMargin + + +appLabelWidth + + thumbLeftMargin + + thumbWidth + + 2 * thumbBgPadding; + + x = (int) ((dm.widthPixels - width) / 2f + appLabelLeftMargin + appLabelWidth + + thumbBgPadding + thumbLeftMargin); + y = (int) (dm.heightPixels + - res.getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_height) - thumbBgPadding); + } else { // if (config.orientation == + // Configuration.ORIENTATION_LANDSCAPE) { + float thumbTopMargin = res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_top_margin); + float thumbBgPadding = res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_bg_padding); + float textPadding = res + .getDimensionPixelSize(R.dimen.status_bar_recents_text_description_padding); + float labelTextSize = res + .getDimensionPixelSize(R.dimen.status_bar_recents_app_label_text_size); + Paint p = new Paint(); + p.setTextSize(labelTextSize); + float labelTextHeight = p.getFontMetricsInt().bottom + - p.getFontMetricsInt().top; + float descriptionTextSize = res + .getDimensionPixelSize(R.dimen.status_bar_recents_app_description_text_size); + p.setTextSize(labelTextSize); + float descriptionTextHeight = p.getFontMetricsInt().bottom + - p.getFontMetricsInt().top; + + float statusBarHeight = res + .getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + float recentsItemTopPadding = statusBarHeight; + + float height = thumbTopMargin + + thumbHeight + + 2 * thumbBgPadding + textPadding + labelTextHeight + + recentsItemTopPadding + textPadding + descriptionTextHeight; + float recentsItemRightPadding = res + .getDimensionPixelSize(R.dimen.status_bar_recents_item_padding); + float recentsScrollViewRightPadding = res + .getDimensionPixelSize(R.dimen.status_bar_recents_right_glow_margin); + x = (int) (dm.widthPixels - res + .getDimensionPixelSize(R.dimen.status_bar_recents_thumbnail_width) + - thumbBgPadding - recentsItemRightPadding - recentsScrollViewRightPadding); + y = (int) ((dm.heightPixels - statusBarHeight - height) / 2f + thumbTopMargin + + recentsItemTopPadding + thumbBgPadding + statusBarHeight); + } + + ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation( + getStatusBarView(), + first, x, y, + null); + mContext.startActivityAsUser(intent, opts.toBundle(), new UserHandle( + UserHandle.USER_CURRENT)); + } + return; + } catch (ActivityNotFoundException e) { + Log.e(TAG, "Failed to launch RecentAppsIntent", e); + } + } + protected class H extends Handler { public void handleMessage(Message m) { switch (m.what) { - case MSG_OPEN_RECENTS_PANEL: - if (DEBUG) Slog.d(TAG, "opening recents panel"); - if (mRecentsPanel != null) { - mRecentsPanel.show(true, false); - } - break; + case MSG_TOGGLE_RECENTS_PANEL: + if (DEBUG) Slog.d(TAG, "toggle recents panel"); + toggleRecentsActivity(); + break; case MSG_CLOSE_RECENTS_PANEL: - if (DEBUG) Slog.d(TAG, "closing recents panel"); - if (mRecentsPanel != null && mRecentsPanel.isShowing()) { - mRecentsPanel.show(false, false); - } - break; + if (DEBUG) Slog.d(TAG, "closing recents panel"); + Intent intent = new Intent(RecentsActivity.CLOSE_RECENTS_INTENT); + intent.setPackage("com.android.systemui"); + mContext.sendBroadcastAsUser(intent, new UserHandle(UserHandle.USER_CURRENT)); + break; case MSG_PRELOAD_RECENT_APPS: if (DEBUG) Slog.d(TAG, "preloading recents"); - mRecentsPanel.preloadRecentTasksList(); + { + // TODO: + // need to implement this + //final RecentsPanelView recentsPanel = getRecentsPanel(); + //if (recentsPanel != null) { + //recentsPanel.preloadRecentTasksList(); + //} + } break; case MSG_CANCEL_PRELOAD_RECENT_APPS: if (DEBUG) Slog.d(TAG, "cancel preloading recents"); - mRecentsPanel.clearRecentTasksList(); + { + // TODO: + // need to implement this + //final RecentsPanelView recentsPanel = getRecentsPanel(); + //if (recentsPanel != null) { + //recentsPanel.clearRecentTasksList(); + //} + } break; case MSG_OPEN_SEARCH_PANEL: if (DEBUG) Slog.d(TAG, "opening search panel"); @@ -559,8 +650,6 @@ public abstract class BaseStatusBar extends SystemUI implements } protected boolean inflateViews(NotificationData.Entry entry, ViewGroup parent) { - int rowHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.notification_row_min_height); int minHeight = mContext.getResources().getDimensionPixelSize(R.dimen.notification_min_height); int maxHeight = @@ -605,7 +694,6 @@ public abstract class BaseStatusBar extends SystemUI implements // TODO(cwren) normalize variable names with those in updateNotification View expandedOneU = null; View expandedLarge = null; - Exception exception = null; try { expandedOneU = oneU.apply(mContext, adaptive, mOnClickHandler); if (large != null) { 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 2886441..9d2678a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -445,9 +445,6 @@ public class PhoneStatusBar extends BaseStatusBar { // if (wimaxRSSI != null) { // mNetworkController.addWimaxIconView(wimaxRSSI); // } - // Recents Panel - mRecentTasksLoader = new RecentTasksLoader(context); - updateRecentsPanel(); // receive broadcasts IntentFilter filter = new IntentFilter(); @@ -460,6 +457,11 @@ public class PhoneStatusBar extends BaseStatusBar { } @Override + protected View getStatusBarView() { + return mStatusBarView; + } + + @Override protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) { boolean opaque = false; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( @@ -507,6 +509,7 @@ public class PhoneStatusBar extends BaseStatusBar { return lp; } + /* protected void updateRecentsPanel() { super.updateRecentsPanel(R.layout.status_bar_recent_panel); // Make .03 alpha the minimum so you always see the item a bit-- slightly below @@ -517,6 +520,7 @@ public class PhoneStatusBar extends BaseStatusBar { mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel); } } + */ @Override protected void updateSearchPanel() { @@ -604,7 +608,7 @@ public class PhoneStatusBar extends BaseStatusBar { mNavigationBarView.reorient(); mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener); - mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPanel); + mNavigationBarView.getRecentsButton().setOnTouchListener(getRecentTasksLoader()); mNavigationBarView.getHomeButton().setOnTouchListener(mHomeSearchActionListener); updateSearchPanel(); } @@ -785,7 +789,6 @@ public class PhoneStatusBar extends BaseStatusBar { @Override protected void onConfigurationChanged(Configuration newConfig) { - updateRecentsPanel(); updateShowSearchHoldoff(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 84697e0..8ca3a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -84,8 +84,7 @@ import java.io.PrintWriter; import java.util.ArrayList; public class TabletStatusBar extends BaseStatusBar implements - InputMethodsPanel.OnHardKeyboardEnabledChangeListener, - RecentsPanelView.OnRecentsPanelVisibilityChangedListener { + InputMethodsPanel.OnHardKeyboardEnabledChangeListener { public static final boolean DEBUG = false; public static final boolean DEBUG_COMPAT_HELP = false; public static final String TAG = "TabletStatusBar"; @@ -305,10 +304,6 @@ public class TabletStatusBar extends BaseStatusBar implements mWindowManager.addView(mNotificationPanel, lp); - // Recents Panel - mRecentTasksLoader = new RecentTasksLoader(context); - updateRecentsPanel(); - // Search Panel mStatusBarView.setBar(this); mHomeButton.setOnTouchListener(mHomeSearchActionListener); @@ -360,7 +355,7 @@ public class TabletStatusBar extends BaseStatusBar implements mWindowManager.addView(mCompatModePanel, lp); - mRecentButton.setOnTouchListener(mRecentsPanel); + //mRecentButton.setOnTouchListener(mRecentsPanel); //TODO: plumb this mPile = (NotificationRowLayout)mNotificationPanel.findViewById(R.id.content); mPile.removeAllViews(); @@ -393,7 +388,6 @@ public class TabletStatusBar extends BaseStatusBar implements loadDimens(); mNotificationPanelParams.height = getNotificationPanelHeight(); mWindowManager.updateViewLayout(mNotificationPanel, mNotificationPanelParams); - mRecentsPanel.updateValuesFromResources(); mShowSearchHoldoff = mContext.getResources().getInteger( R.integer.config_show_search_delay); updateSearchPanel(); @@ -445,6 +439,7 @@ public class TabletStatusBar extends BaseStatusBar implements } } + @Override public View getStatusBarView() { return mStatusBarView; } @@ -656,11 +651,6 @@ public class TabletStatusBar extends BaseStatusBar implements return lp; } - protected void updateRecentsPanel() { - super.updateRecentsPanel(R.layout.system_bar_recent_panel); - mRecentsPanel.setStatusBarView(mStatusBarView); - } - @Override protected void updateSearchPanel() { super.updateSearchPanel(); @@ -1183,14 +1173,6 @@ public class TabletStatusBar extends BaseStatusBar implements } @Override - public void onRecentsPanelVisibilityChanged(boolean visible) { - boolean altBack = visible || mAltBackButtonEnabledForIme; - mCommandQueue.setNavigationIconHints( - altBack ? (mNavigationIconHints | StatusBarManager.NAVIGATION_HINT_BACK_ALT) - : (mNavigationIconHints & ~StatusBarManager.NAVIGATION_HINT_BACK_ALT)); - } - - @Override public void setHardKeyboardStatus(boolean available, boolean enabled) { if (DEBUG) { Slog.d(TAG, "Set hard keyboard status: available=" + available @@ -1241,10 +1223,7 @@ public class TabletStatusBar extends BaseStatusBar implements public void onClickRecentButton() { if (DEBUG) Slog.d(TAG, "clicked recent apps; disabled=" + mDisabled); if ((mDisabled & StatusBarManager.DISABLE_EXPAND) == 0) { - int msg = (mRecentsPanel.getVisibility() == View.VISIBLE) - ? MSG_CLOSE_RECENTS_PANEL : MSG_OPEN_RECENTS_PANEL; - mHandler.removeMessages(msg); - mHandler.sendEmptyMessage(msg); + toggleRecentApps(); } } @@ -1523,14 +1502,6 @@ public class TabletStatusBar extends BaseStatusBar implements flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; } } - if (Intent.ACTION_SCREEN_OFF.equals(action)) { - // If we're turning the screen off, we want to hide the - // recents panel with no animation - // TODO: hide other things, like the notification tray, - // with no animation as well - mRecentsPanel.show(false, false); - flags |= CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL; - } animateCollapse(flags); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 5a598dc..6022fd2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -148,6 +148,10 @@ public class TvStatusBar extends BaseStatusBar { return mView; } + public View getStatusBarView() { + return null; + } + protected int getStatusBarGravity() { return 0; } |
