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 android.view.LayoutInflater;
20 import android.view.View;
21 import android.view.ViewGroup;
22 
23 import androidx.annotation.IntDef;
24 import androidx.annotation.NonNull;
25 import androidx.core.view.ViewCompat;
26 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
27 import androidx.recyclerview.widget.RecyclerView;
28 import androidx.recyclerview.widget.RecyclerView.Adapter;
29 
30 import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
31 import com.android.internal.accessibility.dialog.AccessibilityTarget;
32 import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
33 import com.android.systemui.res.R;
34 
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.util.List;
38 
39 /**
40  * An adapter which shows the set of accessibility targets that can be performed.
41  */
42 public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
43     private int mIconWidthHeight;
44     private int mItemPadding;
45     private final List<AccessibilityTarget> mTargets;
46 
47     @IntDef({
48             ItemType.FIRST_ITEM,
49             ItemType.REGULAR_ITEM,
50             ItemType.LAST_ITEM
51     })
52     @Retention(RetentionPolicy.SOURCE)
53     @interface ItemType {
54         int FIRST_ITEM = 0;
55         int REGULAR_ITEM = 1;
56         int LAST_ITEM = 2;
57     }
58 
AccessibilityTargetAdapter(List<AccessibilityTarget> targets)59     public AccessibilityTargetAdapter(List<AccessibilityTarget> targets) {
60         mTargets = targets;
61     }
62 
63     @NonNull
64     @Override
onCreateViewHolder(@onNull ViewGroup parent, @ItemType int itemType)65     public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, @ItemType int itemType) {
66         final View root = LayoutInflater.from(parent.getContext()).inflate(
67                 R.layout.accessibility_floating_menu_item, parent,
68                 /* attachToRoot= */ false);
69 
70         if (itemType == ItemType.FIRST_ITEM) {
71             return new TopViewHolder(root);
72         }
73 
74         if (itemType == ItemType.LAST_ITEM) {
75             return new BottomViewHolder(root);
76         }
77 
78         return new ViewHolder(root);
79     }
80 
81     @Override
onBindViewHolder(@onNull ViewHolder holder, int position)82     public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
83         final AccessibilityTarget target = mTargets.get(position);
84         holder.mIconView.setBackground(target.getIcon());
85         holder.updateIconWidthHeight(mIconWidthHeight);
86         holder.updateItemPadding(mItemPadding, getItemCount());
87         holder.itemView.setOnClickListener((v) -> target.onSelected());
88         holder.itemView.setStateDescription(target.getStateDescription());
89         holder.itemView.setContentDescription(target.getLabel());
90 
91         final String clickHint = target.getFragmentType() == AccessibilityFragmentType.TOGGLE
92                 ? holder.itemView.getResources().getString(
93                 R.string.accessibility_floating_button_action_double_tap_to_toggle)
94                 : null;
95         ViewCompat.replaceAccessibilityAction(holder.itemView,
96                 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK,
97                 clickHint, /* command= */ null);
98     }
99 
100     @ItemType
101     @Override
getItemViewType(int position)102     public int getItemViewType(int position) {
103         // This LAST_ITEM condition should be checked before others to ensure proper padding when
104         // adding a second target via notifyItemInserted().
105         if (position == (getItemCount() - 1)) {
106             return ItemType.LAST_ITEM;
107         }
108 
109         if (position == 0) {
110             return ItemType.FIRST_ITEM;
111         }
112 
113         return ItemType.REGULAR_ITEM;
114     }
115 
116     @Override
getItemCount()117     public int getItemCount() {
118         return mTargets.size();
119     }
120 
setIconWidthHeight(int iconWidthHeight)121     public void setIconWidthHeight(int iconWidthHeight) {
122         mIconWidthHeight = iconWidthHeight;
123     }
124 
setItemPadding(int itemPadding)125     public void setItemPadding(int itemPadding) {
126         mItemPadding = itemPadding;
127     }
128 
129     static class ViewHolder extends RecyclerView.ViewHolder {
130         final View mIconView;
131 
ViewHolder(View itemView)132         ViewHolder(View itemView) {
133             super(itemView);
134             mIconView = itemView.findViewById(R.id.icon_view);
135         }
136 
updateIconWidthHeight(int newValue)137         void updateIconWidthHeight(int newValue) {
138             final ViewGroup.LayoutParams layoutParams = mIconView.getLayoutParams();
139             if (layoutParams.width == newValue) {
140                 return;
141             }
142             layoutParams.width = newValue;
143             layoutParams.height = newValue;
144             mIconView.setLayoutParams(layoutParams);
145         }
146 
updateItemPadding(int padding, int size)147         void updateItemPadding(int padding, int size) {
148             itemView.setPaddingRelative(padding, padding, padding, 0);
149         }
150     }
151 
152     static class TopViewHolder extends ViewHolder {
TopViewHolder(View itemView)153         TopViewHolder(View itemView) {
154             super(itemView);
155         }
156 
157         @Override
updateItemPadding(int padding, int size)158         void updateItemPadding(int padding, int size) {
159             final int paddingBottom = size <= 1 ? padding : 0;
160             itemView.setPaddingRelative(padding, padding, padding, paddingBottom);
161         }
162     }
163 
164     static class BottomViewHolder extends ViewHolder {
BottomViewHolder(View itemView)165         BottomViewHolder(View itemView) {
166             super(itemView);
167         }
168 
169         @Override
updateItemPadding(int padding, int size)170         void updateItemPadding(int padding, int size) {
171             itemView.setPaddingRelative(padding, padding, padding, padding);
172         }
173     }
174 }
175