Home | History | Annotate | Download | only in deletionhelper
      1 /*
      2  * Copyright (C) 2016 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.storagemanager.deletionhelper;
     18 
     19 import android.content.Context;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
     23 import android.support.v7.preference.Preference;
     24 import android.support.v7.preference.PreferenceGroup;
     25 import android.support.v7.preference.PreferenceViewHolder;
     26 import android.util.AttributeSet;
     27 
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 import android.widget.Checkable;
     31 
     32 import android.widget.ImageView;
     33 import android.widget.ProgressBar;
     34 import android.widget.TextView;
     35 import com.android.internal.annotations.VisibleForTesting;
     36 import com.android.storagemanager.R;
     37 import com.android.storagemanager.deletionhelper.DeletionType.LoadingStatus;
     38 
     39 /**
     40  * CollapsibleCheckboxPreferenceGroup is a preference group that can be expanded or collapsed and
     41  * also has a checkbox.
     42  */
     43 public class CollapsibleCheckboxPreferenceGroup extends PreferenceGroup implements
     44         View.OnClickListener {
     45     private boolean mCollapsed;
     46     private boolean mChecked;
     47     private TextView mTextView;
     48     private ProgressBar mProgressBar;
     49     private View mWidget;
     50     private boolean mLoaded;
     51 
     52     public CollapsibleCheckboxPreferenceGroup(Context context) {
     53         this(context, null);
     54     }
     55 
     56     public CollapsibleCheckboxPreferenceGroup(Context context, AttributeSet attrs) {
     57         super(context, attrs);
     58         setLayoutResource(R.layout.deletion_preference);
     59         setWidgetLayoutResource(R.layout.preference_widget_checkbox);
     60     }
     61 
     62     @Override
     63     public void onBindViewHolder(PreferenceViewHolder holder) {
     64         super.onBindViewHolder(holder);
     65         View checkbox = holder.findViewById(com.android.internal.R.id.checkbox);
     66         mTextView = (TextView) holder.findViewById(android.R.id.summary);
     67         // Ensures that the color of the text is consistent with the checkbox having a tick or not
     68         mTextView.setActivated(mChecked);
     69         if (checkbox != null && checkbox instanceof Checkable) {
     70             ((Checkable) checkbox).setChecked(mChecked);
     71 
     72             // Expand the touch target by making the parent the touch target.
     73             View parent = (View) checkbox.getParent();
     74             parent.setClickable(true);
     75             parent.setFocusable(true);
     76             parent.setOnClickListener(this);
     77         }
     78         mProgressBar = (ProgressBar) holder.findViewById(R.id.progress_bar);
     79         mProgressBar.setVisibility(mLoaded ? View.GONE : View.VISIBLE);
     80         mWidget = holder.findViewById(android.R.id.widget_frame);
     81         mWidget.setVisibility(mLoaded ? View.VISIBLE : View.GONE);
     82 
     83         // CollapsibleCheckboxPreferenceGroup considers expansion to be its "longer-term
     84         // (activation) state."
     85         final ImageView imageView = (ImageView) holder.findViewById(android.R.id.icon);
     86         imageView.setActivated(!mCollapsed);
     87     }
     88 
     89     @Override
     90     public boolean addPreference(Preference p) {
     91         super.addPreference(p);
     92         p.setVisible(!isCollapsed());
     93         return true;
     94     }
     95 
     96     // The preference click handler.
     97     @Override
     98     protected void onClick() {
     99         super.onClick();
    100         setCollapse(!isCollapsed());
    101     }
    102 
    103     // The checkbox view click handler.
    104     @Override
    105     public void onClick(View v) {
    106         super.onClick();
    107         setChecked(!isChecked());
    108 
    109         // We need to find the CheckBox in the parent view that we are using as a touch target.
    110         // If we don't update it before onClick finishes, the accessibility gives invalid
    111         // responses.
    112         ViewGroup parent = (ViewGroup) v;
    113         View child =  parent.findViewById(com.android.internal.R.id.checkbox);
    114         Checkable checkable = (Checkable) child;
    115         checkable.setChecked(mChecked);
    116         // Causes text color change when activated to differentiate selected elements from
    117         // unselected elements.
    118         mTextView.setActivated(mChecked);
    119     }
    120 
    121     /**
    122      * Return if the view is collapsed.
    123      */
    124     public boolean isCollapsed() {
    125         return mCollapsed;
    126     }
    127 
    128     /**
    129      * Returns the checked state of the preference.
    130      */
    131     public boolean isChecked() {
    132         return mChecked;
    133     }
    134 
    135     /**
    136      * Sets the checked state and notifies listeners of the state change.
    137      */
    138     public void setChecked(boolean checked) {
    139         if (mChecked != checked) {
    140             mChecked = checked;
    141 
    142             callChangeListener(checked);
    143             notifyDependencyChange(shouldDisableDependents());
    144             notifyChanged();
    145         }
    146     }
    147 
    148     @Override
    149     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
    150         super.onInitializeAccessibilityNodeInfo(info);
    151         info.setCheckable(true);
    152         info.setChecked(isChecked());
    153     }
    154 
    155     @Override
    156     protected Parcelable onSaveInstanceState() {
    157         final Parcelable superState = super.onSaveInstanceState();
    158 
    159         final SavedState myState = new SavedState(superState);
    160         myState.checked = isChecked();
    161         myState.collapsed = isCollapsed();
    162         return myState;
    163     }
    164 
    165     @Override
    166     protected void onRestoreInstanceState(Parcelable state) {
    167         // Only restore the state if it is valid and our saved state.
    168         if (state == null || !SavedState.class.equals(state.getClass())) {
    169             super.onRestoreInstanceState(state);
    170             return;
    171         }
    172 
    173         SavedState myState = (SavedState) state;
    174         super.onRestoreInstanceState(myState.getSuperState());
    175         setChecked(myState.checked);
    176         setCollapse(myState.collapsed);
    177     }
    178 
    179     private void setCollapse(boolean isCollapsed) {
    180         if (mCollapsed == isCollapsed) {
    181             return;
    182         }
    183 
    184         mCollapsed = isCollapsed;
    185         setAllPreferencesVisibility(!isCollapsed);
    186         notifyChanged();
    187     }
    188 
    189     private void setAllPreferencesVisibility(boolean visible) {
    190         for (int i = 0; i < getPreferenceCount(); i++) {
    191             Preference p = getPreference(i);
    192             p.setVisible(visible);
    193         }
    194     }
    195 
    196     private static class SavedState extends BaseSavedState {
    197         boolean checked;
    198         boolean collapsed;
    199 
    200         public SavedState(Parcel source) {
    201             super(source);
    202             checked = source.readInt() != 0;
    203             collapsed = source.readInt() != 0;
    204         }
    205 
    206         public SavedState(Parcelable superState) {
    207             super(superState);
    208         }
    209 
    210         @Override
    211         public void writeToParcel(Parcel dest, int flags) {
    212             super.writeToParcel(dest, flags);
    213             dest.writeInt(checked ? 1 : 0);
    214             dest.writeInt(collapsed ? 1 : 0);
    215         }
    216 
    217         public static final Parcelable.Creator<SavedState> CREATOR =
    218                 new Parcelable.Creator<SavedState>() {
    219                     public SavedState createFromParcel(Parcel in) {
    220                         return new SavedState(in);
    221                     }
    222 
    223                     public SavedState[] newArray(int size) {
    224                         return new SavedState[size];
    225                     }
    226                 };
    227     }
    228 
    229     @VisibleForTesting
    230     void switchSpinnerToCheckboxOrDisablePreference(long freeableBytes, int loadingStatus) {
    231         mLoaded = loadingStatus != LoadingStatus.LOADING;
    232         setEnabled(loadingStatus != LoadingStatus.EMPTY);
    233         if (!isEnabled()) {
    234             setChecked(false);
    235         }
    236         if (mProgressBar != null) {
    237             mProgressBar.setVisibility(View.GONE);
    238         }
    239         if (mWidget != null) {
    240             mWidget.setVisibility(View.VISIBLE);
    241         }
    242     }
    243 }
    244