diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
commit | da4c7e7ed675c3bf405668739c3012d140856109 (patch) | |
tree | cdd868dba063fecba609a1d819de271f0d51b23e /mobile/android/android-components/components/browser/menu2 | |
parent | Adding upstream version 125.0.3. (diff) | |
download | firefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip |
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/components/browser/menu2')
181 files changed, 5531 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/browser/menu2/README.md b/mobile/android/android-components/components/browser/menu2/README.md new file mode 100644 index 0000000000..dc1b129b65 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/README.md @@ -0,0 +1,51 @@ +# [Android Components](../../../README.md) > Browser > Menu 2 + +A generic menu with customizable items primarily for browser toolbars. + +This replaces the [browser-menu](../menu) component with a new API using immutable objects, +designed to work well with [lib-state](../../lib/state). + +## Usage + +### Setting up the dependency + +Use Gradle to download the library from [maven.mozilla.org](https://maven.mozilla.org/) ([Setup repository](../../../README.md#maven-repository)): + +```Groovy +implementation "org.mozilla.components:browser-menu2:{latest-version}" +``` + +### MenuController +The menu controller is used to control the items in the menu as well as displaying the menu popup. + +Sample code can be found in [Sample Toolbar app](https://github.com/mozilla-mobile/android-components/tree/main/samples/toolbar). + +There are multiple properties that you customize of the browser menu by just adding them into your dimens.xml file. + +```xml +<resources xmlns:tools="http://schemas.android.com/tools"> + + <!--Change how rounded the corners of the menu should be--> + <dimen name="mozac_browser_menu2_corner_radius" tools:ignore="UnusedResources">4dp</dimen> + + <!--Change how much shadow the menu should have--> + <dimen name="mozac_browser_menu2_elevation" tools:ignore="UnusedResources">4dp</dimen> + + <!--Change the width of the menu - can also be set in MenuController#show()--> + <dimen name="mozac_browser_menu2_width" tools:ignore="UnusedResources">250dp</dimen> + + <!--Change the top and bottom padding of the menu--> + <dimen name="mozac_browser_menu2_padding_vertical" tools:ignore="UnusedResources">8dp</dimen> + +</resources> +``` + +Options displayed in the menu are configured by using a list of `MenuCandidate` objects. +The list of options can be sent to the menu by calling `MenuController#submitList()`. +To change the displayed options, simply call `submitList` again with a new list. + +## License + + 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/ diff --git a/mobile/android/android-components/components/browser/menu2/build.gradle b/mobile/android/android-components/components/browser/menu2/build.gradle new file mode 100644 index 0000000000..ccb4e0bdd8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/build.gradle @@ -0,0 +1,50 @@ +/* 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/. */ + +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + defaultConfig { + minSdkVersion config.minSdkVersion + compileSdk config.compileSdkVersion + targetSdkVersion config.targetSdkVersion + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + namespace 'mozilla.components.browser.menu2' +} + +dependencies { + implementation project(':concept-menu') + implementation project(':support-base') + implementation project(':support-ktx') + implementation project(':ui-icons') + + implementation ComponentsDependencies.androidx_appcompat + implementation ComponentsDependencies.androidx_core_ktx + implementation ComponentsDependencies.androidx_recyclerview + implementation ComponentsDependencies.androidx_cardview + implementation ComponentsDependencies.androidx_constraintlayout + implementation ComponentsDependencies.androidx_coordinatorlayout + + implementation ComponentsDependencies.kotlin_coroutines + + testImplementation project(':support-test') + + testImplementation ComponentsDependencies.androidx_test_core + testImplementation ComponentsDependencies.androidx_test_junit + testImplementation ComponentsDependencies.testing_robolectric + testImplementation ComponentsDependencies.testing_coroutines +} + +apply from: '../../../android-lint.gradle' +apply from: '../../../publish.gradle' +ext.configurePublish(config.componentsGroupId, archivesBaseName, project.ext.description) diff --git a/mobile/android/android-components/components/browser/menu2/lint.xml b/mobile/android/android-components/components/browser/menu2/lint.xml new file mode 100644 index 0000000000..81bcc3bfb8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/lint.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<lint> + <issue id="Overdraw" severity="ignore" /> +</lint>
\ No newline at end of file diff --git a/mobile/android/android-components/components/browser/menu2/proguard-rules.pro b/mobile/android/android-components/components/browser/menu2/proguard-rules.pro new file mode 100644 index 0000000000..f1b424510d --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/mobile/android/android-components/components/browser/menu2/src/main/AndroidManifest.xml b/mobile/android/android-components/components/browser/menu2/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..1eccdee26a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/AndroidManifest.xml @@ -0,0 +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/. --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <application android:supportsRtl="true" /> +</manifest> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/BrowserMenuController.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/BrowserMenuController.kt new file mode 100644 index 0000000000..eaf1998661 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/BrowserMenuController.kt @@ -0,0 +1,182 @@ +/* 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.browser.menu2 + +import android.view.Gravity +import android.view.View +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.PopupWindow +import androidx.annotation.VisibleForTesting +import androidx.core.widget.PopupWindowCompat +import mozilla.components.browser.menu2.ext.MenuPositioningData +import mozilla.components.browser.menu2.ext.inferMenuPositioningData +import mozilla.components.browser.menu2.view.MenuView +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.MenuStyle +import mozilla.components.concept.menu.Orientation +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.MenuCandidate +import mozilla.components.concept.menu.candidate.NestedMenuCandidate +import mozilla.components.concept.menu.ext.findNestedMenuCandidate +import mozilla.components.support.base.observer.Observable +import mozilla.components.support.base.observer.ObserverRegistry + +/** + * Controls a popup menu composed of MenuCandidate objects. + * @param visibleSide Sets the menu to open with either the start or end visible. + * @param style Custom styling for this menu controller. + */ +class BrowserMenuController( + private val visibleSide: Side = Side.START, + private val style: MenuStyle? = null, +) : MenuController, Observable<MenuController.Observer> by ObserverRegistry() { + + private var currentPopupInfo: PopupMenuInfo? = null + private var menuCandidates: List<MenuCandidate> = emptyList() + + private val menuDismissListener = PopupWindow.OnDismissListener { + currentPopupInfo = null + notifyObservers { onDismiss() } + } + + override fun show( + anchor: View, + orientation: Orientation?, + autoDismiss: Boolean, + ): PopupWindow { + // If the menu is already displayed do not display it again. + currentPopupInfo?.window?.let { + return it + } + + val view = MenuView(anchor.context).apply { + // Show nested list if present, or the standard menu candidates list. + submitList(menuCandidates) + setVisibleSide(visibleSide) + style?.let { setStyle(it) } + } + + if (autoDismiss) { + // Monitor for changes to the parent layout and dismiss pop up if displayed, else the menu + // could be displayed in the wrong position. For example, if the menu is displayed and the + // device orientation changes from portrait to landscape and vice versa. + anchor.rootView.addOnLayoutChangeListener { _, _, _, right, bottom, _, _, oldRight, oldBottom -> + if (bottom != oldBottom || right != oldRight) { + dismiss() + } + } + } + + return MenuPopupWindow(view).apply { + view.onDismiss = ::dismiss + view.onReopenMenu = ::reopenMenu + setOnDismissListener(menuDismissListener) + inferMenuPositioningData( + containerView = view, + anchor = anchor, + style = style, + orientation = orientation, + )?.let { + displayPopup(it) + } + }.also { + currentPopupInfo = PopupMenuInfo( + window = it, + anchor = anchor, + orientation = orientation, + nested = null, + ) + } + } + + /** + * Re-opens the menu and displays the given nested list. + * No-op if the menu is not yet open. + */ + private fun reopenMenu(nested: NestedMenuCandidate?) { + val info = currentPopupInfo ?: return + info.window.run { + // Dismiss silently + setOnDismissListener(null) + dismiss() + setOnDismissListener(menuDismissListener) + + // Quickly remove the current list + view.submitList(null) + // Display the new nested list + view.submitList(nested?.subMenuItems ?: menuCandidates) + // Attempt tp reopen the menu + inferMenuPositioningData( + containerView = view, + anchor = info.anchor, + style = style, + orientation = info.orientation, + )?.let { + displayPopup(it) + } + } + currentPopupInfo = info.copy(nested = nested) + } + + /** + * Dismiss the menu popup if the menu is visible. + */ + override fun dismiss() { + currentPopupInfo?.window?.dismiss() + } + + /** + * Changes the contents of the menu. + */ + override fun submitList(list: List<MenuCandidate>) { + menuCandidates = list + val info = currentPopupInfo + + // If menu is already open, update the displayed items + if (info != null) { + // If a nested menu is open, it should be displayed + val displayedItems = if (info.nested != null) { + list.findNestedMenuCandidate(info.nested.id)?.subMenuItems + } else { + list + } + + // If the new menu is null, close & reopen the popup on the main list + if (displayedItems == null) { + // close & reopen popup + reopenMenu(nested = null) + } else { + info.window.view.submitList(displayedItems) + } + } + + notifyObservers { onMenuListSubmit(list) } + } + + private class MenuPopupWindow( + val view: MenuView, + ) : PopupWindow(view, WRAP_CONTENT, WRAP_CONTENT, true) + + private data class PopupMenuInfo( + val window: MenuPopupWindow, + val anchor: View, + val orientation: Orientation?, + val nested: NestedMenuCandidate? = null, + ) +} + +/** + * Show a [PopupWindow] given the positioning data. + */ +@VisibleForTesting +internal fun PopupWindow.displayPopup(positioningData: MenuPositioningData) { + inputMethodMode = PopupWindow.INPUT_METHOD_NOT_NEEDED + + animationStyle = positioningData.animation + height = positioningData.containerHeight + + PopupWindowCompat.setOverlapAnchor(this, true) + showAtLocation(positioningData.anchor, Gravity.NO_GRAVITY, positioningData.x, positioningData.y) +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/CompoundMenuCandidateViewHolders.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/CompoundMenuCandidateViewHolders.kt new file mode 100644 index 0000000000..eb592656c2 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/CompoundMenuCandidateViewHolders.kt @@ -0,0 +1,81 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import android.widget.CompoundButton +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.adapter.icons.MenuIconAdapter +import mozilla.components.browser.menu2.ext.applyBackgroundEffect +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.CompoundMenuCandidate +import mozilla.components.concept.menu.candidate.CompoundMenuCandidate.ButtonType + +internal abstract class CompoundMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, + private val dismiss: () -> Unit, +) : MenuCandidateViewHolder<CompoundMenuCandidate>(itemView, inflater), CompoundButton.OnCheckedChangeListener { + + private val layout = itemView as ConstraintLayout + private val compoundButton: CompoundButton = itemView.findViewById(R.id.label) + private val startIcon = MenuIconAdapter(layout, inflater, Side.START, dismiss) + private var onCheckedChangeListener: ((Boolean) -> Unit)? = null + + override fun bind(newCandidate: CompoundMenuCandidate, oldCandidate: CompoundMenuCandidate?) { + super.bind(newCandidate, oldCandidate) + onCheckedChangeListener = newCandidate.onCheckedChange + compoundButton.text = newCandidate.text + startIcon.bind(newCandidate.start, oldCandidate?.start) + compoundButton.applyStyle(newCandidate.textStyle, oldCandidate?.textStyle) + itemView.applyBackgroundEffect(newCandidate.effect, oldCandidate?.effect) + + // isChecked calls the listener automatically + compoundButton.setOnCheckedChangeListener(null) + compoundButton.isChecked = newCandidate.isChecked + compoundButton.setOnCheckedChangeListener(this) + } + + override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { + onCheckedChangeListener?.invoke(isChecked) + dismiss() + } + + companion object { + @LayoutRes + fun getLayoutResource(candidate: CompoundMenuCandidate) = when (candidate.end) { + ButtonType.CHECKBOX -> CompoundCheckboxMenuCandidateViewHolder.layoutResource + ButtonType.SWITCH -> CompoundSwitchMenuCandidateViewHolder.layoutResource + } + } +} + +internal class CompoundCheckboxMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, + dismiss: () -> Unit, +) : CompoundMenuCandidateViewHolder(itemView, inflater, dismiss) { + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_compound_checkbox + } +} + +internal class CompoundSwitchMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, + dismiss: () -> Unit, +) : CompoundMenuCandidateViewHolder(itemView, inflater, dismiss) { + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_compound_switch + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/DecorativeTextMenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/DecorativeTextMenuCandidateViewHolder.kt new file mode 100644 index 0000000000..84ea1b89d6 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/DecorativeTextMenuCandidateViewHolder.kt @@ -0,0 +1,47 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import androidx.annotation.LayoutRes +import androidx.core.view.updateLayoutParams +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate + +internal class DecorativeTextMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, +) : MenuCandidateViewHolder<DecorativeTextMenuCandidate>(itemView, inflater) { + + private val textView: TextView get() = itemView as TextView + + override fun bind( + newCandidate: DecorativeTextMenuCandidate, + oldCandidate: DecorativeTextMenuCandidate?, + ) { + super.bind(newCandidate, oldCandidate) + + textView.text = newCandidate.text + textView.applyStyle(newCandidate.textStyle, oldCandidate?.textStyle) + applyHeight(newCandidate.height, oldCandidate?.height) + } + + private fun applyHeight(newHeight: Int?, oldHeight: Int?) { + if (newHeight != oldHeight) { + textView.updateLayoutParams { + height = newHeight ?: itemView.resources + .getDimensionPixelSize(R.dimen.mozac_browser_menu2_candidate_container_layout_height) + } + } + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_decorative_text + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/DividerMenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/DividerMenuCandidateViewHolder.kt new file mode 100644 index 0000000000..aad9ecffe7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/DividerMenuCandidateViewHolder.kt @@ -0,0 +1,25 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import androidx.annotation.LayoutRes +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.candidate.DividerMenuCandidate + +/** + * View holder that displays a divider. + */ +internal class DividerMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, +) : MenuCandidateViewHolder<DividerMenuCandidate>(itemView, inflater) { + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_divider + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/LastItemViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/LastItemViewHolder.kt new file mode 100644 index 0000000000..3ea96d2418 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/LastItemViewHolder.kt @@ -0,0 +1,33 @@ +/* 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.browser.menu2.adapter + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +/** + * View holders that extend this base class are passed both the new value and last value + * when [bind] is called. Use this information to diff the changes between the two values. + */ +internal abstract class LastItemViewHolder<T>( + itemView: View, +) : RecyclerView.ViewHolder(itemView) { + + protected var lastCandidate: T? = null + + /** + * Updates the held view to reflect changes in the menu option. + * + * @param newCandidate New value to use. + * @param oldCandidate Previously set value. + * If this is the first time [bind] was called, null is passed. + */ + protected abstract fun bind(newCandidate: T, oldCandidate: T?) + + fun bind(option: T) { + bind(option, lastCandidate) + lastCandidate = option + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapter.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapter.kt new file mode 100644 index 0000000000..bc39865177 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapter.kt @@ -0,0 +1,80 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import mozilla.components.concept.menu.candidate.CompoundMenuCandidate +import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate +import mozilla.components.concept.menu.candidate.DividerMenuCandidate +import mozilla.components.concept.menu.candidate.MenuCandidate +import mozilla.components.concept.menu.candidate.NestedMenuCandidate +import mozilla.components.concept.menu.candidate.RowMenuCandidate +import mozilla.components.concept.menu.candidate.TextMenuCandidate + +internal class MenuCandidateListAdapter( + private val inflater: LayoutInflater, + private val dismiss: () -> Unit, + private val reopenMenu: (NestedMenuCandidate) -> Unit, +) : ListAdapter<MenuCandidate, MenuCandidateViewHolder<out MenuCandidate>>(MenuCandidateDiffer) { + + @LayoutRes + override fun getItemViewType(position: Int) = when (val item = getItem(position)) { + is TextMenuCandidate -> TextMenuCandidateViewHolder.layoutResource + is DecorativeTextMenuCandidate -> DecorativeTextMenuCandidateViewHolder.layoutResource + is CompoundMenuCandidate -> CompoundMenuCandidateViewHolder.getLayoutResource(item) + is NestedMenuCandidate -> NestedMenuCandidateViewHolder.layoutResource + is RowMenuCandidate -> RowMenuCandidateViewHolder.layoutResource + is DividerMenuCandidate -> DividerMenuCandidateViewHolder.layoutResource + } + + override fun onCreateViewHolder( + parent: ViewGroup, + @LayoutRes viewType: Int, + ): MenuCandidateViewHolder<out MenuCandidate> { + val view = inflater.inflate(viewType, parent, false) + return when (viewType) { + TextMenuCandidateViewHolder.layoutResource -> + TextMenuCandidateViewHolder(view, inflater, dismiss) + DecorativeTextMenuCandidateViewHolder.layoutResource -> + DecorativeTextMenuCandidateViewHolder(view, inflater) + CompoundCheckboxMenuCandidateViewHolder.layoutResource -> + CompoundCheckboxMenuCandidateViewHolder(view, inflater, dismiss) + CompoundSwitchMenuCandidateViewHolder.layoutResource -> + CompoundSwitchMenuCandidateViewHolder(view, inflater, dismiss) + NestedMenuCandidateViewHolder.layoutResource -> + NestedMenuCandidateViewHolder(view, inflater, dismiss, reopenMenu) + RowMenuCandidateViewHolder.layoutResource -> + RowMenuCandidateViewHolder(view, inflater, dismiss) + DividerMenuCandidateViewHolder.layoutResource -> + DividerMenuCandidateViewHolder(view, inflater) + else -> throw IllegalArgumentException("Invalid viewType $viewType") + } + } + + override fun onBindViewHolder(holder: MenuCandidateViewHolder<out MenuCandidate>, position: Int) { + val item = getItem(position) + when (holder) { + is TextMenuCandidateViewHolder -> holder.bind(item as TextMenuCandidate) + is DecorativeTextMenuCandidateViewHolder -> holder.bind(item as DecorativeTextMenuCandidate) + is CompoundMenuCandidateViewHolder -> holder.bind(item as CompoundMenuCandidate) + is NestedMenuCandidateViewHolder -> holder.bind(item as NestedMenuCandidate) + is RowMenuCandidateViewHolder -> holder.bind(item as RowMenuCandidate) + is DividerMenuCandidateViewHolder -> holder.bind(item as DividerMenuCandidate) + } + } +} + +private object MenuCandidateDiffer : DiffUtil.ItemCallback<MenuCandidate>() { + override fun areItemsTheSame(oldItem: MenuCandidate, newItem: MenuCandidate) = + oldItem::class == newItem::class + + @Suppress("DiffUtilEquals") + override fun areContentsTheSame(oldItem: MenuCandidate, newItem: MenuCandidate) = + oldItem == newItem +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateViewHolder.kt new file mode 100644 index 0000000000..98212b9b36 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateViewHolder.kt @@ -0,0 +1,22 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import androidx.annotation.CallSuper +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.candidate.MenuCandidate + +internal abstract class MenuCandidateViewHolder<T : MenuCandidate>( + itemView: View, + protected val inflater: LayoutInflater, +) : LastItemViewHolder<T>(itemView) { + + @CallSuper + override fun bind(newCandidate: T, oldCandidate: T?) { + itemView.applyStyle(newCandidate.containerStyle, oldCandidate?.containerStyle) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/NestedMenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/NestedMenuCandidateViewHolder.kt new file mode 100644 index 0000000000..c899f5e17f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/NestedMenuCandidateViewHolder.kt @@ -0,0 +1,53 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.adapter.icons.MenuIconAdapter +import mozilla.components.browser.menu2.ext.applyBackgroundEffect +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.NestedMenuCandidate + +internal class NestedMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, + dismiss: () -> Unit, + private val reopenMenu: (NestedMenuCandidate) -> Unit, +) : MenuCandidateViewHolder<NestedMenuCandidate>(itemView, inflater), View.OnClickListener { + + private val layout = itemView as ConstraintLayout + private val textView: TextView get() = itemView.findViewById(R.id.label) + private val startIcon = MenuIconAdapter(layout, inflater, Side.START, dismiss) + private val endIcon = MenuIconAdapter(layout, inflater, Side.END, dismiss) + + init { + itemView.setOnClickListener(this) + } + + override fun bind(newCandidate: NestedMenuCandidate, oldCandidate: NestedMenuCandidate?) { + super.bind(newCandidate, oldCandidate) + + textView.text = newCandidate.text + textView.applyStyle(newCandidate.textStyle, oldCandidate?.textStyle) + itemView.applyBackgroundEffect(newCandidate.effect, oldCandidate?.effect) + startIcon.bind(newCandidate.start, oldCandidate?.start) + endIcon.bind(newCandidate.end, oldCandidate?.end) + } + + override fun onClick(v: View?) { + lastCandidate?.let { reopenMenu(it) } + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_nested + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/RowMenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/RowMenuCandidateViewHolder.kt new file mode 100644 index 0000000000..a2fb8d0b0e --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/RowMenuCandidateViewHolder.kt @@ -0,0 +1,55 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import androidx.annotation.LayoutRes +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.candidate.RowMenuCandidate + +/** + * Displays a row of small menu options. + */ +internal class RowMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, + private val dismiss: () -> Unit, +) : MenuCandidateViewHolder<RowMenuCandidate>(itemView, inflater) { + + private val layout = itemView as LinearLayout + private var buttonViewHolders = emptyList<SmallMenuCandidateViewHolder>() + + override fun bind(newCandidate: RowMenuCandidate, oldCandidate: RowMenuCandidate?) { + super.bind(newCandidate, oldCandidate) + + // If the number of children in the row changes, + // build new holders for each of them. + if (newCandidate.items.size != oldCandidate?.items?.size) { + layout.removeAllViews() + // Create new view holders list + buttonViewHolders = newCandidate.items.map { + val button = inflater.inflate( + SmallMenuCandidateViewHolder.layoutResource, + layout, + false, + ) + layout.addView(button) + SmallMenuCandidateViewHolder(button, dismiss) + } + } + + // Use the button view holders to compare individual menu items in the row. + buttonViewHolders.zip(newCandidate.items).forEach { (viewHolder, item) -> + viewHolder.bind(item) + } + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_row + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/SmallMenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/SmallMenuCandidateViewHolder.kt new file mode 100644 index 0000000000..fb77f1b325 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/SmallMenuCandidateViewHolder.kt @@ -0,0 +1,60 @@ +/* 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.browser.menu2.adapter + +import android.view.View +import androidx.annotation.LayoutRes +import androidx.appcompat.widget.AppCompatImageButton +import androidx.appcompat.widget.TooltipCompat +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.ext.applyIcon +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.candidate.SmallMenuCandidate + +internal class SmallMenuCandidateViewHolder( + itemView: View, + private val dismiss: () -> Unit, +) : LastItemViewHolder<SmallMenuCandidate>(itemView), + View.OnClickListener, + View.OnLongClickListener { + + private val iconView = itemView as AppCompatImageButton + private var onClickListener: (() -> Unit)? = null + private var onLongClickListener: (() -> Boolean)? = null + + init { + iconView.setOnClickListener(this) + iconView.setOnLongClickListener(this) + iconView.isLongClickable = false + } + + override fun bind(newCandidate: SmallMenuCandidate, oldCandidate: SmallMenuCandidate?) { + if (newCandidate.contentDescription != oldCandidate?.contentDescription) { + iconView.contentDescription = newCandidate.contentDescription + TooltipCompat.setTooltipText(iconView, newCandidate.contentDescription) + } + onClickListener = newCandidate.onClick + onLongClickListener = newCandidate.onLongClick + iconView.isLongClickable = newCandidate.onLongClick != null + iconView.applyIcon(newCandidate.icon, oldCandidate?.icon) + iconView.applyStyle(newCandidate.containerStyle, oldCandidate?.containerStyle) + } + + override fun onClick(v: View?) { + onClickListener?.invoke() + dismiss() + } + + override fun onLongClick(v: View?): Boolean { + val result = onLongClickListener?.invoke() ?: false + dismiss() + return result + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_row_small_icon + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/TextMenuCandidateViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/TextMenuCandidateViewHolder.kt new file mode 100644 index 0000000000..f9fcf1839d --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/TextMenuCandidateViewHolder.kt @@ -0,0 +1,55 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.adapter.icons.MenuIconAdapter +import mozilla.components.browser.menu2.ext.applyBackgroundEffect +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.TextMenuCandidate + +internal class TextMenuCandidateViewHolder( + itemView: View, + inflater: LayoutInflater, + private val dismiss: () -> Unit, +) : MenuCandidateViewHolder<TextMenuCandidate>(itemView, inflater), View.OnClickListener { + + private val layout = itemView as ConstraintLayout + private val textView: TextView get() = itemView.findViewById(R.id.label) + private val startIcon = MenuIconAdapter(layout, inflater, Side.START, dismiss) + private val endIcon = MenuIconAdapter(layout, inflater, Side.END, dismiss) + private var onClickListener: (() -> Unit)? = null + + init { + itemView.setOnClickListener(this) + } + + override fun bind(newCandidate: TextMenuCandidate, oldCandidate: TextMenuCandidate?) { + super.bind(newCandidate, oldCandidate) + + textView.text = newCandidate.text + textView.applyStyle(newCandidate.textStyle, oldCandidate?.textStyle) + onClickListener = newCandidate.onClick + itemView.applyBackgroundEffect(newCandidate.effect, oldCandidate?.effect) + startIcon.bind(newCandidate.start, oldCandidate?.start) + endIcon.bind(newCandidate.end, oldCandidate?.end) + } + + override fun onClick(v: View?) { + onClickListener?.invoke() + dismiss() + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_candidate_text + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/DrawableMenuIconViewHolders.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/DrawableMenuIconViewHolders.kt new file mode 100644 index 0000000000..4ffc031a20 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/DrawableMenuIconViewHolders.kt @@ -0,0 +1,224 @@ +/* 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.browser.menu2.adapter.icons + +import android.content.res.ColorStateList +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.view.View +import android.widget.ImageButton +import android.widget.ImageView +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.core.view.isVisible +import kotlinx.coroutines.Job +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.ext.applyIcon +import mozilla.components.browser.menu2.ext.applyNotificationEffect +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.AsyncDrawableMenuIcon +import mozilla.components.concept.menu.candidate.DrawableButtonMenuIcon +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.LowPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.MenuIcon +import mozilla.components.support.base.log.logger.Logger + +internal abstract class MenuIconWithDrawableViewHolder<T : MenuIcon>( + parent: ConstraintLayout, + inflater: LayoutInflater, +) : MenuIconViewHolder<T>(parent, inflater) { + + protected abstract val imageView: ImageView + + protected fun setup(imageView: View, side: Side) { + updateConstraints { + connect(R.id.icon, TOP, PARENT_ID, TOP) + connect(R.id.icon, BOTTOM, PARENT_ID, BOTTOM) + val margin = parent.resources + .getDimensionPixelSize(R.dimen.mozac_browser_menu2_icon_padding_start) + when (side) { + Side.START -> { + connect(imageView.id, START, PARENT_ID, START) + connect(imageView.id, END, R.id.label, START, margin) + connect(R.id.label, START, imageView.id, END) + } + Side.END -> { + connect(imageView.id, END, PARENT_ID, END) + connect(imageView.id, START, R.id.label, END, margin) + connect(R.id.label, END, imageView.id, START) + } + } + } + } + + override fun disconnect() { + parent.removeView(imageView) + super.disconnect() + } +} + +internal class DrawableMenuIconViewHolder( + parent: ConstraintLayout, + inflater: LayoutInflater, + side: Side, +) : MenuIconWithDrawableViewHolder<DrawableMenuIcon>(parent, inflater) { + + override val imageView: ImageView = inflate(layoutResource).findViewById(R.id.icon) + private var effectView: ImageView? = null + + init { + setup(imageView, side) + } + + override fun bind(newIcon: DrawableMenuIcon, oldIcon: DrawableMenuIcon?) { + imageView.applyIcon(newIcon, oldIcon) + + // Only inflate the effect container if needed + if (newIcon.effect != null) { + createEffectView().applyNotificationEffect(newIcon.effect as LowPriorityHighlightEffect, oldIcon?.effect) + } else { + effectView?.isVisible = false + } + } + + private fun createEffectView(): ImageView { + if (effectView == null) { + val effect: ImageView = inflate(notificationDotLayoutResource).findViewById(R.id.notification_dot) + updateConstraints { + connect(effect.id, TOP, imageView.id, TOP) + connect(effect.id, END, imageView.id, END) + } + effectView = effect + } + return effectView!! + } + + override fun disconnect() { + effectView?.let { parent.removeView(it) } + super.disconnect() + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_icon_drawable + val notificationDotLayoutResource = R.layout.mozac_browser_menu2_icon_notification_dot + } +} + +internal class DrawableButtonMenuIconViewHolder( + parent: ConstraintLayout, + inflater: LayoutInflater, + side: Side, + private val dismiss: () -> Unit, +) : MenuIconWithDrawableViewHolder<DrawableButtonMenuIcon>(parent, inflater), View.OnClickListener { + + override val imageView: ImageButton = inflate(layoutResource).findViewById(R.id.icon) + private var onClickListener: (() -> Unit)? = null + + init { + setup(imageView, side) + imageView.setOnClickListener(this) + } + + override fun bind(newIcon: DrawableButtonMenuIcon, oldIcon: DrawableButtonMenuIcon?) { + imageView.applyIcon(newIcon, oldIcon) + onClickListener = newIcon.onClick + } + + override fun onClick(v: View?) { + onClickListener?.invoke() + dismiss() + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_icon_button + } +} + +internal class AsyncDrawableMenuIconViewHolder( + parent: ConstraintLayout, + inflater: LayoutInflater, + side: Side, + private val logger: Logger = Logger("mozac-menu2-AsyncDrawableMenuIconViewHolder"), +) : MenuIconWithDrawableViewHolder<AsyncDrawableMenuIcon>(parent, inflater) { + + private val scope = MainScope() + override val imageView: ImageView = inflate(layoutResource).findViewById(R.id.icon) + private var effectView: ImageView? = null + private var iconJob: Job? = null + + init { + setup(imageView, side) + } + + override fun bind(newIcon: AsyncDrawableMenuIcon, oldIcon: AsyncDrawableMenuIcon?) { + if (newIcon.loadDrawable != oldIcon?.loadDrawable) { + imageView.setImageDrawable(newIcon.loadingDrawable) + iconJob?.cancel() + iconJob = scope.launch { loadIcon(newIcon.loadDrawable, newIcon.fallbackDrawable) } + } + + if (newIcon.tint != oldIcon?.tint) { + imageView.imageTintList = newIcon.tint?.let { ColorStateList.valueOf(it) } + } + + // Only inflate the effect container if needed + if (newIcon.effect != null) { + createEffectView().applyNotificationEffect(newIcon.effect as LowPriorityHighlightEffect, oldIcon?.effect) + } else { + effectView?.isVisible = false + } + } + + @Suppress("TooGenericExceptionCaught") + private suspend fun loadIcon( + loadDrawable: suspend (width: Int, height: Int) -> Drawable?, + fallback: Drawable?, + ) { + val drawable = try { + loadDrawable(imageView.measuredWidth, imageView.measuredHeight) + } catch (throwable: Throwable) { + logger.error( + message = "Failed to load browser action icon, falling back to default.", + throwable = throwable, + ) + fallback + } + imageView.setImageDrawable(drawable) + } + + private fun createEffectView(): ImageView { + if (effectView == null) { + val effect: ImageView = inflate(notificationDotLayoutResource).findViewById(R.id.notification_dot) + updateConstraints { + connect(effect.id, TOP, imageView.id, TOP) + connect(effect.id, END, imageView.id, END) + } + effectView = effect + } + return effectView!! + } + + override fun disconnect() { + effectView?.let { parent.removeView(it) } + scope.cancel() + super.disconnect() + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_icon_drawable + val notificationDotLayoutResource = R.layout.mozac_browser_menu2_icon_notification_dot + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/MenuIconAdapter.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/MenuIconAdapter.kt new file mode 100644 index 0000000000..737796c682 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/MenuIconAdapter.kt @@ -0,0 +1,51 @@ +/* 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.browser.menu2.adapter.icons + +import android.view.LayoutInflater +import androidx.annotation.VisibleForTesting +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.AsyncDrawableMenuIcon +import mozilla.components.concept.menu.candidate.DrawableButtonMenuIcon +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.MenuIcon +import mozilla.components.concept.menu.candidate.TextMenuIcon + +/** + * Helper class to manage a [ConstraintLayout] that can contain menu icon views. + * Different holder classes are used to swap out the child views. + */ +internal class MenuIconAdapter( + private val parent: ConstraintLayout, + private val inflater: LayoutInflater, + private val side: Side, + private val dismiss: () -> Unit, +) { + + private var viewHolder: MenuIconViewHolder<out MenuIcon>? = null + + fun bind(newIcon: MenuIcon?, oldIcon: MenuIcon?) { + if (newIcon == null && oldIcon != null) { + viewHolder?.disconnect() + viewHolder = null + } else if (newIcon != null) { + if (oldIcon == null || newIcon::class != oldIcon::class) { + viewHolder?.disconnect() + viewHolder = createViewHolder(newIcon) + } + + viewHolder?.bindAndCast(newIcon, oldIcon) + } + } + + @VisibleForTesting + internal fun createViewHolder(item: MenuIcon): MenuIconViewHolder<*> = when (item) { + is DrawableMenuIcon -> DrawableMenuIconViewHolder(parent, inflater, side) + is DrawableButtonMenuIcon -> DrawableButtonMenuIconViewHolder(parent, inflater, side, dismiss) + is AsyncDrawableMenuIcon -> AsyncDrawableMenuIconViewHolder(parent, inflater, side) + is TextMenuIcon -> TextMenuIconViewHolder(parent, inflater, side) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/MenuIconViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/MenuIconViewHolder.kt new file mode 100644 index 0000000000..8af37ce947 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/MenuIconViewHolder.kt @@ -0,0 +1,71 @@ +/* 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.browser.menu2.adapter.icons + +import android.view.LayoutInflater +import android.view.View +import androidx.annotation.CallSuper +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.candidate.MenuIcon + +/** + * View holder with a [bind] method that passes the previously bound value. + */ +internal abstract class MenuIconViewHolder<T : MenuIcon>( + protected val parent: ConstraintLayout, + protected val inflater: LayoutInflater, +) { + + @Suppress("Unchecked_Cast") + fun bindAndCast(newIcon: MenuIcon, oldIcon: MenuIcon?) { + bind(newIcon as T, oldIcon as T?) + } + + /** + * Updates the held view to reflect changes in the menu option icon. + * + * @param newIcon New values to use. + * @param oldIcon Previously set values. + */ + protected abstract fun bind(newIcon: T, oldIcon: T?) + + /** + * Inflates the layout resource and adds it to the parent layout. + */ + protected fun inflate(@LayoutRes layoutResource: Int): View { + val view = inflater.inflate(layoutResource, parent, false) + parent.addView(view) + return view + } + + /** + * Changes the constraints applied to [parent]. + */ + protected inline fun updateConstraints(update: ConstraintSet.() -> Unit) { + ConstraintSet().apply { + clone(parent) + update() + applyTo(parent) + } + } + + /** + * Resets the layout and removes any child views. + * Called when the view holder is removed. + */ + @CallSuper + open fun disconnect() { + updateConstraints { + connect(R.id.label, START, PARENT_ID, START) + connect(R.id.label, END, PARENT_ID, END) + } + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/TextMenuIconViewHolder.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/TextMenuIconViewHolder.kt new file mode 100644 index 0000000000..9f1612ef9b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/icons/TextMenuIconViewHolder.kt @@ -0,0 +1,62 @@ +/* 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.browser.menu2.adapter.icons + +import android.view.LayoutInflater +import android.widget.TextView +import androidx.annotation.LayoutRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.ext.applyStyle +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.TextMenuIcon + +internal class TextMenuIconViewHolder( + parent: ConstraintLayout, + inflater: LayoutInflater, + side: Side, +) : MenuIconViewHolder<TextMenuIcon>(parent, inflater) { + + private val textView: TextView = inflate(layoutResource).findViewById(R.id.icon) + + init { + updateConstraints { + connect(textView.id, TOP, PARENT_ID, TOP) + connect(textView.id, BOTTOM, PARENT_ID, BOTTOM) + when (side) { + Side.START -> { + connect(textView.id, START, PARENT_ID, START) + connect(textView.id, END, R.id.label, START) + connect(R.id.label, START, textView.id, END) + } + Side.END -> { + connect(textView.id, END, PARENT_ID, END) + connect(textView.id, START, R.id.label, END) + connect(R.id.label, END, textView.id, START) + } + } + } + } + + override fun bind(newIcon: TextMenuIcon, oldIcon: TextMenuIcon?) { + textView.text = newIcon.text + textView.applyStyle(newIcon.textStyle, oldIcon?.textStyle) + } + + override fun disconnect() { + parent.removeView(textView) + super.disconnect() + } + + companion object { + @LayoutRes + val layoutResource = R.layout.mozac_browser_menu2_icon_text + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/ext/BrowserMenuPositioning.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/ext/BrowserMenuPositioning.kt new file mode 100644 index 0000000000..61fb98aaca --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/ext/BrowserMenuPositioning.kt @@ -0,0 +1,261 @@ +/* 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.browser.menu2.ext + +import android.graphics.Rect +import android.view.View +import androidx.annotation.Px +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.MenuStyle +import mozilla.components.concept.menu.Orientation +import mozilla.components.support.ktx.android.view.isRTL +import kotlin.math.roundToInt + +const val HALF_MENU_ITEM = 0.5 + +@Suppress("ComplexMethod") +internal fun inferMenuPositioningData( + containerView: View, + anchor: View, + style: MenuStyle? = null, + orientation: Orientation?, +): MenuPositioningData? { + // Measure the menu allowing it to expand entirely. + val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + containerView.measure(spec, spec) + + val recyclerView = containerView.findViewById<RecyclerView>(R.id.mozac_browser_menu_recyclerView) + val recyclerViewAdapter = recyclerView.adapter ?: run { + // We might want to track how often and in what circumstances the menu gets called without + // valid parameters once we have a system for that. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1814816 + return null + } + + val hasViewInitializedCorrectly = containerView.measuredHeight > 0 && containerView.measuredWidth > 0 && + recyclerView.measuredHeight > 0 && recyclerViewAdapter.itemCount > 0 + if (!hasViewInitializedCorrectly) { + // Same as above: https://bugzilla.mozilla.org/show_bug.cgi?id=1814816 + return null + } + + val menuHorizontalPadding = containerView.measuredWidth - recyclerView.measuredWidth + val menuVerticalPadding = containerView.measuredHeight - recyclerView.measuredHeight + var horizontalOffset = style?.horizontalOffset ?: 0 + var verticalOffset = style?.verticalOffset ?: 0 + + // Elevation creates some padding between the menu and its container, so that the start corner + // of the menu doesn't match the corner of the anchor view. If the user wants the menu to hide + // the anchor completely, we have to adjust the position of the menu to compensate for the inner + // padding of the menu container. + if (style?.completelyOverlap == true) { + horizontalOffset -= menuHorizontalPadding / 2 + verticalOffset -= menuVerticalPadding / 2 + } + + // The menu height might be adjusted: if there is not enough space to show all the items, + // it will crop the last visible item in half, to give the user a hint that it is scrollable. + val containerViewHeight = calculateContainerHeight( + recyclerView.measuredHeight, + recyclerViewAdapter.itemCount, + containerView.measuredHeight, + style?.verticalOffset ?: 0, + anchor, + ) + + val (availableHeightToTop, availableHeightToBottom) = getMaxAvailableHeightToTopAndBottom(anchor) + val (availableWidthToLeft, availableWidthToRight) = getMaxAvailableWidthToLeftAndRight(anchor) + + val fitsUp = availableHeightToTop + anchor.height >= containerViewHeight + val fitsDown = availableHeightToBottom + anchor.height >= containerViewHeight + val fitsRight = availableWidthToRight + anchor.width >= containerView.measuredWidth + val fitsLeft = availableWidthToLeft + anchor.width >= containerView.measuredWidth + + val notEnoughHorizontalSpace = !fitsRight && !fitsLeft + val fitsBothHorizontalDirections = fitsRight && fitsLeft + val drawingLeft = if (notEnoughHorizontalSpace || fitsBothHorizontalDirections) { + anchor.isRTL + } else { + !fitsRight + } + + val anchorPosition = IntArray(2) + anchor.getLocationInWindow(anchorPosition) + var (anchorX, anchorY) = anchorPosition + + // Position the menu above the anchor if the orientation is UP and there is enough space. + if (orientation == Orientation.UP && fitsUp) { + anchorY -= containerViewHeight - anchor.height + verticalOffset = -verticalOffset + } + + if (drawingLeft) { + anchorX -= containerView.measuredWidth - anchor.width + horizontalOffset = -horizontalOffset + } + + return MenuPositioningData( + anchor = anchor, + x = anchorX + horizontalOffset, + y = anchorY + verticalOffset, + containerHeight = containerViewHeight, + animation = getAnimation(fitsUp, fitsDown, drawingLeft, orientation), + ) +} + +private fun getMaxAvailableHeightToTopAndBottom(anchor: View): Pair<Int, Int> { + val anchorPosition = IntArray(2) + val displayFrame = Rect() + + val appView = anchor.rootView + appView.getWindowVisibleDisplayFrame(displayFrame) + + anchor.getLocationOnScreen(anchorPosition) + + val bottomEdge = displayFrame.bottom + + val distanceToBottom = bottomEdge - (anchorPosition[1] + anchor.height) + val distanceToTop = anchorPosition[1] - displayFrame.top + + return distanceToTop to distanceToBottom +} + +private fun getMaxAvailableWidthToLeftAndRight(anchor: View): Pair<Int, Int> { + val anchorPosition = IntArray(2) + val displayFrame = Rect() + + val appView = anchor.rootView + appView.getWindowVisibleDisplayFrame(displayFrame) + + anchor.getLocationOnScreen(anchorPosition) + + val distanceToLeft = anchorPosition[0] - displayFrame.left + val distanceToRight = displayFrame.right - (anchorPosition[0] + anchor.width) + + return distanceToLeft to distanceToRight +} + +/** + * Determine whether the container view can display all menu items (without scrolling) within + * the available height. + * + * @return The original container height if the container view can display all menu items + * (without scrolling), else calculate the maximum available container height for a scrollable + * view with a half menu item. + */ +private fun calculateContainerHeight( + recyclerViewHeight: Int, + recyclerViewItemCount: Int, + containerViewHeight: Int, + menuStylePadding: Int, + anchor: View, +): Int { + // Get the total screen display height. + val totalHeight = anchor.rootView.measuredHeight + + // Note: We cannot use getWindowVisibleDisplayFrame() as the height is dynamic based on whether + // the keyboard is open. + // Get any displayed system bars e.g. top status bar, bottom navigation bar or soft buttons bar. + val systemBars = + ViewCompat.getRootWindowInsets(anchor)?.getInsets(WindowInsetsCompat.Type.systemBars()) + // Store the vertical status bars. + val topSystemBarHeight = systemBars?.top ?: 0 + val bottomSystemBarHeight = systemBars?.bottom ?: 0 + + // Deduct any status bar heights from the total height. + val availableHeight = totalHeight - (bottomSystemBarHeight + topSystemBarHeight) + + val menuItemHeight = recyclerViewHeight / recyclerViewItemCount + + // We must take the menu container padding into account as this will be applied to the final height. + val containerPadding = containerViewHeight - recyclerViewHeight + + val maxAvailableHeightForRecyclerView = availableHeight - containerPadding - menuStylePadding + + // The number of menu items that can fit exactly (no cropping) within the max app height. + // Round the number of items to the closet Int value to ensure the max space available is utilized. + // E.g if 6.9 items fit, round to 7 so the calculation below will show 6.5 items instead of 5.5 . + val numberOfItemsFitExactly = + (maxAvailableHeightForRecyclerView.toFloat() / menuItemHeight.toFloat()).roundToInt() + + val itemsAlreadyFitContainerHeight = recyclerViewItemCount <= numberOfItemsFitExactly + + return if (itemsAlreadyFitContainerHeight) { + containerViewHeight + } else { + getCroppedMenuContainerHeight(numberOfItemsFitExactly, menuItemHeight, containerPadding) + } +} + +private fun getAnimation( + fitsUp: Boolean, + fitsDown: Boolean, + drawingLeft: Boolean, + orientation: Orientation?, +): Int { + val isUpOrientation = orientation == Orientation.UP + return when { + isUpOrientation && fitsUp -> if (drawingLeft) { + R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightBottom + } else { + R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftBottom + } + fitsDown -> if (drawingLeft) { + R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightTop + } else { + R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop + } + else -> if (drawingLeft) { + R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRight + } else { + R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeft + } + } +} + +private fun getCroppedMenuContainerHeight( + numberOfItemsFitExactly: Int, + menuItemHeight: Int, + containerPadding: Int, +): Int { + // The number of menu items that fit exactly, minus a half menu item (indicates more menu items exist). + val numberOfItemsFitWithOverflow = numberOfItemsFitExactly - HALF_MENU_ITEM + val updatedRecyclerViewHeight = (numberOfItemsFitWithOverflow * menuItemHeight).toInt() + + return updatedRecyclerViewHeight + containerPadding +} + +/** + * Data needed for menu positioning. + */ +data class MenuPositioningData( + /** + * Android View that the PopupWindow should be anchored to. + */ + val anchor: View, + + /** + * [WindowManager#LayoutParams#x] of params the menu will be added with. + */ + @Px val x: Int = 0, + + /** + * [WindowManager#LayoutParams#y] of params the menu will be added with. + */ + @Px val y: Int = 0, + + /** + * [View#measuredHeight] of the menu. + */ + @Px val containerHeight: Int = 0, + + /** + * [PopupWindow#animationStyle] of the menu. + */ + val animation: Int, +) diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/ext/View.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/ext/View.kt new file mode 100644 index 0000000000..b40e893955 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/ext/View.kt @@ -0,0 +1,95 @@ +/* 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.browser.menu2.ext + +import android.content.res.ColorStateList +import android.os.Build +import android.os.Build.VERSION.SDK_INT +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import mozilla.components.concept.menu.candidate.ContainerStyle +import mozilla.components.concept.menu.candidate.HighPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.LowPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.MenuCandidateEffect +import mozilla.components.concept.menu.candidate.MenuEffect +import mozilla.components.concept.menu.candidate.MenuIconWithDrawable +import mozilla.components.concept.menu.candidate.TextStyle +import mozilla.components.support.ktx.android.content.res.resolveAttribute + +/** + * Apply container styles if different from the previous styling. + */ +internal fun View.applyStyle(newStyle: ContainerStyle, oldStyle: ContainerStyle?) { + if (newStyle != oldStyle) { + isVisible = newStyle.isVisible + isEnabled = newStyle.isEnabled + } +} + +/** + * Apply text styles if different from the previous styling. + */ +internal fun TextView.applyStyle(newStyle: TextStyle, oldStyle: TextStyle?) { + if (newStyle != oldStyle) { + newStyle.size?.let { textSize = it } + newStyle.color?.let { setTextColor(it) } + setTypeface(typeface, newStyle.textStyle) + textAlignment = newStyle.textAlignment + } +} + +/** + * Set the image to display based on the [MenuIconWithDrawable]. + */ +internal fun ImageView.applyIcon(newIcon: MenuIconWithDrawable, oldIcon: MenuIconWithDrawable?) { + if (newIcon != oldIcon) { + setImageDrawable(newIcon.drawable) + imageTintList = newIcon.tint?.let { ColorStateList.valueOf(it) } + } +} + +internal fun ImageView.applyNotificationEffect( + newEffect: LowPriorityHighlightEffect?, + oldEffect: MenuEffect?, +) { + if (newEffect != oldEffect) { + isVisible = newEffect != null + imageTintList = newEffect?.notificationTint?.let { ColorStateList.valueOf(it) } + } +} + +/** + * Build a drawable to be used for the background of a menu option. + */ +internal fun View.applyBackgroundEffect( + newEffect: MenuCandidateEffect?, + oldEffect: MenuCandidateEffect?, +) { + if (newEffect == oldEffect) return + + val highlight = newEffect as? HighPriorityHighlightEffect + val selectableBackgroundRes = context.theme + .resolveAttribute(android.R.attr.selectableItemBackground) + + if (highlight != null) { + val selectableBackground = ContextCompat.getDrawable( + context, + selectableBackgroundRes, + ) + + setBackgroundColor(highlight.backgroundTint) + if (SDK_INT >= Build.VERSION_CODES.M) { + foreground = selectableBackground + } + } else { + setBackgroundResource(selectableBackgroundRes) + if (SDK_INT >= Build.VERSION_CODES.M) { + foreground = null + } + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/view/MenuButton2.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/view/MenuButton2.kt new file mode 100644 index 0000000000..eeba85dede --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/view/MenuButton2.kt @@ -0,0 +1,118 @@ +/* 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.browser.menu2.view + +import android.content.Context +import android.content.res.ColorStateList +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout +import android.widget.ImageView +import androidx.annotation.ColorInt +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.MenuButton +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.candidate.HighPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.LowPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.MenuCandidate +import mozilla.components.concept.menu.candidate.MenuEffect +import mozilla.components.concept.menu.ext.effects +import mozilla.components.concept.menu.ext.max +import mozilla.components.support.base.observer.Observable +import mozilla.components.support.base.observer.ObserverRegistry +import mozilla.components.support.ktx.android.view.hideKeyboard + +/** + * A `three-dot` button used for expanding menus. + * + * If you are using a browser toolbar, do not use this class directly. + */ +class MenuButton2 @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : FrameLayout(context, attrs, defStyleAttr), + MenuButton, + View.OnClickListener, + Observable<MenuButton.Observer> by ObserverRegistry() { + + /** + * Sets a [MenuController] that will be used to create a menu when this button is clicked. + */ + override var menuController: MenuController? = null + set(value) { + // Clean up old controller + field?.dismiss() + field?.unregister(menuControllerObserver) + + // Attach new controller + field = value + value?.register(menuControllerObserver, this) + } + + private val menuControllerObserver = object : MenuController.Observer { + /** + * Change the menu button appearance when the menu list changes. + */ + override fun onMenuListSubmit(list: List<MenuCandidate>) { + val effect = list.effects().max() + + // If a highlighted item is found, show the indicator + setEffect(effect) + } + + override fun onDismiss() = notifyObservers { onDismiss() } + } + + private val menuIcon: ImageView + private val highlightView: ImageView + private val notificationIconView: ImageView + + init { + View.inflate(context, R.layout.mozac_browser_menu2_button, this) + setOnClickListener(this) + menuIcon = findViewById(R.id.icon) + highlightView = findViewById(R.id.highlight) + notificationIconView = findViewById(R.id.notification_dot) + } + + /** + * Shows the menu. + */ + override fun onClick(v: View) { + this.hideKeyboard() + val menuController = menuController ?: return + + menuController.show(anchor = this) + notifyObservers { onShow() } + } + + /** + * Show the indicator for a browser menu effect. + */ + override fun setEffect(effect: MenuEffect?) { + when (effect) { + is HighPriorityHighlightEffect -> { + highlightView.imageTintList = ColorStateList.valueOf(effect.backgroundTint) + highlightView.visibility = View.VISIBLE + notificationIconView.visibility = View.GONE + } + is LowPriorityHighlightEffect -> { + notificationIconView.setColorFilter(effect.notificationTint) + highlightView.visibility = View.GONE + notificationIconView.visibility = View.VISIBLE + } + null -> { + highlightView.visibility = View.GONE + notificationIconView.visibility = View.GONE + } + } + } + + /** + * Sets the tint of the 3-dot menu icon. + */ + override fun setColorFilter(@ColorInt color: Int) = menuIcon.setColorFilter(color) +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/view/MenuView.kt b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/view/MenuView.kt new file mode 100644 index 0000000000..d0d0e5d96b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/java/mozilla/components/browser/menu2/view/MenuView.kt @@ -0,0 +1,95 @@ +/* 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.browser.menu2.view + +import android.content.Context +import android.os.Build +import android.os.Build.VERSION.SDK_INT +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.annotation.VisibleForTesting +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.adapter.MenuCandidateListAdapter +import mozilla.components.concept.menu.MenuStyle +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.MenuCandidate +import mozilla.components.concept.menu.candidate.NestedMenuCandidate +import mozilla.components.support.ktx.android.view.onNextGlobalLayout + +/** + * A popup menu composed of [MenuCandidate] objects. + */ +class MenuView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : FrameLayout(context, attrs, defStyleAttr) { + + private val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) + private val menuAdapter = MenuCandidateListAdapter( + inflater = LayoutInflater.from(context), + dismiss = { onDismiss() }, + reopenMenu = { onReopenMenu(it) }, + ) + private val cardView: CardView + private val recyclerView: RecyclerView + + /** + * Called when the menu is clicked and should be dismissed. + */ + var onDismiss: () -> Unit = {} + + /** + * Called when a nested menu should be opened. + */ + var onReopenMenu: (NestedMenuCandidate?) -> Unit = {} + + init { + View.inflate(context, R.layout.mozac_browser_menu2_view, this) + + cardView = findViewById(R.id.mozac_browser_menu_cardView) + recyclerView = findViewById(R.id.mozac_browser_menu_recyclerView) + recyclerView.layoutManager = layoutManager + recyclerView.adapter = menuAdapter + } + + /** + * Changes the contents of the menu. + */ + fun submitList(list: List<MenuCandidate>?) = menuAdapter.submitList(list) + + /** + * Displays either the start or the end of the list. + */ + fun setVisibleSide(side: Side) { + if (SDK_INT >= Build.VERSION_CODES.N) { + layoutManager.stackFromEnd = side == Side.END + } else { + // In devices with Android 6 and below stackFromEnd is not working properly, + // as a result, we have to provided a backwards support. + // See: https://github.com/mozilla-mobile/android-components/issues/3211 + if (side == Side.END) scrollOnceToTheBottom(recyclerView) + } + } + + /** + * Sets the background color for the menu view. + */ + fun setStyle(style: MenuStyle) { + style.backgroundColor?.let { cardView.setCardBackgroundColor(it) } + } + + @VisibleForTesting + internal fun scrollOnceToTheBottom(recyclerView: RecyclerView) { + recyclerView.onNextGlobalLayout { + recyclerView.adapter?.let { recyclerView.scrollToPosition(it.itemCount - 1) } + } + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left.xml new file mode 100644 index 0000000000..de510ecb12 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="0" + android:toXScale="1" + android:fromYScale="0" + android:toYScale="1" + android:pivotX="5%" + android:pivotY="50%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="0" + android:toAlpha="1" + android:duration="@android:integer/config_shortAnimTime" /> + <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromYDelta="0" + android:toYDelta="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left_bottom.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left_bottom.xml new file mode 100644 index 0000000000..6d27c410ea --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left_bottom.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="0" + android:toXScale="1" + android:fromYScale="0" + android:toYScale="1" + android:pivotX="5%" + android:pivotY="100%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="0" + android:toAlpha="1" + android:duration="@android:integer/config_shortAnimTime" /> + <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromYDelta="0" + android:toYDelta="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left_top.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left_top.xml new file mode 100644 index 0000000000..fc141bdbd0 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_left_top.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="0" + android:toXScale="1" + android:fromYScale="0" + android:toYScale="1" + android:pivotX="5%" + android:pivotY="5%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="0" + android:toAlpha="1" + android:duration="@android:integer/config_shortAnimTime" /> + <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromYDelta="0" + android:toYDelta="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right.xml new file mode 100644 index 0000000000..0ca98399a7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="0" + android:toXScale="1" + android:fromYScale="0" + android:toYScale="1" + android:pivotX="95%" + android:pivotY="50%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="0" + android:toAlpha="1" + android:duration="@android:integer/config_shortAnimTime" /> + <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromYDelta="0" + android:toYDelta="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right_bottom.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right_bottom.xml new file mode 100644 index 0000000000..89153ca6e5 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right_bottom.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="0" + android:toXScale="1" + android:fromYScale="0" + android:toYScale="1" + android:pivotX="95%" + android:pivotY="100%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="0" + android:toAlpha="1" + android:duration="@android:integer/config_shortAnimTime" /> + <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromYDelta="0" + android:toYDelta="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right_top.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right_top.xml new file mode 100644 index 0000000000..f0485403ef --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_enter_right_top.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <scale android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromXScale="0" + android:toXScale="1" + android:fromYScale="0" + android:toYScale="1" + android:pivotX="95%" + android:pivotY="5%" + android:duration="@android:integer/config_shortAnimTime" /> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="0" + android:toAlpha="1" + android:duration="@android:integer/config_shortAnimTime" /> + <translate android:interpolator="@android:anim/accelerate_decelerate_interpolator" + android:fromYDelta="0" + android:toYDelta="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_exit.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_exit.xml new file mode 100644 index 0000000000..226166c109 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/anim/menu_exit.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <alpha android:interpolator="@android:anim/linear_interpolator" + android:fromAlpha="1" + android:toAlpha="0" + android:duration="@android:integer/config_shortAnimTime" /> +</set> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_indicator.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_indicator.xml new file mode 100644 index 0000000000..33d8ad19b4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_indicator.xml @@ -0,0 +1,29 @@ +<!-- 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/. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + <group + android:scaleX="0.714" + android:scaleY="0.714" + android:pivotX="20" + android:pivotY="20"> + <path + android:pathData="m38.622,27.309a8,8 0,0 0,-11.314 11.314,19.949 19.949,0 0,1 -7.308,1.377c-11.046,0 -20,-8.954 -20,-20s8.954,-20 20,-20 20,8.954 20,20c0,2.58 -0.488,5.045 -1.378,7.309z" + android:fillColor="#ffffff" + android:fillAlpha=".4" /> + <path + android:pathData="M33,33m-6.4,0a6.4,6.4 0,1 1,12.8 0a6.4,6.4 0,1 1,-12.8 0" + android:fillColor="#ffffff" + android:fillAlpha=".4" /> + <path + android:pathData="M33,33m-4.3,0a4.3,4.3 0,1 1,8.6 0a4.3,4.3 0,1 1,-8.6 0" + android:strokeWidth="1" + android:strokeAlpha=".2" + android:fillColor="#ffffff" + android:strokeColor="#000000" /> + </group> +</vector> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_notification.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_notification.xml new file mode 100644 index 0000000000..b7b5ced0c0 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_notification.xml @@ -0,0 +1,21 @@ +<!-- 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/. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + <group + android:translateX="25.55" + android:translateY="5.55" + android:scaleX="0.75" + android:scaleY="0.75"> + <path + android:pathData="M1,5a4,4 0 1,0 8,0a4,4 0 1,0 -8,0" + android:strokeWidth="1" + android:strokeAlpha=".2" + android:fillColor="#fff" + android:strokeColor="#000" /> + </group> +</vector> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_notification_icon.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_notification_icon.xml new file mode 100644 index 0000000000..8afb175afe --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/drawable/mozac_browser_menu2_notification_icon.xml @@ -0,0 +1,15 @@ +<!-- 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/. --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="8dp" + android:height="8dp" + android:viewportWidth="10" + android:viewportHeight="10"> + <path + android:pathData="M1,5a4,4 0 1,0 8,0a4,4 0 1,0 -8,0" + android:strokeWidth="1" + android:strokeAlpha=".2" + android:fillColor="#fff" + android:strokeColor="#000" /> +</vector> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_button.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_button.xml new file mode 100644 index 0000000000..0d31537664 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_button.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:clickable="true" + android:focusable="true" + android:background="?android:selectableItemBackgroundBorderless" + tools:parentTag="android.widget.FrameLayout"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/highlight" + app:srcCompat="@drawable/mozac_browser_menu2_indicator" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="center" + android:contentDescription="@string/mozac_browser_menu2_highlighted" + android:visibility="gone" /> + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/icon" + app:srcCompat="@drawable/mozac_ic_ellipsis_vertical_24" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="center" + android:contentDescription="@string/mozac_browser_menu2_button" /> + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/notification_dot" + app:srcCompat="@drawable/mozac_browser_menu2_notification" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:scaleType="center" + android:contentDescription="@string/mozac_browser_menu2_highlighted" + android:visibility="gone" /> + +</merge> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_compound_checkbox.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_compound_checkbox.xml new file mode 100644 index 0000000000..eba14d8e6d --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_compound_checkbox.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + style="@style/Mozac.Browser.Menu2.Candidate.Container" + android:layout_width="match_parent" + android:paddingStart="@dimen/mozac_browser_menu2_candidate_container_padding_start" + android:paddingEnd="0dp" + android:orientation="horizontal" + android:clickable="true" + android:focusable="false" + android:gravity="center_vertical" + tools:ignore="DisableBaselineAlignment,KeyboardInaccessibleWidget"> + + <androidx.appcompat.widget.AppCompatCheckBox + android:id="@+id/label" + style="@style/Mozac.Browser.Menu2.Candidate.Label" + android:layout_width="0dp" + android:layout_height="@dimen/mozac_browser_menu2_candidate_container_layout_height" + android:background="@null" + android:button="@null" + android:drawableEnd="?android:attr/listChoiceIndicatorMultiple" + android:drawablePadding="@dimen/mozac_browser_menu2_checkbox_padding" + android:paddingStart="0dp" + android:paddingEnd="@dimen/mozac_browser_menu2_candidate_container_padding_end" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:gravity="center_vertical" + tools:text="Item" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_compound_switch.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_compound_switch.xml new file mode 100644 index 0000000000..d702796ce8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_compound_switch.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + style="@style/Mozac.Browser.Menu2.Candidate.Container" + android:layout_width="match_parent" + android:orientation="horizontal" + android:clickable="true" + android:focusable="false" + android:gravity="center_vertical" + tools:ignore="DisableBaselineAlignment,KeyboardInaccessibleWidget"> + + <androidx.appcompat.widget.SwitchCompat + android:id="@+id/label" + style="@style/Mozac.Browser.Menu2.Candidate.Label" + android:layout_width="0dp" + android:layout_height="@dimen/mozac_browser_menu2_candidate_container_layout_height" + android:background="?android:attr/selectableItemBackgroundBorderless" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:gravity="center_vertical" + tools:text="Item" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_decorative_text.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_decorative_text.xml new file mode 100644 index 0000000000..f2fc0aa9d3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_decorative_text.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/simple_text" + style="@style/Mozac.Browser.Menu2.Candidate.Text" + android:background="@null" + android:layout_width="match_parent" + android:layout_height="@dimen/mozac_browser_menu2_candidate_container_layout_height" + android:clickable="false" + android:focusable="false" + android:gravity="start|center_vertical" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:textAlignment="viewStart" + tools:text="Item" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_divider.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_divider.xml new file mode 100644 index 0000000000..b716250ca9 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_divider.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<View xmlns:android="http://schemas.android.com/apk/res/android" + style="@style/Mozac.Browser.Menu2.Candidate.Divider.Horizontal" + android:importantForAccessibility="no" + android:clickable="false"/> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_nested.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_nested.xml new file mode 100644 index 0000000000..4eff0a72a4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_nested.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + style="@style/Mozac.Browser.Menu2.Candidate.Container" + android:layout_width="match_parent" + android:orientation="horizontal" + android:clickable="true" + android:focusable="true" + android:gravity="center_vertical"> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/label" + style="@style/Mozac.Browser.Menu2.Candidate.Label" + android:layout_width="0dp" + android:clickable="false" + android:focusable="false" + android:gravity="center_vertical" + android:background="@null" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + tools:text="Item" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_row.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_row.xml new file mode 100644 index 0000000000..a46826e4b4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_row.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + style="@android:style/TextAppearance.Material.Menu" + android:layout_width="match_parent" + android:layout_height="@dimen/mozac_browser_menu2_candidate_row_height" + android:gravity="center_vertical" + android:orientation="horizontal" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_row_small_icon.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_row_small_icon.xml new file mode 100644 index 0000000000..02f5bb4ebc --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_row_small_icon.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.appcompat.widget.AppCompatImageButton + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1" + android:orientation="horizontal" + android:background="?android:attr/selectableItemBackground" + android:clickable="true" + android:focusable="true" + tools:src="@android:color/background_dark" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_text.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_text.xml new file mode 100644 index 0000000000..4eff0a72a4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_candidate_text.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.constraintlayout.widget.ConstraintLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + style="@style/Mozac.Browser.Menu2.Candidate.Container" + android:layout_width="match_parent" + android:orientation="horizontal" + android:clickable="true" + android:focusable="true" + android:gravity="center_vertical"> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/label" + style="@style/Mozac.Browser.Menu2.Candidate.Label" + android:layout_width="0dp" + android:clickable="false" + android:focusable="false" + android:gravity="center_vertical" + android:background="@null" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + tools:text="Item" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_button.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_button.xml new file mode 100644 index 0000000000..bb60166414 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_button.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.appcompat.widget.AppCompatImageButton + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/icon" + style="@style/Mozac.Browser.Menu2.Icon" + tools:src="@android:drawable/ic_menu_add" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_drawable.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_drawable.xml new file mode 100644 index 0000000000..b10ae873e0 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_drawable.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.appcompat.widget.AppCompatImageView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/icon" + style="@style/Mozac.Browser.Menu2.Icon" + android:clickable="false" + android:focusable="false" + android:importantForAccessibility="no" + tools:src="@android:drawable/ic_menu_add" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_notification_dot.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_notification_dot.xml new file mode 100644 index 0000000000..54c6c509d6 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_notification_dot.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.appcompat.widget.AppCompatImageView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/notification_dot" + android:layout_gravity="top|end" + android:contentDescription="@string/mozac_browser_menu2_highlighted" + android:layout_width="@dimen/mozac_browser_menu2_icon_notification_dot_size" + android:layout_height="@dimen/mozac_browser_menu2_icon_notification_dot_size" + android:translationX="@dimen/mozac_browser_menu2_icon_notification_dot_translate_x" + android:translationY="@dimen/mozac_browser_menu2_icon_notification_dot_translate_y" + android:background="@android:color/transparent" + app:srcCompat="@drawable/mozac_browser_menu2_notification_icon" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_text.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_text.xml new file mode 100644 index 0000000000..9e94057898 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_icon_text.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<androidx.appcompat.widget.AppCompatTextView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/icon" + style="@style/Mozac.Browser.Menu2.Icon.Text" + android:clickable="false" + android:focusable="false" + android:textAlignment="viewEnd" + tools:text="Ctrl+X" /> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_view.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_view.xml new file mode 100644 index 0000000000..1e05515a6f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/layout/mozac_browser_menu2_view.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + tools:parentTag="FrameLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <androidx.cardview.widget.CardView + style="@style/Mozac.Browser.Menu2" + android:id="@+id/mozac_browser_menu_cardView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:cardCornerRadius="@dimen/mozac_browser_menu2_corner_radius" + app:cardElevation="@dimen/mozac_browser_menu2_elevation" + app:cardUseCompatPadding="true"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/mozac_browser_menu_recyclerView" + android:paddingTop="@dimen/mozac_browser_menu2_padding_vertical" + android:paddingBottom="@dimen/mozac_browser_menu2_padding_vertical" + android:layout_width="@dimen/mozac_browser_menu2_width" + android:layout_height="wrap_content" + android:overScrollMode="never" + tools:listitem="@layout/mozac_browser_menu2_candidate_text" /> + + </androidx.cardview.widget.CardView> +</merge> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-am/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..79d77db735 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-am/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">ምናሌ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">የተተኮረበት</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-an/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-an/strings.xml new file mode 100644 index 0000000000..3fc9124078 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-an/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Destacaus</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ar/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..41f0bf6f11 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ar/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">القائمة</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">عليها الإبراز</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ast/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ast/strings.xml new file mode 100644 index 0000000000..08ab9cea55 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ast/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Rescamplóse</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-az/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-az/strings.xml new file mode 100644 index 0000000000..d3163532fb --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-az/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menyu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Vurğulanmış</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-azb/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-azb/strings.xml new file mode 100644 index 0000000000..6e0898fb50 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-azb/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">منو</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">هایلایت اولدو</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ban/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ban/strings.xml new file mode 100644 index 0000000000..5a0a67e133 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ban/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Kasorot</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-be/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..c335f20e0b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-be/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Меню</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Вылучаны(я)</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-bg/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..6a42322297 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-bg/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Меню</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Откроено</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-bn/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..35491cbc2b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-bn/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">মেনু</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">হাইলাইট করা হয়েছে</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-br/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-br/strings.xml new file mode 100644 index 0000000000..3377f9e8ec --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-br/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Lañser</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Usskedet</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-bs/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..e4be501bc9 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-bs/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meni</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Istaknuto</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ca/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..f34fc74e2e --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ca/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">S’ha ressaltat</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-cak/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-cak/strings.xml new file mode 100644 index 0000000000..32d2e53097 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-cak/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">K\'utsamaj</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Ya\'on ruq\'ij</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ceb/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ceb/strings.xml new file mode 100644 index 0000000000..c31b0a90da --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ceb/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Gipasiugda</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ckb/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ckb/strings.xml new file mode 100644 index 0000000000..d935386713 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ckb/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">پێڕست</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ئاماژەپێکراو</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-co/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-co/strings.xml new file mode 100644 index 0000000000..afa726fc94 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-co/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Listinu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Sopralineatu</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-cs/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..b027ce6479 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-cs/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Nabídka</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Zvýrazněné</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-cy/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000000..fa069ca454 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-cy/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Dewislen</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Amlygwyd</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-da/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..dca8b4f467 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-da/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Fremhævet</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-de/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..be63b6937a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-de/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menü</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Hervorgehoben</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-dsb/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-dsb/strings.xml new file mode 100644 index 0000000000..97cc5b8efc --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-dsb/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meni</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Wuzwignjony</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-el/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..c6dfb2b15a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-el/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Μενού</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Επισημασμένο</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..592d520cac --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-en-rCA/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Highlighted</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-en-rGB/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..592d520cac --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Highlighted</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-eo/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000000..36f886c443 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-eo/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menuo</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Elstarigitaj</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rAR/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000000..85c3558fdf --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Resaltado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rCL/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rCL/strings.xml new file mode 100644 index 0000000000..ff96f0b1a9 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rCL/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Destacado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rES/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..85c3558fdf --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Resaltado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rMX/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rMX/strings.xml new file mode 100644 index 0000000000..ff96f0b1a9 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es-rMX/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Destacado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-es/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..85c3558fdf --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-es/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Resaltado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-et/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..48d020d392 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-et/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menüü</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Esiletõstetud</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-eu/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..fe9175c4cd --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-eu/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menua</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Nabarmendua</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-fa/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..c725cf390f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fa/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">منو</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">برجستهشده</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-fi/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..2df10018d3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fi/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Valikko</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Korostettu</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-fr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..0c17d18f2f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Sélectionné</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-fur/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fur/strings.xml new file mode 100644 index 0000000000..295ae072f1 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fur/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menù</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Evidenziât</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-fy-rNL/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fy-rNL/strings.xml new file mode 100644 index 0000000000..df69165f85 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-fy-rNL/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Markearre</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-gd/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gd/strings.xml new file mode 100644 index 0000000000..085e65afa1 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gd/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">An clàr-taice</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Soillsichte</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-gl/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..3cc8bc80c7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gl/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Realzado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-gn/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gn/strings.xml new file mode 100644 index 0000000000..99dd9a29bc --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gn/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Poravorã</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Hechaukaveha</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-gu-rIN/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gu-rIN/strings.xml new file mode 100644 index 0000000000..a3e12d11ea --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">મેનુ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">પ્રકાશિત કરેલ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-hi-rIN/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hi-rIN/strings.xml new file mode 100644 index 0000000000..c26c684891 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hi-rIN/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">मेन्यू</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">दर्शाए गए</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-hil/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hil/strings.xml new file mode 100644 index 0000000000..592d520cac --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hil/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Highlighted</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-hr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..6b6f279bf5 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Izbornik</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Istaknuto</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-hsb/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hsb/strings.xml new file mode 100644 index 0000000000..a646978d65 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hsb/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meni</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Wuzběhnjeny</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-hu/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..8b3a5be2d7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hu/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menü</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Kiemelt</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-hy-rAM/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..95863f1c45 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Ցանկ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Գունանշված</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ia/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000000..273756232a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ia/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Evidentiate</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-in/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..bdbc257bca --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-in/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Tersorot</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-is/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..25470860c2 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-is/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Valmynd</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Undirstrikað</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-it/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..bcda59c692 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-it/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Evidenziato</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-iw/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..1e259b99f4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-iw/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">תפריט</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">מודגש</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ja/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..573ca56cea --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ja/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">メニュー</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">強調</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ka/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..bcda2c40f3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ka/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">მენიუ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">მონიშნული</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-kaa/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kaa/strings.xml new file mode 100644 index 0000000000..5933838259 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kaa/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menyu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Belgilengen</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-kab/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kab/strings.xml new file mode 100644 index 0000000000..01b0200a52 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kab/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Umuɣ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ittwag deg uqerru</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-kk/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..dc3e56e0a3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kk/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Мәзір</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Ерекшеленген</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-kmr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kmr/strings.xml new file mode 100644 index 0000000000..fef72a1d0e --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kmr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menû</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Berbiçavkirî</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-kn/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000000..eedd3a4d96 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kn/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">ಮೆನು</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ಹೈಲೈಟ್ ಮಾಡಲಾಗಿದೆ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ko/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..6106e64081 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ko/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">메뉴</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">강조 표시됨</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-kw/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kw/strings.xml new file mode 100644 index 0000000000..6ed87c5d94 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-kw/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Rol</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Golowboyntys</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ldrtl/dimens.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ldrtl/dimens.xml new file mode 100644 index 0000000000..5dea33699b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ldrtl/dimens.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<resources> + <dimen name="mozac_browser_menu2_icon_notification_dot_translate_x">-4dp</dimen> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-lij/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-lij/strings.xml new file mode 100644 index 0000000000..3e379e7a7e --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-lij/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menù</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">In evidensa</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-lo/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..0feac7d979 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-lo/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">ເມນູ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ຈຸດເດັ່ນ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-lt/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000000..fef1bedd5b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-lt/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meniu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Paryškinta</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-mix/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-mix/strings.xml new file mode 100644 index 0000000000..fff2691aea --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-mix/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Katsi</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Tu^un nchichi</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-mr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-mr/strings.xml new file mode 100644 index 0000000000..c03e62b772 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-mr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">मेनू</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ठळक</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-my/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..1ddc43e6f8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-my/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">မီနူး</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">အသားပေးအရာ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-nb-rNO/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 0000000000..33264b5c05 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meny</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Uthevet</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ne-rNP/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ne-rNP/strings.xml new file mode 100644 index 0000000000..3040e760b8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">मेनु</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">हाइलाइट गरियो</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-nl/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..0b7f6dfe19 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-nl/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Gemarkeerd</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-nn-rNO/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-nn-rNO/strings.xml new file mode 100644 index 0000000000..1ae9d7d81f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-nn-rNO/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meny</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Utheva</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-oc/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000..9ead4cdd58 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-oc/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menú</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Notables</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-or/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-or/strings.xml new file mode 100644 index 0000000000..dcf46d2ff3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-or/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">ମେନୁ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ହାଇଲାଇଟ୍ କରାଯାଇଥିବା</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-pa-rIN/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..e9704bf09f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">ਮੀਨੂ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ਉਘਾੜੇ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-pa-rPK/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pa-rPK/strings.xml new file mode 100644 index 0000000000..0034100a2a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pa-rPK/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">مینو</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">اُگھاڑے</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-pl/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..d430f3d2b3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pl/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Wyróżnione</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-pt-rBR/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..17464e70f5 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Destacado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-pt-rPT/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..17464e70f5 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Destacado</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-rm/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-rm/strings.xml new file mode 100644 index 0000000000..3ff1b693a9 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-rm/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Cun emfasa</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ro/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..36c3bbc33e --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ro/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meniu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Evidențiat</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ru/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..edaedb47a7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ru/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Меню</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Выделено</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sat/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sat/strings.xml new file mode 100644 index 0000000000..d0d78ee8df --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sat/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">ᱢᱮᱱᱩ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ᱩᱪᱷᱟᱹᱱᱟᱜ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sc/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sc/strings.xml new file mode 100644 index 0000000000..47781dced8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sc/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menù</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">In evidèntzia</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-si/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..360b4ba100 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-si/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">වට්ටෝරුව</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ත්රීවාලෝකිත</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sk/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..83ef834d61 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sk/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Ponuka</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Zvýraznené</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-skr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-skr/strings.xml new file mode 100644 index 0000000000..92d8c77be3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-skr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">مینیو</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">نمایاں کیتا ڳیا</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..5f76d3d0a4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sl/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meni</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Označeno</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sq/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..5059b3ddc0 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sq/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">E theksuar</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..771684ae37 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Мени</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Истакнуто</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-su/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-su/strings.xml new file mode 100644 index 0000000000..f5694fa656 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-su/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Disorot</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-sv-rSE/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sv-rSE/strings.xml new file mode 100644 index 0000000000..53e1a2c120 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-sv-rSE/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Meny</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Markerad</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-szl/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-szl/strings.xml new file mode 100644 index 0000000000..050fdcd8f7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-szl/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Myni</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Ôbznoczōne</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ta/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000000..ef67a234f3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ta/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">பட்டி</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">மிளிர்ப்புகள்</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-te/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..6a8494e72c --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-te/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">మెనూ</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">హైలైట్ చేసినవి</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-tg/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tg/strings.xml new file mode 100644 index 0000000000..f9874801ec --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tg/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Меню</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Таъкид</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-th/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..3b39bcac10 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-th/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">เมนู</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">เน้น</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-tl/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000000..83bba47bf4 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tl/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Mga naka-highlight</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-tr/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..4faac7b60a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tr/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menü</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Vurgulu</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-trs/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-trs/strings.xml new file mode 100644 index 0000000000..da59e49395 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-trs/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menû</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Sa ña\'āan</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-tt/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tt/strings.xml new file mode 100644 index 0000000000..33e7485245 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tt/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Меню</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Аерылган</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-tzm/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tzm/strings.xml new file mode 100644 index 0000000000..671152685f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-tzm/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Umuɣ</string> + </resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ug/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ug/strings.xml new file mode 100644 index 0000000000..444cf11219 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ug/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">تىزىملىك</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">ئالاھىدە</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-uk/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..f5ecb3600e --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-uk/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Меню</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Виділено</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-ur/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..b19fad90ee --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-ur/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">مینیو</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">نمایاں کیا گیا</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-uz/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..45ec9ab80f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-uz/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menyu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Belgilangan</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-vec/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-vec/strings.xml new file mode 100644 index 0000000000..e0e190062a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-vec/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menù</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Evidensià</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-vi/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..f3544ed037 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-vi/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Đã tô sáng</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-yo/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-yo/strings.xml new file mode 100644 index 0000000000..14377ebe7c --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-yo/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">mẹ́nù</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Ti fàmìsí</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-zh-rCN/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..fedb88a7f7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">菜单</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">高亮</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values-zh-rTW/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..d7ce12c735 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">選單</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">強調</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values/colors.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values/colors.xml new file mode 100644 index 0000000000..a77289bfe3 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values/colors.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<resources> + <!-- Empty by default, allows others to theme as they see fit --> + <color name="mozac_browser_menu2_background"></color> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values/dimens.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..fa390df86f --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values/dimens.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<resources> + <dimen name="mozac_browser_menu2_corner_radius">4dp</dimen> + <dimen name="mozac_browser_menu2_elevation">6dp</dimen> + <dimen name="mozac_browser_menu2_width">250dp</dimen> + <dimen name="mozac_browser_menu2_padding_vertical">0dp</dimen> + + <!--Menu Item --> + <dimen name="mozac_browser_menu2_candidate_text_size">16sp</dimen> + <dimen name="mozac_browser_menu2_candidate_container_layout_height">48dp</dimen> + <dimen name="mozac_browser_menu2_candidate_container_padding_start">16dp</dimen> + <dimen name="mozac_browser_menu2_candidate_container_padding_end">16dp</dimen> + <!--Menu Item --> + + <!--BrowserMenuDivider --> + <dimen name="mozac_browser_menu2_candidate_divider_height">1dp</dimen> + <!--BrowserMenuDivider --> + + <!--BrowserMenuHighlightableItem --> + <dimen name="mozac_browser_menu2_icon_notification_dot_translate_x">4dp</dimen> + <dimen name="mozac_browser_menu2_icon_notification_dot_translate_y">-4dp</dimen> + <dimen name="mozac_browser_menu2_icon_notification_dot_size">8dp</dimen> + <!--BrowserMenuHighlightableItem --> + + <!--BrowserMenuCheckbox --> + <dimen name="mozac_browser_menu2_checkbox_padding">12dp</dimen> + <!--BrowserMenuCheckbox --> + + <!--BrowserMenuImageText--> + + <!--Icon--> + <dimen name="mozac_browser_menu2_icon_width">24dp</dimen> + <dimen name="mozac_browser_menu2_icon_height">24dp</dimen> + <dimen name="mozac_browser_menu2_icon_text_size">14sp</dimen> + <!--Icon--> + + <!--Label--> + <dimen name="mozac_browser_menu2_icon_padding_start">20dp</dimen> + <!--Label--> + + <!--BrowserMenuImageText--> + + <!-- BrowserMenuItemToolbar --> + <dimen name="mozac_browser_menu2_candidate_row_height">56dp</dimen> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values/strings.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values/strings.xml new file mode 100644 index 0000000000..ebcceba5ad --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<resources> + <!-- Content description (not visible, for screen readers etc.): Description for the overflow menu button in the browser toolbar. --> + <string name="mozac_browser_menu2_button">Menu</string> + <!-- Content description (not visible, for screen readers etc.): Indicates the overflow menu has a highlight --> + <string name="mozac_browser_menu2_highlighted">Highlighted</string> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/main/res/values/style.xml b/mobile/android/android-components/components/browser/menu2/src/main/res/values/style.xml new file mode 100644 index 0000000000..eb2702e6f1 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/main/res/values/style.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<resources> + + <style name="Mozac.Browser.Menu2" parent=""> + <item name="cardBackgroundColor">@color/mozac_browser_menu2_background</item> + </style> + + <!-- Item Divider --> + <style name="Mozac.Browser.Menu2.Candidate.Divider" parent=""> + <item name="android:background">?android:attr/listDivider</item> + </style> + + <style name="Mozac.Browser.Menu2.Candidate.Divider.Horizontal"> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">@dimen/mozac_browser_menu2_candidate_divider_height</item> + </style> + <!-- Item Divider --> + + <style name="Mozac.Browser.Menu2.Candidate.Container" parent=""> + <item name="android:layout_height">@dimen/mozac_browser_menu2_candidate_container_layout_height</item> + <item name="android:paddingStart">@dimen/mozac_browser_menu2_candidate_container_padding_start</item> + <item name="android:paddingEnd">@dimen/mozac_browser_menu2_candidate_container_padding_end</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> + + <style name="Mozac.Browser.Menu2.Candidate.Text" parent="@android:style/TextAppearance.Material.Menu"> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:textSize">@dimen/mozac_browser_menu2_candidate_text_size</item> + <item name="android:ellipsize">end</item> + <item name="android:lines">1</item> + <item name="android:focusable">true</item> + <item name="android:clickable">true</item> + </style> + + <!-- BrowserMenuImageText --> + <style name="Mozac.Browser.Menu2.Icon" parent=""> + <item name="android:layout_width">@dimen/mozac_browser_menu2_icon_width</item> + <item name="android:layout_height">@dimen/mozac_browser_menu2_icon_height</item> + </style> + + <style name="Mozac.Browser.Menu2.Icon.Text" parent="Mozac.Browser.Menu2.Icon"> + <item name="android:layout_width">wrap_content</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + <item name="android:textSize">@dimen/mozac_browser_menu2_icon_text_size</item> + <item name="android:lines">1</item> + </style> + + <style name="Mozac.Browser.Menu2.Candidate.Label" parent="Mozac.Browser.Menu2.Candidate.Text"> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + </style> + <!-- BrowserMenuImageText --> + + <!-- Animation --> + <style name="Mozac.Browser.Menu2.Animation.OverflowMenuLeftTop" parent=""> + <item name="android:windowEnterAnimation">@anim/menu_enter_left_top</item> + <item name="android:windowExitAnimation">@anim/menu_exit</item> + </style> + + <style name="Mozac.Browser.Menu2.Animation.OverflowMenuRightTop" parent=""> + <item name="android:windowEnterAnimation">@anim/menu_enter_right_top</item> + <item name="android:windowExitAnimation">@anim/menu_exit</item> + </style> + + <style name="Mozac.Browser.Menu2.Animation.OverflowMenuLeftBottom" parent=""> + <item name="android:windowEnterAnimation">@anim/menu_enter_left_bottom</item> + <item name="android:windowExitAnimation">@anim/menu_exit</item> + </style> + + <style name="Mozac.Browser.Menu2.Animation.OverflowMenuRightBottom" parent=""> + <item name="android:windowEnterAnimation">@anim/menu_enter_right_bottom</item> + <item name="android:windowExitAnimation">@anim/menu_exit</item> + </style> + + <style name="Mozac.Browser.Menu2.Animation.OverflowMenuLeft" parent=""> + <item name="android:windowEnterAnimation">@anim/menu_enter_left</item> + <item name="android:windowExitAnimation">@anim/menu_exit</item> + </style> + + <style name="Mozac.Browser.Menu2.Animation.OverflowMenuRight" parent=""> + <item name="android:windowEnterAnimation">@anim/menu_enter_right</item> + <item name="android:windowExitAnimation">@anim/menu_exit</item> + </style> + <!-- Animation --> +</resources> diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/BrowserMenuControllerTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/BrowserMenuControllerTest.kt new file mode 100644 index 0000000000..688b77b578 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/BrowserMenuControllerTest.kt @@ -0,0 +1,132 @@ +/* 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.browser.menu2 + +import android.view.Gravity +import android.widget.Button +import android.widget.PopupWindow +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.menu2.ext.MenuPositioningData +import mozilla.components.browser.menu2.ext.createAnchor +import mozilla.components.browser.menu2.ext.createContainerView +import mozilla.components.browser.menu2.ext.getTargetCoordinates +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate +import mozilla.components.concept.menu.candidate.MenuCandidate +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito + +@RunWith(AndroidJUnit4::class) +class BrowserMenuControllerTest { + + @Test + fun `created popup window is displayed automatically`() { + val menu: MenuController = BrowserMenuController() + menu.submitList(listOf(DecorativeTextMenuCandidate("Hello"))) + + val anchor = Button(testContext) + val popup = menu.show(anchor) + + assertTrue(popup.isShowing) + } + + @Test + fun `WHEN an empty list is submitted to the menu THEN the menu isn't shown`() { + val menu: MenuController = BrowserMenuController() + menu.submitList(emptyList()) + + val anchor = Button(testContext) + val popup = menu.show(anchor) + + assertFalse(popup.isShowing) + } + + @Test + fun `observer is notified when submitList is called`() { + var submitted: List<MenuCandidate>? = null + val menu: MenuController = BrowserMenuController() + menu.register( + object : MenuController.Observer { + override fun onMenuListSubmit(list: List<MenuCandidate>) { + submitted = list + } + + override fun onDismiss() = Unit + }, + ) + + assertNull(submitted) + + menu.submitList(listOf(DecorativeTextMenuCandidate("Hello"))) + assertEquals(listOf(DecorativeTextMenuCandidate("Hello")), submitted) + } + + @Test + fun `dismissing the browser menu will dismiss the popup`() { + var dismissed = false + val menu: MenuController = BrowserMenuController() + menu.submitList(listOf(DecorativeTextMenuCandidate("Hello"))) + + menu.register( + object : MenuController.Observer { + override fun onDismiss() { + dismissed = true + } + + override fun onMenuListSubmit(list: List<MenuCandidate>) = Unit + }, + ) + + menu.dismiss() + assertFalse(dismissed) + + val anchor = Button(testContext) + val popup = menu.show(anchor) + + assertTrue(popup.isShowing) + assertFalse(dismissed) + + menu.dismiss() + + assertFalse(popup.isShowing) + assertTrue(dismissed) + } + + @Test + fun `WHEN displayPopup is called with provided positioning data THEN showAtLocation is called with provided positioning values & menu height and animation are set`() { + val containerView = createContainerView() + val popupWindow = Mockito.spy(PopupWindow()) + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor) + val positioningData = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + popupWindow.displayPopup(positioningData) + + assertEquals(containerView.measuredHeight, popupWindow.height) + assertEquals(positioningData.animation, popupWindow.animationStyle) + + Mockito.verify(popupWindow).showAtLocation( + positioningData.anchor, + Gravity.NO_GRAVITY, + positioningData.x, + positioningData.y, + ) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/CompoundMenuCandidateViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/CompoundMenuCandidateViewHolderTest.kt new file mode 100644 index 0000000000..13128cbad6 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/CompoundMenuCandidateViewHolderTest.kt @@ -0,0 +1,99 @@ +/* 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.browser.menu2.adapter + +import android.content.Context +import android.content.res.Resources +import android.graphics.Color +import android.graphics.Typeface +import android.view.View +import android.widget.CompoundButton +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.candidate.CompoundMenuCandidate +import mozilla.components.concept.menu.candidate.HighPriorityHighlightEffect +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +class CompoundMenuCandidateViewHolderTest { + + private val baseCandidate = CompoundMenuCandidate( + "hello", + isChecked = false, + end = CompoundMenuCandidate.ButtonType.CHECKBOX, + ) + private lateinit var view: ConstraintLayout + private lateinit var compoundButton: CompoundButton + + @Before + fun setup() { + val context: Context = mock() + view = mock() + compoundButton = mock() + + doReturn(context).`when`(view).context + doReturn(compoundButton).`when`(view).findViewById<TextView>(R.id.label) + + val resources: Resources = mock() + doReturn(resources).`when`(context).resources + + doReturn(mock<Resources.Theme>()).`when`(context).theme + } + + @Test + fun `sets container state and text on view`() { + val holder = CompoundCheckboxMenuCandidateViewHolder(view, mock(), mock()) + + holder.bind(baseCandidate) + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = true + verify(compoundButton).text = "hello" + verify(compoundButton).isChecked = false + verify(compoundButton).setTypeface(any(), eq(Typeface.NORMAL)) + verify(compoundButton).textAlignment = View.TEXT_ALIGNMENT_INHERIT + } + + @Test + fun `sets highlight effect`() { + val holder = CompoundSwitchMenuCandidateViewHolder(view, mock(), mock()) + + holder.bind(baseCandidate) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view, never()).setBackgroundResource(anyInt()) + + holder.bind(baseCandidate.copy(effect = HighPriorityHighlightEffect(Color.RED))) + verify(view).setBackgroundColor(Color.RED) + verify(view, never()).setBackgroundResource(anyInt()) + + clearInvocations(view) + + holder.bind(baseCandidate.copy(effect = null)) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view).setBackgroundResource(anyInt()) + } + + @Test + fun `sets change listener`() { + var dismissed = false + val holder = CompoundSwitchMenuCandidateViewHolder(view, mock()) { dismissed = true } + + val candidate = baseCandidate.copy(onCheckedChange = mock()) + holder.bind(candidate) + holder.onCheckedChanged(compoundButton, false) + + assertTrue(dismissed) + verify(candidate.onCheckedChange).invoke(false) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/DecorativeTextMenuCandidateViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/DecorativeTextMenuCandidateViewHolderTest.kt new file mode 100644 index 0000000000..a3fde39c97 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/DecorativeTextMenuCandidateViewHolderTest.kt @@ -0,0 +1,61 @@ +/* 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.browser.menu2.adapter + +import android.content.res.Resources +import android.graphics.Typeface +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import org.junit.Assert.assertEquals +import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +class DecorativeTextMenuCandidateViewHolderTest { + + @Test + fun `sets container state and text on view`() { + val view: TextView = mock() + val holder = DecorativeTextMenuCandidateViewHolder(view, mock()) + + holder.bind(DecorativeTextMenuCandidate("hello")) + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = true + verify(view).text = "hello" + verify(view).setTypeface(any(), eq(Typeface.NORMAL)) + verify(view).textAlignment = View.TEXT_ALIGNMENT_INHERIT + verify(view, never()).layoutParams = any() + } + + @Test + fun `sets view height`() { + val view: TextView = mock() + val params = ViewGroup.LayoutParams(0, 0) + val resources: Resources = mock() + doReturn(params).`when`(view).layoutParams + doReturn(resources).`when`(view).resources + doReturn(48).`when`(resources).getDimensionPixelSize(anyInt()) + + val holder = DecorativeTextMenuCandidateViewHolder(view, mock()) + + holder.bind(DecorativeTextMenuCandidate("hello", height = 30)) + assertEquals(30, params.height) + verify(view).layoutParams = params + + clearInvocations(view) + + holder.bind(DecorativeTextMenuCandidate("hello")) + assertEquals(48, params.height) + verify(view).layoutParams = params + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/DividerMenuCandidateViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/DividerMenuCandidateViewHolderTest.kt new file mode 100644 index 0000000000..493c1d517a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/DividerMenuCandidateViewHolderTest.kt @@ -0,0 +1,24 @@ +/* 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.browser.menu2.adapter + +import android.view.View +import mozilla.components.concept.menu.candidate.DividerMenuCandidate +import mozilla.components.support.test.mock +import org.junit.Test +import org.mockito.Mockito.verify + +class DividerMenuCandidateViewHolderTest { + + @Test + fun `sets visible and enabled state on divider view`() { + val view: View = mock() + val holder = DividerMenuCandidateViewHolder(view, mock()) + + holder.bind(DividerMenuCandidate()) + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = true + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapterTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapterTest.kt new file mode 100644 index 0000000000..144422e618 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapterTest.kt @@ -0,0 +1,75 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.menu.candidate.CompoundMenuCandidate +import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate +import mozilla.components.concept.menu.candidate.DividerMenuCandidate +import mozilla.components.concept.menu.candidate.NestedMenuCandidate +import mozilla.components.concept.menu.candidate.RowMenuCandidate +import mozilla.components.concept.menu.candidate.TextMenuCandidate +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class MenuCandidateListAdapterTest { + + private lateinit var layoutInflater: LayoutInflater + private lateinit var dismiss: () -> Unit + private lateinit var reopenMenu: (NestedMenuCandidate) -> Unit + private lateinit var adapter: MenuCandidateListAdapter + + @Before + fun setup() { + layoutInflater = spy(LayoutInflater.from(testContext)) + dismiss = mock() + reopenMenu = mock() + adapter = MenuCandidateListAdapter(layoutInflater, dismiss, reopenMenu) + } + + @Test + fun `items use layout resource as view type`() { + val items = listOf( + DecorativeTextMenuCandidate("one"), + TextMenuCandidate("two"), + CompoundMenuCandidate("three", false, end = CompoundMenuCandidate.ButtonType.CHECKBOX), + CompoundMenuCandidate("four", false, end = CompoundMenuCandidate.ButtonType.SWITCH), + DividerMenuCandidate(), + RowMenuCandidate(emptyList()), + ) + adapter.submitList(items) + + assertEquals(6, adapter.itemCount) + assertEquals(DecorativeTextMenuCandidateViewHolder.layoutResource, adapter.getItemViewType(0)) + assertEquals(TextMenuCandidateViewHolder.layoutResource, adapter.getItemViewType(1)) + assertEquals(CompoundCheckboxMenuCandidateViewHolder.layoutResource, adapter.getItemViewType(2)) + assertEquals(CompoundSwitchMenuCandidateViewHolder.layoutResource, adapter.getItemViewType(3)) + assertEquals(DividerMenuCandidateViewHolder.layoutResource, adapter.getItemViewType(4)) + assertEquals(RowMenuCandidateViewHolder.layoutResource, adapter.getItemViewType(5)) + } + + @Test + fun `bind will be forwarded to item implementation`() { + adapter.submitList( + listOf( + DividerMenuCandidate(), + ), + ) + + val holder: DividerMenuCandidateViewHolder = mock() + + adapter.onBindViewHolder(holder, 0) + + verify(holder).bind(DividerMenuCandidate()) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/RowMenuCandidateViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/RowMenuCandidateViewHolderTest.kt new file mode 100644 index 0000000000..ca8a0fea9a --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/RowMenuCandidateViewHolderTest.kt @@ -0,0 +1,103 @@ +/* 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.browser.menu2.adapter + +import android.view.LayoutInflater +import android.view.View +import android.widget.LinearLayout +import androidx.appcompat.widget.AppCompatImageButton +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.RowMenuCandidate +import mozilla.components.concept.menu.candidate.SmallMenuCandidate +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class RowMenuCandidateViewHolderTest { + + private lateinit var view: LinearLayout + private lateinit var button: AppCompatImageButton + private lateinit var inflater: LayoutInflater + + @Before + fun setup() { + view = mock() + button = spy(AppCompatImageButton(testContext)) + inflater = mock() + + doReturn(button).`when`(inflater).inflate( + SmallMenuCandidateViewHolder.layoutResource, + view, + false, + ) + } + + @Test + fun `sets container state on view`() { + val holder = RowMenuCandidateViewHolder(view, inflater, mock()) + + holder.bind(RowMenuCandidate(emptyList())) + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = true + verify(view, never()).addView(button) + } + + @Test + fun `creates buttons for small items`() { + val holder = RowMenuCandidateViewHolder(view, inflater, mock()) + + holder.bind( + RowMenuCandidate( + listOf( + SmallMenuCandidate("hello", DrawableMenuIcon(null)), + SmallMenuCandidate("hello", DrawableMenuIcon(null)), + ), + ), + ) + verify(view, times(2)).addView(button) + + clearInvocations(view) + + holder.bind( + RowMenuCandidate( + listOf( + SmallMenuCandidate("test", DrawableMenuIcon(null)), + SmallMenuCandidate("hello", DrawableMenuIcon(null)), + ), + ), + ) + verify(view, never()).removeAllViews() + verify(view, never()).addView(button) + } + + @Test + fun `binds buttons for small items`() { + val holder = RowMenuCandidateViewHolder(view, inflater, mock()) + + holder.bind( + RowMenuCandidate( + listOf( + SmallMenuCandidate("hello", DrawableMenuIcon(null)), + ), + ), + ) + + verify(button).contentDescription = "hello" + verify(button).setImageDrawable(null) + verify(button).imageTintList = null + verify(button).visibility = View.VISIBLE + verify(button).isEnabled = true + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/SmallMenuCandidateViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/SmallMenuCandidateViewHolderTest.kt new file mode 100644 index 0000000000..f128297c2b --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/SmallMenuCandidateViewHolderTest.kt @@ -0,0 +1,130 @@ +/* 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.browser.menu2.adapter + +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.View +import androidx.appcompat.widget.AppCompatImageButton +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.SmallMenuCandidate +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class SmallMenuCandidateViewHolderTest { + + private lateinit var view: AppCompatImageButton + + @Before + fun setup() { + view = spy(AppCompatImageButton(testContext)) + } + + @Test + fun `binds button data`() { + val holder = SmallMenuCandidateViewHolder(view, mock()) + + verify(view).setOnClickListener(holder) + + holder.bind(SmallMenuCandidate("hello", DrawableMenuIcon(null))) + + verify(view).contentDescription = "hello" + verify(view).setImageDrawable(null) + verify(view).imageTintList = null + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = true + + clearInvocations(view) + + holder.bind( + SmallMenuCandidate( + "hello", + DrawableMenuIcon(null, tint = Color.BLUE), + ), + ) + verify(view).setImageDrawable(null) + verify(view).imageTintList = ColorStateList.valueOf(Color.BLUE) + } + + @Test + fun `sets on click listener`() { + var dismissed = false + var clicked = false + val holder = SmallMenuCandidateViewHolder(view) { dismissed = true } + + holder.onClick(null) + assertTrue(dismissed) + assertFalse(clicked) + + holder.bind(SmallMenuCandidate("hello", DrawableMenuIcon(null))) + dismissed = false + + holder.onClick(null) + assertTrue(dismissed) + assertFalse(clicked) + + dismissed = false + holder.bind( + SmallMenuCandidate( + "hello", + DrawableMenuIcon(null), + ) { + clicked = true + }, + ) + + holder.onClick(null) + assertTrue(dismissed) + assertTrue(clicked) + } + + @Test + fun `sets on long click listener`() { + var dismissed = false + var clicked = false + val holder = SmallMenuCandidateViewHolder(view) { dismissed = true } + + holder.onLongClick(null) + assertTrue(dismissed) + assertFalse(clicked) + + holder.bind(SmallMenuCandidate("hello", DrawableMenuIcon(null))) + verify(view).setOnClickListener(holder) + verify(view, atLeastOnce()).isLongClickable = false + dismissed = false + + assertFalse(holder.onLongClick(null)) + assertTrue(dismissed) + assertFalse(clicked) + + dismissed = false + holder.bind( + SmallMenuCandidate( + "hello", + DrawableMenuIcon(null), + onLongClick = { + clicked = true + true + }, + ) {}, + ) + verify(view).isLongClickable = true + + assertTrue(holder.onLongClick(null)) + assertTrue(dismissed) + assertTrue(clicked) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/TextMenuCandidateViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/TextMenuCandidateViewHolderTest.kt new file mode 100644 index 0000000000..1c7f61fe25 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/TextMenuCandidateViewHolderTest.kt @@ -0,0 +1,117 @@ +/* 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.browser.menu2.adapter + +import android.content.Context +import android.content.res.Resources +import android.graphics.Color +import android.graphics.Typeface +import android.view.View +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.candidate.HighPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.TextMenuCandidate +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +class TextMenuCandidateViewHolderTest { + + private lateinit var view: ConstraintLayout + private lateinit var textView: TextView + + @Before + fun setup() { + val context: Context = mock() + view = mock() + textView = mock() + + doReturn(context).`when`(view).context + doReturn(textView).`when`(view).findViewById<TextView>(R.id.label) + + val resources: Resources = mock() + doReturn(resources).`when`(context).resources + + doReturn(mock<Resources.Theme>()).`when`(context).theme + } + + @Test + fun `sets container state and text on view`() { + val holder = TextMenuCandidateViewHolder(view, mock(), mock()) + + verify(view).setOnClickListener(holder) + + holder.bind(TextMenuCandidate("hello")) + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = true + verify(textView).text = "hello" + verify(textView).setTypeface(any(), eq(Typeface.NORMAL)) + verify(textView).textAlignment = View.TEXT_ALIGNMENT_INHERIT + } + + @Test + fun `sets on click listener`() { + var dismissed = false + var clicked = false + val holder = TextMenuCandidateViewHolder(view, mock()) { dismissed = true } + + holder.onClick(null) + assertTrue(dismissed) + assertFalse(clicked) + + holder.bind(TextMenuCandidate("hello")) + dismissed = false + + holder.onClick(null) + assertTrue(dismissed) + assertFalse(clicked) + + dismissed = false + holder.bind(TextMenuCandidate("hello") { clicked = true }) + + holder.onClick(null) + assertTrue(dismissed) + assertTrue(clicked) + } + + @Test + fun `sets highlight effect`() { + val holder = TextMenuCandidateViewHolder(view, mock(), mock()) + + holder.bind(TextMenuCandidate("hello")) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view, never()).setBackgroundResource(anyInt()) + + holder.bind( + TextMenuCandidate( + "hello", + effect = HighPriorityHighlightEffect(Color.RED), + ), + ) + verify(view).setBackgroundColor(Color.RED) + verify(view, never()).setBackgroundResource(anyInt()) + + clearInvocations(view) + + holder.bind( + TextMenuCandidate( + "hello", + effect = null, + ), + ) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view).setBackgroundResource(anyInt()) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/DrawableMenuIconViewHoldersTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/DrawableMenuIconViewHoldersTest.kt new file mode 100644 index 0000000000..9df9c642e8 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/DrawableMenuIconViewHoldersTest.kt @@ -0,0 +1,177 @@ +/* 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.browser.menu2.adapter.icons + +import android.graphics.drawable.Drawable +import android.view.LayoutInflater +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.AsyncDrawableMenuIcon +import mozilla.components.concept.menu.candidate.DrawableButtonMenuIcon +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.support.base.log.logger.Logger +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.rule.MainCoroutineRule +import mozilla.components.support.test.rule.runTestOnMain +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@ExperimentalCoroutinesApi +@RunWith(AndroidJUnit4::class) +class DrawableMenuIconViewHoldersTest { + + @get:Rule + val coroutinesTestRule = MainCoroutineRule() + + private lateinit var parent: ConstraintLayout + private lateinit var layoutInflater: LayoutInflater + private lateinit var imageView: ImageView + private lateinit var imageButton: ImageButton + + @Before + fun setup() { + parent = mock() + layoutInflater = mock() + imageView = mock() + imageButton = mock() + + doReturn(testContext).`when`(parent).context + doReturn(testContext.resources).`when`(parent).resources + doReturn(imageView).`when`(layoutInflater).inflate(DrawableMenuIconViewHolder.layoutResource, parent, false) + doReturn(imageView).`when`(layoutInflater).inflate(AsyncDrawableMenuIconViewHolder.layoutResource, parent, false) + doReturn(imageButton).`when`(layoutInflater).inflate(DrawableButtonMenuIconViewHolder.layoutResource, parent, false) + doReturn(imageView).`when`(imageView).findViewById<TextView>(R.id.icon) + doReturn(imageButton).`when`(imageButton).findViewById<TextView>(R.id.icon) + } + + @Test + fun `icon view holder sets icon on view`() { + val holder = DrawableMenuIconViewHolder(parent, layoutInflater, Side.END) + + val drawable = mock<Drawable>() + holder.bindAndCast(DrawableMenuIcon(drawable), null) + verify(imageView).setImageDrawable(drawable) + verify(imageView).imageTintList = null + } + + @Test + fun `button view holder sets icon on view`() { + val holder = DrawableButtonMenuIconViewHolder(parent, layoutInflater, Side.START) {} + verify(imageButton).setOnClickListener(holder) + + val drawable = mock<Drawable>() + holder.bindAndCast(DrawableButtonMenuIcon(drawable), null) + verify(imageButton).setImageDrawable(drawable) + verify(imageButton).imageTintList = null + } + + @Test + fun `async view holder sets icon on view`() = runTestOnMain { + val holder = AsyncDrawableMenuIconViewHolder(parent, layoutInflater, Side.END) + + val drawable = mock<Drawable>() + holder.bindAndCast(AsyncDrawableMenuIcon(loadDrawable = { _, _ -> drawable }), null) + verify(imageView).setImageDrawable(null) + verify(imageView).setImageDrawable(drawable) + } + + @Test + fun `async view holder uses loading icon and fallback icon`() = runTestOnMain { + val logger = mock<Logger>() + val holder = AsyncDrawableMenuIconViewHolder(parent, layoutInflater, Side.END, logger) + + val loading = mock<Drawable>() + val fallback = mock<Drawable>() + holder.bindAndCast( + AsyncDrawableMenuIcon( + loadDrawable = { _, _ -> throw Exception() }, + loadingDrawable = loading, + fallbackDrawable = fallback, + ), + null, + ) + verify(imageView, never()).setImageDrawable(null) + verify(imageView).setImageDrawable(loading) + verify(imageView).setImageDrawable(fallback) + } + + @Test + fun `icon holder removes image view on disconnect`() { + val holder = DrawableMenuIconViewHolder(parent, layoutInflater, Side.START) + + verify(parent).setConstraintSet(any()) + verify(parent).addView(imageView) + clearInvocations(parent) + + holder.disconnect() + + verify(parent).setConstraintSet(any()) + verify(parent).removeView(imageView) + } + + @Test + fun `button holder removes image view on disconnect`() { + val holder = DrawableButtonMenuIconViewHolder(parent, layoutInflater, Side.END) {} + + verify(parent).setConstraintSet(any()) + verify(parent).addView(imageButton) + clearInvocations(parent) + + holder.disconnect() + + verify(parent).setConstraintSet(any()) + verify(parent).removeView(imageButton) + } + + @Test + fun `async holder removes image view on disconnect`() { + val holder = AsyncDrawableMenuIconViewHolder(parent, layoutInflater, Side.START) + + verify(parent).setConstraintSet(any()) + verify(parent).addView(imageView) + clearInvocations(parent) + + holder.disconnect() + + verify(parent).setConstraintSet(any()) + verify(parent).removeView(imageView) + } + + @Test + fun `button view holder calls dismiss when clicked`() { + var dismissed = false + var clicked = false + + val holder = DrawableButtonMenuIconViewHolder(parent, layoutInflater, Side.START) { + dismissed = true + } + + holder.onClick(imageButton) + assertTrue(dismissed) + assertFalse(clicked) + + val button = DrawableButtonMenuIcon(mock(), onClick = { clicked = true }) + holder.bindAndCast(button, null) + holder.onClick(imageButton) + assertTrue(dismissed) + assertTrue(clicked) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/MenuIconAdapterTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/MenuIconAdapterTest.kt new file mode 100644 index 0000000000..510389e995 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/MenuIconAdapterTest.kt @@ -0,0 +1,86 @@ +/* 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.browser.menu2.adapter.icons + +import android.view.LayoutInflater +import androidx.constraintlayout.widget.ConstraintLayout +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.DrawableButtonMenuIcon +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.TextMenuIcon +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify + +class MenuIconAdapterTest { + + private lateinit var layout: ConstraintLayout + private lateinit var layoutInflater: LayoutInflater + private lateinit var dismiss: () -> Unit + private lateinit var adapter: MenuIconAdapter + + @Before + fun setup() { + layout = mock() + layoutInflater = mock() + dismiss = mock() + adapter = spy(MenuIconAdapter(layout, layoutInflater, Side.START, dismiss)) + } + + @Test + fun `creates viewholder when icon type changes`() { + val mockViewHolder: MenuIconViewHolder<*> = mock() + doReturn(mockViewHolder).`when`(adapter).createViewHolder(any()) + + adapter.bind(null, null) + adapter.bind(TextMenuIcon("hello"), TextMenuIcon("world")) + adapter.bind(null, TextMenuIcon("world")) + verify(adapter, never()).createViewHolder(any()) + + adapter.bind(TextMenuIcon("hello"), null) + verify(adapter).createViewHolder(TextMenuIcon("hello")) + + clearInvocations(adapter) + adapter.bind(TextMenuIcon("hello"), DrawableMenuIcon(mock())) + verify(adapter).createViewHolder(TextMenuIcon("hello")) + } + + @Test + fun `disconnects viewholder when icon is changed`() { + val mockViewHolder: MenuIconViewHolder<*> = mock() + doReturn(mockViewHolder).`when`(adapter).createViewHolder(any()) + adapter.bind(TextMenuIcon("hello"), null) + + verify(mockViewHolder, never()).disconnect() + adapter.bind(DrawableButtonMenuIcon(mock()), TextMenuIcon("hello")) + verify(mockViewHolder).disconnect() + + clearInvocations(mockViewHolder) + adapter.bind(null, DrawableButtonMenuIcon(mock())) + verify(mockViewHolder).disconnect() + } + + @Test + fun `always bind new icon`() { + val mockViewHolder: MenuIconViewHolder<*> = mock() + doReturn(mockViewHolder).`when`(adapter).createViewHolder(any()) + + adapter.bind(TextMenuIcon("hello"), null) + verify(mockViewHolder).bindAndCast(TextMenuIcon("hello"), null) + + adapter.bind(TextMenuIcon("hello"), TextMenuIcon("hello")) + verify(mockViewHolder).bindAndCast(TextMenuIcon("hello"), TextMenuIcon("hello")) + + clearInvocations(mockViewHolder) + adapter.bind(null, TextMenuIcon("hello")) + verify(mockViewHolder, never()).bindAndCast(any(), any()) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/TextMenuIconViewHolderTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/TextMenuIconViewHolderTest.kt new file mode 100644 index 0000000000..bdba1c20f7 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/adapter/icons/TextMenuIconViewHolderTest.kt @@ -0,0 +1,68 @@ +/* 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.browser.menu2.adapter.icons + +import android.graphics.Typeface +import android.view.LayoutInflater +import android.view.View +import android.widget.TextView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.TextMenuIcon +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class TextMenuIconViewHolderTest { + + private lateinit var parent: ConstraintLayout + private lateinit var layoutInflater: LayoutInflater + private lateinit var textView: TextView + + @Before + fun setup() { + parent = mock() + layoutInflater = mock() + textView = mock() + + doReturn(testContext).`when`(parent).context + doReturn(textView).`when`(layoutInflater).inflate(TextMenuIconViewHolder.layoutResource, parent, false) + doReturn(textView).`when`(textView).findViewById<TextView>(R.id.icon) + } + + @Test + fun `sets container state and text on view`() { + val holder = TextMenuIconViewHolder(parent, layoutInflater, Side.START) + + holder.bindAndCast(TextMenuIcon("hello"), null) + verify(textView).text = "hello" + verify(textView).setTypeface(any(), eq(Typeface.NORMAL)) + verify(textView).textAlignment = View.TEXT_ALIGNMENT_INHERIT + } + + @Test + fun `removes text view on disconnect`() { + val holder = TextMenuIconViewHolder(parent, layoutInflater, Side.END) + + verify(parent).setConstraintSet(any()) + verify(parent).addView(textView) + clearInvocations(parent) + + holder.disconnect() + + verify(parent).setConstraintSet(any()) + verify(parent).removeView(textView) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/ext/BrowserMenuPositioningTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/ext/BrowserMenuPositioningTest.kt new file mode 100644 index 0000000000..d9be746793 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/ext/BrowserMenuPositioningTest.kt @@ -0,0 +1,856 @@ +/* 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.browser.menu2.ext + +import android.graphics.Rect +import android.view.View +import android.widget.PopupWindow +import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.RecyclerView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.menu2.R +import mozilla.components.browser.menu2.adapter.MenuCandidateListAdapter +import mozilla.components.concept.menu.MenuStyle +import mozilla.components.concept.menu.Orientation +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.any +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.spy +import org.mockito.Mockito.`when` +import kotlin.math.roundToInt + +/** + * [PopupWindow] const's. + */ +private const val HALF_MENU_ITEM = 0.5 + +/** + * [PopupWindow] UI components. + */ +private const val SCREEN_ROOT_VIEW_HEIGHT = 1000 +private const val SCREEN_ROOT_VIEW_WIDTH = 400 +private const val MENU_ITEM_HEIGHT = 50 +private const val DEFAULT_ITEM_COUNT = 10 +private const val MENU_CONTAINER_WIDTH = 100 +private const val MENU_CONTAINER_PADDING = 10 + +@RunWith(AndroidJUnit4::class) +class BrowserMenuPositioningTest { + private lateinit var popupWindow: PopupWindow + private lateinit var overlapStyle: MenuStyle + private lateinit var offsetStyle: MenuStyle + private lateinit var offsetOverlapStyle: MenuStyle + + @Before + fun setUp() { + overlapStyle = MenuStyle(completelyOverlap = true) + offsetStyle = MenuStyle(horizontalOffset = 10, verticalOffset = 10) + offsetOverlapStyle = + MenuStyle(completelyOverlap = true, horizontalOffset = 10, verticalOffset = 10) + + popupWindow = spy(PopupWindow()) + } + + @Test + fun `WHEN recycler view has no adapter THEN menu positioning data is null`() { + val containerView = createContainerView(hasAdapter = false) + val anchor = mock(View::class.java) + + assertNull(inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP)) + } + + @Test + fun `WHEN container view has no measured height THEN menu positioning data is null`() { + val containerView = createContainerView() + val anchor = mock(View::class.java) + + `when`(containerView.measuredHeight).thenReturn(0) + + assertNull(inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP)) + } + + @Test + fun `WHEN container view has no measured width THEN menu positioning data is null`() { + val containerView = createContainerView() + val anchor = mock(View::class.java) + + `when`(containerView.measuredWidth).thenReturn(0) + + assertNull(inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP)) + } + + @Test + fun `WHEN recycler view has no measured height THEN menu positioning data is null`() { + val containerView = createContainerView(recyclerViewHeight = 0) + val anchor = mock(View::class.java) + + assertNull(inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP)) + } + + @Test + fun `WHEN recycler view has no items THEN menu positioning data is null`() { + val containerView = createContainerView(recyclerViewHeight = 100, itemCount = 0) + val anchor = mock(View::class.java) + + assertNull(inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP)) + } + + @Test + fun `WHEN orientation up & fits available height THEN original height & positioned from the bottom of the anchor with leftBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation up & fits available height & is RTL THEN original height & positioned from the bottom right of the anchor with rightBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation up & does not fit available height & fits down THEN original height & positioned from the top left of the anchor with leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation up & does not fit up or down THEN original height & positioned from the top left of the anchor with leftAnimation`() { + val containerView = createContainerView() + + val notEnoughHeight = containerView.measuredHeight.minus(MENU_CONTAINER_PADDING).minus(1) + val (x, y) = 20 to notEnoughHeight + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeft, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation up & does not fit up or down & is RTL THEN original height & positioned from the bottom right of the anchor with rightAnimation`() { + val containerView = createContainerView() + + val notEnoughHeight = containerView.measuredHeight.minus(MENU_CONTAINER_PADDING).minus(1) + val (x, y) = SCREEN_ROOT_VIEW_HEIGHT - 20 to notEnoughHeight + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionRight = false, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRight, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation down & fits available height THEN original height & positioned from the top left of the anchor with leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation down & fits available height & is RTL THEN original height & positioned from the top right of the anchor with rightTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to 25 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, orientation = Orientation.DOWN) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionRight = false, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN number of menu items don't fit available height THEN set popup with calculated height`() { + val containerView = createContainerView(itemCount = 22) + val anchor = createAnchor(20, 25) + + val positioningData = inferMenuPositioningData(containerView, anchor, orientation = Orientation.UP) + + val availableHeight = anchor.rootView.measuredHeight + val maxAvailableHeightForRecyclerView = availableHeight - MENU_CONTAINER_PADDING + val numberOfItemsFitExactly = + (maxAvailableHeightForRecyclerView.toFloat() / MENU_ITEM_HEIGHT.toFloat()).roundToInt() + val numberOfItemsFitWithOverFlow = numberOfItemsFitExactly - HALF_MENU_ITEM + val updatedRecyclerViewHeight = (numberOfItemsFitWithOverFlow * MENU_ITEM_HEIGHT).toInt() + val calculatedHeight = updatedRecyclerViewHeight + MENU_CONTAINER_PADDING + + assertEquals(calculatedHeight, positioningData?.containerHeight) + } + + @Test + fun `WHEN orientation is null & menu fits down THEN original height & positioned from the top left of the anchor with leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, orientation = null) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN orientation is null & menu does not fit & is RTL THEN showAtLocation with original height & positioned from the top right of the anchor with rightAnimation`() { + val containerView = createContainerView(itemCount = 20) + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to 25 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, orientation = null) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, directionRight = false, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRight, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should completely overlap & orientation up & fits up THEN original height & bottom of the menu positioned exactly to the bottom of the anchor with leftBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, overlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, overlapStyle) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should completely overlap & orientation up & fits up & is RTL THEN original height & bottom right of the menu positioned exactly to the bottom right of the anchor with rightBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, overlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, overlapStyle, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should completely overlap & orientation down & fits down THEN showAtLocation with original height & positioned exactly from the top of the anchor with leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, overlapStyle, Orientation.DOWN) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, overlapStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should completely overlap & orientation down & fits down & is RTL THEN original height & positioned exactly from the top of the anchor with rightTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to 25 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, overlapStyle, Orientation.DOWN) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, overlapStyle, directionUp = false, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should completely overlap & orientation up & does not fit up & fits down THEN original height & positioned exactly from the top left of the anchor with leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, overlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, overlapStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should completely overlap & orientation up & does not fit up or down THEN original height & positioned exactly from the top left of the anchor with leftAnimation`() { + val containerView = createContainerView() + + val notEnoughHeight = containerView.measuredHeight.minus(MENU_CONTAINER_PADDING).minus(1) + val (x, y) = 20 to notEnoughHeight + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, overlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, overlapStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeft, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN has style offsets & orientation up & fits up THEN original height & positioned from the bottom left of the anchor with applied offsets and leftBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetStyle) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN has style offsets & orientation up & fits up & is RTL THEN original height & positioned from the bottom right of the anchor with applied offsets and rightBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, offsetStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetStyle, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN has style offsets & orientation down & fits down THEN original height & positioned from the top left of the anchor with applied offsets and leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN has style offsets & orientation down & fits down & is RTL THEN original height & positioned from the top right of the anchor with applied offsets and rightTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to 25 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, offsetStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetStyle, directionUp = false, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN has style offsets & orientation up & does not fit up & fits down THEN original height & positioned from the top left of the anchor with applied offsets and leftAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN has style offsets & orientation up & does not fit up or down THEN original height & positioned from the top left of the anchor with applied offsets leftAnimation`() { + val containerView = createContainerView() + + val notEnoughHeight = containerView.measuredHeight.minus(MENU_CONTAINER_PADDING).minus(1) + val (x, y) = 20 to notEnoughHeight + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeft, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should overlap & has style offsets & orientation up & fits up THEN original height & positioned exactly from the bottom left of the anchor with applied offsets leftBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetOverlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetOverlapStyle) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should overlap & has style offsets & orientation up & fits up & is RTL THEN original height & positioned exactly to the bottom right of the anchor with applied offsets rightBottomAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to SCREEN_ROOT_VIEW_HEIGHT - 20 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, offsetOverlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetOverlapStyle, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightBottom, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should overlap & has style offsets & orientation down & fits down THEN original height & positioned exactly from the top of the anchor with applied offsets leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetOverlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetOverlapStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should overlap & has style offsets & orientation down & fits down & is RTL THEN original height & positioned exactly from the top of the anchor with applied offsets and rightTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = SCREEN_ROOT_VIEW_WIDTH - 20 to 25 + val anchor = createAnchor(x, y, true) + + val result = inferMenuPositioningData(containerView, anchor, offsetOverlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetOverlapStyle, directionUp = false, directionRight = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuRightTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should overlap & has style offsets & orientation up & fits down only THEN original height & positioned exactly from the top left of the anchor with applied offsets and leftTopAnimation`() { + val containerView = createContainerView() + + val (x, y) = 20 to 25 + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetOverlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetOverlapStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeftTop, + ) + + assertEquals(expected, result) + } + + @Test + fun `WHEN should overlap & has style offsets & orientation up & does not fit up or down THEN original height & positioned exactly from the top left of the anchor with applied offsets and leftAnimation`() { + val containerView = createContainerView() + + val notEnoughHeight = containerView.measuredHeight.minus(MENU_CONTAINER_PADDING).minus(1) + val (x, y) = 20 to notEnoughHeight + val anchor = createAnchor(x, y) + + val result = inferMenuPositioningData(containerView, anchor, offsetOverlapStyle, Orientation.UP) + + val (targetX, targetY) = getTargetCoordinates(x, y, containerView, anchor, offsetOverlapStyle, directionUp = false) + val expected = MenuPositioningData( + anchor = anchor, + x = targetX, + y = targetY, + containerHeight = containerView.measuredHeight, + animation = R.style.Mozac_Browser_Menu2_Animation_OverflowMenuLeft, + ) + + assertEquals(expected, result) + } +} + +internal fun createContainerView( + itemCount: Int = DEFAULT_ITEM_COUNT, + hasAdapter: Boolean = true, + recyclerViewHeight: Int? = null, +): View { + val containerView = mock(View::class.java) + + // Mimicking elevation added spacing. + val recyclerView = createRecyclerView(itemCount, hasAdapter, recyclerViewHeight) + val containerHeight = recyclerView.measuredHeight.plus(MENU_CONTAINER_PADDING) + val containerWidth = recyclerView.measuredWidth.plus(MENU_CONTAINER_PADDING) + `when`(containerView.measuredHeight).thenReturn(containerHeight) + `when`(containerView.measuredWidth).thenReturn(containerWidth) + + doReturn(recyclerView).`when`(containerView) + .findViewById<RecyclerView>(R.id.mozac_browser_menu_recyclerView) + return containerView +} + +private fun createRecyclerView( + itemCount: Int = DEFAULT_ITEM_COUNT, + hasAdapter: Boolean = true, + recyclerViewHeight: Int? = null, +): RecyclerView { + val recyclerView = mock(RecyclerView::class.java) + + if (hasAdapter) { + val adapter = mock(MenuCandidateListAdapter::class.java) + `when`(adapter.itemCount).thenReturn(itemCount) + `when`(recyclerView.adapter).thenReturn(adapter) + } + + `when`(recyclerView.measuredHeight).thenReturn( + recyclerViewHeight + ?: (itemCount * MENU_ITEM_HEIGHT), + ) + + `when`(recyclerView.measuredWidth).thenReturn(MENU_CONTAINER_WIDTH) + + return recyclerView +} + +internal fun createAnchor(x: Int, y: Int, isRTL: Boolean = false): View { + val view = spy(View(testContext)) + + doAnswer { invocation -> + val locationOnScreen = (invocation.getArgument(0) as IntArray) + locationOnScreen[0] = x + locationOnScreen[1] = y + locationOnScreen + }.`when`(view).getLocationOnScreen(any()) + + doAnswer { invocation -> + val locationInWindow = (invocation.getArgument(0) as IntArray) + locationInWindow[0] = x + locationInWindow[1] = y + locationInWindow + }.`when`(view).getLocationInWindow(any()) + + if (isRTL) { + doReturn(ViewCompat.LAYOUT_DIRECTION_RTL).`when`(view).layoutDirection + } else { + doReturn(ViewCompat.LAYOUT_DIRECTION_LTR).`when`(view).layoutDirection + } + doReturn(10).`when`(view).height + doReturn(15).`when`(view).width + + val anchorRootView = createAnchorRootView() + `when`(view.rootView).thenReturn(anchorRootView) + + return view +} + +private fun createAnchorRootView(): View { + val view = spy(View(testContext)) + doAnswer { invocation -> + val displayFrame = (invocation.getArgument(0) as Rect) + displayFrame.left = 0 + displayFrame.right = SCREEN_ROOT_VIEW_WIDTH + displayFrame.bottom = SCREEN_ROOT_VIEW_HEIGHT + displayFrame + }.`when`(view).getWindowVisibleDisplayFrame(any()) + + `when`(view.measuredHeight).thenReturn(SCREEN_ROOT_VIEW_HEIGHT) + + return view +} + +internal fun getTargetCoordinates( + anchorX: Int, + anchorY: Int, + containerView: View, + anchor: View, + style: MenuStyle? = null, + directionUp: Boolean = true, + directionRight: Boolean = true, +): Pair<Int, Int> { + val targetX = getTargetX( + anchorX, + containerView, + anchor, + directionRight, + style?.completelyOverlap ?: false, + style?.horizontalOffset ?: 0, + ) + val targetY = getTargetY( + anchorY, + containerView, + anchor, + directionUp, + style?.completelyOverlap ?: false, + style?.verticalOffset ?: 0, + ) + return targetX to targetY +} + +private fun getTargetX( + anchorX: Int, + containerView: View, + anchor: View, + directionRight: Boolean, + shouldOverlap: Boolean, + horizontalOffset: Int, +): Int { + val targetX = when { + directionRight && shouldOverlap -> anchorX - (MENU_CONTAINER_PADDING / 2) + directionRight && !shouldOverlap -> anchorX + !directionRight && shouldOverlap -> anchorX - (containerView.measuredWidth - anchor.width) + (MENU_CONTAINER_PADDING / 2) + else -> anchorX - (containerView.measuredWidth - anchor.width) + } + + return if (directionRight) { + targetX + horizontalOffset + } else { + targetX - horizontalOffset + } +} + +private fun getTargetY( + anchorY: Int, + containerView: View, + anchor: View, + directionUp: Boolean, + shouldOverlap: Boolean, + verticalOffset: Int, +): Int { + val targetY = when { + directionUp && shouldOverlap -> anchorY - (containerView.measuredHeight - anchor.height) + (MENU_CONTAINER_PADDING / 2) + directionUp && !shouldOverlap -> anchorY - (containerView.measuredHeight - anchor.height) + !directionUp && shouldOverlap -> anchorY - (MENU_CONTAINER_PADDING / 2) + else -> anchorY + } + + return if (directionUp) { + targetY - verticalOffset + } else { + targetY + verticalOffset + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/ext/ViewTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/ext/ViewTest.kt new file mode 100644 index 0000000000..6b5e406bd5 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/ext/ViewTest.kt @@ -0,0 +1,143 @@ +/* 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.browser.menu2.ext + +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.menu.candidate.ContainerStyle +import mozilla.components.concept.menu.candidate.DrawableMenuIcon +import mozilla.components.concept.menu.candidate.HighPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.LowPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.TextStyle +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class ViewTest { + + @Test + fun `apply container style affects visible and enabled`() { + val view: View = mock() + view.applyStyle(ContainerStyle(), ContainerStyle()) + verify(view, never()).visibility = View.VISIBLE + verify(view, never()).isEnabled = true + + view.applyStyle(ContainerStyle(isVisible = false), ContainerStyle()) + verify(view).visibility = View.GONE + verify(view).isEnabled = true + + view.applyStyle(ContainerStyle(isEnabled = false), ContainerStyle()) + verify(view).visibility = View.VISIBLE + verify(view).isEnabled = false + } + + @Test + fun `apply text style affects text styling`() { + val view: TextView = mock() + view.applyStyle(TextStyle(), TextStyle()) + verify(view, never()).textSize = anyFloat() + verify(view, never()).setTextColor(anyInt()) + verify(view, never()).setTypeface(any(), anyInt()) + verify(view, never()).textAlignment = anyInt() + + view.applyStyle(TextStyle(), TextStyle(size = 0f)) + verify(view, never()).textSize = anyFloat() + verify(view, never()).setTextColor(anyInt()) + verify(view).setTypeface(any(), eq(Typeface.NORMAL)) + verify(view).textAlignment = View.TEXT_ALIGNMENT_INHERIT + + view.applyStyle( + TextStyle( + size = 4f, + color = Color.RED, + textStyle = Typeface.ITALIC, + textAlignment = View.TEXT_ALIGNMENT_CENTER, + ), + TextStyle(), + ) + verify(view).textSize = 4f + verify(view).setTextColor(Color.RED) + verify(view).setTypeface(any(), eq(Typeface.ITALIC)) + verify(view).textAlignment = View.TEXT_ALIGNMENT_CENTER + } + + @Test + fun `apply drawable icon`() { + val view: ImageView = mock() + view.applyIcon(DrawableMenuIcon(null), DrawableMenuIcon(null)) + verify(view, never()).setImageDrawable(any()) + verify(view, never()).imageTintList = any() + + val drawable: Drawable = mock() + view.applyIcon(DrawableMenuIcon(drawable), DrawableMenuIcon(null)) + verify(view).setImageDrawable(drawable) + verify(view).imageTintList = null + + view.applyIcon(DrawableMenuIcon(null, Color.RED), DrawableMenuIcon(drawable)) + verify(view).setImageDrawable(drawable) + verify(view).imageTintList = ColorStateList.valueOf(Color.RED) + } + + @Test + fun `apply notification effect`() { + val view: ImageView = mock() + view.applyNotificationEffect(LowPriorityHighlightEffect(Color.BLUE), LowPriorityHighlightEffect(Color.BLUE)) + view.applyNotificationEffect(null, null) + verify(view, never()).visibility = anyInt() + verify(view, never()).imageTintList = any() + + view.applyNotificationEffect(LowPriorityHighlightEffect(Color.BLUE), null) + verify(view).visibility = View.VISIBLE + verify(view).imageTintList = ColorStateList.valueOf(Color.BLUE) + + view.applyNotificationEffect(null, LowPriorityHighlightEffect(Color.BLUE)) + verify(view).visibility = View.GONE + verify(view).imageTintList = null + } + + @Test + fun `sets highlight effect`() { + val view: View = mock() + doReturn(testContext).`when`(view).context + + view.applyBackgroundEffect(null, null) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view, never()).setBackgroundResource(anyInt()) + verify(view, never()).foreground = any() + + view.applyBackgroundEffect(HighPriorityHighlightEffect(Color.RED), HighPriorityHighlightEffect(Color.RED)) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view, never()).setBackgroundResource(anyInt()) + verify(view, never()).foreground = any() + + view.applyBackgroundEffect(HighPriorityHighlightEffect(Color.RED), null) + verify(view).setBackgroundColor(Color.RED) + verify(view, never()).setBackgroundResource(anyInt()) + verify(view).foreground = any() + + clearInvocations(view) + + view.applyBackgroundEffect(null, HighPriorityHighlightEffect(Color.RED)) + verify(view, never()).setBackgroundColor(anyInt()) + verify(view).setBackgroundResource(anyInt()) + verify(view).foreground = null + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/view/MenuButton2Test.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/view/MenuButton2Test.kt new file mode 100644 index 0000000000..29874f78ff --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/view/MenuButton2Test.kt @@ -0,0 +1,120 @@ +/* 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.browser.menu2.view + +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter +import android.widget.ImageView +import androidx.appcompat.widget.AppCompatImageView +import androidx.core.view.children +import androidx.core.view.isVisible +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.menu.MenuController +import mozilla.components.concept.menu.candidate.HighPriorityHighlightEffect +import mozilla.components.concept.menu.candidate.LowPriorityHighlightEffect +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class MenuButton2Test { + private lateinit var menuController: MenuController + private lateinit var menuButton: MenuButton2 + private lateinit var menuIcon: ImageView + private lateinit var highlightView: ImageView + private lateinit var notificationIconView: ImageView + + @Before + fun setup() { + menuController = mock() + menuButton = MenuButton2(testContext) + + val images = menuButton.children.mapNotNull { it as? AppCompatImageView }.toList() + highlightView = images[0] + menuIcon = images[1] + notificationIconView = images[2] + } + + @Test + fun `changing menu controller dismisses old menu`() { + menuButton.menuController = menuController + menuButton.performClick() + + verify(menuController).register(any(), eq(menuButton)) + verify(menuController).show(menuButton) + + menuButton.menuController = mock() + verify(menuController).dismiss() + verify(menuController).unregister(any()) + } + + @Test + fun `changing menu controller to null dismisses old menu`() { + menuButton.menuController = menuController + menuButton.performClick() + + verify(menuController).register(any(), eq(menuButton)) + + menuButton.menuController = null + verify(menuController).dismiss() + verify(menuController).unregister(any()) + } + + @Test + fun `icon has content description`() { + assertEquals("Menu", menuIcon.contentDescription) + assertNotNull(menuIcon.drawable) + } + + @Test + fun `icon color filter can be changed`() { + assertNull(menuIcon.colorFilter) + + menuButton.setColorFilter(0xffffff) + assertEquals(PorterDuffColorFilter(0xffffff, PorterDuff.Mode.SRC_ATOP), menuIcon.colorFilter) + } + + @Test + fun `icon displays high priority highlight`() { + assertFalse(highlightView.isVisible) + assertFalse(notificationIconView.isVisible) + + menuButton.setEffect( + HighPriorityHighlightEffect(Color.RED), + ) + + assertTrue(highlightView.isVisible) + assertFalse(notificationIconView.isVisible) + + assertEquals(ColorStateList.valueOf(Color.RED), highlightView.imageTintList) + } + + @Test + fun `icon displays low priority highlight`() { + assertFalse(highlightView.isVisible) + assertFalse(notificationIconView.isVisible) + + menuButton.setEffect( + LowPriorityHighlightEffect(Color.BLUE), + ) + + assertFalse(highlightView.isVisible) + assertTrue(notificationIconView.isVisible) + + assertEquals(PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_ATOP), notificationIconView.colorFilter) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/view/MenuViewTest.kt b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/view/MenuViewTest.kt new file mode 100644 index 0000000000..5f39e68c56 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/java/mozilla/components/browser/menu2/view/MenuViewTest.kt @@ -0,0 +1,92 @@ +/* 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.browser.menu2.view + +import android.content.res.ColorStateList +import android.graphics.Color +import android.os.Build +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.menu2.R +import mozilla.components.concept.menu.MenuStyle +import mozilla.components.concept.menu.Side +import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate +import mozilla.components.support.test.any +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.doNothing +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.robolectric.annotation.Config + +@RunWith(AndroidJUnit4::class) +class MenuViewTest { + + private val items = listOf( + DecorativeTextMenuCandidate("Hello"), + DecorativeTextMenuCandidate("World"), + ) + private lateinit var menuView: MenuView + private lateinit var cardView: CardView + private lateinit var recyclerView: RecyclerView + + @Before + fun setup() { + menuView = spy(MenuView(testContext)) + cardView = menuView.findViewById(R.id.mozac_browser_menu_cardView) + recyclerView = menuView.findViewById(R.id.mozac_browser_menu_recyclerView) + } + + @Test + fun `recyclerview adapter will have items for every menu item`() { + assertNotNull(recyclerView) + + val recyclerAdapter = recyclerView.adapter!! + assertNotNull(recyclerAdapter) + assertEquals(0, recyclerAdapter.itemCount) + + menuView.submitList(items) + assertEquals(2, recyclerAdapter.itemCount) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.M]) + fun `setVisibleSide will be forwarded to scrollOnceToTheBottom on devices with Android M and below`() { + doNothing().`when`(menuView).scrollOnceToTheBottom(any()) + + menuView.setVisibleSide(Side.END) + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + + assertFalse(layoutManager.stackFromEnd) + verify(menuView).scrollOnceToTheBottom(any()) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.N]) + fun `setVisibleSide changes stackFromEnd on devices with Android N and above`() { + doNothing().`when`(menuView).scrollOnceToTheBottom(any()) + + menuView.setVisibleSide(Side.END) + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + + assertTrue(layoutManager.stackFromEnd) + verify(menuView, never()).scrollOnceToTheBottom(any()) + } + + @Test + fun `setStyle changes background color`() { + menuView.setStyle(MenuStyle(backgroundColor = Color.BLUE)) + assertEquals(ColorStateList.valueOf(Color.BLUE), cardView.cardBackgroundColor) + } +} diff --git a/mobile/android/android-components/components/browser/menu2/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/browser/menu2/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..49324d83c5 --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,3 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) + diff --git a/mobile/android/android-components/components/browser/menu2/src/test/resources/robolectric.properties b/mobile/android/android-components/components/browser/menu2/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/browser/menu2/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 |