[fenix] For https://github.com/mozilla-mobile/fenix/issues/21791 Adds tab auto-close prompt
parent
1bb5f1479d
commit
0658de464f
@ -0,0 +1,44 @@
|
|||||||
|
/* 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.browser
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import mozilla.components.browser.state.state.TabSessionState
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.concept.tabstray.TabsTray
|
||||||
|
import mozilla.components.feature.tabs.ext.toTabs
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class InactiveTabsAutoCloseDialogController(
|
||||||
|
private val browserStore: BrowserStore,
|
||||||
|
private val settings: Settings,
|
||||||
|
private val tabFilter: (TabSessionState) -> Boolean,
|
||||||
|
private val tray: TabsTray
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Dismiss the auto-close dialog.
|
||||||
|
*/
|
||||||
|
fun close() {
|
||||||
|
settings.hasInactiveTabsAutoCloseDialogBeenDismissed = true
|
||||||
|
refeshInactiveTabsSecion()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the auto-close feature with the after a month setting.
|
||||||
|
*/
|
||||||
|
fun enableAutoClosed() {
|
||||||
|
settings.closeTabsAfterOneMonth = true
|
||||||
|
settings.closeTabsAfterOneWeek = false
|
||||||
|
settings.closeTabsAfterOneDay = false
|
||||||
|
settings.manuallyCloseTabs = false
|
||||||
|
refeshInactiveTabsSecion()
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
internal fun refeshInactiveTabsSecion() {
|
||||||
|
val tabs = browserStore.state.toTabs { tabFilter.invoke(it) }
|
||||||
|
tray.updateTabs(tabs)
|
||||||
|
}
|
||||||
|
}
|
@ -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 org.mozilla.fenix.tabstray.browser
|
||||||
|
|
||||||
|
interface InactiveTabsAutoCloseDialogInteractor {
|
||||||
|
fun onCloseClicked()
|
||||||
|
fun onEnabledAutoCloseClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultInactiveTabsAutoCloseDialogInteractor(
|
||||||
|
private val controller: InactiveTabsAutoCloseDialogController
|
||||||
|
) : InactiveTabsAutoCloseDialogInteractor {
|
||||||
|
override fun onCloseClicked() {
|
||||||
|
controller.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEnabledAutoCloseClicked() {
|
||||||
|
controller.enableAutoClosed()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="?toolbarDivider" />
|
||||||
|
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
|
||||||
|
<solid android:color="?above" />
|
||||||
|
</shape>
|
@ -0,0 +1,79 @@
|
|||||||
|
<?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/. -->
|
||||||
|
<FrameLayout 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="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:paddingHorizontal="1dp"
|
||||||
|
android:background="@color/photonLightGrey30">
|
||||||
|
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?above"
|
||||||
|
android:clickable="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/banner_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clickable="false"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:background="@drawable/inactive_tab_auto_close_border_background"
|
||||||
|
android:focusable="true"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/banner_info_message"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="@string/tab_tray_inactive_auto_close_title"
|
||||||
|
android:textAppearance="@style/Header14TextStyle"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/close_button"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/close_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/tab_tray_inactive_auto_close_button_content_description"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_close" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/message"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:text="@string/tab_tray_inactive_auto_close_body"
|
||||||
|
android:textAppearance="@style/Body14TextStyle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/banner_info_message"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/action"
|
||||||
|
style="@style/DialogButtonStyleDark"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginEnd="3dp"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
android:text="@string/tab_tray_inactive_turn_on_auto_close_button"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/message" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
</FrameLayout>
|
@ -0,0 +1,32 @@
|
|||||||
|
/* 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.browser
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class DefaultInactiveTabsAutoCloseDialogInteractorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN onCloseClicked THEN close`() {
|
||||||
|
val controller: InactiveTabsAutoCloseDialogController = mockk(relaxed = true)
|
||||||
|
val interactor = DefaultInactiveTabsAutoCloseDialogInteractor(controller)
|
||||||
|
|
||||||
|
interactor.onCloseClicked()
|
||||||
|
|
||||||
|
verify { controller.close() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN onEnabledAutoCloseClicked THEN enableAutoClosed`() {
|
||||||
|
val controller: InactiveTabsAutoCloseDialogController = mockk(relaxed = true)
|
||||||
|
val interactor = DefaultInactiveTabsAutoCloseDialogInteractor(controller)
|
||||||
|
|
||||||
|
interactor.onEnabledAutoCloseClicked()
|
||||||
|
|
||||||
|
verify { controller.enableAutoClosed() }
|
||||||
|
}
|
||||||
|
}
|
@ -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 org.mozilla.fenix.tabstray.browser
|
||||||
|
|
||||||
|
import io.mockk.Runs
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.just
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.spyk
|
||||||
|
import io.mockk.verify
|
||||||
|
import mozilla.components.browser.state.state.TabSessionState
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.concept.tabstray.TabsTray
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class InactiveTabsAutoCloseDialogControllerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN close THEN update settings and refresh`() {
|
||||||
|
val filter: (TabSessionState) -> Boolean = { !it.content.private }
|
||||||
|
val store = BrowserStore()
|
||||||
|
val settings: Settings = mockk(relaxed = true)
|
||||||
|
val tray: TabsTray = mockk(relaxed = true)
|
||||||
|
val controller = spyk(InactiveTabsAutoCloseDialogController(store, settings, filter, tray))
|
||||||
|
|
||||||
|
every { controller.refeshInactiveTabsSecion() } just Runs
|
||||||
|
|
||||||
|
controller.close()
|
||||||
|
|
||||||
|
verify { settings.hasInactiveTabsAutoCloseDialogBeenDismissed = true }
|
||||||
|
verify { controller.refeshInactiveTabsSecion() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN enableAutoClosed THEN update closeTabsAfterOneMonth settings and refresh`() {
|
||||||
|
val filter: (TabSessionState) -> Boolean = { !it.content.private }
|
||||||
|
val store = BrowserStore()
|
||||||
|
val settings: Settings = mockk(relaxed = true)
|
||||||
|
val tray: TabsTray = mockk(relaxed = true)
|
||||||
|
val controller = spyk(InactiveTabsAutoCloseDialogController(store, settings, filter, tray))
|
||||||
|
|
||||||
|
every { controller.refeshInactiveTabsSecion() } just Runs
|
||||||
|
|
||||||
|
controller.enableAutoClosed()
|
||||||
|
|
||||||
|
verify { settings.closeTabsAfterOneMonth = true }
|
||||||
|
verify { settings.closeTabsAfterOneWeek = false }
|
||||||
|
verify { settings.closeTabsAfterOneDay = false }
|
||||||
|
verify { settings.manuallyCloseTabs = false }
|
||||||
|
verify { controller.refeshInactiveTabsSecion() }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue