diff options
8 files changed, 498 insertions, 0 deletions
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 64dc2afafd6a..e6492aadd765 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -53,6 +53,7 @@ java_defaults { "SettingsLibUtils", "SettingsLibEmergencyNumber", "SettingsLibTopIntroPreference", + "SettingsLibBannerMessagePreference", ], } diff --git a/packages/SettingsLib/BannerMessagePreference/Android.bp b/packages/SettingsLib/BannerMessagePreference/Android.bp new file mode 100644 index 000000000000..095975afa13a --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/Android.bp @@ -0,0 +1,13 @@ +android_library { + name: "SettingsLibBannerMessagePreference", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + static_libs: [ + "androidx.preference_preference", + ], + + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml new file mode 100644 index 000000000000..56b886f7efb1 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.widget"> + + <uses-sdk android:minSdkVersion="21"/> + +</manifest> diff --git a/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml b/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml new file mode 100644 index 000000000000..c1f2c9f9f9d0 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/drawable/ic_warning.xml @@ -0,0 +1,26 @@ +<!-- + Copyright (C) 2020 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M1,21L12,2L23,21H1ZM19.53,19L12,5.99L4.47,19H19.53ZM11,16V18H13V16H11ZM11,10H13V14H11V10Z" + android:fillColor="?android:attr/colorError" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml new file mode 100644 index 000000000000..977e19687960 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/layout/banner_message.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 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:paddingStart="24dp" + android:paddingEnd="16dp" + android:paddingTop="24dp" + android:paddingBottom="8dp" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:src="@drawable/ic_warning"/> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="24dp" + android:orientation="vertical"> + + <TextView + android:id="@+id/banner_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/Banner.Text.Title"/> + <TextView + android:id="@+id/banner_summary" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/Banner.Text.Summary"/> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:gravity="end"> + <Button + android:id="@+id/banner_negative_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> + + <Button + android:id="@+id/banner_positive_btn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml new file mode 100644 index 000000000000..df47c642e402 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/res/values/styles.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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. + --> + +<resources> + <style name="Banner.Text.Title" + parent="@android:style/TextAppearance.Material.Subhead"> + <item name="android:textSize">16sp</item> + <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + </style> + + <style name="Banner.Text.Summary" + parent="@*android:style/TextAppearance.DeviceDefault.Body1"> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textSize">14sp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java new file mode 100644 index 000000000000..5352552d7c66 --- /dev/null +++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2020 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.settingslib.widget; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import androidx.annotation.StringRes; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + +/** + * Banner message is a banner displaying important information (permission request, page error etc), + * and provide actions for user to address. It requires a user action to be dismissed. + */ +public class BannerMessagePreference extends Preference { + + private static final String TAG = "BannerPreference"; + private BannerMessagePreference.ButtonInfo mPositiveButtonInfo; + private BannerMessagePreference.ButtonInfo mNegativeButtonInfo; + + public BannerMessagePreference(Context context) { + super(context); + init(); + } + + public BannerMessagePreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public BannerMessagePreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + holder.setDividerAllowedAbove(true); + holder.setDividerAllowedBelow(true); + + mPositiveButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_positive_btn); + mNegativeButtonInfo.mButton = (Button) holder.findViewById(R.id.banner_negative_btn); + + mPositiveButtonInfo.setUpButton(); + mNegativeButtonInfo.setUpButton(); + + final TextView titleView = (TextView) holder.findViewById(R.id.banner_title); + final TextView summaryView = (TextView) holder.findViewById(R.id.banner_summary); + + titleView.setText(getTitle()); + summaryView.setText(getSummary()); + } + + private void init() { + mPositiveButtonInfo = new BannerMessagePreference.ButtonInfo(); + mNegativeButtonInfo = new BannerMessagePreference.ButtonInfo(); + setSelectable(false); + setLayoutResource(R.layout.banner_message); + } + + /** + * Set the visibility state of positive button. + */ + public BannerMessagePreference setPositiveButtonVisible(boolean isVisible) { + if (isVisible != mPositiveButtonInfo.mIsVisible) { + mPositiveButtonInfo.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Set the visibility state of negative button. + */ + public BannerMessagePreference setNegativeButtonVisible(boolean isVisible) { + if (isVisible != mNegativeButtonInfo.mIsVisible) { + mNegativeButtonInfo.mIsVisible = isVisible; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when positive button is clicked. + */ + public BannerMessagePreference setPositiveButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mPositiveButtonInfo.mListener) { + mPositiveButtonInfo.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Register a callback to be invoked when negative button is clicked. + */ + public BannerMessagePreference setNegativeButtonOnClickListener( + View.OnClickListener listener) { + if (listener != mNegativeButtonInfo.mListener) { + mNegativeButtonInfo.mListener = listener; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in positive button. + */ + public BannerMessagePreference setPositiveButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mPositiveButtonInfo.mText)) { + mPositiveButtonInfo.mText = newText; + notifyChanged(); + } + return this; + } + + /** + * Sets the text to be displayed in negative button. + */ + public BannerMessagePreference setNegativeButtonText(@StringRes int textResId) { + final String newText = getContext().getString(textResId); + if (!TextUtils.equals(newText, mNegativeButtonInfo.mText)) { + mNegativeButtonInfo.mText = newText; + notifyChanged(); + } + return this; + } + + static class ButtonInfo { + private Button mButton; + private CharSequence mText; + private View.OnClickListener mListener; + private boolean mIsVisible = true; + + void setUpButton() { + mButton.setText(mText); + mButton.setOnClickListener(mListener); + + if (shouldBeVisible()) { + mButton.setVisibility(View.VISIBLE); + } else { + mButton.setVisibility(View.GONE); + } + } + + /** + * By default, two buttons are visible. + * If user didn't set a text for a button, then it should not be shown. + */ + private boolean shouldBeVisible() { + return mIsVisible && (!TextUtils.isEmpty(mText)); + } + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java new file mode 100644 index 000000000000..8ab02983bfc2 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BannerMessagePreferenceTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2020 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.settingslib.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import androidx.preference.PreferenceViewHolder; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class BannerMessagePreferenceTest { + + private Context mContext; + private View mRootView; + private BannerMessagePreference mBannerPreference; + private PreferenceViewHolder mHolder; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mRootView = View.inflate(mContext, R.layout.banner_message, null /* parent */); + mHolder = PreferenceViewHolder.createInstanceForTests(mRootView); + mBannerPreference = new BannerMessagePreference(mContext); + } + + @Test + public void onBindViewHolder_shouldSetTitle() { + mBannerPreference.setTitle("test"); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((TextView) mRootView.findViewById(R.id.banner_title)).getText()) + .isEqualTo("test"); + } + + @Test + public void onBindViewHolder_shouldSetSummary() { + mBannerPreference.setSummary("test"); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((TextView) mRootView.findViewById(R.id.banner_summary)).getText()) + .isEqualTo("test"); + } + + @Test + public void setPositiveButtonText_shouldShowPositiveButton() { + mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setNegativeButtonText_shouldShowNegativeButton() { + mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void withoutSetPositiveButtonText_shouldHidePositiveButton() { + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void withoutSetNegativeButtonText_shouldHideNegativeButton() { + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void setPositiveButtonVisible_withTrue_shouldShowPositiveButton() { + mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + + mBannerPreference.setPositiveButtonVisible(true); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setPositiveButtonVisible_withFalse_shouldHidePositiveButton() { + mBannerPreference.setPositiveButtonText(R.string.tts_settings_title); + + mBannerPreference.setPositiveButtonVisible(false); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_positive_btn)).getVisibility()) + .isEqualTo(View.GONE); + } + + @Test + public void setNegativeButtonVisible_withTrue_shouldShowNegativeButton() { + mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + + mBannerPreference.setNegativeButtonVisible(true); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.VISIBLE); + } + + @Test + public void setNegativeButtonVisible_withFalse_shouldHideNegativeButton() { + mBannerPreference.setNegativeButtonText(R.string.tts_settings_title); + + mBannerPreference.setNegativeButtonVisible(false); + mBannerPreference.onBindViewHolder(mHolder); + + assertThat(((Button) mRootView.findViewById(R.id.banner_negative_btn)).getVisibility()) + .isEqualTo(View.GONE); + } +} |