blob: c5de8c606d1e8125f5c09f119514f884b71d2a47 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.storagemanager.deletionhelper;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Checkable;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.storagemanager.R;
import com.android.storagemanager.deletionhelper.DeletionType.LoadingStatus;
/**
* CollapsibleCheckboxPreferenceGroup is a preference group that can be expanded or collapsed and
* also has a checkbox.
*/
public class CollapsibleCheckboxPreferenceGroup extends PreferenceGroup implements
View.OnClickListener {
private boolean mCollapsed;
private boolean mChecked;
private TextView mTextView;
private ProgressBar mProgressBar;
private View mWidget;
private boolean mLoaded;
public CollapsibleCheckboxPreferenceGroup(Context context) {
this(context, null);
}
public CollapsibleCheckboxPreferenceGroup(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.deletion_preference);
setWidgetLayoutResource(R.layout.preference_widget_checkbox);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View checkbox = holder.findViewById(com.android.internal.R.id.checkbox);
mTextView = (TextView) holder.findViewById(android.R.id.summary);
// Ensures that the color of the text is consistent with the checkbox having a tick or not
mTextView.setActivated(mChecked);
if (checkbox != null && checkbox instanceof Checkable) {
((Checkable) checkbox).setChecked(mChecked);
// Expand the touch target by making the parent the touch target.
View parent = (View) checkbox.getParent();
parent.setClickable(true);
parent.setFocusable(true);
parent.setOnClickListener(this);
}
mProgressBar = (ProgressBar) holder.findViewById(R.id.progress_bar);
mProgressBar.setVisibility(mLoaded ? View.GONE : View.VISIBLE);
mWidget = holder.findViewById(android.R.id.widget_frame);
mWidget.setVisibility(mLoaded ? View.VISIBLE : View.GONE);
// CollapsibleCheckboxPreferenceGroup considers expansion to be its "longer-term
// (activation) state."
final ImageView imageView = (ImageView) holder.findViewById(android.R.id.icon);
imageView.setActivated(!mCollapsed);
}
@Override
public boolean addPreference(Preference p) {
super.addPreference(p);
p.setVisible(!isCollapsed());
return true;
}
// The preference click handler.
@Override
protected void onClick() {
super.onClick();
setCollapse(!isCollapsed());
}
// The checkbox view click handler.
@Override
public void onClick(View v) {
super.onClick();
setChecked(!isChecked());
// We need to find the CheckBox in the parent view that we are using as a touch target.
// If we don't update it before onClick finishes, the accessibility gives invalid
// responses.
ViewGroup parent = (ViewGroup) v;
View child = parent.findViewById(com.android.internal.R.id.checkbox);
Checkable checkable = (Checkable) child;
checkable.setChecked(mChecked);
// Causes text color change when activated to differentiate selected elements from
// unselected elements.
mTextView.setActivated(mChecked);
}
/**
* Return if the view is collapsed.
*/
public boolean isCollapsed() {
return mCollapsed;
}
/**
* Returns the checked state of the preference.
*/
public boolean isChecked() {
return mChecked;
}
/**
* Sets the checked state and notifies listeners of the state change.
*/
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
callChangeListener(checked);
notifyDependencyChange(shouldDisableDependents());
notifyChanged();
}
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setCheckable(true);
info.setChecked(isChecked());
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
final SavedState myState = new SavedState(superState);
myState.checked = isChecked();
myState.collapsed = isCollapsed();
return myState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
// Only restore the state if it is valid and our saved state.
if (state == null || !SavedState.class.equals(state.getClass())) {
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
setChecked(myState.checked);
setCollapse(myState.collapsed);
}
private void setCollapse(boolean isCollapsed) {
if (mCollapsed == isCollapsed) {
return;
}
mCollapsed = isCollapsed;
setAllPreferencesVisibility(!isCollapsed);
notifyChanged();
}
private void setAllPreferencesVisibility(boolean visible) {
for (int i = 0; i < getPreferenceCount(); i++) {
Preference p = getPreference(i);
p.setVisible(visible);
}
}
private static class SavedState extends BaseSavedState {
boolean checked;
boolean collapsed;
public SavedState(Parcel source) {
super(source);
checked = source.readInt() != 0;
collapsed = source.readInt() != 0;
}
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(checked ? 1 : 0);
dest.writeInt(collapsed ? 1 : 0);
}
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
@VisibleForTesting
void switchSpinnerToCheckboxOrDisablePreference(long freeableBytes, int loadingStatus) {
mLoaded = loadingStatus != LoadingStatus.LOADING;
setEnabled(loadingStatus != LoadingStatus.EMPTY);
if (!isEnabled()) {
setChecked(false);
}
if (mProgressBar != null) {
mProgressBar.setVisibility(View.GONE);
}
if (mWidget != null) {
mWidget.setVisibility(View.VISIBLE);
}
}
}