1 /* 2 * Copyright (C) 2020 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.quickstep.interaction; 17 18 import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL; 19 20 import android.content.Context; 21 import android.graphics.Insets; 22 import android.graphics.Rect; 23 import android.util.AttributeSet; 24 import android.view.MotionEvent; 25 import android.view.View; 26 import android.view.WindowInsets; 27 import android.widget.RelativeLayout; 28 29 import androidx.annotation.NonNull; 30 import androidx.fragment.app.FragmentManager; 31 32 import com.android.launcher3.R; 33 import com.android.launcher3.Utilities; 34 35 /** Root layout that TutorialFragment uses to intercept motion events. */ 36 public class RootSandboxLayout extends RelativeLayout { 37 38 private final Rect mTempStepIndicatorBounds = new Rect(); 39 private final Rect mTempInclusionBounds = new Rect(); 40 private final Rect mTempExclusionBounds = new Rect(); 41 42 private View mFeedbackView; 43 private View mTutorialStepView; 44 private View mSkipButton; 45 private View mDoneButton; 46 RootSandboxLayout(Context context)47 public RootSandboxLayout(Context context) { 48 super(context); 49 } 50 RootSandboxLayout(Context context, AttributeSet attrs)51 public RootSandboxLayout(Context context, AttributeSet attrs) { 52 super(context, attrs); 53 } 54 RootSandboxLayout(Context context, AttributeSet attrs, int defStyleAttr)55 public RootSandboxLayout(Context context, AttributeSet attrs, int defStyleAttr) { 56 super(context, attrs, defStyleAttr); 57 } 58 59 @Override onInterceptTouchEvent(MotionEvent motionEvent)60 public boolean onInterceptTouchEvent(MotionEvent motionEvent) { 61 return ((TutorialFragment) FragmentManager.findFragment(this)) 62 .onInterceptTouch(motionEvent); 63 } 64 65 /** 66 * Returns this view's fullscreen height. This method is agnostic of this view's actual height. 67 */ getFullscreenHeight()68 public int getFullscreenHeight() { 69 Insets insets = getRootWindowInsets().getInsets(WindowInsets.Type.systemBars()); 70 71 return getHeight() + insets.top + insets.bottom; 72 } 73 74 @Override onFinishInflate()75 protected void onFinishInflate() { 76 super.onFinishInflate(); 77 if (ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) { 78 return; 79 } 80 mFeedbackView = findViewById(R.id.gesture_tutorial_fragment_feedback_view); 81 mTutorialStepView = 82 mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step); 83 mSkipButton = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_close_button); 84 mDoneButton = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_action_button); 85 86 mFeedbackView.addOnLayoutChangeListener( 87 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { 88 if (mSkipButton.getVisibility() != VISIBLE 89 && mDoneButton.getVisibility() != VISIBLE) { 90 return; 91 } 92 // Either the skip or the done button is ever shown at once, never both. 93 boolean showingSkipButton = mSkipButton.getVisibility() == VISIBLE; 94 boolean isRTL = Utilities.isRtl(getContext().getResources()); 95 updateTutorialStepViewTranslation( 96 showingSkipButton ? mSkipButton : mDoneButton, 97 // Translate the step indicator away from whichever button is being 98 // shown. The skip button in on the left in LTR or on the right in RTL. 99 // The done button is on the right in LTR or left in RTL. 100 (showingSkipButton && !isRTL) || (!showingSkipButton && isRTL)); 101 }); 102 } 103 updateTutorialStepViewTranslation( @onNull View anchorView, boolean translateToRight)104 private void updateTutorialStepViewTranslation( 105 @NonNull View anchorView, boolean translateToRight) { 106 mTempStepIndicatorBounds.set( 107 mTutorialStepView.getLeft(), 108 mTutorialStepView.getTop(), 109 mTutorialStepView.getRight(), 110 mTutorialStepView.getBottom()); 111 mTempInclusionBounds.set(0, 0, mFeedbackView.getWidth(), mFeedbackView.getHeight()); 112 mTempExclusionBounds.set( 113 anchorView.getLeft(), 114 anchorView.getTop(), 115 anchorView.getRight(), 116 anchorView.getBottom()); 117 118 Utilities.translateOverlappingView( 119 mTutorialStepView, 120 mTempStepIndicatorBounds, 121 mTempInclusionBounds, 122 mTempExclusionBounds, 123 translateToRight ? Utilities.TRANSLATE_RIGHT : Utilities.TRANSLATE_LEFT); 124 } 125 } 126