• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.systemui.accessibility.floatingmenu;
18 
19 import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
20 
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.view.LayoutInflater;
24 import android.view.View;
25 import android.view.ViewGroup;
26 
27 import androidx.annotation.IntDef;
28 import androidx.annotation.NonNull;
29 import androidx.annotation.VisibleForTesting;
30 import androidx.core.view.ViewCompat;
31 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
32 import androidx.recyclerview.widget.RecyclerView;
33 import androidx.recyclerview.widget.RecyclerView.Adapter;
34 
35 import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
36 import com.android.internal.accessibility.dialog.AccessibilityTarget;
37 import com.android.settingslib.bluetooth.HearingAidDeviceManager;
38 import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus;
39 import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
40 import com.android.systemui.accessibility.hearingaid.HearingDeviceStatusDrawableInfo;
41 import com.android.systemui.res.R;
42 
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.util.List;
46 
47 /**
48  * An adapter which shows the set of accessibility targets that can be performed.
49  */
50 public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
51     @VisibleForTesting static final int PAYLOAD_HEARING_STATUS_DRAWABLE = 1;
52 
53     private int mIconWidthHeight;
54     private int mBadgeWidthHeight;
55     private int mItemPadding;
56     private final List<AccessibilityTarget> mTargets;
57 
58     private int mHearingDeviceStatus;
59     private boolean mBadgeOnLeftSide = false;
60 
61     @IntDef({
62             ItemType.FIRST_ITEM,
63             ItemType.REGULAR_ITEM,
64             ItemType.LAST_ITEM
65     })
66     @Retention(RetentionPolicy.SOURCE)
67     @interface ItemType {
68         int FIRST_ITEM = 0;
69         int REGULAR_ITEM = 1;
70         int LAST_ITEM = 2;
71     }
72 
AccessibilityTargetAdapter(@onNull List<AccessibilityTarget> targets)73     public AccessibilityTargetAdapter(@NonNull List<AccessibilityTarget> targets) {
74         mTargets = targets;
75     }
76 
77     @NonNull
78     @Override
onCreateViewHolder(@onNull ViewGroup parent, @ItemType int itemType)79     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, @ItemType int itemType) {
80         final View root = LayoutInflater.from(parent.getContext()).inflate(
81                 R.layout.accessibility_floating_menu_item, parent,
82                 /* attachToRoot= */ false);
83 
84         if (itemType == ItemType.FIRST_ITEM) {
85             return new TopViewHolder(root);
86         }
87 
88         if (itemType == ItemType.LAST_ITEM) {
89             return new BottomViewHolder(root);
90         }
91 
92         return new ViewHolder(root);
93     }
94 
95     @Override
onBindViewHolder(@onNull ViewHolder holder, int position)96     public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
97         final AccessibilityTarget target = mTargets.get(position);
98         holder.mIconView.setBackground(target.getIcon());
99         holder.mRightBadgeView.setBackground(null);
100         holder.mLeftBadgeView.setBackground(null);
101         holder.updateIconSize(mIconWidthHeight);
102         holder.updateItemPadding(mItemPadding, getItemCount());
103         holder.itemView.setOnClickListener((v) -> target.onSelected());
104         holder.itemView.setStateDescription(target.getStateDescription());
105         holder.itemView.setContentDescription(target.getLabel());
106 
107         final String clickHint = target.getFragmentType() == AccessibilityFragmentType.TOGGLE
108                 ? holder.itemView.getResources().getString(
109                 R.string.accessibility_floating_button_action_double_tap_to_toggle)
110                 : null;
111         ViewCompat.replaceAccessibilityAction(holder.itemView,
112                 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK,
113                 clickHint, /* command= */ null);
114 
115         if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) {
116             if (ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.equals(
117                     ComponentName.unflattenFromString(target.getId()))) {
118                 updateHearingDeviceStatusDrawable(holder, mHearingDeviceStatus);
119             }
120         }
121     }
122 
123     @Override
onBindViewHolder(@onNull ViewHolder holder, int position, @NonNull List<Object> payloads)124     public void onBindViewHolder(@NonNull ViewHolder holder, int position,
125             @NonNull List<Object> payloads) {
126         if (payloads.isEmpty()) {
127             onBindViewHolder(holder, position);
128             return;
129         }
130 
131         if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) {
132             payloads.forEach(payload -> {
133                 if (payload instanceof Integer cmd) {
134                     if (cmd == PAYLOAD_HEARING_STATUS_DRAWABLE) {
135                         updateHearingDeviceStatusDrawable(holder, mHearingDeviceStatus);
136                     }
137                 }
138             });
139         }
140     }
141 
142     @ItemType
143     @Override
getItemViewType(int position)144     public int getItemViewType(int position) {
145         // This LAST_ITEM condition should be checked before others to ensure proper padding when
146         // adding a second target via notifyItemInserted().
147         if (position == (getItemCount() - 1)) {
148             return ItemType.LAST_ITEM;
149         }
150 
151         if (position == 0) {
152             return ItemType.FIRST_ITEM;
153         }
154 
155         return ItemType.REGULAR_ITEM;
156     }
157 
158     @Override
getItemCount()159     public int getItemCount() {
160         return mTargets.size();
161     }
162 
setIconWidthHeight(int iconWidthHeight)163     public void setIconWidthHeight(int iconWidthHeight) {
164         mIconWidthHeight = iconWidthHeight;
165     }
166 
setBadgeWidthHeight(int badgeWidthHeight)167     public void setBadgeWidthHeight(int badgeWidthHeight) {
168         mBadgeWidthHeight = badgeWidthHeight;
169     }
170 
setItemPadding(int itemPadding)171     public void setItemPadding(int itemPadding) {
172         mItemPadding = itemPadding;
173     }
174 
setBadgeOnLeftSide(boolean leftSide)175     public void setBadgeOnLeftSide(boolean leftSide) {
176         mBadgeOnLeftSide = leftSide;
177     }
178 
179     /**
180      * Notifies to update the hearing device status drawable at the given target index.
181      *
182      * @param status the connection status for hearing devices.
183      *               {@link HearingAidDeviceManager.ConnectionStatus}
184      * @param targetIndex The index of the hearing aid device in the target list, or -1 if not
185      *                    exist.
186      */
onHearingDeviceStatusChanged(@earingAidDeviceManager.ConnectionStatus int status, int targetIndex)187     public void onHearingDeviceStatusChanged(@HearingAidDeviceManager.ConnectionStatus int status,
188             int targetIndex) {
189         mHearingDeviceStatus = status;
190 
191         if (targetIndex >= 0) {
192             notifyItemChanged(targetIndex, PAYLOAD_HEARING_STATUS_DRAWABLE);
193         }
194     }
195 
updateHearingDeviceStatusDrawable(ViewHolder holder, @ConnectionStatus int status)196     private void updateHearingDeviceStatusDrawable(ViewHolder holder,
197             @ConnectionStatus int status) {
198         final Context context = holder.itemView.getContext();
199         HearingDeviceStatusDrawableInfo.StatusDrawableInfo statusDrawableInfo =
200                 HearingDeviceStatusDrawableInfo.get(status);
201         final int baseDrawableId = statusDrawableInfo.baseDrawableId();
202         final int stateDescriptionId = statusDrawableInfo.stateDescriptionId();
203         final int indicatorDrawableId = statusDrawableInfo.indicatorDrawableId();
204 
205         holder.mIconView.setBackground(
206                 (baseDrawableId != 0) ? context.getDrawable(baseDrawableId) : null);
207         holder.mRightBadgeView.setBackground(
208                 (indicatorDrawableId != 0) ? context.getDrawable(indicatorDrawableId) : null);
209         holder.mLeftBadgeView.setBackground(
210                 (indicatorDrawableId != 0) ? context.getDrawable(indicatorDrawableId) : null);
211         holder.itemView.setStateDescription(
212                 (stateDescriptionId != 0) ? context.getString(stateDescriptionId) : null);
213         holder.updateBadgeSize(mBadgeWidthHeight);
214 
215         if (mBadgeOnLeftSide) {
216             holder.mRightBadgeView.setVisibility(View.INVISIBLE);
217             holder.mLeftBadgeView.setVisibility(View.VISIBLE);
218         } else {
219             holder.mRightBadgeView.setVisibility(View.VISIBLE);
220             holder.mLeftBadgeView.setVisibility(View.INVISIBLE);
221         }
222     }
223 
224     static class ViewHolder extends RecyclerView.ViewHolder {
225         final View mIconView;
226         final View mRightBadgeView;
227         final View mLeftBadgeView;
228 
ViewHolder(View itemView)229         ViewHolder(View itemView) {
230             super(itemView);
231             mIconView = itemView.findViewById(R.id.icon_view);
232             mRightBadgeView = itemView.findViewById(R.id.right_badge_view);
233             mLeftBadgeView = itemView.findViewById(R.id.left_badge_view);
234         }
235 
updateIconSize(int newValue)236         void updateIconSize(int newValue) {
237             final ViewGroup.LayoutParams layoutParams = mIconView.getLayoutParams();
238             if (layoutParams.width == newValue) {
239                 return;
240             }
241             layoutParams.width = newValue;
242             layoutParams.height = newValue;
243             mIconView.setLayoutParams(layoutParams);
244         }
245 
updateBadgeSize(int newValue)246         void updateBadgeSize(int newValue) {
247             final ViewGroup.LayoutParams rightLayoutParams = mRightBadgeView.getLayoutParams();
248             if (rightLayoutParams.width == newValue) {
249                 return;
250             }
251             rightLayoutParams.width = newValue;
252             rightLayoutParams.height = newValue;
253             final ViewGroup.LayoutParams leftLayoutParams = mLeftBadgeView.getLayoutParams();
254             if (leftLayoutParams.width == newValue) {
255                 return;
256             }
257             leftLayoutParams.width = newValue;
258             leftLayoutParams.height = newValue;
259 
260             mRightBadgeView.setLayoutParams(rightLayoutParams);
261             mLeftBadgeView.setLayoutParams(leftLayoutParams);
262         }
263 
updateItemPadding(int padding, int size)264         void updateItemPadding(int padding, int size) {
265             itemView.setPaddingRelative(padding, padding, padding, 0);
266         }
267     }
268 
269     static class TopViewHolder extends ViewHolder {
TopViewHolder(View itemView)270         TopViewHolder(View itemView) {
271             super(itemView);
272         }
273 
274         @Override
updateItemPadding(int padding, int size)275         void updateItemPadding(int padding, int size) {
276             final int paddingBottom = size <= 1 ? padding : 0;
277             itemView.setPaddingRelative(padding, padding, padding, paddingBottom);
278         }
279     }
280 
281     static class BottomViewHolder extends ViewHolder {
BottomViewHolder(View itemView)282         BottomViewHolder(View itemView) {
283             super(itemView);
284         }
285 
286         @Override
updateItemPadding(int padding, int size)287         void updateItemPadding(int padding, int size) {
288             itemView.setPaddingRelative(padding, padding, padding, padding);
289         }
290     }
291 }
292