/* * Copyright (C) 2023 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.settings.inputmethod; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.graphics.Color; import android.graphics.Point; import android.os.Bundle; import android.view.Display; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import com.android.settings.R; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import java.util.ArrayList; import java.util.Arrays; public class TrackpadGestureDialogFragment extends BottomSheetDialogFragment { private Context mContext; private LayoutInflater mInflater; private View mViewArrowPrevious; private View mViewArrowNext; private ViewPager mViewPager; private ArrayList mPageList; private ImageView[] mDotIndicators; private View[] mViewPagerItems; private Button mButtonStartSkip; private Button mButtonStartRestart; private Button mButtonEndNext; private Button mButtonEndDone; private static final int DOT_INDICATOR_SIZE = 12; private static final int DOT_INDICATOR_LEFT_PADDING = 6; private static final int DOT_INDICATOR_RIGHT_PADDING = 6; public TrackpadGestureDialogFragment() {} @Override public void onAttach(Context context) { super.onAttach(context); mContext = context; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public void onStart() { super.onStart(); final Dialog dialog = getDialog(); if (dialog == null) { return; } Window window = dialog.getWindow(); if (window == null) { return; } setLayoutEdgeToEdge(window); final Point size = getScreenSize(); final WindowManager.LayoutParams attributes = window.getAttributes(); attributes.width = (int) (size.x * 0.75); window.setAttributes(attributes); } @Override public void onPause() { super.onPause(); dismiss(); } private static void setLayoutEdgeToEdge(Window window) { View windowDecorView = window.getDecorView(); windowDecorView.setSystemUiVisibility( windowDecorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); window.setNavigationBarColor(Color.TRANSPARENT); } @Override public Dialog onCreateDialog(final Bundle savedInstanceState) { Dialog dialog = super.onCreateDialog(savedInstanceState); mInflater = mContext.getSystemService(LayoutInflater.class); View contentView = mInflater.inflate(R.layout.trackpad_gesture_preview, null); addViewPager(contentView); dialog.setContentView(contentView); Window gestureDialogWindow = dialog.getWindow(); gestureDialogWindow.setType(TYPE_SYSTEM_DIALOG); // Workaround for solve issue about dialog not full expanded when landscape. FrameLayout bottomSheet = (FrameLayout) dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet); bottomSheet.setBackgroundResource(android.R.color.transparent); BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet); if (!isGestureNavigationEnabled()) { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } ViewTreeObserver observer = contentView.getViewTreeObserver(); observer.addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { contentView.getViewTreeObserver().removeOnGlobalLayoutListener(this); int contentViewHeight = contentView.getMeasuredHeight(); int navigationBarHeight = getNavigationBarHeight(); behavior.setPeekHeight(contentViewHeight - navigationBarHeight); } }); // The gesture education view shouldn't be draggable." behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override public void onStateChanged(View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_DRAGGING) { if (isGestureNavigationEnabled()) { behavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } else { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } } } @Override public void onSlide(View bottomSheet, float slideOffset) { // Do nothing. } }); return dialog; } private ArrayList getViewPagerResource() { return new ArrayList( Arrays.asList( R.layout.gesture_tip1_go_home, R.layout.gesture_tip2_go_back, R.layout.gesture_tip3_recent_apps, R.layout.gesture_tip4_notifications, R.layout.gesture_tip5_switch_apps)); } private void addViewPager(View preview) { mViewPager = preview.findViewById(R.id.viewpager); int viewPagerResourceSize = getViewPagerResource().size(); mViewPagerItems = new View[viewPagerResourceSize]; for (int i = 0; i < viewPagerResourceSize; i++) { mViewPagerItems[i] = mInflater.inflate(getViewPagerResource().get(i), null /* root */); } mPageList = new ArrayList(); for (int i = 0; i < mViewPagerItems.length; i++) { mPageList.add(mViewPagerItems[i]); } mViewPager.setAdapter(new GesturePagerAdapter(mPageList)); mButtonStartRestart = (Button) preview.findViewById(R.id.button_restart); mButtonStartRestart.setOnClickListener(v -> { final int firstPos = mViewPager.getCurrentItem() - mViewPagerItems.length; mViewPager.setCurrentItem(firstPos, true); }); mButtonEndDone = (Button) preview.findViewById(R.id.button_done); mButtonEndDone.setOnClickListener(v -> { dismiss(); }); mButtonStartSkip = (Button) preview.findViewById(R.id.button_skip); mButtonStartSkip.setOnClickListener(v -> { dismiss(); }); mButtonEndNext = (Button) preview.findViewById(R.id.button_next); mButtonEndNext.setOnClickListener(v -> { final int nextPos = mViewPager.getCurrentItem() + 1; mViewPager.setCurrentItem(nextPos, true); }); mViewPager.addOnPageChangeListener(createPageListener()); final ViewGroup viewGroup = (ViewGroup) preview.findViewById(R.id.viewGroup); mDotIndicators = new ImageView[mPageList.size()]; for (int i = 0; i < mPageList.size(); i++) { final ImageView imageView = new ImageView(mContext); final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(DOT_INDICATOR_SIZE, DOT_INDICATOR_SIZE); lp.setMargins(DOT_INDICATOR_LEFT_PADDING, 0, DOT_INDICATOR_RIGHT_PADDING, 0); imageView.setLayoutParams(lp); mDotIndicators[i] = imageView; viewGroup.addView(mDotIndicators[i]); } } private static class GesturePagerAdapter extends PagerAdapter { private final ArrayList mPageViewList; GesturePagerAdapter(ArrayList pageViewList) { mPageViewList = pageViewList; } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (mPageViewList.get(position) != null) { container.removeView(mPageViewList.get(position)); } } @Override public Object instantiateItem(ViewGroup container, int position) { container.addView(mPageViewList.get(position)); return mPageViewList.get(position); } @Override public int getCount() { return mPageViewList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return object == view; } } private ViewPager.OnPageChangeListener createPageListener() { return new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled( int position, float positionOffset, int positionOffsetPixels) { if (positionOffset != 0) { for (int i = 0; i < mPageList.size(); i++) { mViewPagerItems[i].setVisibility(View.VISIBLE); } } else { updateIndicator(position); } } @Override public void onPageSelected(int position) {} @Override public void onPageScrollStateChanged(int state) {} }; } private void updateIndicator(int position) { for (int i = 0; i < mPageList.size(); i++) { if (position == i) { mDotIndicators[i].setBackgroundResource( R.drawable.ic_color_page_indicator_focused); mViewPagerItems[i].setVisibility(View.VISIBLE); } else { mDotIndicators[i].setBackgroundResource( R.drawable.ic_color_page_indicator_unfocused); mViewPagerItems[i].setVisibility(View.INVISIBLE); } } if (position >= 0 && position < mViewPagerItems.length - 1) { mButtonStartSkip.setVisibility(View.VISIBLE); mButtonEndNext.setVisibility(View.VISIBLE); mButtonStartRestart.setVisibility(View.GONE); mButtonEndDone.setVisibility(View.GONE); } else { mButtonStartSkip.setVisibility(View.GONE); mButtonEndNext.setVisibility(View.GONE); mButtonStartRestart.setVisibility(View.VISIBLE); mButtonEndDone.setVisibility(View.VISIBLE); } } private Point getScreenSize() { final Point size = new Point(); final Activity activity = (Activity) mContext; final Display display = activity.getWindowManager().getDefaultDisplay(); display.getSize(size); return size; } private int getNavigationBarHeight() { final Activity activity = (Activity) mContext; WindowInsets insets = activity.getWindowManager().getCurrentWindowMetrics().getWindowInsets(); return insets.getInsets(WindowInsets.Type.navigationBars()).bottom; } private boolean isGestureNavigationEnabled() { return NAV_BAR_MODE_GESTURAL == mContext.getResources().getInteger( com.android.internal.R.integer.config_navBarInteractionMode); } }