diff options
Diffstat (limited to 'packages')
37 files changed, 1077 insertions, 859 deletions
diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml index 4fb26ae..8141fa7 100644 --- a/packages/BackupRestoreConfirmation/AndroidManifest.xml +++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml @@ -1,20 +1,21 @@ <?xml version="1.0" encoding="utf-8"?> <!-- -** Copyright 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. +/* + * Copyright (c) 2014 Google Inc. + * + * 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. + */ --> - <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.backupconfirm" > diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 179bcd1..6b77a7c 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -42,7 +42,7 @@ <activity android:name=".SettingsActivity" android:label="@string/menu_settings" - android:theme="@android:style/Theme.Holo.Light.DialogWhenLarge" + android:theme="@android:style/Theme.DeviceDefault.Light.DialogWhenLarge" android:exported="false" /> <provider diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml index 19d2ebe..8d31444 100644 --- a/packages/DocumentsUI/res/values-sw720dp/styles.xml +++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml @@ -15,11 +15,11 @@ --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="Theme" parent="@android:style/Theme.Holo.Light"> + <style name="Theme" parent="@android:style/Theme.DeviceDefault.Light"> <item name="android:actionOverflowButtonStyle">@style/DarkerOverflow</item> <item name="android:windowBackground">@*android:drawable/dialog_full_holo_light</item> <item name="android:colorBackgroundCacheHint">@null</item> <item name="android:windowIsTranslucent">true</item> - <item name="android:windowAnimationStyle">@*android:style/Animation.Holo.Dialog</item> + <item name="android:windowAnimationStyle">@*android:style/Animation.DeviceDefault.Dialog</item> </style> </resources> diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml index 0c8f712..a416eb4 100644 --- a/packages/DocumentsUI/res/values/styles.xml +++ b/packages/DocumentsUI/res/values/styles.xml @@ -29,11 +29,11 @@ <!-- Normally just a redirection, but this is used to make ourselves a dialog on large tablets --> - <style name="Theme" parent="@android:style/Theme.Holo.Light"> + <style name="Theme" parent="@android:style/Theme.DeviceDefault.Light"> <item name="android:actionOverflowButtonStyle">@style/DarkerOverflow</item> </style> - <style name="DarkerOverflow" parent="@android:style/Widget.Holo.Light.ActionButton.Overflow"> + <style name="DarkerOverflow" parent="@android:style/Widget.DeviceDefault.Light.ActionButton.Overflow"> <item name="android:src">@drawable/ic_menu_overflow</item> </style> diff --git a/packages/Keyguard/res/values/styles.xml b/packages/Keyguard/res/values/styles.xml index d4f98af..80fcf75 100644 --- a/packages/Keyguard/res/values/styles.xml +++ b/packages/Keyguard/res/values/styles.xml @@ -68,7 +68,7 @@ <item name="android:textSize">@dimen/widget_big_font_size</item> </style> - <style name="Widget.TransportControl.SeekBar" parent="@android:style/Widget.Holo.SeekBar"> + <style name="Widget.TransportControl.SeekBar" parent="@android:style/Widget.DeviceDefault.Light.SeekBar"> <item name="android:indeterminateOnly">false</item> <item name="android:progressDrawable">@drawable/scrubber_progress_horizontal_holo_light</item> <item name="android:indeterminateDrawable">@drawable/scrubber_progress_horizontal_holo_light</item> diff --git a/packages/Keyguard/test/AndroidManifest.xml b/packages/Keyguard/test/AndroidManifest.xml index b801e4b..1638127 100644 --- a/packages/Keyguard/test/AndroidManifest.xml +++ b/packages/Keyguard/test/AndroidManifest.xml @@ -23,7 +23,7 @@ <application android:label="@string/app_name" android:icon="@drawable/app_icon"> <activity android:name=".KeyguardTestActivity" android:label="@string/app_name" - android:theme="@android:style/Theme.Holo"> + android:theme="@android:style/Theme.DeviceDefault.Light"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml index 6fc77df..4488b6a 100644 --- a/packages/PrintSpooler/res/layout/select_printer_activity.xml +++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml @@ -61,7 +61,7 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:indeterminate="true" - style="@android:style/Widget.Holo.ProgressBar.Horizontal"> + style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal"> </ProgressBar> </LinearLayout> diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml index 86f4a37..94ab895 100644 --- a/packages/PrintSpooler/res/values/themes.xml +++ b/packages/PrintSpooler/res/values/themes.xml @@ -16,7 +16,7 @@ <resources> - <style name="PrintJobConfigActivityTheme" parent="@android:style/Theme.Holo.Light.NoActionBar"> + <style name="PrintJobConfigActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowSoftInputMode">stateAlwaysHidden|adjustResize</item> <item name="android:windowIsTranslucent">true</item> @@ -25,11 +25,11 @@ <item name="android:windowIsFloating">true</item> </style> - <style name="SelectPrinterActivityTheme" parent="@android:style/Theme.Holo.Light"> + <style name="SelectPrinterActivityTheme" parent="@android:style/Theme.DeviceDefault.Light"> <item name="android:actionBarStyle">@style/SelectPrinterActivityActionBarStyle</item> </style> - <style name="SelectPrinterActivityActionBarStyle" parent="@android:style/Widget.Holo.ActionBar"> + <style name="SelectPrinterActivityActionBarStyle" parent="@android:style/Widget.DeviceDefault.Light.ActionBar"> <item name="android:displayOptions">showTitle</item> </style> diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 29e8d1d..19286c8 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -1,3 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (c) 2014 Google Inc. + * + * 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. + */ +--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.shell" coreApp="true" @@ -83,7 +101,7 @@ <activity android:name=".BugreportWarningActivity" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true" android:exported="false" /> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index b09cc1d..3424eed 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -1,3 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (c) 2014 Google Inc. + * + * 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. + */ +--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" package="com.android.systemui" @@ -157,7 +175,7 @@ <activity android:name=".usb.UsbConfirmActivity" android:exported="true" android:permission="android.permission.MANAGE_USB" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> @@ -166,7 +184,7 @@ <activity android:name=".usb.UsbPermissionActivity" android:exported="true" android:permission="android.permission.MANAGE_USB" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> @@ -175,7 +193,7 @@ <activity android:name=".usb.UsbResolverActivity" android:exported="true" android:permission="android.permission.MANAGE_USB" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> @@ -184,7 +202,7 @@ <activity android:name=".usb.UsbAccessoryUriActivity" android:exported="true" android:permission="android.permission.MANAGE_USB" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> @@ -192,7 +210,7 @@ <!-- started from UsbDebuggingManager --> <activity android:name=".usb.UsbDebuggingActivity" android:permission="android.permission.MANAGE_USB" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:finishOnCloseSystemDialogs="true" android:excludeFromRecents="true"> </activity> @@ -202,7 +220,7 @@ android:name=".net.NetworkOverLimitActivity" android:exported="true" android:permission="android.permission.MANAGE_NETWORK_POLICY" - android:theme="@android:style/Theme.Holo.Panel" + android:theme="@android:style/Theme.DeviceDefault.Light.Panel" android:finishOnCloseSystemDialogs="true" android:launchMode="singleTop" android:taskAffinity="com.android.systemui.net" diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml index f5ce222..96da21f 100644 --- a/packages/SystemUI/res/layout/recents_task_view.xml +++ b/packages/SystemUI/res/layout/recents_task_view.xml @@ -28,21 +28,29 @@ android:layout_gravity="top|center_horizontal" android:background="#e6444444"> <ImageView - android:id="@+id/activity_icon" - android:layout_width="@dimen/recents_task_view_icon_size" - android:layout_height="@dimen/recents_task_view_icon_size" - android:layout_gravity="top|left" + android:id="@+id/application_icon" + android:layout_width="@dimen/recents_task_view_application_icon_size" + android:layout_height="@dimen/recents_task_view_application_icon_size" + android:layout_gravity="center_vertical|left" android:padding="8dp" /> <TextView android:id="@+id/activity_description" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical|left" - android:layout_marginLeft="@dimen/recents_task_view_icon_size" + android:layout_marginLeft="@dimen/recents_task_view_application_icon_size" + android:layout_marginRight="@dimen/recents_task_view_activity_icon_size" android:textSize="24sp" android:textColor="#ffffffff" android:text="@string/recents_empty_message" android:fontFamily="sans-serif-thin" /> + <ImageView + android:id="@+id/activity_icon" + android:layout_width="@dimen/recents_task_view_activity_icon_size" + android:layout_height="@dimen/recents_task_view_activity_icon_size" + android:layout_gravity="center_vertical|right" + android:padding="12dp" + android:visibility="invisible" /> </com.android.systemui.recents.views.TaskBarView> </com.android.systemui.recents.views.TaskView> diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 1da66bb..a7ec064 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -24,19 +24,10 @@ android:id="@+id/notification_panel" android:layout_width="0dp" android:layout_height="wrap_content" - android:background="@drawable/notification_panel_bg" android:paddingTop="@dimen/notification_panel_padding_top" android:layout_marginStart="@dimen/notification_panel_margin_left" > - <View - android:id="@+id/handle" - android:layout_width="match_parent" - android:layout_height="@dimen/close_handle_height" - android:background="@drawable/status_bar_close" - android:visibility="invisible" - /> - <include layout="@layout/carrier_label" android:layout_height="@dimen/carrier_label_height" @@ -69,6 +60,7 @@ /> <FrameLayout + android:id="@+id/notification_container_parent" android:layout_width="match_parent" android:layout_height="wrap_content" > @@ -77,21 +69,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - - <ScrollView - android:id="@+id/scroll" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:fadingEdge="none" - android:overScrollMode="ifContentScrolls" - > - <com.android.systemui.statusbar.policy.NotificationRowLayout - android:id="@+id/latestItems" - android:layout_width="match_parent" - android:layout_height="wrap_content" - systemui:rowHeight="@dimen/notification_row_min_height" - /> - </ScrollView> <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 38e1083..1c6d5ad 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -230,8 +230,11 @@ <!-- Default distance from each snap target that GlowPadView considers a "hit" --> <dimen name="glowpadview_inner_radius">15dip</dimen> - <!-- The size of the icon in the recents task view. --> - <dimen name="recents_task_view_icon_size">60dp</dimen> + <!-- The size of the application icon in the recents task view. --> + <dimen name="recents_task_view_application_icon_size">60dp</dimen> + + <!-- The size of the activity icon in the recents task view. --> + <dimen name="recents_task_view_activity_icon_size">60dp</dimen> <!-- Space below the notification stack --> <dimen name="notification_stack_margin_bottom">0dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 14af020..76cadd7 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -16,12 +16,12 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="RecentsStyle" parent="@android:style/Theme.Holo.Wallpaper.NoTitleBar"> + <style name="RecentsStyle" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar"> <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item> </style> <!-- Alternate Recents theme --> - <style name="RecentsTheme" parent="@android:style/Theme.Holo.Wallpaper.NoTitleBar"> + <style name="RecentsTheme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar"> <item name="android:windowTranslucentStatus">true</item> <item name="android:windowTranslucentNavigation">true</item> <item name="android:windowAnimationStyle">@style/Animation.RecentsActivity</item> diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java index 90b0c49..1832d37 100644 --- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java +++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java @@ -34,6 +34,8 @@ import android.view.View.OnClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; +import com.android.systemui.statusbar.policy.ScrollAdapter; + public class ExpandHelper implements Gefingerpoken, OnClickListener { public interface Callback { View getChildAtRawPosition(float x, float y); @@ -609,19 +611,5 @@ public class ExpandHelper implements Gefingerpoken, OnClickListener { } mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM); } - - public interface ScrollAdapter { - - /** - * @return Whether the view returned by {@link #getHostView()} is scrolled to the top - * and can therefore be expanded by a single finger drag - */ - public boolean isScrolledToTop(); - - /** - * @return The view in which the scrolling is performed - */ - public View getHostView(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index 227e19d..8543b97 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -23,35 +23,26 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; -import android.os.SystemProperties; import android.os.UserHandle; import android.util.DisplayMetrics; -import android.util.Log; import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; import com.android.systemui.R; -import com.android.systemui.RecentsComponent; -import com.android.systemui.SystemUI; import java.util.List; @@ -113,6 +104,7 @@ public class AlternateRecentsComponent { final static String sRecentsService = "com.android.systemui.recents.RecentsService"; Context mContext; + SystemServicesProxy mSystemServicesProxy; // Recents service binding Messenger mService = null; @@ -127,6 +119,7 @@ public class AlternateRecentsComponent { public AlternateRecentsComponent(Context context) { mContext = context; + mSystemServicesProxy = new SystemServicesProxy(context); mMessenger = new Messenger(new RecentsMessageHandler()); } @@ -219,17 +212,16 @@ public class AlternateRecentsComponent { /** Loads the first task thumbnail */ Bitmap loadFirstTaskThumbnail() { - ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(1, - ActivityManager.RECENT_IGNORE_UNAVAILABLE | ActivityManager.RECENT_INCLUDE_PROFILES, + SystemServicesProxy ssp = mSystemServicesProxy; + List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1, UserHandle.CURRENT.getIdentifier()); for (ActivityManager.RecentTaskInfo t : tasks) { // Skip tasks in the home stack - if (am.isInHomeStack(t.persistentId)) { + if (ssp.isInHomeStack(t.persistentId)) { return null; } - Bitmap thumbnail = am.getTaskTopThumbnail(t.persistentId); + Bitmap thumbnail = ssp.getTaskThumbnail(t.persistentId); return thumbnail; } return null; @@ -237,13 +229,12 @@ public class AlternateRecentsComponent { /** Returns whether there is a first task */ boolean hasFirstTask() { - ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(1, - ActivityManager.RECENT_IGNORE_UNAVAILABLE | ActivityManager.RECENT_INCLUDE_PROFILES, + SystemServicesProxy ssp = mSystemServicesProxy; + List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1, UserHandle.CURRENT.getIdentifier()); for (ActivityManager.RecentTaskInfo t : tasks) { // Skip tasks in the home stack - if (am.isInHomeStack(t.persistentId)) { + if (ssp.isInHomeStack(t.persistentId)) { continue; } @@ -294,8 +285,8 @@ public class AlternateRecentsComponent { // If Recents is the front most activity, then we should just communicate with it directly // to launch the first task or dismiss itself - ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); - List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1); + SystemServicesProxy ssp = mSystemServicesProxy; + List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1); if (!tasks.isEmpty()) { ComponentName topActivity = tasks.get(0).topActivity; diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index 2b08141..8c5c8fa 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -31,6 +31,12 @@ public class Constants { public static final boolean EnableToggleNewRecentsActivity = false; // This disables the bitmap and icon caches to public static final boolean DisableBackgroundCache = false; + // For debugging, this enables us to create mock recents tasks + public static final boolean EnableSystemServicesProxy = false; + // For debugging, this defines the number of mock recents packages to create + public static final int SystemServicesProxyMockPackageCount = 12; + // For debugging, this defines the number of mock recents tasks to create + public static final int SystemServicesProxyMockTaskCount = 75; // Timing certain paths public static final String TimeRecentsStartupKey = "startup"; @@ -73,8 +79,6 @@ public class Constants { public static class RecentsTaskLoader { // XXX: This should be calculated on the first load public static final int PreloadFirstTasksCount = 5; - // For debugging, this allows us to multiply the number of cards for each task - public static final int TaskEntryMultiplier = 1; } public static class TaskStackView { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java index 928c732..e193a95 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java @@ -20,7 +20,6 @@ import android.app.ActivityManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.pm.ActivityInfo; -import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -109,18 +108,20 @@ class TaskResourceLoader implements Runnable { Handler mLoadThreadHandler; Handler mMainThreadHandler; + SystemServicesProxy mSystemServicesProxy; TaskResourceLoadQueue mLoadQueue; - DrawableLruCache mIconCache; + DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; boolean mCancelled; boolean mWaitingOnLoadQueue; /** Constructor, creates a new loading thread that loads task resources in the background */ - public TaskResourceLoader(TaskResourceLoadQueue loadQueue, DrawableLruCache iconCache, + public TaskResourceLoader(TaskResourceLoadQueue loadQueue, + DrawableLruCache applicationIconCache, BitmapLruCache thumbnailCache) { mLoadQueue = loadQueue; - mIconCache = iconCache; + mApplicationIconCache = applicationIconCache; mThumbnailCache = thumbnailCache; mMainThreadHandler = new Handler(); mLoadThread = new HandlerThread("Recents-TaskResourceLoader"); @@ -135,6 +136,7 @@ class TaskResourceLoader implements Runnable { Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|start]"); mContext = context; mCancelled = false; + mSystemServicesProxy = new SystemServicesProxy(context); // Notify the load thread to start loading synchronized(mLoadThread) { mLoadThread.notifyAll(); @@ -146,6 +148,7 @@ class TaskResourceLoader implements Runnable { Console.log(Constants.DebugFlags.App.TaskDataLoader, "[TaskResourceLoader|stop]"); // Mark as cancelled for the thread to pick up mCancelled = true; + mSystemServicesProxy = null; // If we are waiting for the load queue for more tasks, then we can just reset the // Context now, since nothing is using it if (mWaitingOnLoadQueue) { @@ -175,66 +178,60 @@ class TaskResourceLoader implements Runnable { } } } else { + SystemServicesProxy ssp = mSystemServicesProxy; + // Load the next item from the queue Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask(); final Task t = nextTaskData.first; final boolean forceLoadTask = nextTaskData.second; if (t != null) { - try { - Drawable loadIcon = mIconCache.get(t.key); - Bitmap loadThumbnail = mThumbnailCache.get(t.key); - Console.log(Constants.DebugFlags.App.TaskDataLoader, - " [TaskResourceLoader|load]", - t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail + - " forceLoad: " + forceLoadTask); - // Load the icon - if (loadIcon == null || forceLoadTask) { - PackageManager pm = mContext.getPackageManager(); - ActivityInfo info = pm.getActivityInfo(t.key.baseIntent.getComponent(), - PackageManager.GET_META_DATA); - Drawable icon = info.loadIcon(pm); - if (!mCancelled) { - if (icon != null) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, - " [TaskResourceLoader|loadIcon]", - icon); - loadIcon = icon; - mIconCache.put(t.key, icon); - } - } - } - // Load the thumbnail - if (loadThumbnail == null || forceLoadTask) { - ActivityManager am = (ActivityManager) - mContext.getSystemService(Context.ACTIVITY_SERVICE); - Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id); - if (!mCancelled) { - if (thumbnail != null) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, - " [TaskResourceLoader|loadThumbnail]", - thumbnail); - loadThumbnail = thumbnail; - mThumbnailCache.put(t.key, thumbnail); - } else { - Console.logError(mContext, - "Failed to load task top thumbnail for: " + - t.key.baseIntent.getComponent().getPackageName()); - } + Drawable loadIcon = mApplicationIconCache.get(t.key); + Bitmap loadThumbnail = mThumbnailCache.get(t.key); + Console.log(Constants.DebugFlags.App.TaskDataLoader, + " [TaskResourceLoader|load]", + t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail + + " forceLoad: " + forceLoadTask); + // Load the application icon + if (loadIcon == null || forceLoadTask) { + ActivityInfo info = ssp.getActivityInfo(t.key.baseIntent.getComponent()); + Drawable icon = ssp.getActivityIcon(info); + if (!mCancelled) { + if (icon != null) { + Console.log(Constants.DebugFlags.App.TaskDataLoader, + " [TaskResourceLoader|loadIcon]", + icon); + loadIcon = icon; + mApplicationIconCache.put(t.key, icon); } } + } + // Load the thumbnail + if (loadThumbnail == null || forceLoadTask) { + Bitmap thumbnail = ssp.getTaskThumbnail(t.key.id); if (!mCancelled) { - // Notify that the task data has changed - final Drawable newIcon = loadIcon; - final Bitmap newThumbnail = loadThumbnail; - mMainThreadHandler.post(new Runnable() { - @Override - public void run() { - t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask); - } - }); + if (thumbnail != null) { + Console.log(Constants.DebugFlags.App.TaskDataLoader, + " [TaskResourceLoader|loadThumbnail]", + thumbnail); + loadThumbnail = thumbnail; + mThumbnailCache.put(t.key, thumbnail); + } else { + Console.logError(mContext, + "Failed to load task top thumbnail for: " + + t.key.baseIntent.getComponent().getPackageName()); + } } - } catch (PackageManager.NameNotFoundException ne) { - ne.printStackTrace(); + } + if (!mCancelled) { + // Notify that the task data has changed + final Drawable newIcon = loadIcon; + final Bitmap newThumbnail = loadThumbnail; + mMainThreadHandler.post(new Runnable() { + @Override + public void run() { + t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask); + } + }); } } @@ -296,7 +293,8 @@ class BitmapLruCache extends LruCache<Task.TaskKey, Bitmap> { public class RecentsTaskLoader { static RecentsTaskLoader sInstance; - DrawableLruCache mIconCache; + SystemServicesProxy mSystemServicesProxy; + DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; TaskResourceLoadQueue mLoadQueue; TaskResourceLoader mLoader; @@ -304,7 +302,7 @@ public class RecentsTaskLoader { int mMaxThumbnailCacheSize; int mMaxIconCacheSize; - BitmapDrawable mDefaultIcon; + BitmapDrawable mDefaultApplicationIcon; Bitmap mDefaultThumbnail; /** Private Constructor */ @@ -324,11 +322,12 @@ public class RecentsTaskLoader { "[RecentsTaskLoader|init]", "thumbnailCache: " + thumbnailCacheSize + " iconCache: " + iconCacheSize); - // Initialize the cache and loaders + // Initialize the proxy, cache and loaders + mSystemServicesProxy = new SystemServicesProxy(context); mLoadQueue = new TaskResourceLoadQueue(); - mIconCache = new DrawableLruCache(iconCacheSize); + mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); - mLoader = new TaskResourceLoader(mLoadQueue, mIconCache, mThumbnailCache); + mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache); // Create the default assets Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); @@ -339,10 +338,10 @@ public class RecentsTaskLoader { c.setBitmap(mDefaultThumbnail); c.drawColor(0x00000000); c.setBitmap(null); - mDefaultIcon = new BitmapDrawable(context.getResources(), icon); + mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon); Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|defaultBitmaps]", - "icon: " + mDefaultIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed); + "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed); } /** Initializes the recents task loader */ @@ -358,6 +357,11 @@ public class RecentsTaskLoader { return sInstance; } + /** Returns the system services proxy */ + public SystemServicesProxy getSystemServicesProxy() { + return mSystemServicesProxy; + } + /** Reload the set of recent tasks */ SpaceNode reload(Context context, int preloadCount) { Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]"); @@ -367,141 +371,118 @@ public class RecentsTaskLoader { SpaceNode root = new SpaceNode(context); root.setStack(stack); - try { - long t1 = System.currentTimeMillis(); - - PackageManager pm = context.getPackageManager(); - ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + long t1 = System.currentTimeMillis(); - // Get the recent tasks - List<ActivityManager.RecentTaskInfo> tasks = am.getRecentTasksForUser(25, - ActivityManager.RECENT_IGNORE_UNAVAILABLE | - ActivityManager.RECENT_INCLUDE_PROFILES, UserHandle.CURRENT.getIdentifier()); - Collections.reverse(tasks); - Console.log(Constants.DebugFlags.App.TimeSystemCalls, - "[RecentsTaskLoader|getRecentTasks]", - "" + (System.currentTimeMillis() - t1) + "ms"); - Console.log(Constants.DebugFlags.App.TaskDataLoader, - "[RecentsTaskLoader|tasks]", "" + tasks.size()); + // Get the recent tasks + SystemServicesProxy ssp = mSystemServicesProxy; + List<ActivityManager.RecentTaskInfo> tasks = + ssp.getRecentTasks(25, UserHandle.CURRENT.getIdentifier()); + Collections.reverse(tasks); + Console.log(Constants.DebugFlags.App.TimeSystemCalls, + "[RecentsTaskLoader|getRecentTasks]", + "" + (System.currentTimeMillis() - t1) + "ms"); + Console.log(Constants.DebugFlags.App.TaskDataLoader, + "[RecentsTaskLoader|tasks]", "" + tasks.size()); - // Remove home/recents tasks - Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); - while (iter.hasNext()) { - ActivityManager.RecentTaskInfo t = iter.next(); + // Remove home/recents tasks + Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); + while (iter.hasNext()) { + ActivityManager.RecentTaskInfo t = iter.next(); - // Skip tasks in the home stack - if (am.isInHomeStack(t.persistentId)) { - iter.remove(); - continue; - } - // Skip tasks from this Recents package - if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) { - iter.remove(); - continue; - } + // Skip tasks in the home stack + if (ssp.isInHomeStack(t.persistentId)) { + iter.remove(); + continue; } + // Skip tasks from this Recents package + if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) { + iter.remove(); + continue; + } + } - // Add each task to the task stack - t1 = System.currentTimeMillis(); - int taskCount = tasks.size(); - for (int i = 0; i < taskCount; i++) { - ActivityManager.RecentTaskInfo t = tasks.get(i); - ActivityInfo info = pm.getActivityInfo(t.baseIntent.getComponent(), - PackageManager.GET_META_DATA); - String title = info.loadLabel(pm).toString(); - boolean isForemostTask = (i == (taskCount - 1)); - - // Preload the specified number of apps - if (i >= (taskCount - preloadCount)) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, - "[RecentsTaskLoader|preloadTask]", - "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName()); - - String label = (t.activityLabel == null ? title : t.activityLabel.toString()); - BitmapDrawable bd = null; - if (t.activityIcon != null) { - bd = new BitmapDrawable(res, t.activityIcon); + // Add each task to the task stack + t1 = System.currentTimeMillis(); + int taskCount = tasks.size(); + for (int i = 0; i < taskCount; i++) { + ActivityManager.RecentTaskInfo t = tasks.get(i); + ActivityInfo info = ssp.getActivityInfo(t.baseIntent.getComponent()); + String activityLabel = (t.activityLabel == null ? ssp.getActivityLabel(info) : + t.activityLabel.toString()); + Bitmap activityIcon = t.activityIcon; + boolean isForemostTask = (i == (taskCount - 1)); + + // Create a new task + Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, activityLabel, + activityIcon); + + // Preload the specified number of apps + if (i >= (taskCount - preloadCount)) { + Console.log(Constants.DebugFlags.App.TaskDataLoader, + "[RecentsTaskLoader|preloadTask]", + "i: " + i + " task: " + t.baseIntent.getComponent().getPackageName()); + + // Load the icon (if possible and not the foremost task, from the cache) + if (!isForemostTask) { + task.applicationIcon = mApplicationIconCache.get(task.key); + if (task.applicationIcon != null) { + // Even though we get things from the cache, we should update them + // if they've changed in the bg + tasksToForceLoad.add(task); } - Task task = new Task(t.persistentId, (t.id > -1), t.baseIntent, label, bd); - - // Load the icon (if possible and not the foremost task, from the cache) - if (task.icon != null) { - mIconCache.put(task.key, task.icon); + } + if (task.applicationIcon == null) { + task.applicationIcon = ssp.getActivityIcon(info); + if (task.applicationIcon != null) { + mApplicationIconCache.put(task.key, task.applicationIcon); } else { - if (!isForemostTask) { - task.icon = mIconCache.get(task.key); - if (task.icon != null) { - // Even though we get things from the cache, we should update them - // if they've changed in the bg - tasksToForceLoad.add(task); - } - } - if (task.icon == null) { - task.icon = info.loadIcon(pm); - if (task.icon != null) { - mIconCache.put(task.key, task.icon); - } else { - task.icon = mDefaultIcon; - } - } - } - - // Load the thumbnail (if possible and not the foremost task, from the cache) - if (!isForemostTask) { - task.thumbnail = mThumbnailCache.get(task.key); - if (task.thumbnail != null) { - // Even though we get things from the cache, we should update them if - // they've changed in the bg - tasksToForceLoad.add(task); - } - } - if (task.thumbnail == null) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, - "[RecentsTaskLoader|loadingTaskThumbnail]"); - task.thumbnail = am.getTaskTopThumbnail(t.id); - if (task.thumbnail != null) { - mThumbnailCache.put(task.key, task.thumbnail); - } else { - task.thumbnail = mDefaultThumbnail; - } + task.applicationIcon = mDefaultApplicationIcon; } + } - // Create as many tasks a we want to multiply by - for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, - " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName()); - stack.addTask(task); + // Load the thumbnail (if possible and not the foremost task, from the cache) + if (!isForemostTask) { + task.thumbnail = mThumbnailCache.get(task.key); + if (task.thumbnail != null) { + // Even though we get things from the cache, we should update them if + // they've changed in the bg + tasksToForceLoad.add(task); } - } else { - // Create as many tasks a we want to multiply by - for (int j = 0; j < Constants.Values.RecentsTaskLoader.TaskEntryMultiplier; j++) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, - " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName()); - stack.addTask(new Task(t.persistentId, (t.id > -1), t.baseIntent, title, - null, null)); + } + if (task.thumbnail == null) { + Console.log(Constants.DebugFlags.App.TaskDataLoader, + "[RecentsTaskLoader|loadingTaskThumbnail]"); + task.thumbnail = ssp.getTaskThumbnail(task.key.id); + if (task.thumbnail != null) { + mThumbnailCache.put(task.key, task.thumbnail); + } else { + task.thumbnail = mDefaultThumbnail; } } } - Console.log(Constants.DebugFlags.App.TimeSystemCalls, - "[RecentsTaskLoader|getAllTaskTopThumbnail]", - "" + (System.currentTimeMillis() - t1) + "ms"); - - /* - // Get all the stacks - t1 = System.currentTimeMillis(); - List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos(); - Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms"); - Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size()); - for (ActivityManager.StackInfo s : stackInfos) { - Console.log(Constants.DebugFlags.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString()); - if (stacks.containsKey(s.stackId)) { - stacks.get(s.stackId).setRect(s.bounds); - } + + // Add the task to the stack + Console.log(Constants.DebugFlags.App.TaskDataLoader, + " [RecentsTaskLoader|task]", t.baseIntent.getComponent().getPackageName()); + stack.addTask(task); + } + Console.log(Constants.DebugFlags.App.TimeSystemCalls, + "[RecentsTaskLoader|getAllTaskTopThumbnail]", + "" + (System.currentTimeMillis() - t1) + "ms"); + + /* + // Get all the stacks + t1 = System.currentTimeMillis(); + List<ActivityManager.StackInfo> stackInfos = ams.getAllStackInfos(); + Console.log(Constants.DebugFlags.App.TimeSystemCalls, "[RecentsTaskLoader|getAllStackInfos]", "" + (System.currentTimeMillis() - t1) + "ms"); + Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|stacks]", "" + tasks.size()); + for (ActivityManager.StackInfo s : stackInfos) { + Console.log(Constants.DebugFlags.App.TaskDataLoader, " [RecentsTaskLoader|stack]", s.toString()); + if (stacks.containsKey(s.stackId)) { + stacks.get(s.stackId).setRect(s.bounds); } - */ - } catch (Exception e) { - e.printStackTrace(); } + */ // Start the task loader mLoader.start(context); @@ -516,16 +497,16 @@ public class RecentsTaskLoader { /** Acquires the task resource data from the pool. */ public void loadTaskData(Task t) { - Drawable icon = mIconCache.get(t.key); + Drawable applicationIcon = mApplicationIconCache.get(t.key); Bitmap thumbnail = mThumbnailCache.get(t.key); Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|loadTask]", - t + " icon: " + icon + " thumbnail: " + thumbnail + + t + " applicationIcon: " + applicationIcon + " thumbnail: " + thumbnail + " thumbnailCacheSize: " + mThumbnailCache.size()); boolean requiresLoad = false; - if (icon == null) { - icon = mDefaultIcon; + if (applicationIcon == null) { + applicationIcon = mDefaultApplicationIcon; requiresLoad = true; } if (thumbnail == null) { @@ -535,7 +516,7 @@ public class RecentsTaskLoader { if (requiresLoad) { mLoadQueue.addTask(t, false); } - t.notifyTaskDataLoaded(thumbnail, icon, false); + t.notifyTaskDataLoaded(thumbnail, applicationIcon, false); } /** Releases the task resource data back into the pool. */ @@ -545,7 +526,7 @@ public class RecentsTaskLoader { " thumbnailCacheSize: " + mThumbnailCache.size()); mLoadQueue.removeTask(t); - t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon); + t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); } /** Completely removes the resource data from the pool. */ @@ -555,8 +536,8 @@ public class RecentsTaskLoader { mLoadQueue.removeTask(t); mThumbnailCache.remove(t.key); - mIconCache.remove(t.key); - t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultIcon); + mApplicationIconCache.remove(t.key); + t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon); } /** Stops the task loader and clears all pending tasks */ @@ -579,19 +560,19 @@ public class RecentsTaskLoader { case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND: // We are leaving recents, so trim the data a bit mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 2); - mIconCache.trimToSize(mMaxIconCacheSize / 2); + mApplicationIconCache.trimToSize(mMaxIconCacheSize / 2); break; case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: case ComponentCallbacks2.TRIM_MEMORY_MODERATE: // We are going to be low on memory mThumbnailCache.trimToSize(mMaxThumbnailCacheSize / 4); - mIconCache.trimToSize(mMaxIconCacheSize / 4); + mApplicationIconCache.trimToSize(mMaxIconCacheSize / 4); break; case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: // We are low on memory, so release everything mThumbnailCache.evictAll(); - mIconCache.evictAll(); + mApplicationIconCache.evictAll(); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java new file mode 100644 index 0000000..f147fbc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents; + +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Acts as a shim around the real system services that we need to access data from, and provides + * a point of injection when testing UI. + */ +public class SystemServicesProxy { + ActivityManager mAm; + PackageManager mPm; + String mPackage; + + Bitmap mDummyIcon; + + /** Private constructor */ + public SystemServicesProxy(Context context) { + mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mPm = context.getPackageManager(); + mPackage = context.getPackageName(); + + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + // Create a dummy icon + mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8); + Canvas c = new Canvas(mDummyIcon); + c.drawColor(0xFFFF0000); + c.setBitmap(null); + } + } + + /** Returns a list of the recents tasks */ + public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) { + if (mAm == null) return null; + + // If we are mocking, then create some recent tasks + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + ArrayList<ActivityManager.RecentTaskInfo> tasks = + new ArrayList<ActivityManager.RecentTaskInfo>(); + int count = Math.min(numTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount); + for (int i = 0; i < count; i++) { + // Create a dummy component name + int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount; + ComponentName cn = new ComponentName("com.android.test" + packageIndex, + "com.android.test" + i + ".Activity"); + // Create the recent task info + ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); + rti.id = rti.persistentId = i; + rti.baseIntent = new Intent(); + rti.baseIntent.setComponent(cn); + rti.description = rti.activityLabel = + Long.toString(Math.abs(new Random().nextLong()), 36); + if (i % 2 == 0) { + rti.activityIcon = Bitmap.createBitmap(mDummyIcon); + } + tasks.add(rti); + } + return tasks; + } + + return mAm.getRecentTasksForUser(numTasks, + ActivityManager.RECENT_IGNORE_UNAVAILABLE | + ActivityManager.RECENT_INCLUDE_PROFILES, userId); + } + + /** Returns a list of the running tasks */ + public List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) { + if (mAm == null) return null; + return mAm.getRunningTasks(numTasks); + } + + /** Returns whether the specified task is in the home stack */ + public boolean isInHomeStack(int taskId) { + if (mAm == null) return false; + + // If we are mocking, then just return false + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + return false; + } + + return mAm.isInHomeStack(taskId); + } + + /** Returns the top task thumbnail for the given task id */ + public Bitmap getTaskThumbnail(int taskId) { + if (mAm == null) return null; + + // If we are mocking, then just return a dummy thumbnail + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + Bitmap thumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(thumbnail); + c.drawColor(0xFF00ff00); + c.setBitmap(null); + return thumbnail; + } + + return mAm.getTaskTopThumbnail(taskId); + } + + /** Moves a task to the front with the specified activity options */ + public void moveTaskToFront(int taskId, ActivityOptions opts) { + if (mAm == null) return; + if (Constants.DebugFlags.App.EnableSystemServicesProxy) return; + + if (opts != null) { + mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME, + opts.toBundle()); + } else { + mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME); + } + } + + /** Removes the task and kills the process */ + public void removeTask(int taskId) { + if (mAm == null) return; + if (Constants.DebugFlags.App.EnableSystemServicesProxy) return; + + mAm.removeTask(taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS); + } + + /** Returns the activity info for a given component name */ + public ActivityInfo getActivityInfo(ComponentName cn) { + if (mPm == null) return null; + if (Constants.DebugFlags.App.EnableSystemServicesProxy) return null; + + try { + return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + /** Returns the activity label */ + public String getActivityLabel(ActivityInfo info) { + if (mPm == null) return null; + + // If we are mocking, then return a mock label + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + return "Recent Task"; + } + + return info.loadLabel(mPm).toString(); + } + + /** Returns the activity icon */ + public Drawable getActivityIcon(ActivityInfo info) { + if (mPm == null) return null; + + // If we are mocking, then return a mock label + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + return new ColorDrawable(0xFFff0000); + } + + return info.loadIcon(mPm); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index 677334d..ed2ab2a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -19,7 +19,6 @@ package com.android.systemui.recents.model; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; -import com.android.systemui.recents.Constants; /** @@ -61,23 +60,19 @@ public class Task { } public TaskKey key; - public String title; - public Drawable icon; + public Drawable applicationIcon; + public String activityLabel; + public Bitmap activityIcon; public Bitmap thumbnail; public boolean isActive; TaskCallbacks mCb; - public Task(int id, boolean isActive, Intent intent, String activityTitle, Drawable icon) { - this(id, isActive, intent, activityTitle, icon, null); - } - - public Task(int id, boolean isActive, Intent intent, String activityTitle, Drawable icon, - Bitmap thumbnail) { + public Task(int id, boolean isActive, Intent intent, String activityTitle, + Bitmap activityIcon) { this.key = new TaskKey(id, intent); - this.title = activityTitle; - this.icon = icon; - this.thumbnail = thumbnail; + this.activityLabel = activityTitle; + this.activityIcon = activityIcon; this.isActive = isActive; } @@ -87,8 +82,9 @@ public class Task { } /** Notifies the callback listeners that this task has been loaded */ - public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon, boolean reloadingTaskData) { - this.icon = icon; + public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable applicationIcon, + boolean reloadingTaskData) { + this.applicationIcon = applicationIcon; this.thumbnail = thumbnail; if (mCb != null) { mCb.onTaskDataLoaded(reloadingTaskData); @@ -96,8 +92,8 @@ public class Task { } /** Notifies the callback listeners that this task has been unloaded */ - public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultIcon) { - icon = defaultIcon; + public void notifyTaskDataUnloaded(Bitmap defaultThumbnail, Drawable defaultApplicationIcon) { + applicationIcon = defaultApplicationIcon; thumbnail = defaultThumbnail; if (mCb != null) { mCb.onTaskDataUnloaded(); @@ -106,14 +102,7 @@ public class Task { @Override public boolean equals(Object o) { - // If we have multiple task entries for the same task, then we do the simple object - // equality check - if (Constants.Values.RecentsTaskLoader.TaskEntryMultiplier > 1) { - return super.equals(o); - } - - // Otherwise, check that the id and intent match (the other fields can be asynchronously - // loaded and is unsuitable to testing the identity of this Task) + // Check that the id matches Task t = (Task) o; return key.equals(t.key); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index e89bde5..cb52794 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -16,7 +16,6 @@ package com.android.systemui.recents.views; -import android.app.ActivityManager; import android.app.ActivityOptions; import android.content.ActivityNotFoundException; import android.content.Context; @@ -30,6 +29,7 @@ import android.widget.FrameLayout; import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.recents.RecentsTaskLoader; import com.android.systemui.recents.model.SpaceNode; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; @@ -246,14 +246,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV if (task.isActive) { // Bring an active task to the foreground - ActivityManager am = (ActivityManager) - stackView.getContext().getSystemService(Context.ACTIVITY_SERVICE); - if (opts != null) { - am.moveTaskToFront(task.key.id, ActivityManager.MOVE_TASK_WITH_HOME, - opts.toBundle()); - } else { - am.moveTaskToFront(task.key.id, ActivityManager.MOVE_TASK_WITH_HOME); - } + RecentsTaskLoader.getInstance().getSystemServicesProxy() + .moveTaskToFront(task.key.id, opts); } else { // Launch the activity with the desired animation Intent i = new Intent(task.key.baseIntent); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index 235c6cc..c9a6d67 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -17,21 +17,12 @@ package com.android.systemui.recents.views; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Typeface; import android.util.AttributeSet; import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import com.android.systemui.R; -import com.android.systemui.recents.Constants; -import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.model.Task; @@ -39,6 +30,7 @@ import com.android.systemui.recents.model.Task; class TaskBarView extends FrameLayout { Task mTask; + ImageView mApplicationIcon; ImageView mActivityIcon; TextView mActivityDescription; @@ -61,6 +53,7 @@ class TaskBarView extends FrameLayout { @Override protected void onFinishInflate() { // Initialize the icon and description views + mApplicationIcon = (ImageView) findViewById(R.id.application_icon); mActivityIcon = (ImageView) findViewById(R.id.activity_icon); mActivityDescription = (TextView) findViewById(R.id.activity_description); } @@ -68,9 +61,13 @@ class TaskBarView extends FrameLayout { /** Binds the bar view to the task */ void rebindToTask(Task t, boolean animate) { mTask = t; - if (t.icon != null) { - mActivityIcon.setImageDrawable(t.icon); - mActivityDescription.setText(t.title); + if (t.applicationIcon != null) { + mApplicationIcon.setImageDrawable(t.applicationIcon); + mActivityDescription.setText(t.activityLabel); + if (t.activityIcon != null) { + mActivityIcon.setImageBitmap(t.activityIcon); + mActivityIcon.setVisibility(View.VISIBLE); + } if (animate) { // XXX: Investigate how expensive it will be to create a second bitmap and crossfade } @@ -80,7 +77,9 @@ class TaskBarView extends FrameLayout { /** Unbinds the bar view from the task */ void unbindFromTask() { mTask = null; - mActivityIcon.setImageDrawable(null); + mApplicationIcon.setImageDrawable(null); + mActivityIcon.setImageBitmap(null); + mActivityIcon.setVisibility(View.INVISIBLE); mActivityDescription.setText(""); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index aeb571d..dfd608c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -22,7 +22,6 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.Activity; -import android.app.ActivityManager; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; @@ -40,6 +39,7 @@ import com.android.systemui.recents.Console; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.RecentsTaskLoader; +import com.android.systemui.recents.SystemServicesProxy; import com.android.systemui.recents.Utilities; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; @@ -234,7 +234,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // When we are picking up a new view from the view pool, prepare it for any // following animation by putting it in a reasonable place if (mStackViewsAnimationDuration > 0 && i != 0) { - int fromIndex = (transform.t < 0) ? (i - 1) : (i + 1); + int fromIndex = (transform.t < 0) ? (visibleRange[0] - 1) : + (visibleRange[1] + 1); tv.updateViewPropertiesToTaskTransform(null, getStackTransform(fromIndex, stackScroll), 0); } @@ -1268,12 +1269,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { loader.deleteTaskData(task); // Remove the task from activity manager - final ActivityManager am = (ActivityManager) - activity.getSystemService(Context.ACTIVITY_SERVICE); - if (am != null) { - am.removeTask(tv.getTask().key.id, - ActivityManager.REMOVE_TASK_KILL_PROCESS); - } + RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(tv.getTask().key.id); // If there are no remaining tasks, then either unfilter the current stack, or just close // the activity if there are no filtered stacks diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index e04a1b2..2c27d44 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -75,7 +75,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, Task. // Bind the views mThumbnailView = (TaskThumbnailView) findViewById(R.id.task_view_thumbnail); mBarView = (TaskBarView) findViewById(R.id.task_view_bar); - mBarView.mActivityIcon.setOnClickListener(this); + mBarView.mApplicationIcon.setOnClickListener(this); if (mTaskDataLoaded) { onTaskDataLoaded(false); } diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java index d584043..c99f691 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java +++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java @@ -83,22 +83,7 @@ public class ToggleSlider extends RelativeLayout } public void onCheckedChanged(CompoundButton toggle, boolean checked) { - Drawable thumb; - Drawable slider; - final Resources res = getContext().getResources(); - if (checked) { - thumb = res.getDrawable( - com.android.internal.R.drawable.scrubber_control_disabled_holo); - slider = res.getDrawable( - R.drawable.status_bar_settings_slider_disabled); - } else { - thumb = res.getDrawable( - com.android.internal.R.drawable.scrubber_control_selector_holo); - slider = res.getDrawable( - com.android.internal.R.drawable.scrubber_progress_horizontal_holo_dark); - } - mSlider.setThumb(thumb); - mSlider.setProgressDrawable(slider); + mSlider.setEnabled(checked); if (mListener != null) { mListener.onChanged(this, mTracking, checked, mSlider.getProgress()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index e5e287d..f349036 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -41,7 +41,6 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -78,6 +77,7 @@ import com.android.systemui.RecentsComponent; import com.android.systemui.SearchPanelView; import com.android.systemui.SystemUI; import com.android.systemui.statusbar.phone.KeyguardTouchDelegate; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import java.util.ArrayList; import java.util.Locale; @@ -98,8 +98,6 @@ public abstract class BaseStatusBar extends SystemUI implements protected static final int MSG_HIDE_HEADS_UP = 1027; protected static final int MSG_ESCALATE_HEADS_UP = 1028; - public static final boolean ENABLE_NOTIFICATION_STACK = SystemProperties - .getBoolean("persist.notifications.use_stack", false); protected static final boolean ENABLE_HEADS_UP = true; // scores above this threshold should be displayed in heads up mode. protected static final int INTERRUPTION_THRESHOLD = 10; @@ -120,7 +118,7 @@ public abstract class BaseStatusBar extends SystemUI implements // all notifications protected NotificationData mNotificationData = new NotificationData(); - protected ViewGroup mPile; + protected NotificationStackScrollLayout mStackScroller; protected NotificationData.Entry mInterruptingNotificationEntry; protected long mInterruptingNotificationTime; @@ -1033,7 +1031,7 @@ public abstract class BaseStatusBar extends SystemUI implements } // Construct the expanded view. NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView); - if (!inflateViews(entry, mPile)) { + if (!inflateViews(entry, mStackScroller)) { handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " + notification); return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 6be6d4d..2d2f2f1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -17,45 +17,51 @@ package com.android.systemui.statusbar.phone; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.util.EventLog; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityEvent; -import com.android.systemui.EventLogTags; import com.android.systemui.R; import com.android.systemui.statusbar.GestureRecorder; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; public class NotificationPanelView extends PanelView { public static final boolean DEBUG_GESTURES = true; - Drawable mHandleBar; - int mHandleBarHeight; - View mHandleView; - int mFingers; PhoneStatusBar mStatusBar; - boolean mOkToFlip; + private NotificationStackScrollLayout mNotificationStackScroller; + private int[] mTempLocation = new int[2]; + private int[] mTempChildLocation = new int[2]; + private View mNotificationParent; + public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); } public void setStatusBar(PhoneStatusBar bar) { + if (mStatusBar != null) { + mStatusBar.setOnFlipRunnable(null); + } mStatusBar = bar; + if (bar != null) { + mStatusBar.setOnFlipRunnable(new Runnable() { + @Override + public void run() { + requestPanelHeightUpdate(); + } + }); + } } @Override protected void onFinishInflate() { super.onFinishInflate(); - Resources resources = getContext().getResources(); - mHandleBar = resources.getDrawable(R.drawable.status_bar_close); - mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height); - mHandleView = findViewById(R.id.handle); + mNotificationStackScroller = (NotificationStackScrollLayout) + findViewById(R.id.notification_stack_scroller); + mNotificationParent = findViewById(R.id.notification_container_parent); } @Override @@ -80,61 +86,86 @@ public class NotificationPanelView extends PanelView { return super.dispatchPopulateAccessibilityEvent(event); } - // We draw the handle ourselves so that it's always glued to the bottom of the window. + /** + * Gets the relative position of a view on the screen in regard to this view. + * + * @param requestedView the view we want to find the relative position for + * @return + */ + private int getRelativeTop(View requestedView) { + getLocationOnScreen(mTempLocation); + requestedView.getLocationOnScreen(mTempChildLocation); + return mTempChildLocation[1] - mTempLocation[1]; + } + @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (changed) { - final int pl = getPaddingLeft(); - final int pr = getPaddingRight(); - mHandleBar.setBounds(pl, 0, getWidth() - pr, (int) mHandleBarHeight); - } + public boolean onTouchEvent(MotionEvent event) { + // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference + // implementation. + return super.onTouchEvent(event); } @Override - public void draw(Canvas canvas) { - super.draw(canvas); - final int off = (int) (getHeight() - mHandleBarHeight - getPaddingBottom()); - canvas.translate(0, off); - mHandleBar.setState(mHandleView.getDrawableState()); - mHandleBar.draw(canvas); - canvas.translate(0, -off); + protected boolean isScrolledToBottom() { + if (!isInSettings()) { + return mNotificationStackScroller.isScrolledToBottom(); + } + return super.isScrolledToBottom(); } @Override - public boolean onTouchEvent(MotionEvent event) { - if (DEBUG_GESTURES) { - if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { - EventLog.writeEvent(EventLogTags.SYSUI_NOTIFICATIONPANEL_TOUCH, - event.getActionMasked(), (int) event.getX(), (int) event.getY()); - } + protected int getMaxPanelHeight() { + if (!isInSettings()) { + int maxPanelHeight = super.getMaxPanelHeight(); + int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin(); + return maxPanelHeight - emptyBottomMargin; } - if (PhoneStatusBar.SETTINGS_DRAG_SHORTCUT && mStatusBar.mHasFlipSettings) { - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mOkToFlip = getExpandedHeight() == 0; - break; - case MotionEvent.ACTION_POINTER_DOWN: - if (mOkToFlip) { - float miny = event.getY(0); - float maxy = miny; - for (int i=1; i<event.getPointerCount(); i++) { - final float y = event.getY(i); - if (y < miny) miny = y; - if (y > maxy) maxy = y; - } - if (maxy - miny < mHandleBarHeight) { - if (getMeasuredHeight() < mHandleBarHeight) { - mStatusBar.switchToSettings(); - } else { - mStatusBar.flipToSettings(); - } - mOkToFlip = false; - } - } - break; - } + return super.getMaxPanelHeight(); + } + + private boolean isInSettings() { + return mStatusBar != null && mStatusBar.isFlippedToSettings(); + } + + @Override + protected void onHeightUpdated(float expandedHeight) { + updateNotificationStackHeight(expandedHeight); + } + + /** + * Update the height of the {@link #mNotificationStackScroller} to the new expanded height. + * This is much more efficient than doing it over the layout pass. + * + * @param expandedHeight the new expanded height + */ + private void updateNotificationStackHeight(float expandedHeight) { + float childOffset = getRelativeTop(mNotificationStackScroller) + - mNotificationParent.getTranslationY(); + int newStackHeight = (int) (expandedHeight - childOffset); + int itemHeight = mNotificationStackScroller.getItemHeight(); + int bottomStackPeekSize = mNotificationStackScroller.getBottomStackPeekSize(); + int minStackHeight = itemHeight + bottomStackPeekSize; + if (newStackHeight >= minStackHeight) { + mNotificationParent.setTranslationY(0); + mNotificationStackScroller.setCurrentStackHeight(newStackHeight); + } else { + + // We did not reach the position yet where we actually start growing, + // so we translate the stack upwards. + int translationY = (newStackHeight - minStackHeight); + // A slight parallax effect is introduced in order for the stack to catch up with + // the top card. + float partiallyThere = (float) newStackHeight / minStackHeight; + partiallyThere = Math.max(0, partiallyThere); + translationY += (1 - partiallyThere) * bottomStackPeekSize; + mNotificationParent.setTranslationY(translationY); + mNotificationStackScroller.setCurrentStackHeight( + (int) (expandedHeight - (childOffset + translationY))); } - return mHandleView.dispatchTouchEvent(event); + } + + @Override + protected int getDesiredMeasureHeight() { + return mMaxPanelHeight; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 4b2c3e1..3c8af30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -25,6 +25,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.widget.FrameLayout; import com.android.systemui.R; @@ -69,7 +70,7 @@ public class PanelView extends FrameLayout { private View mHandleView; private float mPeekHeight; - private float mTouchOffset; + private float mInitialOffsetOnTouch; private float mExpandedFraction = 0; private float mExpandedHeight = 0; private boolean mJustPeeked; @@ -77,6 +78,7 @@ public class PanelView extends FrameLayout { private boolean mRubberbanding; private boolean mTracking; private int mTrackingPointer; + private int mTouchSlop; private TimeAnimator mTimeAnimator; private ObjectAnimator mPeekAnimator; @@ -198,7 +200,6 @@ public class PanelView extends FrameLayout { } } - private int[] mAbsPos = new int[2]; PanelBar mBar; private final TimeListener mAnimationCallback = new TimeListener() { @@ -220,7 +221,7 @@ public class PanelView extends FrameLayout { }; private float mVel, mAccel; - private int mFullHeight = 0; + protected int mMaxPanelHeight = 0; private String mViewName; protected float mInitialTouchY; protected float mFinalTouchY; @@ -253,13 +254,13 @@ public class PanelView extends FrameLayout { mTimeAnimator.start(); mRubberbanding = mRubberbandingEnabled // is it enabled at all? - && mExpandedHeight > getFullHeight() // are we past the end? + && mExpandedHeight > getMaxPanelHeight() // are we past the end? && mVel >= -mFlingGestureMinDistPx; // was this not possibly a "close" gesture? if (mRubberbanding) { mClosing = true; } else if (mVel == 0) { // if the panel is less than halfway open, close it - mClosing = (mFinalTouchY / getFullHeight()) < 0.5f; + mClosing = (mFinalTouchY / getMaxPanelHeight()) < 0.5f; } else { mClosing = mExpandedHeight > 0 && mVel < 0; } @@ -268,7 +269,7 @@ public class PanelView extends FrameLayout { if (DEBUG) logf("tick: v=%.2fpx/s dt=%.4fs", mVel, dt); if (DEBUG) logf("tick: before: h=%d", (int) mExpandedHeight); - final float fh = getFullHeight(); + final float fh = getMaxPanelHeight(); boolean braking = false; if (BRAKES) { if (mClosing) { @@ -351,6 +352,9 @@ public class PanelView extends FrameLayout { mPeekHeight = res.getDimension(R.dimen.peek_height) + getPaddingBottom() // our window might have a dropshadow - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow + + final ViewConfiguration configuration = ViewConfiguration.get(getContext()); + mTouchSlop = configuration.getScaledTouchSlop(); } private void trackMovement(MotionEvent event) { @@ -363,10 +367,221 @@ public class PanelView extends FrameLayout { event.offsetLocation(-deltaX, -deltaY); } - // Pass all touches along to the handle, allowing the user to drag the panel closed from its interior @Override public boolean onTouchEvent(MotionEvent event) { - return mHandleView.dispatchTouchEvent(event); + + /* + * We capture touch events here and update the expand height here in case according to + * the users fingers. This also handles multi-touch. + * + * If the user just clicks shortly, we give him a quick peek of the shade. + * + * Flinging is also enabled in order to open or close the shade. + */ + + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float y = event.getY(pointerIndex); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mTracking = true; + if (mHandleView != null) { + mHandleView.setPressed(true); + postInvalidate(); // catch the press state change + } + + mInitialTouchY = y; + initVelocityTracker(); + trackMovement(event); + mTimeAnimator.cancel(); // end any outstanding animations + mBar.onTrackingStarted(PanelView.this); + mInitialOffsetOnTouch = mExpandedHeight; + if (mExpandedHeight == 0) { + mJustPeeked = true; + runPeekAnimation(); + } + break; + + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + final float newY = event.getY(newIndex); + mTrackingPointer = event.getPointerId(newIndex); + mInitialOffsetOnTouch = mExpandedHeight; + mInitialTouchY = newY; + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY + mInitialOffsetOnTouch; + if (h > mPeekHeight) { + if (mPeekAnimator != null && mPeekAnimator.isStarted()) { + mPeekAnimator.cancel(); + } + mJustPeeked = false; + } + if (!mJustPeeked) { + setExpandedHeightInternal(h); + mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); + } + + trackMovement(event); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mFinalTouchY = y; + mTracking = false; + mTrackingPointer = -1; + if (mHandleView != null) { + mHandleView.setPressed(false); + postInvalidate(); // catch the press state change + } + mBar.onTrackingStopped(PanelView.this); + trackMovement(event); + + float vel = getCurrentVelocity(); + fling(vel, true); + + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + break; + } + return true; + } + + private float getCurrentVelocity() { + float vel = 0; + float yVel = 0, xVel = 0; + boolean negative = false; + + // the velocitytracker might be null if we got a bad input stream + if (mVelocityTracker == null) { + return 0; + } + + mVelocityTracker.computeCurrentVelocity(1000); + + yVel = mVelocityTracker.getYVelocity(); + negative = yVel < 0; + + xVel = mVelocityTracker.getXVelocity(); + if (xVel < 0) { + xVel = -xVel; + } + if (xVel > mFlingGestureMaxXVelocityPx) { + xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis + } + + vel = (float) Math.hypot(yVel, xVel); + if (vel > mFlingGestureMaxOutputVelocityPx) { + vel = mFlingGestureMaxOutputVelocityPx; + } + + // if you've barely moved your finger, we treat the velocity as 0 + // preventing spurious flings due to touch screen jitter + final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY); + if (deltaY < mFlingGestureMinDistPx + || vel < mFlingExpandMinVelocityPx + ) { + vel = 0; + } + + if (negative) { + vel = -vel; + } + + if (DEBUG) { + logf("gesture: dy=%f vel=(%f,%f) vlinear=%f", + deltaY, + xVel, yVel, + vel); + } + return vel; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent event) { + + /* + * If the user drags anywhere inside the panel we intercept it if he moves his finger + * upwards. This allows closing the shade from anywhere inside the panel. + * + * We only do this if the current content is scrolled to the bottom, + * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture + * possible. + */ + int pointerIndex = event.findPointerIndex(mTrackingPointer); + if (pointerIndex < 0) { + pointerIndex = 0; + mTrackingPointer = event.getPointerId(pointerIndex); + } + final float y = event.getY(pointerIndex); + boolean scrolledToBottom = isScrolledToBottom(); + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + mTracking = true; + if (mHandleView != null) { + mHandleView.setPressed(true); + // catch the press state change + postInvalidate(); + } + mInitialTouchY = y; + initVelocityTracker(); + trackMovement(event); + mTimeAnimator.cancel(); // end any outstanding animations + if (mExpandedHeight == 0 || y > getContentHeight()) { + return true; + } + break; + case MotionEvent.ACTION_POINTER_UP: + final int upPointer = event.getPointerId(event.getActionIndex()); + if (mTrackingPointer == upPointer) { + // gesture is ongoing, find a new pointer to track + final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; + mTrackingPointer = event.getPointerId(newIndex); + final float newY = event.getY(newIndex); + mInitialTouchY = newY; + } + break; + + case MotionEvent.ACTION_MOVE: + final float h = y - mInitialTouchY; + trackMovement(event); + if (scrolledToBottom) { + if (h < -mTouchSlop) { + mInitialOffsetOnTouch = mExpandedHeight; + mInitialTouchY = y; + return true; + } + } + break; + } + return false; + } + + private void initVelocityTracker() { + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + } + mVelocityTracker = FlingTracker.obtain(); + } + + protected boolean isScrolledToBottom() { + return false; + } + + protected float getContentHeight() { + return mExpandedHeight; } @Override @@ -375,134 +590,6 @@ public class PanelView extends FrameLayout { mHandleView = findViewById(R.id.handle); loadDimens(); - - if (DEBUG) logf("handle view: " + mHandleView); - if (mHandleView != null) { - mHandleView.setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - int pointerIndex = event.findPointerIndex(mTrackingPointer); - if (pointerIndex < 0) { - pointerIndex = 0; - mTrackingPointer = event.getPointerId(pointerIndex); - } - final float y = event.getY(pointerIndex); - final float rawDelta = event.getRawY() - event.getY(); - final float rawY = y + rawDelta; - if (DEBUG) logf("handle.onTouch: a=%s p=[%d,%d] y=%.1f rawY=%.1f off=%.1f", - MotionEvent.actionToString(event.getAction()), - mTrackingPointer, pointerIndex, - y, rawY, mTouchOffset); - PanelView.this.getLocationOnScreen(mAbsPos); - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: - mTracking = true; - mHandleView.setPressed(true); - postInvalidate(); // catch the press state change - mInitialTouchY = y; - mVelocityTracker = FlingTracker.obtain(); - trackMovement(event); - mTimeAnimator.cancel(); // end any outstanding animations - mBar.onTrackingStarted(PanelView.this); - mTouchOffset = (rawY - mAbsPos[1]) - mExpandedHeight; - if (mExpandedHeight == 0) { - mJustPeeked = true; - runPeekAnimation(); - } - break; - - case MotionEvent.ACTION_POINTER_UP: - final int upPointer = event.getPointerId(event.getActionIndex()); - if (mTrackingPointer == upPointer) { - // gesture is ongoing, find a new pointer to track - final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1; - final float newY = event.getY(newIndex); - final float newRawY = newY + rawDelta; - mTrackingPointer = event.getPointerId(newIndex); - mTouchOffset = (newRawY - mAbsPos[1]) - mExpandedHeight; - mInitialTouchY = newY; - } - break; - - case MotionEvent.ACTION_MOVE: - final float h = rawY - mAbsPos[1] - mTouchOffset; - if (h > mPeekHeight) { - if (mPeekAnimator != null && mPeekAnimator.isStarted()) { - mPeekAnimator.cancel(); - } - mJustPeeked = false; - } - if (!mJustPeeked) { - PanelView.this.setExpandedHeightInternal(h); - mBar.panelExpansionChanged(PanelView.this, mExpandedFraction); - } - - trackMovement(event); - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - mFinalTouchY = y; - mTracking = false; - mTrackingPointer = -1; - mHandleView.setPressed(false); - postInvalidate(); // catch the press state change - mBar.onTrackingStopped(PanelView.this); - trackMovement(event); - - float vel = 0, yVel = 0, xVel = 0; - boolean negative = false; - - if (mVelocityTracker != null) { - // the velocitytracker might be null if we got a bad input stream - mVelocityTracker.computeCurrentVelocity(1000); - - yVel = mVelocityTracker.getYVelocity(); - negative = yVel < 0; - - xVel = mVelocityTracker.getXVelocity(); - if (xVel < 0) { - xVel = -xVel; - } - if (xVel > mFlingGestureMaxXVelocityPx) { - xVel = mFlingGestureMaxXVelocityPx; // limit how much we care about the x axis - } - - vel = (float)Math.hypot(yVel, xVel); - if (vel > mFlingGestureMaxOutputVelocityPx) { - vel = mFlingGestureMaxOutputVelocityPx; - } - - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - - // if you've barely moved your finger, we treat the velocity as 0 - // preventing spurious flings due to touch screen jitter - final float deltaY = Math.abs(mFinalTouchY - mInitialTouchY); - if (deltaY < mFlingGestureMinDistPx - || vel < mFlingExpandMinVelocityPx - ) { - vel = 0; - } - - if (negative) { - vel = -vel; - } - - if (DEBUG) logf("gesture: dy=%f vel=(%f,%f) vlinear=%f", - deltaY, - xVel, yVel, - vel); - - fling(vel, true); - - break; - } - return true; - }}); - } } public void fling(float vel, boolean always) { @@ -543,19 +630,18 @@ public class PanelView extends FrameLayout { // Did one of our children change size? int newHeight = getMeasuredHeight(); - if (newHeight != mFullHeight) { - mFullHeight = newHeight; - // If the user isn't actively poking us, let's rubberband to the content - if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() - && mExpandedHeight > 0 && mExpandedHeight != mFullHeight) { - mExpandedHeight = mFullHeight; - } + if (newHeight != mMaxPanelHeight) { + mMaxPanelHeight = newHeight; } heightMeasureSpec = MeasureSpec.makeMeasureSpec( - (int) mExpandedHeight, MeasureSpec.AT_MOST); // MeasureSpec.getMode(heightMeasureSpec)); + getDesiredMeasureHeight(), MeasureSpec.AT_MOST); setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } + protected int getDesiredMeasureHeight() { + return (int) mExpandedHeight; + } + public void setExpandedHeight(float height) { if (DEBUG) logf("setExpandedHeight(%.1f)", height); @@ -569,8 +655,20 @@ public class PanelView extends FrameLayout { @Override protected void onLayout (boolean changed, int left, int top, int right, int bottom) { - if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, (int)mExpandedHeight, mFullHeight); + if (DEBUG) logf("onLayout: changed=%s, bottom=%d eh=%d fh=%d", changed?"T":"f", bottom, + (int)mExpandedHeight, mMaxPanelHeight); super.onLayout(changed, left, top, right, bottom); + requestPanelHeightUpdate(); + } + + protected void requestPanelHeightUpdate() { + float currentMaxPanelHeight = getMaxPanelHeight(); + + // If the user isn't actively poking us, let's update the height + if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted() + && mExpandedHeight > 0 && currentMaxPanelHeight != mExpandedHeight) { + setExpandedHeightInternal(currentMaxPanelHeight); + } } public void setExpandedHeightInternal(float h) { @@ -583,7 +681,7 @@ public class PanelView extends FrameLayout { h = 0; } - float fh = getFullHeight(); + float fh = getMaxPanelHeight(); if (fh == 0) { // Hmm, full height hasn't been computed yet } @@ -593,9 +691,13 @@ public class PanelView extends FrameLayout { mExpandedHeight = h; - if (DEBUG) logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f"); + if (DEBUG) { + logf("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, + mTracking ? "T" : "f", mRubberbanding ? "T" : "f"); + } + + onHeightUpdated(mExpandedHeight); - requestLayout(); // FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); // lp.height = (int) mExpandedHeight; // setLayoutParams(lp); @@ -603,13 +705,23 @@ public class PanelView extends FrameLayout { mExpandedFraction = Math.min(1f, (fh == 0) ? 0 : h / fh); } - private float getFullHeight() { - if (mFullHeight <= 0) { - if (DEBUG) logf("Forcing measure() since fullHeight=" + mFullHeight); + protected void onHeightUpdated(float expandedHeight) { + requestLayout(); + } + + /** + * This returns the maximum height of the panel. Children should override this if their + * desired height is not the full height. + * + * @return the default implementation simply returns the maximum height. + */ + protected int getMaxPanelHeight() { + if (mMaxPanelHeight <= 0) { + if (DEBUG) logf("Forcing measure() since mMaxPanelHeight=" + mMaxPanelHeight); measure(MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(android.view.ViewGroup.LayoutParams.WRAP_CONTENT, MeasureSpec.EXACTLY)); } - return mFullHeight; + return mMaxPanelHeight; } public void setExpandedFraction(float frac) { @@ -621,7 +733,7 @@ public class PanelView extends FrameLayout { } frac = 0; } - setExpandedHeight(getFullHeight() * frac); + setExpandedHeight(getMaxPanelHeight() * frac); } public float getExpandedHeight() { @@ -633,7 +745,7 @@ public class PanelView extends FrameLayout { } public boolean isFullyExpanded() { - return mExpandedHeight >= getFullHeight(); + return mExpandedHeight >= getMaxPanelHeight(); } public boolean isFullyCollapsed() { @@ -681,12 +793,12 @@ public class PanelView extends FrameLayout { } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(String.format("[PanelView(%s): expandedHeight=%f fullHeight=%f closing=%s" + pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%f closing=%s" + " tracking=%s rubberbanding=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s" + "]", this.getClass().getSimpleName(), getExpandedHeight(), - getFullHeight(), + getMaxPanelHeight(), mClosing?"T":"f", mTracking?"T":"f", mRubberbanding?"T":"f", 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 2257aaa..4730f2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -86,7 +86,6 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.DemoMode; import com.android.systemui.EventLogTags; import com.android.systemui.R; -import com.android.systemui.SwipeHelper; import com.android.systemui.statusbar.BaseStatusBar; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; @@ -101,8 +100,6 @@ import com.android.systemui.statusbar.policy.DateView; import com.android.systemui.statusbar.policy.HeadsUpNotificationView; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; -import com.android.systemui.statusbar.policy.NotificationRowLayout; -import com.android.systemui.statusbar.policy.OnSizeChangedListener; import com.android.systemui.statusbar.policy.RotationLockController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -172,7 +169,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { Display mDisplay; Point mCurrentDisplaySize = new Point(); private float mHeadsUpVerticalOffset; - private int[] mPilePosition = new int[2]; + private int[] mStackScrollerPosition = new int[2]; StatusBarWindowView mStatusBarWindow; PhoneStatusBarView mStatusBarView; @@ -198,7 +195,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // expanded notifications NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window - View mNotificationScroller; View mExpandedContents; int mNotificationPanelGravity; int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx; @@ -350,6 +346,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } }}; + private Runnable mOnFlipRunnable; + + public void setOnFlipRunnable(Runnable onFlipRunnable) { + mOnFlipRunnable = onFlipRunnable; + } + @Override public void setZenMode(int mode) { super.setZenMode(mode); @@ -417,7 +419,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); mStatusBarView.setPanelHolder(holder); - mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(R.id.notification_panel); + mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( + R.id.notification_panel); mNotificationPanel.setStatusBar(this); mNotificationPanelIsFullScreenWidth = (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT); @@ -443,7 +446,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mHeadsUpNotificationView.setBar(this); } if (MULTIUSER_DEBUG) { - mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info); + mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( + R.id.header_debug_info); mNotificationPanelDebugText.setVisibility(View.VISIBLE); } @@ -482,33 +486,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents); mTickerView = mStatusBarView.findViewById(R.id.ticker); - View legacyScrollView = mStatusBarWindow.findViewById(R.id.scroll); - NotificationStackScrollLayout notificationStack - = (NotificationStackScrollLayout) mStatusBarWindow - .findViewById(R.id.notification_stack_scroller); - if (ENABLE_NOTIFICATION_STACK) { - notificationStack.setLongPressListener(getNotificationLongClicker()); - mPile = notificationStack; - legacyScrollView.setVisibility(View.GONE); - - // The scrollview and the notification container are unified now! - // TODO: remove mNotificationScroller entirely once we fully switch to the new Layout - mNotificationScroller = notificationStack; - } else { - mNotificationScroller = legacyScrollView; - // less drawing during pulldowns - mNotificationScroller.setVerticalScrollBarEnabled(false); - NotificationRowLayout rowLayout - = (NotificationRowLayout) mStatusBarWindow.findViewById(R.id.latestItems); - rowLayout.setLayoutTransitionsEnabled(false); - rowLayout.setLongPressListener(getNotificationLongClicker()); - mPile = rowLayout; - notificationStack.setVisibility(View.GONE); - } - - mExpandedContents = mPile; // was: expanded.findViewById(R.id.notificationLinearLayout); - + mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( + R.id.notification_stack_scroller); + mStackScroller.setLongPressListener(getNotificationLongClicker()); + mExpandedContents = mStackScroller; mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header); @@ -551,7 +533,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } } if (mHasFlipSettings) { - mNotificationButton = (ImageView) mStatusBarWindow.findViewById(R.id.notification_button); + mNotificationButton = (ImageView) mStatusBarWindow.findViewById( + R.id.notification_button); if (mNotificationButton != null) { mNotificationButton.setOnClickListener(mNotificationButtonListener); } @@ -593,17 +576,18 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (isAPhone) { mEmergencyCallLabel = (TextView) mStatusBarWindow.findViewById(R.id.emergency_calls_only); - if (mEmergencyCallLabel != null) { - mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); - mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { }}); - mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, - int oldLeft, int oldTop, int oldRight, int oldBottom) { - updateCarrierLabelVisibility(false); - }}); - } + // TODO: Uncomment when correctly positioned +// if (mEmergencyCallLabel != null) { +// mNetworkController.addEmergencyLabelView(mEmergencyCallLabel); +// mEmergencyCallLabel.setOnClickListener(new View.OnClickListener() { +// public void onClick(View v) { }}); +// mEmergencyCallLabel.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { +// @Override +// public void onLayoutChange(View v, int left, int top, int right, int bottom, +// int oldLeft, int oldTop, int oldRight, int oldBottom) { +// updateCarrierLabelVisibility(false); +// }}); +// } } mCarrierLabel = (TextView)mStatusBarWindow.findViewById(R.id.carrier_label); @@ -621,13 +605,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } // set up the dynamic hide/show of the label - if (!ENABLE_NOTIFICATION_STACK) - ((NotificationRowLayout) mPile).setOnSizeChangedListener(new OnSizeChangedListener() { - @Override - public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { - updateCarrierLabelVisibility(false); - } - }); + // TODO: uncomment, handle this for the Stack scroller aswell +// ((NotificationRowLayout) mStackScroller) +// .setOnSizeChangedListener(new OnSizeChangedListener() { +// @Override +// public void onSizeChanged(View view, int w, int h, int oldw, int oldh) { +// updateCarrierLabelVisibility(false); } // Quick Settings (where available, some restrictions apply) @@ -1066,7 +1049,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } private void loadNotificationShade() { - if (mPile == null) return; + if (mStackScroller == null) return; int N = mNotificationData.size(); @@ -1092,21 +1075,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } ArrayList<View> toRemove = new ArrayList<View>(); - for (int i=0; i<mPile.getChildCount(); i++) { - View child = mPile.getChildAt(i); + for (int i=0; i< mStackScroller.getChildCount(); i++) { + View child = mStackScroller.getChildAt(i); if (!toShow.contains(child)) { toRemove.add(child); } } for (View remove : toRemove) { - mPile.removeView(remove); + mStackScroller.removeView(remove); } for (int i=0; i<toShow.size(); i++) { View v = toShow.get(i); if (v.getParent() == null) { - mPile.addView(v, i); + mStackScroller.addView(v, i); } } @@ -1178,15 +1161,17 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { // The idea here is to only show the carrier label when there is enough room to see it, // i.e. when there aren't enough notifications to fill the panel. if (SPEW) { - Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d", - mPile.getHeight(), mNotificationScroller.getHeight(), mCarrierLabelHeight)); + Log.d(TAG, String.format("stackScrollerh=%d scrollh=%d carrierh=%d", + mStackScroller.getHeight(), mStackScroller.getHeight(), + mCarrierLabelHeight)); } final boolean emergencyCallsShownElsewhere = mEmergencyCallLabel != null; final boolean makeVisible = !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly()) - && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight) - && mNotificationScroller.getVisibility() == View.VISIBLE; + && mStackScroller.getHeight() < (mNotificationPanel.getHeight() + - mCarrierLabelHeight - mNotificationHeaderHeight) + && mStackScroller.getVisibility() == View.VISIBLE; if (force || mCarrierLabelVisible != makeVisible) { mCarrierLabelVisible = makeVisible; @@ -1229,7 +1214,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (mHasFlipSettings && mFlipSettingsView != null && mFlipSettingsView.getVisibility() == View.VISIBLE - && mNotificationScroller.getVisibility() != View.VISIBLE) { + && mStackScroller.getVisibility() != View.VISIBLE) { // the flip settings panel is unequivocally showing; we should not be shown mClearButton.setVisibility(View.INVISIBLE); } else if (mClearButton.isShown()) { @@ -1483,9 +1468,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } mExpandedVisible = true; - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(true); - } if (mNavigationBarView != null) mNavigationBarView.setSlippery(true); @@ -1600,7 +1582,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } mNotificationPanel.expand(); - if (mHasFlipSettings && mNotificationScroller.getVisibility() != View.VISIBLE) { + if (mHasFlipSettings && mStackScroller.getVisibility() != View.VISIBLE) { flipToNotifications(); } @@ -1614,11 +1596,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); if (mClearButtonAnim != null) mClearButtonAnim.cancel(); - mNotificationScroller.setVisibility(View.VISIBLE); + mStackScroller.setVisibility(View.VISIBLE); mScrollViewAnim = start( startDelay(FLIP_DURATION_OUT, interpolator(mDecelerateInterpolator, - ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 0f, 1f) + ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 0f, 1f) .setDuration(FLIP_DURATION_IN) ))); mFlipSettingsViewAnim = start( @@ -1645,6 +1627,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { updateCarrierLabelVisibility(false); } }, FLIP_DURATION - 150); + if (mOnFlipRunnable != null) { + mOnFlipRunnable.run(); + } } @Override @@ -1676,11 +1661,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mFlipSettingsView.setScaleX(1f); mFlipSettingsView.setVisibility(View.VISIBLE); mSettingsButton.setVisibility(View.GONE); - mNotificationScroller.setVisibility(View.GONE); - mNotificationScroller.setScaleX(0f); + mStackScroller.setVisibility(View.GONE); + mStackScroller.setScaleX(0f); mNotificationButton.setVisibility(View.VISIBLE); mNotificationButton.setAlpha(1f); mClearButton.setVisibility(View.GONE); + if (mOnFlipRunnable != null) { + mOnFlipRunnable.run(); + } + } + + public boolean isFlippedToSettings() { + if (mFlipSettingsView != null) { + return mFlipSettingsView.getVisibility() == View.VISIBLE; + } + return false; } public void flipToSettings() { @@ -1704,15 +1699,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { mScrollViewAnim = start( setVisibilityWhenDone( interpolator(mAccelerateInterpolator, - ObjectAnimator.ofFloat(mNotificationScroller, View.SCALE_X, 1f, 0f) + ObjectAnimator.ofFloat(mStackScroller, View.SCALE_X, 1f, 0f) ) .setDuration(FLIP_DURATION_OUT), - mNotificationScroller, View.INVISIBLE)); + mStackScroller, View.INVISIBLE)); mSettingsButtonAnim = start( setVisibilityWhenDone( ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f) .setDuration(FLIP_DURATION), - mNotificationScroller, View.INVISIBLE)); + mStackScroller, View.INVISIBLE)); mNotificationButton.setVisibility(View.VISIBLE); mNotificationButtonAnim = start( ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f) @@ -1727,6 +1722,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { updateCarrierLabelVisibility(false); } }, FLIP_DURATION - 150); + if (mOnFlipRunnable != null) { + mOnFlipRunnable.run(); + } } public void flipPanels() { @@ -1766,8 +1764,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (mNotificationButtonAnim != null) mNotificationButtonAnim.cancel(); if (mClearButtonAnim != null) mClearButtonAnim.cancel(); - mNotificationScroller.setScaleX(1f); - mNotificationScroller.setVisibility(View.VISIBLE); + mStackScroller.setScaleX(1f); + mStackScroller.setVisibility(View.VISIBLE); mSettingsButton.setAlpha(1f); mSettingsButton.setVisibility(View.VISIBLE); mNotificationPanel.setVisibility(View.GONE); @@ -1777,9 +1775,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { } mExpandedVisible = false; - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).setLayoutTransitionsEnabled(false); - } if (mNavigationBarView != null) mNavigationBarView.setSlippery(false); visibilityChanged(false); @@ -1806,53 +1801,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); } - /** - * Enables or disables layers on the children of the notifications pile. - * - * When layers are enabled, this method attempts to enable layers for the minimal - * number of children. Only children visible when the notification area is fully - * expanded will receive a layer. The technique used in this method might cause - * more children than necessary to get a layer (at most one extra child with the - * current UI.) - * - * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE} - */ - private void setPileLayers(int layerType) { - final int count = mPile.getChildCount(); - - switch (layerType) { - case View.LAYER_TYPE_NONE: - for (int i = 0; i < count; i++) { - mPile.getChildAt(i).setLayerType(layerType, null); - } - break; - case View.LAYER_TYPE_HARDWARE: - final int[] location = new int[2]; - mNotificationPanel.getLocationInWindow(location); - - final int left = location[0]; - final int top = location[1]; - final int right = left + mNotificationPanel.getWidth(); - final int bottom = top + getExpandedViewMaxHeight(); - - final Rect childBounds = new Rect(); - - for (int i = 0; i < count; i++) { - final View view = mPile.getChildAt(i); - view.getLocationInWindow(location); - - childBounds.set(location[0], location[1], - location[0] + view.getWidth(), location[1] + view.getHeight()); - - if (childBounds.intersects(left, top, right, bottom)) { - view.setLayerType(layerType, null); - } - } - - break; - } - } - public boolean interceptTouchEvent(MotionEvent event) { if (DEBUG_GESTURES) { if (event.getActionMasked() != MotionEvent.ACTION_MOVE) { @@ -2230,11 +2178,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { pw.println(" mTicking=" + mTicking); pw.println(" mTracking=" + mTracking); pw.println(" mDisplayMetrics=" + mDisplayMetrics); - pw.println(" mPile: " + viewInfo(mPile)); + pw.println(" mStackScroller: " + viewInfo(mStackScroller)); pw.println(" mTickerView: " + viewInfo(mTickerView)); - pw.println(" mNotificationScroller: " + viewInfo(mNotificationScroller) - + " scroll " + mNotificationScroller.getScrollX() - + "," + mNotificationScroller.getScrollY()); + pw.println(" mStackScroller: " + viewInfo(mStackScroller) + + " scroll " + mStackScroller.getScrollX() + + "," + mStackScroller.getScrollY()); } pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); @@ -2409,8 +2357,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) { mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx); - mPile.getLocationOnScreen(mPilePosition); - mHeadsUpVerticalOffset = mPilePosition[1] - mNaturalBarHeight; + mStackScroller.getLocationOnScreen(mStackScrollerPosition); + mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight; } updateCarrierLabelVisibility(false); @@ -2428,7 +2376,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { private View.OnClickListener mClearButtonListener = new View.OnClickListener() { public void onClick(View v) { - // TODO: Handle this better with notification stack scroller synchronized (mNotificationData) { mPostCollapseCleanup = new Runnable() { @Override @@ -2437,86 +2384,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode { Log.v(TAG, "running post-collapse cleanup"); } try { - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).setViewRemoval(true); - } mBarService.onClearAllNotifications(mCurrentUserId); } catch (Exception ex) { } } }; - if(ENABLE_NOTIFICATION_STACK) { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - return; - } - - // animate-swipe all dismissable notifications, then animate the shade closed - int numChildren = mPile.getChildCount(); - - int scrollTop = mNotificationScroller.getScrollY(); - int scrollBottom = scrollTop + mNotificationScroller.getHeight(); - final ArrayList<View> snapshot = new ArrayList<View>(numChildren); - for (int i=0; i<numChildren; i++) { - final View child = mPile.getChildAt(i); - if (((SwipeHelper.Callback) mPile).canChildBeDismissed(child) - && child.getBottom() > scrollTop && child.getTop() < scrollBottom) { - snapshot.add(child); - } - } - if (snapshot.isEmpty()) { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - return; - } - new Thread(new Runnable() { - @Override - public void run() { - // Decrease the delay for every row we animate to give the sense of - // accelerating the swipes - final int ROW_DELAY_DECREMENT = 10; - int currentDelay = 140; - int totalDelay = 0; - - - if (!ENABLE_NOTIFICATION_STACK) { - // Set the shade-animating state to avoid doing other work during - // all of these animations. In particular, avoid layout and - // redrawing when collapsing the shade. - ((NotificationRowLayout) mPile).setViewRemoval(false); - } - - View sampleView = snapshot.get(0); - int width = sampleView.getWidth(); - final int dir = sampleView.isLayoutRtl() ? -1 : +1; - final int velocity = dir * width * 8; // 1000/8 = 125 ms duration - for (final View _v : snapshot) { - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (!ENABLE_NOTIFICATION_STACK) { - ((NotificationRowLayout) mPile).dismissRowAnimated( - _v, velocity); - } else { - ((NotificationStackScrollLayout) mPile).dismissRowAnimated( - _v, velocity); - } - } - }, totalDelay); - currentDelay = Math.max(50, currentDelay - ROW_DELAY_DECREMENT); - totalDelay += currentDelay; - } - // Delay the collapse animation until after all swipe animations have - // finished. Provide some buffer because there may be some extra delay - // before actually starting each swipe animation. Ideally, we'd - // synchronize the end of those animations with the start of the collaps - // exactly. - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); - } - }, totalDelay + 225); - } - }).start(); + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE); + return; + // TODO: Handle this better with notification stack scroller } } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index eeae081..a7121c4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -24,13 +24,13 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; import android.view.ViewRootImpl; import android.widget.FrameLayout; import com.android.systemui.ExpandHelper; import com.android.systemui.R; import com.android.systemui.statusbar.BaseStatusBar; +import com.android.systemui.statusbar.policy.ScrollAdapter; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -40,9 +40,8 @@ public class StatusBarWindowView extends FrameLayout public static final boolean DEBUG = BaseStatusBar.DEBUG; private ExpandHelper mExpandHelper; - private ViewGroup latestItems; + private NotificationStackScrollLayout mStackScrollLayout; private NotificationPanelView mNotificationPanel; - private View mNotificationScroller; PhoneStatusBar mService; @@ -56,37 +55,15 @@ public class StatusBarWindowView extends FrameLayout protected void onAttachedToWindow () { super.onAttachedToWindow(); - ExpandHelper.ScrollAdapter scrollAdapter; - if (BaseStatusBar.ENABLE_NOTIFICATION_STACK) { - NotificationStackScrollLayout stackScrollLayout = - (NotificationStackScrollLayout) findViewById(R.id.notification_stack_scroller); - - // ScrollView and notification container are unified in a single view now. - latestItems = stackScrollLayout; - scrollAdapter = stackScrollLayout; - mNotificationScroller = stackScrollLayout; - } else { - latestItems = (ViewGroup) findViewById(R.id.latestItems); - mNotificationScroller = findViewById(R.id.scroll); - scrollAdapter = new ExpandHelper.ScrollAdapter() { - @Override - public boolean isScrolledToTop() { - return mNotificationScroller.getScrollY() == 0; - } - - @Override - public View getHostView() { - return mNotificationScroller; - } - }; - } + mStackScrollLayout = (NotificationStackScrollLayout) findViewById( + R.id.notification_stack_scroller); mNotificationPanel = (NotificationPanelView) findViewById(R.id.notification_panel); int minHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_min_height); int maxHeight = getResources().getDimensionPixelSize(R.dimen.notification_row_max_height); - mExpandHelper = new ExpandHelper(getContext(), (ExpandHelper.Callback) latestItems, + mExpandHelper = new ExpandHelper(getContext(), mStackScrollLayout, minHeight, maxHeight); mExpandHelper.setEventSource(this); - mExpandHelper.setScrollAdapter(scrollAdapter); + mExpandHelper.setScrollAdapter(mStackScrollLayout); // We really need to be able to animate while window animations are going on // so that activities may be started asynchronously from panel animations @@ -113,7 +90,7 @@ public class StatusBarWindowView extends FrameLayout public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercept = false; if (mNotificationPanel.isFullyExpanded() - && mNotificationScroller.getVisibility() == View.VISIBLE) { + && mStackScrollLayout.getVisibility() == View.VISIBLE) { intercept = mExpandHelper.onInterceptTouchEvent(ev); } if (!intercept) { @@ -122,7 +99,7 @@ public class StatusBarWindowView extends FrameLayout if (intercept) { MotionEvent cancellation = MotionEvent.obtain(ev); cancellation.setAction(MotionEvent.ACTION_CANCEL); - latestItems.onInterceptTouchEvent(cancellation); + mStackScrollLayout.onInterceptTouchEvent(cancellation); cancellation.recycle(); } return intercept; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java new file mode 100644 index 0000000..f35e22d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ScrollAdapter.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.policy; + +import android.view.View; + +/** + * A scroll adapter which can be queried for meta information about the scroll state + */ +public interface ScrollAdapter { + + /** + * @return Whether the view returned by {@link #getHostView()} is scrolled to the top + */ + public boolean isScrolledToTop(); + + /** + * @return Whether the view returned by {@link #getHostView()} is scrolled to the bottom + */ + public boolean isScrolledToBottom(); + + /** + * @return The view in which the scrolling is performed + */ + public View getHostView(); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index f6eeb6d..04b7f53 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -39,12 +39,13 @@ import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.stack.StackScrollState.ViewState; +import com.android.systemui.statusbar.policy.ScrollAdapter; /** * A layout which handles a dynamic amount of notifications and presents them in a scrollable stack. */ public class NotificationStackScrollLayout extends ViewGroup - implements SwipeHelper.Callback, ExpandHelper.Callback, ExpandHelper.ScrollAdapter { + implements SwipeHelper.Callback, ExpandHelper.Callback, ScrollAdapter { private static final String TAG = "NotificationStackScrollLayout"; private static final boolean DEBUG = false; @@ -55,7 +56,7 @@ public class NotificationStackScrollLayout extends ViewGroup private static final int INVALID_POINTER = -1; private SwipeHelper mSwipeHelper; - private boolean mAllowScrolling = true; + private boolean mSwipingInProgress = true; private int mCurrentStackHeight = Integer.MAX_VALUE; private int mOwnScrollY; private int mMaxLayoutHeight; @@ -89,7 +90,7 @@ public class NotificationStackScrollLayout extends ViewGroup * The current State this Layout is in */ private final StackScrollState mCurrentStackScrollState = new StackScrollState(this); - + private OnChildLocationsChangedListener mListener; public NotificationStackScrollLayout(Context context) { @@ -279,7 +280,7 @@ public class NotificationStackScrollLayout extends ViewGroup } /** - * Get the current height of the view. This is at most the size of the view given by a the + * Get the current height of the view. This is at most the msize of the view given by a the * layout but it can also be made smaller by setting {@link #mCurrentStackHeight} * * @return either the layout height or the externally defined height, whichever is smaller @@ -288,6 +289,14 @@ public class NotificationStackScrollLayout extends ViewGroup return Math.min(mMaxLayoutHeight, mCurrentStackHeight); } + public int getItemHeight() { + return mCollapsedSize; + } + + public int getBottomStackPeekSize() { + return mBottomStackPeekSize; + } + public void setLongPressListener(View.OnLongClickListener listener) { mSwipeHelper.setLongPressListener(listener); } @@ -298,15 +307,15 @@ public class NotificationStackScrollLayout extends ViewGroup if (veto != null && veto.getVisibility() != View.GONE) { veto.performClick(); } - allowScrolling(true); + setSwipingInProgress(false); } public void onBeginDrag(View v) { - allowScrolling(false); + setSwipingInProgress(true); } public void onDragCancelled(View v) { - allowScrolling(true); + setSwipingInProgress(false); } public View getChildAtPosition(MotionEvent ev) { @@ -365,8 +374,11 @@ public class NotificationStackScrollLayout extends ViewGroup return (veto != null && veto.getVisibility() != View.GONE); } - private void allowScrolling(boolean allow) { - mAllowScrolling = allow; + private void setSwipingInProgress(boolean isSwiped) { + mSwipingInProgress = isSwiped; + if(isSwiped) { + requestDisallowInterceptTouchEvent(true); + } } @Override @@ -386,7 +398,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean onTouchEvent(MotionEvent ev) { boolean scrollerWantsIt = false; - if (mAllowScrolling) { + if (!mSwipingInProgress) { scrollerWantsIt = onScrollTouch(ev); } boolean horizontalSwipeWantsIt = false; @@ -409,12 +421,6 @@ public class NotificationStackScrollLayout extends ViewGroup } boolean isBeingDragged = !mScroller.isFinished(); setIsBeingDragged(isBeingDragged); - if (isBeingDragged) { - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } - } /* * If being flinged and user touches, stop the fling. isFinished @@ -439,10 +445,6 @@ public class NotificationStackScrollLayout extends ViewGroup final int y = (int) ev.getY(activePointerIndex); int deltaY = mLastMotionY - y; if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) { - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } setIsBeingDragged(true); if (deltaY > 0) { deltaY -= mTouchSlop; @@ -642,7 +644,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (getChildCount() > 0) { int contentHeight = getContentHeight(); scrollRange = Math.max(0, - contentHeight - mMaxLayoutHeight + mCollapsedSize); + contentHeight - mMaxLayoutHeight + mBottomStackPeekSize); } return scrollRange; } @@ -697,7 +699,7 @@ public class NotificationStackScrollLayout extends ViewGroup @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean scrollWantsIt = false; - if (mAllowScrolling) { + if (!mSwipingInProgress) { scrollWantsIt = onInterceptTouchEventScroll(ev); } boolean swipeWantsIt = false; @@ -763,10 +765,6 @@ public class NotificationStackScrollLayout extends ViewGroup mLastMotionY = y; initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); - final ViewParent parent = getParent(); - if (parent != null) { - parent.requestDisallowInterceptTouchEvent(true); - } } break; } @@ -823,6 +821,7 @@ public class NotificationStackScrollLayout extends ViewGroup private void setIsBeingDragged(boolean isDragged) { mIsBeingDragged = isDragged; if (isDragged) { + requestDisallowInterceptTouchEvent(true); mSwipeHelper.removeLongPressCallback(); } } @@ -841,10 +840,19 @@ public class NotificationStackScrollLayout extends ViewGroup } @Override + public boolean isScrolledToBottom() { + return mOwnScrollY >= getScrollRange(); + } + + @Override public View getHostView() { return this; } + public int getEmptyBottomMargin() { + return Math.max(getHeight() - mContentHeight, 0); + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 6d2ba6a..4745f3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -86,7 +86,7 @@ public class StackScrollAlgorithm { // First we reset the view states to their default values. resultState.resetViewStates(); - // The first element is always in there so it's initialized with 1.0f. + // The first element is always in there so it's initialized with 1.0f; algorithmState.itemsInTopStack = 1.0f; algorithmState.partialInTop = 0.0f; algorithmState.lastTopStackIndex = 0; @@ -102,7 +102,7 @@ public class StackScrollAlgorithm { // Phase 3: updateZValuesForState(resultState, algorithmState); - // Write the algorithm state to the result. + // write the algorithm state to the result resultState.setScrollY(algorithmState.scrollY); } @@ -151,7 +151,7 @@ public class StackScrollAlgorithm { // Case 2: // First element of regular scrollview comes next, so the position is just the // scrolling position - nextYPosition = scrollOffset; + nextYPosition = Math.min(scrollOffset, transitioningPositionStart); childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING; } else if (nextYPosition >= transitioningPositionStart) { if (currentYPosition >= transitioningPositionStart) { @@ -180,6 +180,7 @@ public class StackScrollAlgorithm { if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) { Log.wtf(LOG_TAG, "Failed to assign location for child " + i); } + nextYPosition = Math.max(0, nextYPosition); currentYPosition = nextYPosition; yPositionInScrollView = yPositionInScrollViewAfterElement; } @@ -253,6 +254,8 @@ public class StackScrollAlgorithm { nextYPosition = mCollapsedSize + mPaddingBetweenElements - mTopStackIndentationFunctor.getValue( algorithmState.itemsInTopStack - i - 1); + nextYPosition = Math.min(nextYPosition, mLayoutHeight - mCollapsedSize + - mBottomStackPeekSize); if (paddedIndex == 0) { childViewState.alpha = 1.0f - algorithmState.partialInTop; childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN; diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml index ef640d5..03d920a 100644 --- a/packages/VpnDialogs/AndroidManifest.xml +++ b/packages/VpnDialogs/AndroidManifest.xml @@ -1,10 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (c) 2014 Google Inc. + * + * 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. + */ +--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.vpndialogs"> <application android:label="VpnDialogs" android:allowBackup="false" > <activity android:name=".ConfirmDialog" - android:theme="@*android:style/Theme.Holo.Dialog.Alert"> + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT"/> @@ -12,7 +30,7 @@ </activity> <activity android:name=".ManageDialog" - android:theme="@*android:style/Theme.Holo.Dialog.Alert" + android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:noHistory="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/packages/WallpaperCropper/AndroidManifest.xml b/packages/WallpaperCropper/AndroidManifest.xml index 27755bd..81d1085 100644 --- a/packages/WallpaperCropper/AndroidManifest.xml +++ b/packages/WallpaperCropper/AndroidManifest.xml @@ -1,3 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* + * Copyright (c) 2014 Google Inc. + * + * 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. + */ +--> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.wallpapercropper" > <uses-permission android:name="android.permission.SET_WALLPAPER" /> diff --git a/packages/WallpaperCropper/res/layout/wallpaper_cropper.xml b/packages/WallpaperCropper/res/layout/wallpaper_cropper.xml index 97d7001..cf78989 100644 --- a/packages/WallpaperCropper/res/layout/wallpaper_cropper.xml +++ b/packages/WallpaperCropper/res/layout/wallpaper_cropper.xml @@ -28,7 +28,7 @@ android:layout_height="match_parent" /> <ProgressBar android:id="@+id/loading" - style="@android:style/Widget.Holo.ProgressBar.Large" + style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Large" android:visibility="invisible" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/WallpaperCropper/res/values/styles.xml b/packages/WallpaperCropper/res/values/styles.xml index 2b63fe0..b27a387 100644 --- a/packages/WallpaperCropper/res/values/styles.xml +++ b/packages/WallpaperCropper/res/values/styles.xml @@ -15,13 +15,13 @@ --> <resources> - <style name="Theme.WallpaperCropper" parent="@android:style/Theme.Holo"> + <style name="Theme.WallpaperCropper" parent="@android:style/Theme.DeviceDefault.Light"> <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item> <item name="android:windowFullscreen">true</item> <item name="android:windowActionBarOverlay">true</item> </style> - <style name="WallpaperCropperActionBar" parent="android:style/Widget.Holo.ActionBar"> + <style name="WallpaperCropperActionBar" parent="android:style/Widget.DeviceDefault.Light.ActionBar"> <item name="android:displayOptions">showCustom</item> <item name="android:background">#88000000</item> </style> |