1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.launcher3.views; 17 18 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent; 19 20 import android.animation.PropertyValuesHolder; 21 import android.content.Context; 22 import android.graphics.Rect; 23 import android.util.AttributeSet; 24 import android.view.LayoutInflater; 25 import android.view.TouchDelegate; 26 import android.view.View; 27 import android.view.accessibility.AccessibilityEvent; 28 29 import com.android.launcher3.Insettable; 30 import com.android.launcher3.Launcher; 31 import com.android.launcher3.R; 32 import com.android.launcher3.anim.Interpolators; 33 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; 34 35 public class BottomUserEducationView extends AbstractSlideInView implements Insettable { 36 37 private static final String KEY_SHOWED_BOTTOM_USER_EDUCATION = "showed_bottom_user_education"; 38 39 private static final int DEFAULT_CLOSE_DURATION = 200; 40 41 private final Rect mInsets = new Rect(); 42 43 private View mCloseButton; 44 BottomUserEducationView(Context context, AttributeSet attr)45 public BottomUserEducationView(Context context, AttributeSet attr) { 46 this(context, attr, 0); 47 } 48 BottomUserEducationView(Context context, AttributeSet attrs, int defStyleAttr)49 public BottomUserEducationView(Context context, AttributeSet attrs, 50 int defStyleAttr) { 51 super(context, attrs, defStyleAttr); 52 mContent = this; 53 } 54 55 @Override onFinishInflate()56 protected void onFinishInflate() { 57 super.onFinishInflate(); 58 mCloseButton = findViewById(R.id.close_bottom_user_tip); 59 mCloseButton.setOnClickListener(view -> handleClose(true)); 60 } 61 62 @Override onLayout(boolean changed, int l, int t, int r, int b)63 protected void onLayout(boolean changed, int l, int t, int r, int b) { 64 super.onLayout(changed, l, t, r, b); 65 setTranslationShift(mTranslationShift); 66 expandTouchAreaOfCloseButton(); 67 } 68 69 @Override logActionCommand(int command)70 public void logActionCommand(int command) { 71 // Since this is on-boarding popup, it is not a user controlled action. 72 } 73 74 @Override getLogContainerType()75 public int getLogContainerType() { 76 return ContainerType.TIP; 77 } 78 79 @Override isOfType(int type)80 protected boolean isOfType(int type) { 81 return (type & TYPE_ON_BOARD_POPUP) != 0; 82 } 83 84 @Override setInsets(Rect insets)85 public void setInsets(Rect insets) { 86 // Extend behind left, right, and bottom insets. 87 int leftInset = insets.left - mInsets.left; 88 int rightInset = insets.right - mInsets.right; 89 int bottomInset = insets.bottom - mInsets.bottom; 90 mInsets.set(insets); 91 setPadding(getPaddingLeft() + leftInset, getPaddingTop(), 92 getPaddingRight() + rightInset, getPaddingBottom() + bottomInset); 93 } 94 95 @Override handleClose(boolean animate)96 protected void handleClose(boolean animate) { 97 handleClose(animate, DEFAULT_CLOSE_DURATION); 98 if (animate) { 99 // We animate only when the user is visible, which is a proxy for an explicit 100 // close action. 101 mLauncher.getSharedPrefs().edit() 102 .putBoolean(KEY_SHOWED_BOTTOM_USER_EDUCATION, true).apply(); 103 sendCustomAccessibilityEvent( 104 BottomUserEducationView.this, 105 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED, 106 getContext().getString(R.string.bottom_work_tab_user_education_closed)); 107 } 108 } 109 open(boolean animate)110 private void open(boolean animate) { 111 if (mIsOpen || mOpenCloseAnimator.isRunning()) { 112 return; 113 } 114 mIsOpen = true; 115 if (animate) { 116 mOpenCloseAnimator.setValues( 117 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED)); 118 mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); 119 mOpenCloseAnimator.start(); 120 } else { 121 setTranslationShift(TRANSLATION_SHIFT_OPENED); 122 } 123 } 124 showIfNeeded(Launcher launcher)125 public static void showIfNeeded(Launcher launcher) { 126 if (launcher.getSharedPrefs().getBoolean(KEY_SHOWED_BOTTOM_USER_EDUCATION, false)) { 127 return; 128 } 129 130 LayoutInflater layoutInflater = LayoutInflater.from(launcher); 131 BottomUserEducationView bottomUserEducationView = 132 (BottomUserEducationView) layoutInflater.inflate( 133 R.layout.work_tab_bottom_user_education_view, launcher.getDragLayer(), 134 false); 135 launcher.getDragLayer().addView(bottomUserEducationView); 136 bottomUserEducationView.open(true); 137 } 138 expandTouchAreaOfCloseButton()139 private void expandTouchAreaOfCloseButton() { 140 Rect hitRect = new Rect(); 141 mCloseButton.getHitRect(hitRect); 142 hitRect.left -= mCloseButton.getWidth(); 143 hitRect.top -= mCloseButton.getHeight(); 144 hitRect.right += mCloseButton.getWidth(); 145 hitRect.bottom += mCloseButton.getHeight(); 146 View parent = (View) mCloseButton.getParent(); 147 parent.setTouchDelegate(new TouchDelegate(hitRect, mCloseButton)); 148 } 149 } 150