diff options
Diffstat (limited to 'com/android/systemui/recents/SwipeUpOnboarding.java')
-rw-r--r-- | com/android/systemui/recents/SwipeUpOnboarding.java | 252 |
1 files changed, 252 insertions, 0 deletions
diff --git a/com/android/systemui/recents/SwipeUpOnboarding.java b/com/android/systemui/recents/SwipeUpOnboarding.java new file mode 100644 index 00000000..0494e1b0 --- /dev/null +++ b/com/android/systemui/recents/SwipeUpOnboarding.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2018 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 static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; + +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.RippleDrawable; +import android.os.Build; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.systemui.Prefs; +import com.android.systemui.R; +import com.android.systemui.recents.misc.SysUiTaskStackChangeListener; +import com.android.systemui.shared.system.ActivityManagerWrapper; + +/** + * Shows onboarding for the new recents interaction in P (codenamed quickstep). + */ +@TargetApi(Build.VERSION_CODES.P) +public class SwipeUpOnboarding { + + private static final String TAG = "SwipeUpOnboarding"; + private static final boolean RESET_PREFS_FOR_DEBUG = false; + private static final long SHOW_DELAY_MS = 500; + private static final long SHOW_HIDE_DURATION_MS = 300; + // Don't show the onboarding until the user has launched this number of apps. + private static final int SHOW_ON_APP_LAUNCH = 2; + + private final Context mContext; + private final WindowManager mWindowManager; + private final View mLayout; + private final TextView mTextView; + private final ImageView mDismissView; + private final ColorDrawable mBackgroundDrawable; + private final int mDarkBackgroundColor; + private final int mLightBackgroundColor; + private final int mDarkContentColor; + private final int mLightContentColor; + private final RippleDrawable mDarkRipple; + private final RippleDrawable mLightRipple; + + private boolean mTaskListenerRegistered; + private ComponentName mLauncherComponent; + private boolean mLayoutAttachedToWindow; + private boolean mBackgroundIsLight; + + private final SysUiTaskStackChangeListener mTaskListener = new SysUiTaskStackChangeListener() { + @Override + public void onTaskStackChanged() { + ActivityManager.RunningTaskInfo info = ActivityManagerWrapper.getInstance() + .getRunningTask(ACTIVITY_TYPE_UNDEFINED /* ignoreActivityType */); + int activityType = info.configuration.windowConfiguration.getActivityType(); + int numAppsLaunched = Prefs.getInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0); + if (activityType == ACTIVITY_TYPE_STANDARD) { + numAppsLaunched++; + if (numAppsLaunched >= 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; + final Resources res = context.getResources(); + mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_swipe_up_onboarding, null); + mTextView = (TextView) mLayout.findViewById(R.id.onboarding_text); + mDismissView = (ImageView) mLayout.findViewById(R.id.dismiss); + mDarkBackgroundColor = res.getColor(android.R.color.background_dark); + mLightBackgroundColor = res.getColor(android.R.color.background_light); + mDarkContentColor = res.getColor(R.color.primary_text_default_material_light); + mLightContentColor = res.getColor(R.color.primary_text_default_material_dark); + mDarkRipple = new RippleDrawable(res.getColorStateList(R.color.ripple_material_light), + null, null); + mLightRipple = new RippleDrawable(res.getColorStateList(R.color.ripple_material_dark), + null, null); + mBackgroundDrawable = new ColorDrawable(mDarkBackgroundColor); + + mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener); + mLayout.setBackground(mBackgroundDrawable); + mDismissView.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) { + // TODO: re-enable this once we have the proper callback for when a swipe up was performed. + final boolean disableOnboarding = true; + if (disableOnboarding) { + return; + } + 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); + } + } + } + + public void setContentDarkIntensity(float contentDarkIntensity) { + boolean backgroundIsLight = contentDarkIntensity > 0.5f; + if (backgroundIsLight != mBackgroundIsLight) { + mBackgroundIsLight = backgroundIsLight; + mBackgroundDrawable.setColor(mBackgroundIsLight + ? mLightBackgroundColor : mDarkBackgroundColor); + int contentColor = mBackgroundIsLight ? mDarkContentColor : mLightContentColor; + mTextView.setTextColor(contentColor); + mTextView.getCompoundDrawables()[3].setColorFilter(contentColor, + PorterDuff.Mode.SRC_IN); + mDismissView.setColorFilter(contentColor); + mDismissView.setBackground(mBackgroundIsLight ? mDarkRipple : mLightRipple); + } + } + + 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; + } +} |