From fb63fe85f28a94d6a8c88c64e91fb4565dfbed3e Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 16 Jan 2018 12:14:06 -0800 Subject: Add swipe up onboarding from apps After launching 3 apps, we create a window at the bottom attached to the nav bar to teach users to swipe for recents. There is an X on this window to dismiss it, but we will keep showing the onboarding every time they open apps until they perform the swipe up action. Test: manual Bug: 70180942 Change-Id: I4b15fac918b7b1633a3c09ab0819f2acb1dce697 --- packages/SystemUI/res/drawable/ic_chevron_up.xml | 24 +++ .../res/layout/recents_swipe_up_onboarding.xml | 39 ++++ packages/SystemUI/res/values/strings.xml | 2 + .../shared/system/ActivityManagerWrapper.java | 8 +- .../com/android/systemui/OverviewProxyService.java | 4 + .../SystemUI/src/com/android/systemui/Prefs.java | 4 + .../systemui/recents/SwipeUpOnboarding.java | 204 +++++++++++++++++++++ .../statusbar/phone/NavigationBarView.java | 17 +- 8 files changed, 299 insertions(+), 3 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_chevron_up.xml create mode 100644 packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml create mode 100644 packages/SystemUI/src/com/android/systemui/recents/SwipeUpOnboarding.java diff --git a/packages/SystemUI/res/drawable/ic_chevron_up.xml b/packages/SystemUI/res/drawable/ic_chevron_up.xml new file mode 100644 index 000000000000..835d0adbef58 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_chevron_up.xml @@ -0,0 +1,24 @@ + + + + diff --git a/packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml b/packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml new file mode 100644 index 000000000000..b6bd3b32995e --- /dev/null +++ b/packages/SystemUI/res/layout/recents_swipe_up_onboarding.xml @@ -0,0 +1,39 @@ + + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6437903bd12a..0b2f2d27ec31 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -809,6 +809,8 @@ Clear all Drag here to use split screen + + Swipe up to switch apps Split Horizontal diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 1c99d3846d8e..c9a6ea9939f5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -30,6 +30,7 @@ import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IAssistDataReceiver; +import android.app.WindowConfiguration.ActivityType; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -96,11 +97,14 @@ public class ActivityManagerWrapper { * @return the top running task (can be {@code null}). */ public ActivityManager.RunningTaskInfo getRunningTask() { + return getRunningTask(ACTIVITY_TYPE_RECENTS /* ignoreActivityType */); + } + + public ActivityManager.RunningTaskInfo getRunningTask(@ActivityType int ignoreActivityType) { // Note: The set of running tasks from the system is ordered by recency try { List tasks = - ActivityManager.getService().getFilteredTasks(1, - ACTIVITY_TYPE_RECENTS /* ignoreActivityType */, + ActivityManager.getService().getFilteredTasks(1, ignoreActivityType, WINDOWING_MODE_PINNED /* ignoreWindowingMode */); if (tasks.isEmpty()) { return null; diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java index 0be522bfb8cb..244c1b990448 100644 --- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java @@ -195,6 +195,10 @@ public class OverviewProxyService implements CallbackController= SHOW_ON_APP_LAUNCH) { + show(); + } else { + Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, numAppsLaunched); + } + } else { + String runningPackage = info.topActivity.getPackageName(); + // TODO: use callback from the overview proxy service to handle this case + if (runningPackage.equals(mLauncherComponent.getPackageName()) + && activityType == ACTIVITY_TYPE_RECENTS) { + Prefs.putBoolean(mContext, Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, true); + onDisconnectedFromLauncher(); + } else { + hide(false); + } + } + } + }; + + private final View.OnAttachStateChangeListener mOnAttachStateChangeListener + = new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) { + if (view == mLayout) { + mLayoutAttachedToWindow = true; + } + } + + @Override + public void onViewDetachedFromWindow(View view) { + if (view == mLayout) { + mLayoutAttachedToWindow = false; + } + } + }; + + public SwipeUpOnboarding(Context context) { + mContext = context; + mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_swipe_up_onboarding, null); + mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener); + mLayout.findViewById(R.id.dismiss).setOnClickListener(v -> hide(true)); + + if (RESET_PREFS_FOR_DEBUG) { + Prefs.putBoolean(mContext, Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, false); + Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0); + } + } + + public void onConnectedToLauncher(ComponentName launcherComponent) { + mLauncherComponent = launcherComponent; + boolean alreadyLearnedSwipeUpForRecents = Prefs.getBoolean(mContext, + Prefs.Key.HAS_SWIPED_UP_FOR_RECENTS, false); + if (!mTaskListenerRegistered && !alreadyLearnedSwipeUpForRecents) { + ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskListener); + mTaskListenerRegistered = true; + } + } + + public void onDisconnectedFromLauncher() { + if (mTaskListenerRegistered) { + ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskListener); + mTaskListenerRegistered = false; + } + hide(false); + } + + public void onConfigurationChanged(Configuration newConfiguration) { + if (newConfiguration.orientation != Configuration.ORIENTATION_PORTRAIT) { + hide(false); + } + } + + public void show() { + // Only show in portrait. + int orientation = mContext.getResources().getConfiguration().orientation; + if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) { + mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); + mWindowManager.addView(mLayout, getWindowLayoutParams()); + int layoutHeight = mLayout.getHeight(); + if (layoutHeight == 0) { + mLayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + layoutHeight = mLayout.getMeasuredHeight(); + } + mLayout.setTranslationY(layoutHeight); + mLayout.setAlpha(0); + mLayout.animate() + .translationY(0) + .alpha(1f) + .withLayer() + .setStartDelay(SHOW_DELAY_MS) + .setDuration(SHOW_HIDE_DURATION_MS) + .setInterpolator(new DecelerateInterpolator()) + .start(); + } + } + + public void hide(boolean animate) { + if (mLayoutAttachedToWindow) { + if (animate) { + mLayout.animate() + .translationY(mLayout.getHeight()) + .alpha(0f) + .withLayer() + .setDuration(SHOW_HIDE_DURATION_MS) + .setInterpolator(new AccelerateInterpolator()) + .withEndAction(() -> mWindowManager.removeView(mLayout)) + .start(); + } else { + mWindowManager.removeView(mLayout); + } + } + } + + private WindowManager.LayoutParams getWindowLayoutParams() { + int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, + flags, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.setTitle("SwipeUpOnboarding"); + lp.gravity = Gravity.BOTTOM; + return lp; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 059ce929b290..4b20a896982a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -57,10 +57,11 @@ import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.phone.NavGesture; import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; +import com.android.systemui.recents.SwipeUpOnboarding; import com.android.systemui.stackdivider.Divider; -import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable; import com.android.systemui.statusbar.policy.DeadZone; import com.android.systemui.statusbar.policy.KeyButtonDrawable; +import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -124,6 +125,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener