// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.chromium.chrome.R;
import java.util.List;
/**
* A RadioButton with a title and descriptive text to the right.
*/
public class RadioButtonWithDescription extends RelativeLayout implements OnClickListener {
private RadioButton mRadioButton;
private TextView mTitle;
private TextView mDescription;
private List<RadioButtonWithDescription> mGroup;
private static final String SUPER_STATE_KEY = "superState";
private static final String CHECKED_KEY = "isChecked";
/**
* Constructor for inflating via XML.
*/
public RadioButtonWithDescription(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.radio_button_with_description, this, true);
mRadioButton = (RadioButton) findViewById(R.id.radio_button);
mTitle = (TextView) findViewById(R.id.title);
mDescription = (TextView) findViewById(R.id.description);
if (attrs != null) applyAttributes(attrs);
// We want RadioButtonWithDescription to handle the clicks itself.
mRadioButton.setClickable(false);
setOnClickListener(this);
}
private void applyAttributes(AttributeSet attrs) {
TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs,
R.styleable.RadioButtonWithDescription, 0, 0);
String titleText = a.getString(R.styleable.RadioButtonWithDescription_titleText);
if (titleText != null) mTitle.setText(titleText);
a.recycle();
}
@Override
public void onClick(View v) {
if (mGroup != null) {
for (RadioButtonWithDescription button : mGroup) {
button.setChecked(false);
}
}
setChecked(true);
}
/**
* Sets the text shown in the title section.
*/
public void setTitleText(CharSequence text) {
mTitle.setText(text);
}
/**
* Sets the text shown in the description section.
*/
public void setDescriptionText(CharSequence text) {
mDescription.setText(text);
}
/**
* Returns true if checked.
*/
public boolean isChecked() {
return mRadioButton.isChecked();
}
/**
* Sets the checked status.
*/
public void setChecked(boolean checked) {
mRadioButton.setChecked(checked);
}
/**
* Sets the group of RadioButtonWithDescriptions that should be unchecked when this button is
* checked.
* @param group A list containing all elements of the group.
*/
public void setRadioButtonGroup(List<RadioButtonWithDescription> group) {
mGroup = group;
}
@Override
protected Parcelable onSaveInstanceState() {
// Since this View is designed to be used multiple times in the same layout and contains
// children with ids, Android gets confused. This is because it will see two Views in the
// hierarchy with the same id (eg, the RadioButton that makes up this View).
//
// eg:
// LinearLayout (no id):
// |-> RadioButtonWithDescription (id=sync_confirm_import_choice)
// | |-> RadioButton (id=radio_button)
// | |-> TextView (id=title)
// | \-> TextView (id=description)
// \-> RadioButtonWithDescription (id=sync_keep_separate_choice)
// |-> RadioButton (id=radio_button)
// |-> TextView (id=title)
// \-> TextView (id=description)
//
// This causes the automagic state saving and recovery to do the wrong thing and restore all
// of these Views to the state of the last one it saved.
// Therefore we manually save the state of the child Views here so the state can be
// associated with the id of the RadioButtonWithDescription, which should be unique and
// not the id of the RadioButtons, which will be duplicated.
//
// Note: We disable Activity recreation on many config changes (such as orientation),
// but not for all of them (eg, locale or font scale change). So this code will only be
// called on the latter ones, or when the Activity is destroyed due to memory constraints.
Bundle saveState = new Bundle();
saveState.putParcelable(SUPER_STATE_KEY, super.onSaveInstanceState());
saveState.putBoolean(CHECKED_KEY, isChecked());
return saveState;
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
if (state instanceof Bundle) {
super.onRestoreInstanceState(((Bundle) state).getParcelable(SUPER_STATE_KEY));
setChecked(((Bundle) state).getBoolean(CHECKED_KEY));
} else {
super.onRestoreInstanceState(state);
}
}
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
// This method and dispatchRestoreInstanceState prevent the Android automagic state save
// and restore from touching this View's children.
dispatchFreezeSelfOnly(container);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
dispatchThawSelfOnly(container);
}
}