[fenix] For https://github.com/mozilla-mobile/fenix/issues/21565 - Adds inactive tabs onboarding popup
parent
e606d8c42a
commit
f372c5c69f
@ -0,0 +1,114 @@
|
||||
/* 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 org.mozilla.fenix.tabstray
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.lib.state.helpers.AbstractBinding
|
||||
import mozilla.components.support.ktx.android.util.dpToPx
|
||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.infobanner.InfoBanner
|
||||
import org.mozilla.fenix.databinding.ComponentTabstray2Binding
|
||||
import org.mozilla.fenix.databinding.OnboardingInactiveTabsCfrBinding
|
||||
import org.mozilla.fenix.tabstray.ext.inactiveTabs
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class TabsTrayInactiveTabsOnboardingBinding(
|
||||
private val context: Context,
|
||||
private val store: BrowserStore,
|
||||
private val tabsTrayBinding: ComponentTabstray2Binding?,
|
||||
private val settings: Settings,
|
||||
private val navigationInteractor: NavigationInteractor
|
||||
) : AbstractBinding<BrowserState>(store) {
|
||||
|
||||
@VisibleForTesting
|
||||
internal var banner: InfoBanner? = null
|
||||
|
||||
override suspend fun onState(flow: Flow<BrowserState>) {
|
||||
flow.map { state -> state.normalTabs.size }
|
||||
.ifChanged()
|
||||
.collect {
|
||||
val inactiveTabsList =
|
||||
if (settings.inactiveTabsAreEnabled) { store.state.inactiveTabs } else emptyList()
|
||||
if (inactiveTabsList.isNotEmpty() && shouldShowOnboardingForInactiveTabs()) {
|
||||
createInactiveCFR()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldShowOnboardingForInactiveTabs() =
|
||||
settings.shouldShowInactiveTabsOnboardingPopup &&
|
||||
settings.canShowCfr
|
||||
|
||||
private fun createInactiveCFR() {
|
||||
val context: Context = context
|
||||
val anchorPosition = IntArray(2)
|
||||
val popupBinding = OnboardingInactiveTabsCfrBinding.inflate(LayoutInflater.from(context))
|
||||
val popup = Dialog(context)
|
||||
|
||||
popup.apply {
|
||||
setContentView(popupBinding.root)
|
||||
setCancelable(false)
|
||||
// removing title or setting it as an empty string does not prevent a11y services from assigning one
|
||||
setTitle(" ")
|
||||
}
|
||||
popupBinding.closeInfoBanner.setOnClickListener {
|
||||
popup.dismiss()
|
||||
settings.shouldShowInactiveTabsOnboardingPopup = false
|
||||
}
|
||||
|
||||
popupBinding.bannerInfoMessage.setOnClickListener {
|
||||
popup.dismiss()
|
||||
settings.shouldShowInactiveTabsOnboardingPopup = false
|
||||
navigationInteractor.onTabSettingsClicked()
|
||||
}
|
||||
|
||||
val messageText = context.getString(R.string.tab_tray_inactive_onboarding_message)
|
||||
val actionText = context.getString(R.string.tab_tray_inactive_onboarding_button_text)
|
||||
val spannableStringBuilder = SpannableStringBuilder(messageText)
|
||||
|
||||
spannableStringBuilder.append(" ")
|
||||
.append(actionText, UnderlineSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
popupBinding.bannerInfoMessage.text = spannableStringBuilder
|
||||
|
||||
tabsTrayBinding?.tabsTray?.getLocationOnScreen(anchorPosition)
|
||||
|
||||
val (x, y) = anchorPosition
|
||||
|
||||
if (x == 0 && y == 0) {
|
||||
return
|
||||
}
|
||||
|
||||
popupBinding.root.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
|
||||
|
||||
popup.window?.apply {
|
||||
val attr = attributes
|
||||
setGravity(Gravity.START or Gravity.TOP)
|
||||
attr.x = x + 15.dpToPx(context.resources.displayMetrics)
|
||||
attr.y = y + 20.dpToPx(context.resources.displayMetrics)
|
||||
attributes = attr
|
||||
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
}
|
||||
popup.show()
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?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"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/cfr_triangle_width"
|
||||
android:layout_height="@dimen/cfr_triangle_height"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="@dimen/cfr_triangle_margin_edge"
|
||||
android:importantForAccessibility="no"
|
||||
app:srcCompat="@drawable/ic_cfr_triangle"
|
||||
app:tint="#7542E5" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/banner_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/onboarding_popup_shape">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/banner_info_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:maxWidth="200dp"
|
||||
android:text="@string/tab_tray_inactive_onboarding_message"
|
||||
android:textAppearance="@style/Body16TextStyle"
|
||||
android:textColor="@color/contrast_text_light_theme"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/close_info_banner"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/close_info_banner"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/content_description_close_button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_close"
|
||||
app:tint="@color/contrast_text_light_theme" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
Loading…
Reference in New Issue