1 /*
2  * Copyright (C) 2018 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.statusbar.phone;
18 
19 import android.annotation.IdRes;
20 import android.annotation.NonNull;
21 import android.view.View;
22 
23 import java.io.PrintWriter;
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 public class ContextualButtonGroup extends ButtonDispatcher {
28     private static final int INVALID_INDEX = -1;
29 
30     // List of pairs that contains the button and if the button was visible within this group
31     private final List<ButtonData> mButtonData = new ArrayList<>();
32 
ContextualButtonGroup(@dRes int containerId)33     public ContextualButtonGroup(@IdRes int containerId) {
34         super(containerId);
35     }
36 
37     /**
38      * Add a contextual button to the group. The order of adding increases in its priority. The
39      * priority is used to determine which button should be visible when setting multiple button's
40      * visibility {@see setButtonVisibility}.
41      * @param button the button added to the group
42      */
addButton(@onNull ContextualButton button)43     public void addButton(@NonNull ContextualButton button) {
44         button.attachToGroup(this);
45         mButtonData.add(new ButtonData(button));
46     }
47 
getContextButton(@dRes int buttonResId)48     public ContextualButton getContextButton(@IdRes int buttonResId) {
49         int index = getContextButtonIndex(buttonResId);
50         if (index != INVALID_INDEX) {
51             return mButtonData.get(index).button;
52         }
53         return null;
54     }
55 
getVisibleContextButton()56     public ContextualButton getVisibleContextButton() {
57         for (int i = mButtonData.size() - 1; i >= 0; --i) {
58             if (mButtonData.get(i).markedVisible) {
59                 return mButtonData.get(i).button;
60             }
61         }
62         return null;
63     }
64 
65     /**
66      * Set the visibility of the button by {@param buttonResId} with {@param visible}. Only one
67      * button is shown at a time. The input button will only show up if it has higher priority than
68      * a previous button, otherwise it will be marked as visible and shown later if all higher
69      * priority buttons are invisible. Therefore hiding a button will show the next marked visible
70      * button. This group's view will be visible if at least one button is visible.
71      * @return if the button is visible after operation
72      * @throws RuntimeException if the input id does not match any of the ids in the group
73      */
setButtonVisibility(@dRes int buttonResId, boolean visible)74     public int setButtonVisibility(@IdRes int buttonResId, boolean visible) {
75         final int index = getContextButtonIndex(buttonResId);
76         if (index == INVALID_INDEX) {
77             throw new RuntimeException("Cannot find the button id of " + buttonResId
78                     + " in context group");
79         }
80         setVisibility(View.INVISIBLE);
81         mButtonData.get(index).markedVisible = visible;
82 
83         // Make all buttons invisible except the first markedVisible button
84         boolean alreadyFoundVisibleButton = false;
85         int i = mButtonData.size() - 1;
86         for (; i >= 0; --i) {
87             final ButtonData buttonData = mButtonData.get(i);
88             if (!alreadyFoundVisibleButton && buttonData.markedVisible) {
89                 buttonData.setVisibility(View.VISIBLE);
90                 setVisibility(View.VISIBLE);
91                 alreadyFoundVisibleButton = true;
92             } else {
93                 buttonData.setVisibility(View.INVISIBLE);
94             }
95         }
96         return mButtonData.get(index).button.getVisibility();
97     }
98 
99     /**
100      * See if button is group visible. Group visible determines if a button can be visible when
101      * higher priority buttons go invisible.
102      * @param buttonResId the button to see if it is group visible
103      * @return true if button is group visible
104      */
isButtonVisibleWithinGroup(@dRes int buttonResId)105     public boolean isButtonVisibleWithinGroup(@IdRes int buttonResId) {
106         final int index = getContextButtonIndex(buttonResId);
107         return index != INVALID_INDEX && mButtonData.get(index).markedVisible;
108     }
109 
110     /**
111      * Update all the icons that are attached to this group. This will get all the buttons to update
112      * their icons for their buttons.
113      */
updateIcons()114     public void updateIcons() {
115         for (ButtonData data : mButtonData) {
116             data.button.updateIcon();
117         }
118     }
119 
dump(PrintWriter pw)120     public void dump(PrintWriter pw) {
121         View view = getCurrentView();
122         pw.println("ContextualButtonGroup {");
123         pw.println("      getVisibleContextButton(): " + getVisibleContextButton());
124         pw.println("      isVisible(): " + isVisible());
125         pw.println("      attached(): " + (view != null && view.isAttachedToWindow()));
126         pw.println("      mButtonData [ ");
127         for (int i = mButtonData.size() - 1; i >= 0; --i) {
128             final ButtonData data = mButtonData.get(i);
129             view = data.button.getCurrentView();
130             pw.println("            " + i + ": markedVisible=" + data.markedVisible
131                     + " visible=" + data.button.getVisibility()
132                     + " attached=" + (view != null && view.isAttachedToWindow())
133                     + " alpha=" + data.button.getAlpha());
134         }
135         pw.println("      ]");
136         pw.println("    }");
137     }
138 
getContextButtonIndex(@dRes int buttonResId)139     private int getContextButtonIndex(@IdRes int buttonResId) {
140         for (int i = 0; i < mButtonData.size(); ++i) {
141             if (mButtonData.get(i).button.getId() == buttonResId) {
142                 return i;
143             }
144         }
145         return INVALID_INDEX;
146     }
147 
148     private final static class ButtonData {
149         ContextualButton button;
150         boolean markedVisible;
151 
ButtonData(ContextualButton button)152         ButtonData(ContextualButton button) {
153             this.button = button;
154             this.markedVisible = false;
155         }
156 
setVisibility(int visiblity)157         void setVisibility(int visiblity) {
158             button.setVisibility(visiblity);
159         }
160     }
161 }
162