1 /* 2 * Copyright (C) 2022 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.systemui.accessibility.floatingmenu; 18 19 import static android.util.TypedValue.COMPLEX_UNIT_PX; 20 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 21 22 import android.annotation.IntDef; 23 import android.content.ComponentCallbacks; 24 import android.content.Context; 25 import android.content.res.Configuration; 26 import android.content.res.Resources; 27 import android.graphics.Rect; 28 import android.text.Layout; 29 import android.view.Gravity; 30 import android.view.View; 31 import android.view.ViewTreeObserver; 32 import android.widget.Button; 33 import android.widget.FrameLayout; 34 import android.widget.LinearLayout; 35 import android.widget.TextView; 36 37 import androidx.annotation.NonNull; 38 39 import com.android.systemui.res.R; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 44 /** 45 * The message view with the action prompt to whether to undo operation for users when removing 46 * the {@link MenuView}. 47 */ 48 class MenuMessageView extends LinearLayout implements 49 ViewTreeObserver.OnComputeInternalInsetsListener, ComponentCallbacks { 50 private final TextView mTextView; 51 private final Button mUndoButton; 52 53 @IntDef({ 54 Index.TEXT_VIEW, 55 Index.UNDO_BUTTON 56 }) 57 @Retention(RetentionPolicy.SOURCE) 58 @interface Index { 59 int TEXT_VIEW = 0; 60 int UNDO_BUTTON = 1; 61 } 62 MenuMessageView(Context context)63 MenuMessageView(Context context) { 64 super(context); 65 66 setLayoutDirection(LAYOUT_DIRECTION_LOCALE); 67 setVisibility(GONE); 68 69 mTextView = new TextView(context); 70 mUndoButton = new Button(context); 71 72 addView(mTextView, Index.TEXT_VIEW, 73 new LayoutParams(/* width= */ 0, WRAP_CONTENT, /* weight= */ 1)); 74 addView(mUndoButton, Index.UNDO_BUTTON, new LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); 75 76 // The message box is not focusable, but will announce its contents when it appears. 77 // The textView and button are still interactable. 78 setClickable(false); 79 setFocusable(false); 80 setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE); 81 } 82 83 @Override onConfigurationChanged(@onNull Configuration newConfig)84 public void onConfigurationChanged(@NonNull Configuration newConfig) { 85 updateResources(); 86 } 87 88 @Override onLowMemory()89 public void onLowMemory() { 90 // Do nothing. 91 } 92 93 @Override onAttachedToWindow()94 protected void onAttachedToWindow() { 95 super.onAttachedToWindow(); 96 97 final FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(WRAP_CONTENT, 98 WRAP_CONTENT); 99 containerParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; 100 setLayoutParams(containerParams); 101 setGravity(Gravity.CENTER_VERTICAL); 102 103 mUndoButton.setBackground(null); 104 105 updateResources(); 106 107 getContext().registerComponentCallbacks(this); 108 getViewTreeObserver().addOnComputeInternalInsetsListener(this); 109 } 110 111 @Override onDetachedFromWindow()112 protected void onDetachedFromWindow() { 113 super.onDetachedFromWindow(); 114 115 getContext().unregisterComponentCallbacks(this); 116 getViewTreeObserver().removeOnComputeInternalInsetsListener(this); 117 } 118 119 @Override onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo)120 public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { 121 inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 122 123 if (getVisibility() == VISIBLE) { 124 final int x = (int) getX(); 125 final int y = (int) getY(); 126 inoutInfo.touchableRegion.union(new Rect(x, y, x + getWidth(), y + getHeight())); 127 } 128 } 129 130 /** 131 * Registers a listener to be invoked when this undo action button is clicked. It should be 132 * called after {@link View#onAttachedToWindow()}. 133 * 134 * @param listener The listener that will run 135 */ setUndoListener(OnClickListener listener)136 void setUndoListener(OnClickListener listener) { 137 mUndoButton.setOnClickListener(listener); 138 } 139 updateResources()140 private void updateResources() { 141 final Resources res = getResources(); 142 143 final int containerPadding = 144 res.getDimensionPixelSize( 145 R.dimen.accessibility_floating_menu_message_container_horizontal_padding); 146 final int margin = res.getDimensionPixelSize( 147 R.dimen.accessibility_floating_menu_message_margin); 148 final FrameLayout.LayoutParams containerParams = 149 (FrameLayout.LayoutParams) getLayoutParams(); 150 containerParams.setMargins(margin, margin, margin, margin); 151 setLayoutParams(containerParams); 152 setBackground(res.getDrawable(R.drawable.accessibility_floating_message_background)); 153 setPadding(containerPadding, /* top= */ 0, containerPadding, /* bottom= */ 0); 154 setMinimumWidth( 155 res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_message_min_width)); 156 setMinimumHeight( 157 res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_message_min_height)); 158 setElevation( 159 res.getDimensionPixelSize(R.dimen.accessibility_floating_menu_message_elevation)); 160 161 final int textPadding = 162 res.getDimensionPixelSize( 163 R.dimen.accessibility_floating_menu_message_text_vertical_padding); 164 final int textColor = res.getColor(R.color.accessibility_floating_menu_message_text); 165 final int textSize = res.getDimensionPixelSize( 166 R.dimen.accessibility_floating_menu_message_text_size); 167 mTextView.setPadding(/* left= */ 0, textPadding, /* right= */ 0, textPadding); 168 mTextView.setTextSize(COMPLEX_UNIT_PX, textSize); 169 mTextView.setTextColor(textColor); 170 mTextView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL); 171 172 mUndoButton.setText(res.getString(R.string.accessibility_floating_button_undo)); 173 mUndoButton.setTextSize(COMPLEX_UNIT_PX, textSize); 174 mUndoButton.setTextColor(textColor); 175 mUndoButton.setAllCaps(true); 176 } 177 } 178