Update ActionButtonsPreference style

- Add rounded background
 - Add divider between buttons
 Screenshots:
   https://screenshot.googleplex.com/5b3fanqgpbpufyD.png
   https://screenshot.googleplex.com/5jG85UeVk64pAMg.png

Bug: 187923403
Test: atest ActionButtonsPreferenceTest
Change-Id: I04bebbfc676c5c5d019a4dce1b8e31bfc69360d7
This commit is contained in:
Wesley.CW Wang
2021-05-12 20:25:55 +08:00
parent 90cff49c35
commit e49dc874d3
7 changed files with 296 additions and 15 deletions

View File

@@ -23,4 +23,15 @@
column="15"/>
</issue>
<issue
id="NewApi"
message="`?android:attr/dialogCornerRadius` requires API level 28 (current min is 21)"
errorLine1=" android:radius=&quot;?android:attr/dialogCornerRadius&quot;"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="frameworks/base/packages/SettingsLib/ActionButtonsPreference/res/drawable/settingslib_rounded_background.xml"
line="23"
column="9"/>
</issue>
</issues>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface" />
<corners
android:radius="?android:attr/dialogCornerRadius"
/>
</shape>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:paddingHorizontal="8dp"
android:orientation="horizontal"
android:background="@drawable/settingslib_rounded_background">
<Button
android:id="@+id/button1"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<View
android:id="@+id/divider1"
android:layout_width="1dp"
android:layout_height="match_parent"
android:paddingHorizontal="4dp"
android:visibility="gone"
android:background="?android:colorBackground" />
<Button
android:id="@+id/button2"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<View
android:id="@+id/divider2"
android:layout_width="1dp"
android:layout_height="match_parent"
android:paddingHorizontal="4dp"
android:visibility="gone"
android:background="?android:colorBackground" />
<Button
android:id="@+id/button3"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<View
android:id="@+id/divider3"
android:layout_width="1dp"
android:layout_height="match_parent"
android:paddingHorizontal="4dp"
android:visibility="gone"
android:background="?android:colorBackground" />
<Button
android:id="@+id/button4"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>

View File

@@ -24,29 +24,29 @@
<Button
android:id="@+id/button1"
style="@style/SettingsActionButton"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button2"
style="@style/SettingsActionButton"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button3"
style="@style/SettingsActionButton"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<Button
android:id="@+id/button4"
style="@style/SettingsActionButton"
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
</LinearLayout>
</LinearLayout>

View File

@@ -16,11 +16,10 @@
-->
<resources>
<style name="SettingsActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
<style name="SettingsLibActionButton" parent="android:Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:drawablePadding">4dp</item>
<item name="android:drawableTint">@*android:color/btn_colored_borderless_text_material</item>
<item name="android:layout_marginEnd">8dp</item>
<item name="android:paddingTop">20dp</item>
<item name="android:paddingBottom">20dp</item>
</style>
</resources>
</resources>

View File

@@ -27,16 +27,20 @@ import android.widget.Button;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.core.os.BuildCompat;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import java.util.ArrayList;
import java.util.List;
/**
* This preference provides a four buttons layout with Settings style.
* It looks like below
*
* --------------------------------------------------
* button1 | button2 | button3 | button4 |
* --------------------------------------------------
* ---------------------------------------
* - button1 | button2 | button3 | button4 -
* ---------------------------------------
*
* User can set title / icon / click listener for each button.
*
@@ -49,10 +53,16 @@ import androidx.preference.PreferenceViewHolder;
public class ActionButtonsPreference extends Preference {
private static final String TAG = "ActionButtonPreference";
private final ButtonInfo mButton1Info = new ButtonInfo();
private final ButtonInfo mButton2Info = new ButtonInfo();
private final ButtonInfo mButton3Info = new ButtonInfo();
private final ButtonInfo mButton4Info = new ButtonInfo();
private final List<ButtonInfo> mVisibleButtonInfos = new ArrayList<>(4);
private View mDivider1;
private View mDivider2;
private View mDivider3;
public ActionButtonsPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
@@ -76,25 +86,49 @@ public class ActionButtonsPreference extends Preference {
}
private void init() {
setLayoutResource(R.layout.settings_action_buttons);
setLayoutResource(R.layout.settingslib_action_buttons);
setSelectable(false);
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
holder.setDividerAllowedAbove(true);
holder.setDividerAllowedBelow(true);
if (!BuildCompat.isAtLeastS()) {
holder.setDividerAllowedAbove(true);
holder.setDividerAllowedBelow(true);
}
mButton1Info.mButton = (Button) holder.findViewById(R.id.button1);
mButton2Info.mButton = (Button) holder.findViewById(R.id.button2);
mButton3Info.mButton = (Button) holder.findViewById(R.id.button3);
mButton4Info.mButton = (Button) holder.findViewById(R.id.button4);
mDivider1 = holder.findViewById(R.id.divider1);
mDivider2 = holder.findViewById(R.id.divider2);
mDivider3 = holder.findViewById(R.id.divider3);
mButton1Info.setUpButton();
mButton2Info.setUpButton();
mButton3Info.setUpButton();
mButton4Info.setUpButton();
// Add visible button into list only
if (mButton1Info.isVisible()) {
mVisibleButtonInfos.add(mButton1Info);
}
if (mButton2Info.isVisible()) {
mVisibleButtonInfos.add(mButton2Info);
}
if (mButton3Info.isVisible()) {
mVisibleButtonInfos.add(mButton3Info);
}
if (mButton4Info.isVisible()) {
mVisibleButtonInfos.add(mButton4Info);
}
setupDivider1();
setupDivider2();
setupDivider3();
}
/**
@@ -357,6 +391,28 @@ public class ActionButtonsPreference extends Preference {
return this;
}
private void setupDivider1() {
// Display divider1 only if button1 and button2 is visible
if (mDivider1 != null && mButton1Info.isVisible() && mButton2Info.isVisible()) {
mDivider1.setVisibility(View.VISIBLE);
}
}
private void setupDivider2() {
// Display divider2 only if button3 is visible and button2 or button3 is visible
if (mDivider2 != null && mButton3Info.isVisible()
&& (mButton1Info.isVisible() || mButton2Info.isVisible())) {
mDivider2.setVisibility(View.VISIBLE);
}
}
private void setupDivider3() {
// Display divider3 only if button4 is visible and 2 visible buttons at least
if (mDivider3 != null && mVisibleButtonInfos.size() > 1 && mButton4Info.isVisible()) {
mDivider3.setVisibility(View.VISIBLE);
}
}
static class ButtonInfo {
private Button mButton;
private CharSequence mText;
@@ -379,6 +435,10 @@ public class ActionButtonsPreference extends Preference {
}
}
boolean isVisible() {
return mButton.getVisibility() == View.VISIBLE;
}
/**
* By default, four buttons are visible.
* However, there are two cases which button should be invisible.

View File

@@ -48,7 +48,7 @@ public class ActionButtonsPreferenceTest {
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mRootView = View.inflate(mContext, R.layout.settings_action_buttons, null /* parent */);
mRootView = View.inflate(mContext, R.layout.settingslib_action_buttons, null /* parent */);
mHolder = PreferenceViewHolder.createInstanceForTests(mRootView);
mPref = new ActionButtonsPreference(mContext);
}
@@ -251,6 +251,114 @@ public class ActionButtonsPreferenceTest {
assertThat(drawablesAroundText[1 /* top */]).isNull();
}
@Test
public void onBindViewHolder_setAllButton_shouldShowAllDivider() {
mPref.setButton1Text(R.string.install_other_apps);
mPref.setButton2Text(R.string.install_other_apps);
mPref.setButton3Text(R.string.install_other_apps);
mPref.setButton4Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.VISIBLE);
}
@Test
public void onBindViewHolder_setAllButtonWithoutButton2_shouldHideDivider1() {
mPref.setButton1Text(R.string.install_other_apps);
mPref.setButton3Text(R.string.install_other_apps);
mPref.setButton4Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.VISIBLE);
}
@Test
public void onBindViewHolder_setAllButtonWithoutButton3_shouldHideDivider2() {
mPref.setButton1Text(R.string.install_other_apps);
mPref.setButton2Text(R.string.install_other_apps);
mPref.setButton4Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.VISIBLE);
}
@Test
public void onBindViewHolder_setButton1And4_shouldShowDivider3Only() {
mPref.setButton1Text(R.string.install_other_apps);
mPref.setButton4Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.VISIBLE);
}
@Test
public void onBindViewHolder_setOneButtonOnly_noDivider() {
mPref.setButton4Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.GONE);
}
@Test
public void onBindViewHolder_setButton1And2_shouldShowDivider1Only() {
mPref.setButton1Text(R.string.install_other_apps);
mPref.setButton2Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.GONE);
}
@Test
public void onBindViewHolder_setButton1And3_shouldShowDivider2Only() {
mPref.setButton1Text(R.string.install_other_apps);
mPref.setButton3Text(R.string.install_other_apps);
mPref.onBindViewHolder(mHolder);
assertThat(mRootView.findViewById(R.id.divider1).getVisibility())
.isEqualTo(View.GONE);
assertThat(mRootView.findViewById(R.id.divider2).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(mRootView.findViewById(R.id.divider3).getVisibility())
.isEqualTo(View.GONE);
}
public static ActionButtonsPreference createMock() {
final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
when(pref.setButton1Text(anyInt())).thenReturn(pref);