1 /*
2  * Copyright (C) 2019 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.permissioncontroller.role.ui;
18 
19 import android.content.Context;
20 import android.util.AttributeSet;
21 import android.view.View;
22 import android.view.ViewGroup;
23 import android.widget.Checkable;
24 import android.widget.LinearLayout;
25 
26 import androidx.annotation.AttrRes;
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.annotation.StyleRes;
30 
31 /**
32  * This is a simple wrapper for {@link android.widget.LinearLayout} that implements the
33  * {@link android.widget.Checkable} interface by keeping an internal 'checked' state flag.
34  * <p>
35  * This can be used as the root view for a custom list item layout for
36  * {@link android.widget.AbsListView} elements with a
37  * {@link android.widget.AbsListView#setChoiceMode(int) choiceMode} set.
38  */
39 public class CheckableLinearLayout extends LinearLayout implements Checkable {
40 
41     private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
42 
43     private boolean mChecked = false;
44 
CheckableLinearLayout(@onNull Context context)45     public CheckableLinearLayout(@NonNull Context context) {
46         super(context);
47     }
48 
CheckableLinearLayout(@onNull Context context, @Nullable AttributeSet attrs)49     public CheckableLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
50         super(context, attrs);
51     }
52 
CheckableLinearLayout(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr)53     public CheckableLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs,
54             @AttrRes int defStyleAttr) {
55         super(context, attrs, defStyleAttr);
56     }
57 
CheckableLinearLayout(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)58     public CheckableLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs,
59             @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
60         super(context, attrs, defStyleAttr, defStyleRes);
61     }
62 
63     @Override
isChecked()64     public boolean isChecked() {
65         return mChecked;
66     }
67 
68     @Override
setChecked(boolean checked)69     public void setChecked(boolean checked) {
70         if (mChecked == checked) {
71             return;
72         }
73 
74         mChecked = checked;
75         refreshDrawableState();
76         updateChildrenChecked();
77     }
78 
79     @Override
toggle()80     public void toggle() {
81         setChecked(!mChecked);
82     }
83 
84     @NonNull
85     @Override
onCreateDrawableState(int extraSpace)86     public int[] onCreateDrawableState(int extraSpace) {
87         int[] state = super.onCreateDrawableState(extraSpace + 1);
88         if (isChecked()) {
89             mergeDrawableStates(state, CHECKED_STATE_SET);
90         }
91         return state;
92     }
93 
updateChildrenChecked()94     private void updateChildrenChecked() {
95         updateChildrenChecked(this, mChecked);
96     }
97 
98     // We call setChecked() on checkable children so that accessibility can get the correct state.
updateChildrenChecked(@onNull ViewGroup viewGroup, boolean checked)99     private static void updateChildrenChecked(@NonNull ViewGroup viewGroup, boolean checked) {
100         int count = viewGroup.getChildCount();
101         for (int i = 0; i < count; i++) {
102             View child = viewGroup.getChildAt(i);
103             if (child.isDuplicateParentStateEnabled()) {
104                 if (child instanceof Checkable) {
105                     ((Checkable) child).setChecked(checked);
106                 } else if (child instanceof ViewGroup) {
107                     updateChildrenChecked((ViewGroup) child, checked);
108                 }
109             }
110         }
111     }
112 }
113