From 60d9b34047ecf956284dc207d8abfa29f7608b37 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Tue, 28 Jul 2020 11:00:32 -0700 Subject: [PATCH 1/2] For mozilla-mobile/fenix#8765: Add site item widget --- components/ui/widgets/build.gradle | 7 ++ .../ui/widgets/WidgetSiteItemView.kt | 78 +++++++++++++++++++ .../res/layout/mozac_widget_site_item.xml | 74 ++++++++++++++++++ .../ui/widgets/src/main/res/values/attrs.xml | 2 + .../ui/widgets/src/main/res/values/dimens.xml | 6 ++ .../ui/widgets/src/main/res/values/styles.xml | 13 ++++ 6 files changed, 180 insertions(+) create mode 100644 components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt create mode 100644 components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml diff --git a/components/ui/widgets/build.gradle b/components/ui/widgets/build.gradle index 34f2db0a066..59ed2970589 100644 --- a/components/ui/widgets/build.gradle +++ b/components/ui/widgets/build.gradle @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' android { compileSdkVersion config.compileSdkVersion @@ -10,6 +11,8 @@ android { defaultConfig { minSdkVersion config.minSdkVersion targetSdkVersion config.targetSdkVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -21,10 +24,14 @@ android { } dependencies { + implementation Dependencies.kotlin_stdlib + implementation project(':ui-colors') implementation project(':ui-icons') + implementation Dependencies.androidx_appcompat implementation Dependencies.androidx_constraintlayout + implementation Dependencies.androidx_core_ktx implementation Dependencies.google_material } diff --git a/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt b/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt new file mode 100644 index 00000000000..a4d26a77275 --- /dev/null +++ b/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt @@ -0,0 +1,78 @@ +package mozilla.components.ui.widgets + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.view.isVisible + +/** + * Shared UI widget for showing a website in a list of websites, + * such as in bookmarks or history. + */ +class WidgetSiteItemView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : ConstraintLayout(context, attrs, defStyleAttr) { + + private val labelView: TextView by lazy { findViewById(R.id.label) } + private val captionView: TextView by lazy { findViewById(R.id.caption) } + private val iconWrapper: FrameLayout by lazy { findViewById(R.id.favicon_wrapper) } + private val secondaryButton: ImageButton by lazy { findViewById(R.id.secondary_button) } + + /** + * ImageView that should display favicons. + */ + val iconView: ImageView by lazy { findViewById(R.id.favicon) } + + init { + LayoutInflater.from(context).inflate(R.layout.mozac_widget_site_item, this, true) + } + + /** + * Sets the text displayed inside of the site item view. + * @param label Main label text, such as a site title. + * @param caption Sub caption text, such as a URL. If null, the caption is hidden. + */ + fun setText(label: CharSequence, caption: CharSequence?) { + labelView.text = label + captionView.text = caption + captionView.isVisible = caption != null + } + + /** + * Add a view that will overlay the favicon, such as a checkmark. + */ + fun addIconOverlay(overlay: View) { + iconWrapper.addView(overlay) + } + + /** + * Add a secondary button, such as an overflow menu. + * @param icon Drawable to display in the button. + */ + fun setSecondaryButton( + icon: Drawable, + contentDescription: CharSequence, + onClickListener: (View) -> Unit + ) { + secondaryButton.isVisible = true + secondaryButton.setImageDrawable(icon) + secondaryButton.contentDescription = contentDescription + secondaryButton.setOnClickListener(onClickListener) + } + + /** + * Removes the secondary button if it was previously set in [setSecondaryButton]. + */ + fun removeSecondaryButton() { + secondaryButton.isVisible = false + } +} diff --git a/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml b/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml new file mode 100644 index 00000000000..9d426c739dc --- /dev/null +++ b/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + diff --git a/components/ui/widgets/src/main/res/values/attrs.xml b/components/ui/widgets/src/main/res/values/attrs.xml index c29f3355b0c..a9df5e294b3 100644 --- a/components/ui/widgets/src/main/res/values/attrs.xml +++ b/components/ui/widgets/src/main/res/values/attrs.xml @@ -9,4 +9,6 @@ + + diff --git a/components/ui/widgets/src/main/res/values/dimens.xml b/components/ui/widgets/src/main/res/values/dimens.xml index 89058ea6dc8..9f6d4587fd1 100644 --- a/components/ui/widgets/src/main/res/values/dimens.xml +++ b/components/ui/widgets/src/main/res/values/dimens.xml @@ -5,4 +5,10 @@ 40dp 8dp + + 56dp + 16sp + 12sp + 32dp + 4dp diff --git a/components/ui/widgets/src/main/res/values/styles.xml b/components/ui/widgets/src/main/res/values/styles.xml index 0b833009bc3..5164ef7ae49 100644 --- a/components/ui/widgets/src/main/res/values/styles.xml +++ b/components/ui/widgets/src/main/res/values/styles.xml @@ -41,4 +41,17 @@ @dimen/mozac_widget_favicon_padding @drawable/mozac_widget_favicon_background + + + From 6fc391406681e0c0083411dde547ec7c5b228e63 Mon Sep 17 00:00:00 2001 From: Tiger Oakes Date: Tue, 28 Jul 2020 17:32:30 -0700 Subject: [PATCH 2/2] Add tests --- components/ui/widgets/build.gradle | 6 ++ .../ui/widgets/WidgetSiteItemView.kt | 32 ++++++- .../res/layout/mozac_widget_site_item.xml | 6 +- .../ui/widgets/src/main/res/values/attrs.xml | 5 + .../ui/widgets/src/main/res/values/styles.xml | 6 ++ .../ui/widgets/WidgetSiteItemViewTest.kt | 92 +++++++++++++++++++ .../src/test/resources/robolectric.properties | 1 + docs/changelog.md | 6 ++ 8 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 components/ui/widgets/src/test/java/mozilla/components/ui/widgets/WidgetSiteItemViewTest.kt create mode 100644 components/ui/widgets/src/test/resources/robolectric.properties diff --git a/components/ui/widgets/build.gradle b/components/ui/widgets/build.gradle index 59ed2970589..0ea2c155628 100644 --- a/components/ui/widgets/build.gradle +++ b/components/ui/widgets/build.gradle @@ -33,6 +33,12 @@ dependencies { implementation Dependencies.androidx_constraintlayout implementation Dependencies.androidx_core_ktx implementation Dependencies.google_material + + testImplementation project(":support-test") + + testImplementation Dependencies.androidx_test_junit + testImplementation Dependencies.testing_robolectric + testImplementation Dependencies.testing_mockito } apply from: '../../../publish.gradle' diff --git a/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt b/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt index a4d26a77275..0de67e57a89 100644 --- a/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt +++ b/components/ui/widgets/src/main/java/mozilla/components/ui/widgets/WidgetSiteItemView.kt @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + package mozilla.components.ui.widgets import android.content.Context @@ -9,12 +13,15 @@ import android.widget.FrameLayout import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView +import androidx.annotation.DrawableRes +import androidx.annotation.StringRes +import androidx.appcompat.content.res.AppCompatResources.getDrawable import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible /** * Shared UI widget for showing a website in a list of websites, - * such as in bookmarks or history. + * such as in bookmarks, history, site exceptions, or collections. */ class WidgetSiteItemView @JvmOverloads constructor( context: Context, @@ -38,6 +45,7 @@ class WidgetSiteItemView @JvmOverloads constructor( /** * Sets the text displayed inside of the site item view. + * * @param label Main label text, such as a site title. * @param caption Sub caption text, such as a URL. If null, the caption is hidden. */ @@ -56,10 +64,13 @@ class WidgetSiteItemView @JvmOverloads constructor( /** * Add a secondary button, such as an overflow menu. + * * @param icon Drawable to display in the button. + * @param contentDescription Accessible description of the button's purpose. + * @param onClickListener Listener called when the button is clicked. */ fun setSecondaryButton( - icon: Drawable, + icon: Drawable?, contentDescription: CharSequence, onClickListener: (View) -> Unit ) { @@ -69,6 +80,23 @@ class WidgetSiteItemView @JvmOverloads constructor( secondaryButton.setOnClickListener(onClickListener) } + /** + * Add a secondary button, such as an overflow menu. + * + * @param icon Drawable to display in the button. + * @param contentDescription Accessible description of the button's purpose. + * @param onClickListener Listener called when the button is clicked. + */ + fun setSecondaryButton( + @DrawableRes icon: Int, + @StringRes contentDescription: Int, + onClickListener: (View) -> Unit + ) = setSecondaryButton( + icon = getDrawable(context, icon), + contentDescription = context.getString(contentDescription), + onClickListener = onClickListener + ) + /** * Removes the secondary button if it was previously set in [setSecondaryButton]. */ diff --git a/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml b/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml index 9d426c739dc..ec63f5c7372 100644 --- a/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml +++ b/components/ui/widgets/src/main/res/layout/mozac_widget_site_item.xml @@ -15,7 +15,8 @@ android:id="@+id/favicon_wrapper" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginHorizontal="16dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" app:layout_constraintHorizontal_chainStyle="spread_inside" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" @@ -58,7 +59,8 @@ android:layout_width="@dimen/mozac_widget_site_item_secondary_button_size" android:layout_height="@dimen/mozac_widget_site_item_secondary_button_size" android:padding="@dimen/mozac_widget_site_item_secondary_button_padding" - android:layout_marginHorizontal="12dp" + android:layout_marginStart="12dp" + android:layout_marginEnd="12dp" android:background="?android:attr/selectableItemBackgroundBorderless" android:visibility="gone" tools:visibility="visible" diff --git a/components/ui/widgets/src/main/res/values/attrs.xml b/components/ui/widgets/src/main/res/values/attrs.xml index a9df5e294b3..60a3b3a35ff 100644 --- a/components/ui/widgets/src/main/res/values/attrs.xml +++ b/components/ui/widgets/src/main/res/values/attrs.xml @@ -7,8 +7,13 @@ + + + + + diff --git a/components/ui/widgets/src/main/res/values/styles.xml b/components/ui/widgets/src/main/res/values/styles.xml index 5164ef7ae49..515b98e12e7 100644 --- a/components/ui/widgets/src/main/res/values/styles.xml +++ b/components/ui/widgets/src/main/res/values/styles.xml @@ -4,6 +4,12 @@ - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +