diff --git a/res/layout/preference_widget_master_checkbox.xml b/res/layout/preference_widget_master_checkbox.xml new file mode 100644 index 00000000000..e61839d57d7 --- /dev/null +++ b/res/layout/preference_widget_master_checkbox.xml @@ -0,0 +1,26 @@ + + + + diff --git a/src/com/android/settings/widget/MasterCheckBoxPreference.java b/src/com/android/settings/widget/MasterCheckBoxPreference.java new file mode 100644 index 00000000000..333c9aa799a --- /dev/null +++ b/src/com/android/settings/widget/MasterCheckBoxPreference.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 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.settings.widget; + +import android.content.Context; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.CheckBox; + +import com.android.settings.R; +import com.android.settingslib.TwoTargetPreference; + +/** + * A custom preference that provides inline checkbox. It has a mandatory field for title, and + * optional fields for icon and sub-text. + */ +public class MasterCheckBoxPreference extends TwoTargetPreference { + + private CheckBox mCheckBox; + private boolean mChecked; + + public MasterCheckBoxPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public MasterCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public MasterCheckBoxPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MasterCheckBoxPreference(Context context) { + super(context); + } + + @Override + protected int getSecondTargetResId() { + return R.layout.preference_widget_master_checkbox; + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + final View widgetView = holder.findViewById(android.R.id.widget_frame); + if (widgetView != null) { + widgetView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mCheckBox != null && !mCheckBox.isEnabled()) { + return; + } + setChecked(!mChecked); + if (!callChangeListener(mChecked)) { + setChecked(!mChecked); + } else { + persistBoolean(mChecked); + } + } + }); + } + + mCheckBox = (CheckBox) holder.findViewById(R.id.checkboxWidget); + if (mCheckBox != null) { + mCheckBox.setContentDescription(getTitle()); + mCheckBox.setChecked(mChecked); + } + } + + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + if (mCheckBox != null) { + mCheckBox.setEnabled(enabled); + } + } + + public boolean isChecked() { + return mCheckBox != null && mChecked; + } + + public void setChecked(boolean checked) { + mChecked = checked; + if (mCheckBox != null) { + mCheckBox.setChecked(checked); + } + } + + public CheckBox getCheckBox() { + return mCheckBox; + } +} diff --git a/tests/robotests/src/com/android/settings/widget/MasterCheckBoxPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/MasterCheckBoxPreferenceTest.java new file mode 100644 index 00000000000..e6530d5eb53 --- /dev/null +++ b/tests/robotests/src/com/android/settings/widget/MasterCheckBoxPreferenceTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2018 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.settings.widget; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; +import android.support.v7.preference.PreferenceViewHolder; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.CheckBox; + +import com.android.settings.R; +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class MasterCheckBoxPreferenceTest { + + private Context mContext; + private MasterCheckBoxPreference mPreference; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mPreference = new MasterCheckBoxPreference(mContext); + } + + @Test + public void createNewPreference_shouldSetLayout() { + assertThat(mPreference.getWidgetLayoutResource()) + .isEqualTo(R.layout.preference_widget_master_checkbox); + } + + @Test + public void setChecked_shouldUpdateCheckBoxCheckedState() { + final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate( + R.layout.preference_widget_master_checkbox, null)); + final CheckBox checkBox = (CheckBox) holder.findViewById(R.id.checkboxWidget); + mPreference.onBindViewHolder(holder); + + mPreference.setChecked(true); + assertThat(checkBox.isChecked()).isTrue(); + + mPreference.setChecked(false); + assertThat(checkBox.isChecked()).isFalse(); + } + + @Test + public void setEnabled_shouldUpdateCheckBoxEnabledState() { + final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate( + R.layout.preference_widget_master_checkbox, null)); + final CheckBox checkBox = (CheckBox) holder.findViewById(R.id.checkboxWidget); + mPreference.onBindViewHolder(holder); + + mPreference.setEnabled(true); + assertThat(checkBox.isEnabled()).isTrue(); + + mPreference.setEnabled(false); + assertThat(checkBox.isEnabled()).isFalse(); + } + + @Test + public void clickWidgetView_shouldToggleCheckBox() { + final LayoutInflater inflater = LayoutInflater.from(mContext); + final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( + inflater.inflate(R.layout.preference_two_target, null)); + final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame); + inflater.inflate(R.layout.preference_widget_master_checkbox, widgetView, true); + final CheckBox checkBox = (CheckBox) holder.findViewById(R.id.checkboxWidget); + mPreference.onBindViewHolder(holder); + + widgetView.performClick(); + assertThat(checkBox.isChecked()).isTrue(); + + widgetView.performClick(); + assertThat(checkBox.isChecked()).isFalse(); + } + + @Test + public void clickWidgetView_shouldNotToggleCheckBoxIfDisabled() { + final LayoutInflater inflater = LayoutInflater.from(mContext); + final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( + inflater.inflate(R.layout.preference_two_target, null)); + final LinearLayout widgetView = holder.itemView.findViewById(android.R.id.widget_frame); + inflater.inflate(R.layout.preference_widget_master_checkbox, widgetView, true); + final CheckBox checkBox = (CheckBox) holder.findViewById(R.id.checkboxWidget); + mPreference.onBindViewHolder(holder); + mPreference.setEnabled(false); + + widgetView.performClick(); + assertThat(checkBox.isChecked()).isFalse(); + } + + @Test + public void clickWidgetView_shouldNotifyPreferenceChanged() { + final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext).inflate(R.layout.preference_two_target, null)); + final View widgetView = holder.findViewById(android.R.id.widget_frame); + final OnPreferenceChangeListener listener = mock(OnPreferenceChangeListener.class); + mPreference.setOnPreferenceChangeListener(listener); + mPreference.onBindViewHolder(holder); + + mPreference.setChecked(false); + widgetView.performClick(); + verify(listener).onPreferenceChange(mPreference, true); + + mPreference.setChecked(true); + widgetView.performClick(); + verify(listener).onPreferenceChange(mPreference, false); + } + + @Test + public void onBindViewHolder_checkBoxShouldHaveContentDescription() { + final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests( + LayoutInflater.from(mContext) + .inflate(R.layout.preference_widget_master_checkbox, null)); + final CheckBox checkBox = (CheckBox) holder.findViewById(R.id.checkboxWidget); + final String label = "TestButton"; + mPreference.setTitle(label); + + mPreference.onBindViewHolder(holder); + + assertThat(checkBox.getContentDescription()).isEqualTo(label); + } +}