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 17 package com.android.launcher3.widget; 18 19 import android.annotation.SuppressLint; 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.ViewGroup; 27 import android.view.ViewParent; 28 import android.view.WindowInsets; 29 import android.widget.ScrollView; 30 31 import com.android.launcher3.DeviceProfile; 32 import com.android.launcher3.R; 33 import com.android.launcher3.dragndrop.AddItemActivity; 34 import com.android.launcher3.views.AbstractSlideInView; 35 36 /** 37 * Bottom sheet for the pin widget. 38 */ 39 public class AddItemWidgetsBottomSheet extends AbstractSlideInView<AddItemActivity> implements 40 View.OnApplyWindowInsetsListener { 41 42 private static final int DEFAULT_CLOSE_DURATION = 200; 43 44 private final Rect mInsets; 45 private ScrollView mWidgetPreviewScrollView; 46 47 private int mContentHorizontalMarginInPx; 48 AddItemWidgetsBottomSheet(Context context, AttributeSet attrs)49 public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs) { 50 this(context, attrs, 0); 51 } 52 AddItemWidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr)53 public AddItemWidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) { 54 super(context, attrs, defStyleAttr); 55 mInsets = new Rect(); 56 mContentHorizontalMarginInPx = getResources().getDimensionPixelSize( 57 R.dimen.widget_list_horizontal_margin); 58 } 59 60 /** 61 * Attaches to activity container and animates open the bottom sheet. 62 */ show()63 public void show() { 64 ViewParent parent = getParent(); 65 if (parent instanceof ViewGroup) { 66 ((ViewGroup) parent).removeView(this); 67 } 68 attachToContainer(); 69 setOnApplyWindowInsetsListener(this); 70 animateOpen(); 71 } 72 73 @Override onControllerInterceptTouchEvent(MotionEvent ev)74 public boolean onControllerInterceptTouchEvent(MotionEvent ev) { 75 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 76 mNoIntercept = false; 77 // Suppress drag to dismiss gesture if the scroll view is being scrolled. 78 if (getPopupContainer().isEventOverView(mWidgetPreviewScrollView, ev) 79 && mWidgetPreviewScrollView.getScrollY() > 0) { 80 mNoIntercept = true; 81 } 82 } 83 return super.onControllerInterceptTouchEvent(ev); 84 } 85 86 @Override onLayout(boolean changed, int l, int t, int r, int b)87 protected void onLayout(boolean changed, int l, int t, int r, int b) { 88 int width = r - l; 89 int height = b - t; 90 91 // Lay out content as center bottom aligned. 92 int contentWidth = mContent.getMeasuredWidth(); 93 int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left; 94 mContent.layout(contentLeft, height - mContent.getMeasuredHeight(), 95 contentLeft + contentWidth, height); 96 97 setTranslationShift(mTranslationShift); 98 } 99 100 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)101 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 102 DeviceProfile deviceProfile = mActivityContext.getDeviceProfile(); 103 int widthUsed; 104 if (deviceProfile.isTablet) { 105 int margin = deviceProfile.allAppsLeftRightMargin; 106 widthUsed = Math.max(2 * margin, 2 * (mInsets.left + mInsets.right)); 107 } else if (mInsets.bottom > 0) { 108 widthUsed = mInsets.left + mInsets.right; 109 } else { 110 Rect padding = deviceProfile.workspacePadding; 111 widthUsed = Math.max(padding.left + padding.right, 112 2 * (mInsets.left + mInsets.right)); 113 } 114 115 measureChildWithMargins(mContent, widthMeasureSpec, 116 widthUsed, heightMeasureSpec, deviceProfile.bottomSheetTopPadding); 117 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), 118 MeasureSpec.getSize(heightMeasureSpec)); 119 } 120 121 @Override onFinishInflate()122 protected void onFinishInflate() { 123 super.onFinishInflate(); 124 mContent = findViewById(R.id.add_item_bottom_sheet_content); 125 mWidgetPreviewScrollView = findViewById(R.id.widget_preview_scroll_view); 126 } 127 animateOpen()128 private void animateOpen() { 129 if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) { 130 return; 131 } 132 mIsOpen = true; 133 setUpDefaultOpenAnimation().start(); 134 } 135 136 @Override handleClose(boolean animate)137 protected void handleClose(boolean animate) { 138 handleClose(animate, DEFAULT_CLOSE_DURATION); 139 } 140 141 @Override isOfType(@loatingViewType int type)142 protected boolean isOfType(@FloatingViewType int type) { 143 return (type & TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP) != 0; 144 } 145 146 @Override getScrimColor(Context context)147 protected int getScrimColor(Context context) { 148 // Don't add a scrim when using the standalone picker activity. The background dimming is 149 // handled by applying dimBackground in the activity theme, so the scrim doesn't slide in 150 // with the window. 151 return -1; 152 } 153 154 @SuppressLint("NewApi") // Already added API check. 155 @Override onApplyWindowInsets(View view, WindowInsets windowInsets)156 public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) { 157 Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars()); 158 mInsets.set(insets.left, insets.top, insets.right, insets.bottom); 159 mContent.setPadding(mContent.getPaddingStart(), mContent.getPaddingTop(), 160 mContent.getPaddingEnd(), mInsets.bottom); 161 162 int contentHorizontalMarginInPx = getResources().getDimensionPixelSize( 163 R.dimen.widget_list_horizontal_margin); 164 if (contentHorizontalMarginInPx != mContentHorizontalMarginInPx) { 165 setContentHorizontalMargin(findViewById(R.id.widget_appName), 166 contentHorizontalMarginInPx); 167 setContentHorizontalMargin(findViewById(R.id.widget_drag_instruction), 168 contentHorizontalMarginInPx); 169 setContentHorizontalMargin(findViewById(R.id.widget_cell), contentHorizontalMarginInPx); 170 setContentHorizontalMargin(findViewById(R.id.actions_container), 171 contentHorizontalMarginInPx); 172 mContentHorizontalMarginInPx = contentHorizontalMarginInPx; 173 } 174 return windowInsets; 175 } 176 setContentHorizontalMargin(View view, int contentHorizontalMargin)177 private static void setContentHorizontalMargin(View view, int contentHorizontalMargin) { 178 ViewGroup.MarginLayoutParams layoutParams = 179 ((ViewGroup.MarginLayoutParams) view.getLayoutParams()); 180 layoutParams.setMarginStart(contentHorizontalMargin); 181 layoutParams.setMarginEnd(contentHorizontalMargin); 182 } 183 } 184