Copione merged onto master
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
commit
78b14e2919
|
@ -32,3 +32,9 @@ jobs:
|
|||
treeherder-symbol: bump-ac
|
||||
target-tasks-method: bump_android_components
|
||||
when: [{hour: 14, minute: 0}]
|
||||
- name: screenshots
|
||||
job:
|
||||
type: decision-task
|
||||
treeherder-symbol: screenshots-D
|
||||
target-tasks-method: screenshots
|
||||
when: [{weekday: 'Monday', hour: 10, minute: 0}]
|
||||
|
|
|
@ -5,7 +5,6 @@ plugins {
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'jacoco'
|
||||
apply from: "$project.rootDir/automation/gradle/versionCode.gradle"
|
||||
apply plugin: 'androidx.navigation.safeargs.kotlin'
|
||||
|
|
|
@ -23,10 +23,11 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
|
|||
hasUserGesture: Boolean,
|
||||
isSameDomain: Boolean,
|
||||
isRedirect: Boolean,
|
||||
isDirectNavigation: Boolean
|
||||
isDirectNavigation: Boolean,
|
||||
isSubframeRequest: Boolean
|
||||
): RequestInterceptor.InterceptionResponse? {
|
||||
return appContext.components.services.accountsAuthFeature.interceptor.onLoadRequest(
|
||||
engineSession, uri, hasUserGesture, isSameDomain, isRedirect, isDirectNavigation
|
||||
engineSession, uri, hasUserGesture, isSameDomain, isRedirect, isDirectNavigation, isSubframeRequest
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,9 +92,10 @@ class MenuScreenShotTest : ScreenshotTest() {
|
|||
Screengrab.screenshot("SettingsSubMenuDefaultBrowserRobot_settings-default-browser")
|
||||
mDevice.pressBack()
|
||||
|
||||
settingsTP()
|
||||
Screengrab.screenshot("settings-enhanced-tp")
|
||||
mDevice.pressBack()
|
||||
// Disabled for Pixel 2
|
||||
// settingsTP()
|
||||
// Screengrab.screenshot("settings-enhanced-tp")
|
||||
// mDevice.pressBack()
|
||||
|
||||
loginsAndPassword()
|
||||
Screengrab.screenshot("SettingsSubMenuLoginsAndPasswords-settings-logins-passwords")
|
||||
|
|
|
@ -234,7 +234,7 @@ class BookmarksTest {
|
|||
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
|
||||
}.openThreeDotMenu(defaultWebPage.url) {
|
||||
}.clickOpenInNewTab {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyNormalModeSelected()
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ class BookmarksTest {
|
|||
IdlingRegistry.getInstance().register(bookmarksListIdlingResource!!)
|
||||
}.openThreeDotMenu(defaultWebPage.url) {
|
||||
}.clickOpenInPrivateTab {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("Link 1")
|
||||
verifyLinkContextMenuItems(genericURL.url)
|
||||
clickContextOpenLinkInNewTab()
|
||||
|
@ -83,7 +83,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("Link 2")
|
||||
verifyLinkContextMenuItems(genericURL.url)
|
||||
clickContextOpenLinkInPrivateTab()
|
||||
|
@ -105,7 +105,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("Link 3")
|
||||
verifyLinkContextMenuItems(genericURL.url)
|
||||
clickContextCopyLink()
|
||||
|
@ -125,12 +125,13 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("Link 1")
|
||||
verifyLinkContextMenuItems(genericURL.url)
|
||||
clickContextShareLink(genericURL.url) // verify share intent is matched with associated URL
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Intermittent: https://github.com/mozilla-mobile/fenix/issues/12367")
|
||||
@Test
|
||||
fun verifyContextOpenImageNewTab() {
|
||||
|
@ -141,7 +142,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("test_link_image")
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
clickContextOpenImageNewTab()
|
||||
|
@ -161,7 +162,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("test_link_image")
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
clickContextCopyImageLocation()
|
||||
|
@ -182,7 +183,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("test_link_image")
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
clickContextSaveImage()
|
||||
|
@ -209,7 +210,7 @@ class ContextMenusTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
// verifyPageContent(pageLinks.content)
|
||||
mDevice.waitForIdle()
|
||||
longClickMatchingText("Link 1")
|
||||
verifyLinkContextMenuItems(genericURL.url)
|
||||
dismissContentContextMenu(genericURL.url)
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/* 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.ui
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.ui.robots.DeepLinkRobot
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of deep links
|
||||
* - fenix://home
|
||||
* - fenix://open
|
||||
* - fenix://settings_notifications — take the user to the notification settings page
|
||||
* - fenix://settings_privacy — take the user to the privacy settings page.
|
||||
* - fenix://settings_search_engine — take the user to the search engine page, to set the default search engine.
|
||||
* - fenix://home_collections — take the user to the home screen to see the list of collections.
|
||||
* - fenix://urls_history — take the user to the history list.
|
||||
* - fenix://urls_bookmarks — take the user to the bookmarks list
|
||||
* - fenix://settings_logins — take the user to the settings page to do with logins (not the saved logins).
|
||||
**/
|
||||
class DeepLinkTest {
|
||||
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
private val robot = DeepLinkRobot()
|
||||
|
||||
@get:Rule
|
||||
val activityIntentTestRule = HomeActivityIntentTestRule()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
setDispatcher(AndroidAssetDispatcher())
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openHomeScreen() {
|
||||
robot.openHomeScreen {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
robot.openSettings { /* move away from the home screen */ }
|
||||
robot.openHomeScreen {
|
||||
verifyHomeComponent()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openURL() {
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
robot.openURL(genericURL.url.toString()) {
|
||||
verifyUrl(genericURL.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openBookmarks() {
|
||||
robot.openBookmarks {
|
||||
// verify we can see headings.
|
||||
verifyFolderTitle("Desktop Bookmarks")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openHistory() {
|
||||
robot.openHistory {
|
||||
verifyHistoryMenuView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openCollections() {
|
||||
robot.openHomeScreen { /* do nothing */ }.dismissOnboarding()
|
||||
robot.openCollections {
|
||||
verifyCollectionsHeader()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openSettings() {
|
||||
robot.openSettings {
|
||||
verifyBasicsHeading()
|
||||
verifyAdvancedHeading()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openSettingsLogins() {
|
||||
robot.openSettingsLogins {
|
||||
verifyDefaultView()
|
||||
verifyDefaultValueSyncLogins()
|
||||
verifyDefaultValueAutofillLogins()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openSettingsPrivacy() {
|
||||
robot.openSettingsPrivacy {
|
||||
verifyPrivacyHeading()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openSettingsTrackingProtection() {
|
||||
robot.openSettingsTrackingProtection {
|
||||
verifyEnhancedTrackingProtectionHeader()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openSettingsSearchEngine() {
|
||||
robot.openSettingsSearchEngine {
|
||||
verifyDefaultSearchEngineHeader()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openSettingsNotifications() {
|
||||
robot.openSettingsNotification {
|
||||
verifyNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openMakeDefaultBrowser() {
|
||||
robot.openMakeDefaultBrowser {
|
||||
verifyMakeDefaultBrowser()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -82,7 +82,7 @@ class DownloadTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
clickLinkMatchingText(defaultWebPage.content)
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,7 @@ class DownloadTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
clickLinkMatchingText(defaultWebPage.content)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.mozilla.fenix.helpers.TestAssetHelper
|
|||
import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem
|
||||
import org.mozilla.fenix.ui.robots.historyMenu
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.mDevice
|
||||
import org.mozilla.fenix.ui.robots.multipleSelectionToolbar
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
|
@ -70,7 +71,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryMenuView()
|
||||
|
@ -86,7 +87,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.openThreeDotMenu {
|
||||
|
@ -101,7 +102,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.openThreeDotMenu {
|
||||
|
@ -119,12 +120,12 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.openThreeDotMenu {
|
||||
}.clickOpenInNormalTab {
|
||||
// verifyPageContent(firstWebPage.content)
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyNormalModeSelected()
|
||||
}
|
||||
|
@ -136,12 +137,12 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.openThreeDotMenu {
|
||||
}.clickOpenInPrivateTab {
|
||||
// verifyPageContent(firstWebPage.content)
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
|
@ -153,7 +154,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.openThreeDotMenu {
|
||||
|
@ -168,7 +169,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
clickDeleteHistoryButton()
|
||||
|
@ -184,7 +185,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
|
@ -206,7 +207,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openTabDrawer {
|
||||
closeTab()
|
||||
}.openHomeScreen { }.openThreeDotMenu {
|
||||
|
@ -228,7 +229,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
|
@ -249,12 +250,11 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
}.openTabDrawer { }.openHomeScreen { }
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
// verifyPageContent("Page content: 2")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
|
@ -277,7 +277,7 @@ class HistoryTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
// verifyPageContent("Page content: 1")
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
|
|
|
@ -58,10 +58,8 @@ class MediaNotificationTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(videoTestPage.url) {
|
||||
// verifyPageContent(videoTestPage.content)
|
||||
mDevice.waitForIdle()
|
||||
clickMediaPlayerPlayButton()
|
||||
waitForPlaybackToStart()
|
||||
// verifyPageContent("Media file is playing")
|
||||
}.openNotificationShade {
|
||||
verifySystemNotificationExists(videoTestPage.title)
|
||||
clickMediaSystemNotificationControlButton("Pause")
|
||||
|
@ -92,7 +90,7 @@ class MediaNotificationTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||
// verifyPageContent(audioTestPage.content)
|
||||
mDevice.waitForIdle()
|
||||
clickMediaPlayerPlayButton()
|
||||
waitForPlaybackToStart()
|
||||
}.openNotificationShade {
|
||||
|
@ -125,10 +123,9 @@ class MediaNotificationTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||
// verifyPageContent(audioTestPage.content)
|
||||
mDevice.waitForIdle()
|
||||
clickMediaPlayerPlayButton()
|
||||
waitForPlaybackToStart()
|
||||
// verifyPageContent("Media file is playing")
|
||||
}.openTabDrawer {
|
||||
verifyTabMediaControlButtonState("Pause")
|
||||
clickTabMediaControlButton()
|
||||
|
@ -146,10 +143,9 @@ class MediaNotificationTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||
// verifyPageContent(audioTestPage.content)
|
||||
mDevice.waitForIdle()
|
||||
clickMediaPlayerPlayButton()
|
||||
waitForPlaybackToStart()
|
||||
// verifyPageContent("Media file is playing")
|
||||
}.openNotificationShade {
|
||||
verifySystemNotificationExists("A site is playing media")
|
||||
clickMediaSystemNotificationControlButton("Pause")
|
||||
|
|
|
@ -55,18 +55,12 @@ class NavigationToolbarTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(nextWebPage.url) {
|
||||
// verifyPageContent(nextWebPage.content)
|
||||
}
|
||||
|
||||
// Re-open the three-dot menu for verification
|
||||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
verifyThreeDotMenuExists()
|
||||
}.goBack {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
verifyUrl(nextWebPage.url.toString())
|
||||
mDevice.pressBack()
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,12 +71,14 @@ class NavigationToolbarTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(nextWebPage.url) {
|
||||
// verifyPageContent(nextWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
verifyUrl(nextWebPage.url.toString())
|
||||
mDevice.pressBack()
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}
|
||||
|
||||
// Re-open the three-dot menu for verification
|
||||
|
@ -91,7 +87,7 @@ class NavigationToolbarTest {
|
|||
verifyThreeDotMenuExists()
|
||||
verifyForwardButton()
|
||||
}.goForward {
|
||||
// verifyPageContent(nextWebPage.content)
|
||||
verifyUrl(nextWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +97,7 @@ class NavigationToolbarTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(refreshWebPage.url) {
|
||||
// verifyPageContent("DEFAULT")
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
// Use refresh from the three-dot menu
|
||||
|
@ -110,7 +106,7 @@ class NavigationToolbarTest {
|
|||
verifyThreeDotMenuExists()
|
||||
verifyRefreshButton()
|
||||
}.refreshPage {
|
||||
// verifyPageContent("REFRESHED")
|
||||
verifyPageContent("REFRESHED")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,7 +116,7 @@ class NavigationToolbarTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +127,7 @@ class NavigationToolbarTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(loremIpsumWebPage.url) {
|
||||
// verifyPageContent(loremIpsumWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
|||
import org.mozilla.fenix.helpers.ViewVisibilityIdlingResource
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.ui.robots.mDevice
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of content context menus
|
||||
|
@ -70,7 +71,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
// verifyPageContent(readerViewPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(readerViewNotificationDot)
|
||||
|
@ -99,7 +100,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericPage.url) {
|
||||
// verifyPageContent(genericPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
readerViewRobot {
|
||||
|
@ -120,7 +121,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
// verifyPageContent(readerViewPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(readerViewNotificationDot)
|
||||
|
@ -152,7 +153,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
// verifyPageContent(readerViewPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(readerViewNotificationDot)
|
||||
|
@ -187,7 +188,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
// verifyPageContent(readerViewPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(readerViewNotificationDot)
|
||||
|
@ -222,7 +223,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
// verifyPageContent(readerViewPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(readerViewNotificationDot)
|
||||
|
@ -263,7 +264,7 @@ class ReaderViewTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
// verifyPageContent(readerViewPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
IdlingRegistry.getInstance().register(readerViewNotificationDot)
|
||||
|
|
|
@ -133,27 +133,24 @@ class SettingsBasicsTest {
|
|||
homeScreen {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(page1.url) {
|
||||
// verifyPageContent(page1.content)
|
||||
}.openThreeDotMenu {
|
||||
clickAddBookmarkButton()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(page2.url) {
|
||||
// verifyPageContent(page2.content)
|
||||
}.openThreeDotMenu {
|
||||
clickAddBookmarkButton()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(page3.url) {
|
||||
// verifyPageContent(page3.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
verifyNoHistoryBookmarks()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -50,7 +50,7 @@ class ShareButtonTest {
|
|||
// - Visit a URL, wait until it's loaded
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
// From the 3-dot menu next to the Select share menu
|
||||
|
|
|
@ -50,12 +50,10 @@ class SmokeTest {
|
|||
homeScreen {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
verifyNavURLBarItems()
|
||||
}.openNavigationToolbar {
|
||||
}.goBackToWebsite {
|
||||
// Check disabled due to intermittent failures
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openTabDrawer {
|
||||
verifyExistingTabList()
|
||||
}.openHomeScreen {
|
||||
|
|
|
@ -66,7 +66,7 @@ class TabbedBrowsingTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
verifyTabCounter("1")
|
||||
}.openTabDrawer {
|
||||
verifyExistingTabList()
|
||||
|
@ -92,7 +92,7 @@ class TabbedBrowsingTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
mDevice.waitForIdle()
|
||||
verifyTabCounter("1")
|
||||
}.openTabDrawer {
|
||||
verifyExistingTabList()
|
||||
|
@ -110,7 +110,6 @@ class TabbedBrowsingTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openTabDrawer {
|
||||
verifyExistingTabList()
|
||||
}.openTabsListThreeDotMenu {
|
||||
|
@ -128,7 +127,6 @@ class TabbedBrowsingTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openTabDrawer {
|
||||
verifyPrivateModeSelected()
|
||||
verifyExistingTabList()
|
||||
|
@ -149,7 +147,6 @@ class TabbedBrowsingTest {
|
|||
genericURLS.forEachIndexed { index, element ->
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(element.url) {
|
||||
// verifyPageContent(element.content)
|
||||
}.openTabDrawer {
|
||||
verifyExistingOpenTabs("Test_Page_${index + 1}")
|
||||
verifyCloseTabsButton("Test_Page_${index + 1}")
|
||||
|
@ -182,7 +179,6 @@ class TabbedBrowsingTest {
|
|||
genericURLS.forEachIndexed { index, element ->
|
||||
navigationToolbar {
|
||||
}.openNewTabAndEnterToBrowser(element.url) {
|
||||
// verifyPageContent(element.content)
|
||||
}.openTabDrawer {
|
||||
verifyExistingOpenTabs("Test_Page_${index + 1}")
|
||||
verifyCloseTabsButton("Test_Page_${index + 1}")
|
||||
|
|
|
@ -53,7 +53,6 @@ class TopSitesTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
verifyAddFirefoxHome()
|
||||
}.addToFirefoxHome {
|
||||
|
@ -72,7 +71,6 @@ class TopSitesTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
verifyAddFirefoxHome()
|
||||
}.addToFirefoxHome {
|
||||
|
@ -82,7 +80,6 @@ class TopSitesTest {
|
|||
verifyExistingTopSitesList()
|
||||
verifyExistingTopSitesTabs(defaultWebPageTitle)
|
||||
}.openTopSiteTabWithTitle(title = defaultWebPageTitle) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
verifyUrl(defaultWebPage.url.toString().replace("http://", ""))
|
||||
}.openTabDrawer {
|
||||
}.openHomeScreen {
|
||||
|
@ -103,7 +100,6 @@ class TopSitesTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
verifyAddFirefoxHome()
|
||||
}.addToFirefoxHome {
|
||||
|
@ -126,7 +122,6 @@ class TopSitesTest {
|
|||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
// verifyPageContent(defaultWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
verifyAddFirefoxHome()
|
||||
}.addToFirefoxHome {
|
||||
|
|
|
@ -39,7 +39,6 @@ import org.mozilla.fenix.ext.components
|
|||
import org.mozilla.fenix.helpers.Constants.LONG_CLICK_DURATION
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeShort
|
||||
import org.mozilla.fenix.helpers.click
|
||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||
|
||||
|
@ -81,13 +80,11 @@ class BrowserRobot {
|
|||
/* Asserts that the text within DOM element with ID="testContent" has the given text, i.e.
|
||||
* document.querySelector('#testContent').innerText == expectedText
|
||||
*
|
||||
* This function is not working at intended and needs a replacement.
|
||||
*/
|
||||
|
||||
/* fun verifyPageContent(expectedText: String) {
|
||||
// val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
// mDevice.waitNotNull(Until.findObject(By.textContains(expectedText)), waitingTime)
|
||||
}*/
|
||||
fun verifyPageContent(expectedText: String) {
|
||||
assertTrue(mDevice.findObject(UiSelector().text(expectedText)).waitForExists(waitingTime))
|
||||
}
|
||||
|
||||
fun verifyTabCounter(expectedText: String) {
|
||||
onView(withId(R.id.counter_text))
|
||||
|
@ -336,21 +333,13 @@ class BrowserRobot {
|
|||
}
|
||||
|
||||
fun waitForPlaybackToStart() {
|
||||
mDevice.waitNotNull(
|
||||
hasObject(
|
||||
text("Media file is playing")
|
||||
), waitingTimeShort
|
||||
)
|
||||
val playStateMessage = mDevice.findObject(UiSelector().text("Media file is playing"))
|
||||
assertTrue(playStateMessage.waitForExists(waitingTime))
|
||||
}
|
||||
|
||||
fun verifyMediaIsPaused() {
|
||||
mDevice.waitNotNull(
|
||||
hasObject(
|
||||
text("Media file is paused")
|
||||
), waitingTimeShort
|
||||
)
|
||||
|
||||
mDevice.findObject(UiSelector().text("Media file is paused")).exists()
|
||||
val pausedStateMessage = mDevice.findObject(UiSelector().text("Media file is paused"))
|
||||
assertTrue(pausedStateMessage.waitForExists(waitingTime))
|
||||
}
|
||||
|
||||
class Transition {
|
||||
|
|
|
@ -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 org.mozilla.fenix.ui.robots
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.mozilla.fenix.BuildConfig.DEEP_LINK_SCHEME
|
||||
|
||||
class DeepLinkRobot {
|
||||
private fun openDeepLink(url: String) {
|
||||
val context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
val intent = Intent().apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
data = Uri.parse("$DEEP_LINK_SCHEME://$url")
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
addCategory(Intent.CATEGORY_BROWSABLE)
|
||||
}
|
||||
try {
|
||||
context.startActivity(intent)
|
||||
} catch (ex: ActivityNotFoundException) {
|
||||
intent.setPackage(null)
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
|
||||
fun openURL(url: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||
val deepLink = Uri.parse("open")
|
||||
.buildUpon()
|
||||
.appendQueryParameter("url", url)
|
||||
.build()
|
||||
.toString()
|
||||
openDeepLink(deepLink)
|
||||
return browserScreen(interact)
|
||||
}
|
||||
|
||||
fun openHomeScreen(interact: HomeScreenRobot.() -> Unit) =
|
||||
openDeepLink("home").run { homeScreen(interact) }
|
||||
|
||||
fun openBookmarks(interact: BookmarksRobot.() -> Unit) =
|
||||
openDeepLink("urls_bookmarks").run { bookmarksMenu(interact) }
|
||||
|
||||
fun openHistory(interact: HistoryRobot.() -> Unit) =
|
||||
openDeepLink("urls_history").run { historyMenu(interact) }
|
||||
|
||||
fun openCollections(interact: HomeScreenRobot.() -> Unit) =
|
||||
openDeepLink("home_collections").run { homeScreen(interact) }
|
||||
|
||||
fun openSettings(interact: SettingsRobot.() -> Unit) =
|
||||
openDeepLink("settings").run { settings(interact) }
|
||||
|
||||
fun openSettingsPrivacy(interact: SettingsRobot.() -> Unit) =
|
||||
openDeepLink("settings_privacy").run { settings(interact) }
|
||||
|
||||
fun openSettingsLogins(interact: SettingsSubMenuLoginsAndPasswordRobot.() -> Unit) =
|
||||
openDeepLink("settings_logins").run { settingsSubMenuLoginsAndPassword(interact) }
|
||||
|
||||
fun openSettingsTrackingProtection(interact: SettingsSubMenuEnhancedTrackingProtectionRobot.() -> Unit) =
|
||||
openDeepLink("settings_tracking_protection").run {
|
||||
settingsSubMenuEnhancedTrackingProtection(interact)
|
||||
}
|
||||
|
||||
fun openSettingsSearchEngine(interact: SettingsSubMenuSearchRobot.() -> Unit) =
|
||||
openDeepLink("settings_search_engine").run {
|
||||
SettingsSubMenuSearchRobot().interact()
|
||||
SettingsSubMenuSearchRobot.Transition()
|
||||
}
|
||||
|
||||
fun openSettingsNotification(interact: SystemSettingsRobot.() -> Unit) =
|
||||
openDeepLink("settings_notifications").run { systemSettings(interact) }
|
||||
|
||||
fun openMakeDefaultBrowser(interact: SystemSettingsRobot.() -> Unit) =
|
||||
openDeepLink("make_default_browser").run { systemSettings(interact) }
|
||||
}
|
||||
|
||||
private fun settings(interact: SettingsRobot.() -> Unit) =
|
||||
SettingsRobot().interact().run { SettingsRobot.Transition() }
|
|
@ -0,0 +1,27 @@
|
|||
/* 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.ui.robots
|
||||
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
|
||||
|
||||
class SystemSettingsRobot {
|
||||
fun verifyNotifications() {
|
||||
Intents.intended(hasAction("android.settings.APP_NOTIFICATION_SETTINGS"))
|
||||
}
|
||||
|
||||
fun verifyMakeDefaultBrowser() {
|
||||
Intents.intended(hasAction(SettingsRobot.DEFAULT_APPS_SETTINGS_ACTION))
|
||||
}
|
||||
|
||||
class Transition {
|
||||
// Difficult to know where this will go
|
||||
}
|
||||
}
|
||||
|
||||
fun systemSettings(interact: SystemSettingsRobot.() -> Unit): SystemSettingsRobot.Transition {
|
||||
SystemSettingsRobot().interact()
|
||||
return SystemSettingsRobot.Transition()
|
||||
}
|
|
@ -79,24 +79,42 @@
|
|||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="enable_private_browsing"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="home"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings"/>
|
||||
android:host="home_collections"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="turn_on_sync"/>
|
||||
android:host="install_search_widget"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_search_engine"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_accessibility"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_delete_browsing_data"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="enable_private_browsing"/>
|
||||
android:host="make_default_browser"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="open"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="make_default_browser"/>
|
||||
android:host="settings"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_accessibility"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_addon_manager"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_delete_browsing_data"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_logins"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_notifications"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_privacy"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_search_engine"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="settings_tracking_protection"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="turn_on_sync"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="urls_bookmarks"/>
|
||||
<data android:scheme="${deepLinkScheme}"
|
||||
android:host="urls_history"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
|
|
@ -22,10 +22,13 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
|
|||
hasUserGesture: Boolean,
|
||||
isSameDomain: Boolean,
|
||||
isRedirect: Boolean,
|
||||
isDirectNavigation: Boolean
|
||||
isDirectNavigation: Boolean,
|
||||
isSubframeRequest: Boolean
|
||||
): RequestInterceptor.InterceptionResponse? {
|
||||
return context.components.services.appLinksInterceptor
|
||||
.onLoadRequest(engineSession, uri, hasUserGesture, isSameDomain, isRedirect, isDirectNavigation)
|
||||
.onLoadRequest(
|
||||
engineSession, uri, hasUserGesture, isSameDomain, isRedirect, isDirectNavigation, isSubframeRequest
|
||||
)
|
||||
}
|
||||
|
||||
override fun onErrorRequest(
|
||||
|
|
|
@ -13,7 +13,6 @@ import androidx.appcompat.app.AppCompatDelegate
|
|||
import androidx.core.content.getSystemService
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -67,7 +66,6 @@ open class FenixApplication : LocaleAwareApplication() {
|
|||
var visibilityLifecycleCallback: VisibilityLifecycleCallback? = null
|
||||
private set
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
@ -116,7 +114,6 @@ open class FenixApplication : LocaleAwareApplication() {
|
|||
Log.addSink(AndroidLogSink())
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@CallSuper
|
||||
open fun setupInMainProcessOnly() {
|
||||
run {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package org.mozilla.fenix
|
||||
|
||||
import androidx.navigation.NavDirections
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
|
||||
/**
|
||||
* Used with [HomeActivity] global navigation to indicate which fragment is being opened.
|
||||
|
@ -14,6 +15,14 @@ import androidx.navigation.NavDirections
|
|||
*/
|
||||
enum class GlobalDirections(val navDirections: NavDirections, val destinationId: Int) {
|
||||
Home(NavGraphDirections.actionGlobalHome(), R.id.homeFragment),
|
||||
Bookmarks(
|
||||
NavGraphDirections.actionGlobalBookmarkFragment(BookmarkRoot.Root.id),
|
||||
R.id.bookmarkFragment
|
||||
),
|
||||
History(
|
||||
NavGraphDirections.actionGlobalHistoryFragment(),
|
||||
R.id.historyFragment
|
||||
),
|
||||
Settings(
|
||||
NavGraphDirections.actionGlobalSettingsFragment(),
|
||||
R.id.settingsFragment
|
||||
|
@ -37,5 +46,13 @@ enum class GlobalDirections(val navDirections: NavDirections, val destinationId:
|
|||
SettingsAddonManager(
|
||||
NavGraphDirections.actionGlobalSettingsAddonsManagementFragment(),
|
||||
R.id.addonsManagementFragment
|
||||
),
|
||||
SettingsLogins(
|
||||
NavGraphDirections.actionGlobalSavedLoginsAuthFragment(),
|
||||
R.id.saveLoginSettingFragment
|
||||
),
|
||||
SettingsTrackingProtection(
|
||||
NavGraphDirections.actionGlobalTrackingProtectionFragment(),
|
||||
R.id.trackingProtectionFragment
|
||||
)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.session.Session
|
||||
|
@ -104,6 +105,7 @@ import org.mozilla.fenix.utils.RunWhenReadyQueue
|
|||
* - home screen
|
||||
* - browser screen
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
||||
open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
||||
|
||||
|
|
|
@ -158,7 +158,6 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
|
|||
onPositiveButtonClicked = onPositiveButtonClicked
|
||||
)
|
||||
dialog.show(parentFragmentManager, PERMISSIONS_DIALOG_FRAGMENT_TAG)
|
||||
requireContext().components.analytics.metrics.track(Event.AddonInstalled(addon.id))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
|
||||
package org.mozilla.fenix.components
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.toUri
|
||||
import mozilla.components.feature.addons.AddonManager
|
||||
import mozilla.components.feature.addons.amo.AddonCollectionProvider
|
||||
|
@ -106,5 +106,5 @@ class Components(private val context: Context) {
|
|||
val migrationStore by lazy { MigrationStore() }
|
||||
val performance by lazy { PerformanceComponent() }
|
||||
val push by lazy { Push(context, analytics.crashReporter) }
|
||||
val wifiConnectionMonitor by lazy { WifiConnectionMonitor(context as Application) }
|
||||
val wifiConnectionMonitor by lazy { WifiConnectionMonitor(context.getSystemService()!!) }
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ import org.mozilla.fenix.BuildConfig.DIGITAL_ASSET_LINKS_TOKEN
|
|||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ReleaseChannel
|
||||
import org.mozilla.fenix.downloads.DownloadService
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
|
@ -97,11 +96,7 @@ class Core(private val context: Context) {
|
|||
* disabled in Fenix Release builds for now.
|
||||
* This is consistent with both Fennec and Firefox Desktop.
|
||||
*/
|
||||
val shouldEnableWebcompatReporter = Config.channel !in setOf(
|
||||
ReleaseChannel.FenixProduction,
|
||||
ReleaseChannel.FennecProduction
|
||||
)
|
||||
if (shouldEnableWebcompatReporter) {
|
||||
if (Config.channel.isNightlyOrDebug || Config.channel.isBeta) {
|
||||
WebCompatReporterFeature.install(it)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,6 +102,15 @@ class LeanplumMetricsService(private val application: Application) : MetricsServ
|
|||
|
||||
val installedApps = MozillaProductDetector.getInstalledMozillaProducts(application)
|
||||
|
||||
val trackingProtection = application.settings().run {
|
||||
when {
|
||||
!shouldUseTrackingProtection -> "none"
|
||||
useStandardTrackingProtection -> "standard"
|
||||
useStrictTrackingProtection -> "strict"
|
||||
else -> "custom"
|
||||
}
|
||||
}
|
||||
|
||||
Leanplum.start(application, hashMapOf(
|
||||
"default_browser" to MozillaProductDetector.getMozillaBrowserDefault(application).orEmpty(),
|
||||
"fennec_installed" to installedApps.contains(MozillaProducts.FIREFOX.productName),
|
||||
|
@ -110,6 +119,8 @@ class LeanplumMetricsService(private val application: Application) : MetricsServ
|
|||
"fxa_signed_in" to application.settings().fxaSignedIn,
|
||||
"fxa_has_synced_items" to application.settings().fxaHasSyncedItems,
|
||||
"search_widget_installed" to application.settings().searchWidgetInstalled,
|
||||
"tracking_protection_enabled" to application.settings().shouldUseTrackingProtection,
|
||||
"tracking_protection_setting" to trackingProtection,
|
||||
"fenix" to true
|
||||
))
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ package org.mozilla.fenix.components.searchengine
|
|||
import android.content.Context
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
|
@ -33,14 +32,16 @@ import java.util.Locale
|
|||
open class FenixSearchEngineProvider(
|
||||
private val context: Context
|
||||
) : SearchEngineProvider, CoroutineScope by CoroutineScope(Job() + Dispatchers.IO) {
|
||||
private val locationService: LocationService = if (Config.channel.isDebug) {
|
||||
LocationService.dummy()
|
||||
} else {
|
||||
MozillaLocationService(
|
||||
context,
|
||||
context.components.core.client,
|
||||
BuildConfig.MLS_TOKEN
|
||||
)
|
||||
private val locationService = with(MozillaLocationService(
|
||||
context,
|
||||
context.components.core.client,
|
||||
BuildConfig.MLS_TOKEN
|
||||
)) {
|
||||
if (Config.channel.isDebug || !this.hasRegionCached()) {
|
||||
LocationService.dummy()
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
// We have two search engine types: one based on MLS reported region, one based only on Locale.
|
||||
|
@ -93,17 +94,6 @@ open class FenixSearchEngineProvider(
|
|||
|
||||
private var loadedSearchEngines = refreshAsync()
|
||||
|
||||
// https://github.com/mozilla-mobile/fenix/issues/9935
|
||||
// Create new getter that will return the fallback SearchEngineList if
|
||||
// the main one hasn't completed yet
|
||||
private val searchEngines: Deferred<SearchEngineList>
|
||||
get() =
|
||||
if (isRegionCachedByLocationService) {
|
||||
loadedSearchEngines
|
||||
} else {
|
||||
fallbackEngines
|
||||
}
|
||||
|
||||
fun getDefaultEngine(context: Context): SearchEngine {
|
||||
val engines = installedSearchEngines(context)
|
||||
val selectedName = context.settings().defaultSearchEngineName
|
||||
|
@ -117,7 +107,7 @@ open class FenixSearchEngineProvider(
|
|||
*/
|
||||
fun installedSearchEngines(context: Context): SearchEngineList = runBlocking {
|
||||
val installedIdentifiers = installedSearchEngineIdentifiers(context)
|
||||
val engineList = searchEngines.await()
|
||||
val engineList = loadedSearchEngines.await()
|
||||
|
||||
engineList.copy(
|
||||
list = engineList.list.filter {
|
||||
|
@ -188,7 +178,11 @@ open class FenixSearchEngineProvider(
|
|||
}
|
||||
|
||||
private fun refreshAsync() = async {
|
||||
val engineList = baseSearchEngines.await()
|
||||
val engineList = if (isRegionCachedByLocationService) {
|
||||
baseSearchEngines.await()
|
||||
} else {
|
||||
fallbackEngines.await()
|
||||
}
|
||||
val bundledList = bundledSearchEngines.await().list
|
||||
val customList = customSearchEngines.await().list
|
||||
|
||||
|
|
|
@ -126,7 +126,6 @@ suspend fun String.toRoundedDrawable(context: Context, client: Client) = bitmapF
|
|||
}
|
||||
|
||||
suspend fun bitmapForUrl(url: String, client: Client): Bitmap? = withContext(Dispatchers.IO) {
|
||||
// TODO cache this image, see https://github.com/mozilla-mobile/fenix/issues/9531
|
||||
// Code below will cache it in Gecko's cache, which ensures that as long as we've fetched it once,
|
||||
// we will be able to display this avatar as long as the cache isn't purged (e.g. via 'clear user data').
|
||||
val body = try {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* 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.ext
|
||||
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.widget.TextView
|
||||
import androidx.core.text.toSpannable
|
||||
|
||||
/**
|
||||
* Adds an underline effect to the text displayed in the TextView.
|
||||
*/
|
||||
fun TextView.addUnderline() {
|
||||
val currentText = text
|
||||
text = currentText.toSpannable().apply {
|
||||
setSpan(UnderlineSpan(), 0, currentText.length, 0)
|
||||
}
|
||||
}
|
|
@ -4,16 +4,19 @@
|
|||
|
||||
package org.mozilla.fenix.home.intent
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.provider.Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS
|
||||
import android.provider.Settings
|
||||
import androidx.navigation.NavController
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.BuildConfig
|
||||
import org.mozilla.fenix.GlobalDirections
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.SearchWidgetCreator
|
||||
import org.mozilla.fenix.ext.alreadyOnDestination
|
||||
|
||||
/**
|
||||
|
@ -24,7 +27,7 @@ class DeepLinkIntentProcessor(
|
|||
) : HomeIntentProcessor {
|
||||
|
||||
override fun process(intent: Intent, navController: NavController, out: Intent): Boolean {
|
||||
val scheme = intent.scheme?.contains("fenix") ?: return false
|
||||
val scheme = intent.scheme?.equals(BuildConfig.DEEP_LINK_SCHEME, ignoreCase = true) ?: return false
|
||||
return if (scheme) {
|
||||
intent.data?.let { handleDeepLink(it, navController) }
|
||||
true
|
||||
|
@ -39,12 +42,22 @@ class DeepLinkIntentProcessor(
|
|||
|
||||
val globalDirections = when (deepLink.host) {
|
||||
"home", "enable_private_browsing" -> GlobalDirections.Home
|
||||
"urls_bookmarks" -> GlobalDirections.Bookmarks
|
||||
"urls_history" -> GlobalDirections.History
|
||||
"settings" -> GlobalDirections.Settings
|
||||
"turn_on_sync" -> GlobalDirections.Sync
|
||||
"settings_search_engine" -> GlobalDirections.SearchEngine
|
||||
"settings_accessibility" -> GlobalDirections.Accessibility
|
||||
"settings_delete_browsing_data" -> GlobalDirections.DeleteData
|
||||
"settings_addon_manager" -> GlobalDirections.SettingsAddonManager
|
||||
"settings_logins" -> GlobalDirections.SettingsLogins
|
||||
"settings_tracking_protection" -> GlobalDirections.SettingsTrackingProtection
|
||||
// We'd like to highlight views within the fragment
|
||||
// https://github.com/mozilla-mobile/fenix/issues/11856
|
||||
// The current version of UI has these features in more complex screens.
|
||||
"settings_privacy" -> GlobalDirections.Settings
|
||||
"home_collections" -> GlobalDirections.Home
|
||||
|
||||
else -> return
|
||||
}
|
||||
|
||||
|
@ -63,7 +76,7 @@ class DeepLinkIntentProcessor(
|
|||
}
|
||||
"make_default_browser" -> {
|
||||
if (SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val settingsIntent = Intent(ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
|
||||
val settingsIntent = Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS)
|
||||
activity.startActivity(settingsIntent)
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +89,38 @@ class DeepLinkIntentProcessor(
|
|||
)
|
||||
}
|
||||
}
|
||||
"settings_notifications" -> {
|
||||
val intent = notificationSettings(activity)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
"install_search_widget" -> {
|
||||
if (SDK_INT >= Build.VERSION_CODES.O) {
|
||||
SearchWidgetCreator.createSearchWidget(activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun notificationSettings(context: Context, channel: String? = null) =
|
||||
Intent().apply {
|
||||
when {
|
||||
SDK_INT >= Build.VERSION_CODES.O -> {
|
||||
action = channel?.let {
|
||||
putExtra(Settings.EXTRA_CHANNEL_ID, it)
|
||||
Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS
|
||||
} ?: Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||
}
|
||||
SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> {
|
||||
action = "android.settings.APP_NOTIFICATION_SETTINGS"
|
||||
putExtra("app_package", context.packageName)
|
||||
putExtra("app_uid", context.applicationInfo.uid)
|
||||
}
|
||||
else -> {
|
||||
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
addCategory(Intent.CATEGORY_DEFAULT)
|
||||
data = Uri.parse("package:" + context.packageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,15 +9,14 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.top.sites.TopSite
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.home.HomeFragmentState
|
||||
import org.mozilla.fenix.home.HomeScreenViewModel
|
||||
import org.mozilla.fenix.home.Mode
|
||||
import org.mozilla.fenix.home.OnboardingState
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
|
||||
// This method got a little complex with the addition of the tab tray feature flag
|
||||
// When we remove the tabs from the home screen this will get much simpler again.
|
||||
|
@ -110,7 +109,6 @@ private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapI
|
|||
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
class SessionControlView(
|
||||
override val containerView: View?,
|
||||
interactor: SessionControlInteractor,
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
|
||||
package org.mozilla.fenix.home.sessioncontrol.viewholders
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.private_browsing_description.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.addUnderline
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor
|
||||
|
||||
class PrivateBrowsingDescriptionViewHolder(
|
||||
|
@ -24,13 +23,9 @@ class PrivateBrowsingDescriptionViewHolder(
|
|||
view.private_session_description.text = resources.getString(
|
||||
R.string.private_browsing_placeholder_description_2, appName
|
||||
)
|
||||
val commonMythsText = view.private_session_common_myths.text.toString()
|
||||
val textWithLink = SpannableString(commonMythsText).apply {
|
||||
setSpan(UnderlineSpan(), 0, commonMythsText.length, 0)
|
||||
}
|
||||
with(view.private_session_common_myths) {
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
text = textWithLink
|
||||
addUnderline()
|
||||
setOnClickListener {
|
||||
interactor.onPrivateBrowsingLearnMoreClicked()
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
|||
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.content.getDrawableWithTint
|
||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.setBounds
|
||||
|
@ -16,9 +16,9 @@ import org.mozilla.fenix.ext.setBounds
|
|||
* Sets the drawableStart of a header in an onboarding card.
|
||||
*/
|
||||
fun TextView.setOnboardingIcon(@DrawableRes id: Int) {
|
||||
val icon = AppCompatResources.getDrawable(context, id)
|
||||
val size = context.resources.getDimensionPixelSize(R.dimen.onboarding_header_icon_height_width)
|
||||
icon?.setBounds(size)
|
||||
icon?.setTint(context.getColorFromAttr(R.attr.onboardingSelected))
|
||||
val icon = context.getDrawableWithTint(id, context.getColorFromAttr(R.attr.onboardingSelected))?.apply {
|
||||
val size = context.resources.getDimensionPixelSize(R.dimen.onboarding_header_icon_height_width)
|
||||
setBounds(size)
|
||||
}
|
||||
putCompoundDrawablesRelative(start = icon)
|
||||
}
|
||||
|
|
|
@ -5,11 +5,11 @@
|
|||
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.navigation.Navigation
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.onboarding_manual_signin.view.*
|
||||
import mozilla.components.support.ktx.android.content.getDrawableWithTint
|
||||
import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
|
@ -34,8 +34,10 @@ class OnboardingManualSignInViewHolder(view: View) : RecyclerView.ViewHolder(vie
|
|||
|
||||
val appName = context.getString(R.string.app_name)
|
||||
headerText.text = context.getString(R.string.onboarding_firefox_account_header, appName)
|
||||
val icon = AppCompatResources.getDrawable(context, R.drawable.ic_onboarding_firefox_accounts)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.white_color))
|
||||
val icon = context.getDrawableWithTint(
|
||||
R.drawable.ic_onboarding_firefox_accounts,
|
||||
ContextCompat.getColor(context, R.color.white_color)
|
||||
)
|
||||
headerText.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
|
||||
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.onboarding_whats_new.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.addUnderline
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
|
||||
|
||||
|
@ -25,12 +24,7 @@ class OnboardingWhatsNewViewHolder(
|
|||
val appName = view.context.getString(R.string.app_name)
|
||||
view.description_text.text = view.context.getString(R.string.onboarding_whats_new_description, appName)
|
||||
|
||||
val getAnswersText = view.get_answers.text.toString()
|
||||
val textWithLink = SpannableString(getAnswersText).apply {
|
||||
setSpan(UnderlineSpan(), 0, getAnswersText.length, 0)
|
||||
}
|
||||
|
||||
view.get_answers.text = textWithLink
|
||||
view.get_answers.addUnderline()
|
||||
view.get_answers.setOnClickListener {
|
||||
interactor.onWhatsNewGetAnswersClicked()
|
||||
view.context.components.analytics.metrics.track(Event.OnboardingWhatsNew)
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package org.mozilla.fenix.home.tips
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.button_tip_item.view.*
|
||||
|
@ -11,6 +9,7 @@ import org.mozilla.fenix.R
|
|||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.components.tips.TipType
|
||||
import org.mozilla.fenix.ext.addUnderline
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
|
||||
|
@ -36,12 +35,7 @@ class ButtonTipViewHolder(
|
|||
if (tip.learnMoreURL == null) {
|
||||
tip_learn_more.visibility = View.GONE
|
||||
} else {
|
||||
val learnMoreText = context.getString(R.string.search_suggestions_onboarding_learn_more_link)
|
||||
val textWithLink = SpannableString(learnMoreText).apply {
|
||||
setSpan(UnderlineSpan(), 0, learnMoreText.length, 0)
|
||||
}
|
||||
|
||||
tip_learn_more.text = textWithLink
|
||||
tip_learn_more.addUnderline()
|
||||
|
||||
tip_learn_more.setOnClickListener {
|
||||
(context as HomeActivity).openToBrowserAndLoad(
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
* 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
|
||||
package org.mozilla.fenix.migration
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import mozilla.components.lib.state.ext.flowScoped
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
|
@ -16,6 +17,8 @@ class MigrationTelemetryListener(
|
|||
private val metrics: MetricController,
|
||||
private val store: MigrationStore
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun start() {
|
||||
// Observe for migration completed.
|
||||
store.flowScoped { flow ->
|
||||
|
|
|
@ -7,8 +7,8 @@ package org.mozilla.fenix.perf
|
|||
import android.app.Activity
|
||||
import android.view.View
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.reportFullyDrawnSafe
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
|
||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination.APP_LINK
|
||||
|
@ -36,7 +36,7 @@ class StartupReportFullyDrawn {
|
|||
state is StartupState.Cold && state.destination == APP_LINK) {
|
||||
// Instrumenting the first frame drawn should be good enough for app link for now.
|
||||
isInstrumented = true
|
||||
attachReportFullyDrawn(activity, activity.rootContainer)
|
||||
attachReportFullyDrawn(activity, activity.findViewById(R.id.rootContainer))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ class StartupReportFullyDrawn {
|
|||
}
|
||||
}
|
||||
|
||||
private fun attachReportFullyDrawn(activity: HomeActivity, view: View) {
|
||||
private fun attachReportFullyDrawn(activity: Activity, view: View) {
|
||||
// For greater accuracy, we could add an onDrawListener instead of a preDrawListener but:
|
||||
// - single use onDrawListeners are not built-in and it's non-trivial to write one
|
||||
// - the difference in timing is minimal (< 7ms on Pixel 2)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package org.mozilla.fenix.search.telemetry
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
@ -85,6 +86,7 @@ abstract class BaseSearchTelemetry {
|
|||
internal fun getProviderForUrl(url: String): SearchProviderModel? =
|
||||
providerList.find { provider -> provider.regexp.containsMatchIn(url) }
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal fun installWebExtension(
|
||||
engine: Engine,
|
||||
store: BrowserStore,
|
||||
|
|
|
@ -15,10 +15,10 @@ import mozilla.components.concept.engine.Engine
|
|||
import mozilla.components.concept.storage.HistoryStorage
|
||||
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.support.ktx.android.view.hideKeyboard
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.search.SearchFragmentState
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
|
||||
/**
|
||||
* Interface for the Toolbar Interactor. This interface is implemented by objects that want
|
||||
|
@ -71,10 +71,9 @@ class ToolbarView(
|
|||
false
|
||||
}
|
||||
|
||||
background =
|
||||
AppCompatResources.getDrawable(
|
||||
context, ThemeManager.resolveAttribute(R.attr.foundation, context)
|
||||
)
|
||||
background = AppCompatResources.getDrawable(
|
||||
context, context.theme.resolveAttribute(R.attr.foundation)
|
||||
)
|
||||
|
||||
edit.hint = context.getString(R.string.search_hint)
|
||||
|
||||
|
|
|
@ -16,32 +16,44 @@ interface LocaleSettingsController {
|
|||
}
|
||||
|
||||
class DefaultLocaleSettingsController(
|
||||
private val context: Context,
|
||||
private val activity: Activity,
|
||||
private val localeSettingsStore: LocaleSettingsStore
|
||||
) : LocaleSettingsController {
|
||||
|
||||
override fun handleLocaleSelected(locale: Locale) {
|
||||
if (localeSettingsStore.state.selectedLocale == locale &&
|
||||
!LocaleManager.isDefaultLocaleSelected(context)) {
|
||||
!LocaleManager.isDefaultLocaleSelected(activity)) {
|
||||
return
|
||||
}
|
||||
localeSettingsStore.dispatch(LocaleSettingsAction.Select(locale))
|
||||
LocaleManager.setNewLocale(context, locale.toLanguageTag())
|
||||
LocaleManager.updateBaseConfiguration(context, locale)
|
||||
(context as Activity).recreate()
|
||||
LocaleManager.setNewLocale(activity, locale.toLanguageTag())
|
||||
LocaleManager.updateBaseConfiguration(activity, locale)
|
||||
activity.recreate()
|
||||
}
|
||||
|
||||
override fun handleDefaultLocaleSelected() {
|
||||
if (LocaleManager.isDefaultLocaleSelected(context)) {
|
||||
if (LocaleManager.isDefaultLocaleSelected(activity)) {
|
||||
return
|
||||
}
|
||||
localeSettingsStore.dispatch(LocaleSettingsAction.Select(localeSettingsStore.state.localeList[0]))
|
||||
LocaleManager.resetToSystemDefault(context)
|
||||
LocaleManager.updateBaseConfiguration(context, localeSettingsStore.state.localeList[0])
|
||||
(context as Activity).recreate()
|
||||
LocaleManager.resetToSystemDefault(activity)
|
||||
LocaleManager.updateBaseConfiguration(activity, localeSettingsStore.state.localeList[0])
|
||||
activity.recreate()
|
||||
}
|
||||
|
||||
override fun handleSearchQueryTyped(query: String) {
|
||||
localeSettingsStore.dispatch(LocaleSettingsAction.Search(query))
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the locale for the configuration of the app context's resources
|
||||
*/
|
||||
@Suppress("Deprecation")
|
||||
fun LocaleManager.updateBaseConfiguration(context: Context, locale: Locale) {
|
||||
val resources = context.applicationContext.resources
|
||||
val config = resources.configuration
|
||||
config.setLocale(locale)
|
||||
config.setLayoutDirection(locale)
|
||||
resources.updateConfiguration(config, resources.displayMetrics)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,9 @@
|
|||
package org.mozilla.fenix.settings.advanced
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.locale_settings_item.view.*
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import org.mozilla.fenix.R
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -95,67 +90,3 @@ class LocaleAdapter(private val interactor: LocaleSettingsViewInteractor) :
|
|||
DEFAULT, LOCALE;
|
||||
}
|
||||
}
|
||||
|
||||
class LocaleViewHolder(
|
||||
view: View,
|
||||
selectedLocale: Locale,
|
||||
private val interactor: LocaleSettingsViewInteractor
|
||||
) : BaseLocaleViewHolder(view, selectedLocale) {
|
||||
private val icon = view.locale_selected_icon
|
||||
private val title = view.locale_title_text
|
||||
private val subtitle = view.locale_subtitle_text
|
||||
|
||||
override fun bind(locale: Locale) {
|
||||
// capitalisation is done using the rules of the appropriate locale (endonym and exonym)
|
||||
title.text = locale.getDisplayName(locale).capitalize(locale)
|
||||
subtitle.text = locale.displayName.capitalize(Locale.getDefault())
|
||||
icon.isVisible = isCurrentLocaleSelected(locale, isDefault = false)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
interactor.onLocaleSelected(locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SystemLocaleViewHolder(
|
||||
view: View,
|
||||
selectedLocale: Locale,
|
||||
private val interactor: LocaleSettingsViewInteractor
|
||||
) : BaseLocaleViewHolder(view, selectedLocale) {
|
||||
private val icon = view.locale_selected_icon
|
||||
private val title = view.locale_title_text
|
||||
private val subtitle = view.locale_subtitle_text
|
||||
|
||||
override fun bind(locale: Locale) {
|
||||
title.text = itemView.context.getString(R.string.default_locale_text)
|
||||
subtitle.visibility = View.GONE
|
||||
icon.isVisible = isCurrentLocaleSelected(locale, isDefault = true)
|
||||
itemView.setOnClickListener {
|
||||
interactor.onDefaultLocaleSelected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseLocaleViewHolder(
|
||||
view: View,
|
||||
private val selectedLocale: Locale
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
|
||||
internal fun isCurrentLocaleSelected(locale: Locale, isDefault: Boolean): Boolean {
|
||||
return if (isDefault) {
|
||||
locale == selectedLocale && LocaleManager.isDefaultLocaleSelected(itemView.context)
|
||||
} else {
|
||||
locale == selectedLocale && !LocaleManager.isDefaultLocaleSelected(itemView.context)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun bind(locale: Locale)
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to Kotlin's capitalize with locale parameter, but that method is currently experimental
|
||||
*/
|
||||
private fun String.capitalize(locale: Locale): String {
|
||||
return substring(0, 1).toUpperCase(locale) + substring(1)
|
||||
}
|
||||
|
|
|
@ -51,15 +51,3 @@ fun LocaleManager.getSelectedLocale(
|
|||
fun LocaleManager.isDefaultLocaleSelected(context: Context): Boolean {
|
||||
return getCurrentLocale(context) == null
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the locale for the configuration of the app context's resources
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
fun LocaleManager.updateBaseConfiguration(context: Context, locale: Locale) {
|
||||
val resources = context.applicationContext.resources
|
||||
val config = resources.configuration
|
||||
config.setLocale(locale)
|
||||
config.setLayoutDirection(locale)
|
||||
resources.updateConfiguration(config, resources.displayMetrics)
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class LocaleSettingsFragment : Fragment() {
|
|||
store = getStore()
|
||||
interactor = LocaleSettingsInteractor(
|
||||
controller = DefaultLocaleSettingsController(
|
||||
context = requireContext(),
|
||||
activity = requireActivity(),
|
||||
localeSettingsStore = store
|
||||
)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/* 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.settings.advanced
|
||||
|
||||
import android.view.View
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.view.isVisible
|
||||
import kotlinx.android.synthetic.main.locale_settings_item.*
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.utils.view.ViewHolder
|
||||
import java.util.Locale
|
||||
|
||||
class LocaleViewHolder(
|
||||
view: View,
|
||||
selectedLocale: Locale,
|
||||
private val interactor: LocaleSettingsViewInteractor
|
||||
) : BaseLocaleViewHolder(view, selectedLocale) {
|
||||
|
||||
override fun bind(locale: Locale) {
|
||||
// capitalisation is done using the rules of the appropriate locale (endonym and exonym)
|
||||
locale_title_text.text = locale.getDisplayName(locale).capitalize(locale)
|
||||
locale_subtitle_text.text = locale.displayName.capitalize(Locale.getDefault())
|
||||
locale_selected_icon.isVisible = isCurrentLocaleSelected(locale, isDefault = false)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
interactor.onLocaleSelected(locale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SystemLocaleViewHolder(
|
||||
view: View,
|
||||
selectedLocale: Locale,
|
||||
private val interactor: LocaleSettingsViewInteractor
|
||||
) : BaseLocaleViewHolder(view, selectedLocale) {
|
||||
|
||||
override fun bind(locale: Locale) {
|
||||
locale_title_text.text = itemView.context.getString(R.string.default_locale_text)
|
||||
locale_subtitle_text.visibility = View.GONE
|
||||
locale_selected_icon.isVisible = isCurrentLocaleSelected(locale, isDefault = true)
|
||||
itemView.setOnClickListener {
|
||||
interactor.onDefaultLocaleSelected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseLocaleViewHolder(
|
||||
view: View,
|
||||
private val selectedLocale: Locale
|
||||
) : ViewHolder(view) {
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
|
||||
internal fun isCurrentLocaleSelected(locale: Locale, isDefault: Boolean): Boolean {
|
||||
return if (isDefault) {
|
||||
locale == selectedLocale && LocaleManager.isDefaultLocaleSelected(itemView.context)
|
||||
} else {
|
||||
locale == selectedLocale && !LocaleManager.isDefaultLocaleSelected(itemView.context)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun bind(locale: Locale)
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to Kotlin's capitalize with locale parameter, but that method is currently experimental
|
||||
*/
|
||||
private fun String.capitalize(locale: Locale): String {
|
||||
return substring(0, 1).toUpperCase(locale) + substring(1)
|
||||
}
|
|
@ -54,6 +54,7 @@ sealed class LoginsAction : Action {
|
|||
data class UpdateLoginsList(val list: List<SavedLogin>) : LoginsAction()
|
||||
data class UpdateCurrentLogin(val item: SavedLogin) : LoginsAction()
|
||||
data class SortLogins(val sortingStrategy: SortingStrategy) : LoginsAction()
|
||||
data class LoginSelected(val item: SavedLogin) : LoginsAction()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -110,6 +111,13 @@ private fun savedLoginsStateReducer(
|
|||
state
|
||||
)
|
||||
}
|
||||
is LoginsAction.LoginSelected -> {
|
||||
state.copy(
|
||||
isLoading = true,
|
||||
loginList = emptyList(),
|
||||
filteredItems = emptyList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ class LoginsListViewHolder(
|
|||
updateFavIcon(item.origin)
|
||||
|
||||
view.setOnClickListener {
|
||||
interactor.itemClicked(item)
|
||||
interactor.onItemClicked(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,12 +37,10 @@ import org.mozilla.fenix.BrowserDirection
|
|||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.redirectToReAuth
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.ext.showToolbar
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
|
||||
@SuppressWarnings("TooManyFunctions")
|
||||
class SavedLoginsFragment : Fragment() {
|
||||
|
@ -88,9 +86,14 @@ class SavedLoginsFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
val savedLoginsController: SavedLoginsController =
|
||||
SavedLoginsController(savedLoginsStore, requireContext().settings())
|
||||
savedLoginsInteractor =
|
||||
SavedLoginsInteractor(savedLoginsController, ::itemClicked, ::openLearnMore)
|
||||
SavedLoginsController(
|
||||
store = savedLoginsStore,
|
||||
navController = findNavController(),
|
||||
browserNavigator = ::openToBrowserAndLoad,
|
||||
settings = requireContext().settings(),
|
||||
metrics = requireContext().components.analytics.metrics
|
||||
)
|
||||
savedLoginsInteractor = SavedLoginsInteractor(savedLoginsController)
|
||||
savedLoginsView = SavedLoginsView(view.savedLoginsLayout, savedLoginsInteractor)
|
||||
loadAndMapLogins()
|
||||
return view
|
||||
|
@ -138,20 +141,8 @@ class SavedLoginsFragment : Fragment() {
|
|||
super.onPause()
|
||||
}
|
||||
|
||||
private fun itemClicked(item: SavedLogin) {
|
||||
context?.components?.analytics?.metrics?.track(Event.OpenOneLogin)
|
||||
val directions =
|
||||
SavedLoginsFragmentDirections.actionSavedLoginsFragmentToLoginDetailFragment(item.guid)
|
||||
findNavController().navigate(directions)
|
||||
}
|
||||
|
||||
private fun openLearnMore() {
|
||||
(activity as HomeActivity).openToBrowserAndLoad(
|
||||
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
|
||||
(SupportUtils.SumoTopic.SYNC_SETUP),
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromSavedLoginsFragment
|
||||
)
|
||||
private fun openToBrowserAndLoad(searchTermOrURL: String, newTab: Boolean, from: BrowserDirection) {
|
||||
(activity as HomeActivity).openToBrowserAndLoad(searchTermOrURL, newTab, from)
|
||||
}
|
||||
|
||||
private fun loadAndMapLogins() {
|
||||
|
@ -222,11 +213,15 @@ class SavedLoginsFragment : Fragment() {
|
|||
sortingStrategyMenu = SavedLoginsSortingStrategyMenu(requireContext(), itemToHighlight) {
|
||||
when (it) {
|
||||
SavedLoginsSortingStrategyMenu.Item.AlphabeticallySort -> {
|
||||
savedLoginsInteractor.sort(SortingStrategy.Alphabetically(requireContext().applicationContext))
|
||||
savedLoginsInteractor.onSortingStrategyChanged(
|
||||
SortingStrategy.Alphabetically(requireContext().applicationContext)
|
||||
)
|
||||
}
|
||||
|
||||
SavedLoginsSortingStrategyMenu.Item.LastUsedSort -> {
|
||||
savedLoginsInteractor.sort(SortingStrategy.LastUsed(requireContext().applicationContext))
|
||||
savedLoginsInteractor.onSortingStrategyChanged(
|
||||
SortingStrategy.LastUsed(requireContext().applicationContext)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,18 +4,21 @@
|
|||
|
||||
package org.mozilla.fenix.settings.logins
|
||||
|
||||
import android.text.SpannableString
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.navigation.NavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_saved_logins.view.*
|
||||
import kotlinx.android.synthetic.main.component_saved_logins.view.progress_bar
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.ext.addUnderline
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
|
@ -39,14 +42,10 @@ class SavedLoginsView(
|
|||
itemAnimator = null
|
||||
}
|
||||
|
||||
val learnMoreText = view.saved_passwords_empty_learn_more.text.toString()
|
||||
val textWithLink = SpannableString(learnMoreText).apply {
|
||||
setSpan(UnderlineSpan(), 0, learnMoreText.length, 0)
|
||||
}
|
||||
with(view.saved_passwords_empty_learn_more) {
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
text = textWithLink
|
||||
setOnClickListener { interactor.onLearnMore() }
|
||||
addUnderline()
|
||||
setOnClickListener { interactor.onLearnMoreClicked() }
|
||||
}
|
||||
|
||||
with(view.saved_passwords_empty_message) {
|
||||
|
@ -60,6 +59,7 @@ class SavedLoginsView(
|
|||
}
|
||||
|
||||
fun update(state: LoginsListState) {
|
||||
// todo MVI views should not have logic. Needs refactoring.
|
||||
if (state.isLoading) {
|
||||
view.progress_bar.isVisible = true
|
||||
} else {
|
||||
|
@ -73,29 +73,63 @@ class SavedLoginsView(
|
|||
|
||||
/**
|
||||
* Interactor for the saved logins screen
|
||||
*
|
||||
* @param savedLoginsController [SavedLoginsController] which will be delegated for all users interactions.
|
||||
*/
|
||||
class SavedLoginsInteractor(
|
||||
private val savedLoginsController: SavedLoginsController,
|
||||
private val itemClicked: (SavedLogin) -> Unit,
|
||||
private val learnMore: () -> Unit
|
||||
private val savedLoginsController: SavedLoginsController
|
||||
) {
|
||||
fun itemClicked(item: SavedLogin) {
|
||||
itemClicked.invoke(item)
|
||||
fun onItemClicked(item: SavedLogin) {
|
||||
savedLoginsController.handleItemClicked(item)
|
||||
}
|
||||
fun onLearnMore() {
|
||||
learnMore.invoke()
|
||||
|
||||
fun onLearnMoreClicked() {
|
||||
savedLoginsController.handleLearnMoreClicked()
|
||||
}
|
||||
fun sort(sortingStrategy: SortingStrategy) {
|
||||
|
||||
fun onSortingStrategyChanged(sortingStrategy: SortingStrategy) {
|
||||
savedLoginsController.handleSort(sortingStrategy)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for the saved logins screen
|
||||
*
|
||||
* @param store Store used to hold in-memory collection state.
|
||||
* @param navController NavController manages app navigation within a NavHost.
|
||||
* @param browserNavigator Controller allowing browser navigation to any Uri.
|
||||
* @param settings SharedPreferences wrapper for easier usage.
|
||||
* @param metrics Controller that handles telemetry events.
|
||||
*/
|
||||
class SavedLoginsController(val store: LoginsFragmentStore, val settings: Settings) {
|
||||
class SavedLoginsController(
|
||||
private val store: LoginsFragmentStore,
|
||||
private val navController: NavController,
|
||||
private val browserNavigator: (
|
||||
searchTermOrURL: String,
|
||||
newTab: Boolean,
|
||||
from: BrowserDirection
|
||||
) -> Unit,
|
||||
private val settings: Settings,
|
||||
private val metrics: MetricController
|
||||
) {
|
||||
fun handleSort(sortingStrategy: SortingStrategy) {
|
||||
store.dispatch(LoginsAction.SortLogins(sortingStrategy))
|
||||
settings.savedLoginsSortingStrategy = sortingStrategy
|
||||
}
|
||||
|
||||
fun handleItemClicked(item: SavedLogin) {
|
||||
store.dispatch(LoginsAction.LoginSelected(item))
|
||||
metrics.track(Event.OpenOneLogin)
|
||||
navController.navigate(
|
||||
SavedLoginsFragmentDirections.actionSavedLoginsFragmentToLoginDetailFragment(item.guid)
|
||||
)
|
||||
}
|
||||
|
||||
fun handleLearnMoreClicked() {
|
||||
browserNavigator.invoke(
|
||||
SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.SYNC_SETUP),
|
||||
true,
|
||||
BrowserDirection.FromSavedLoginsFragment
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ import mozilla.components.concept.sync.OAuthAccount
|
|||
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
/**
|
||||
* Starts and stops SyncedTabsStorage based on the authentication state.
|
||||
* @param context Used to get synced tabs storage, due to cyclic dependency.
|
||||
* @param accountManager Used to check and observe account authentication state.
|
||||
*/
|
||||
class SyncedTabsIntegration(
|
||||
private val context: Context,
|
||||
private val accountManager: FxaAccountManager
|
||||
|
|
|
@ -10,6 +10,10 @@ import android.view.View
|
|||
import android.widget.FrameLayout
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.synthetic.main.component_sync_tabs.view.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -23,6 +27,7 @@ class SyncedTabsLayout @JvmOverloads constructor(
|
|||
override var listener: SyncedTabsView.Listener? = null
|
||||
|
||||
private val adapter = SyncedTabsAdapter { listener?.onTabClicked(it) }
|
||||
private val coroutineScope = CoroutineScope(Dispatchers.Main)
|
||||
|
||||
init {
|
||||
inflate(getContext(), R.layout.component_sync_tabs, this)
|
||||
|
@ -34,42 +39,46 @@ class SyncedTabsLayout @JvmOverloads constructor(
|
|||
}
|
||||
|
||||
override fun onError(error: SyncedTabsView.ErrorType) {
|
||||
// We may still be displaying a "loading" spinner, hide it.
|
||||
stopLoading()
|
||||
coroutineScope.launch {
|
||||
// We may still be displaying a "loading" spinner, hide it.
|
||||
stopLoading()
|
||||
|
||||
val stringResId = when (error) {
|
||||
SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device
|
||||
SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing
|
||||
SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> R.string.synced_tabs_connect_to_sync_account
|
||||
SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION -> R.string.synced_tabs_reauth
|
||||
SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> R.string.synced_tabs_no_tabs
|
||||
val stringResId = when (error) {
|
||||
SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device
|
||||
SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing
|
||||
SyncedTabsView.ErrorType.SYNC_UNAVAILABLE -> R.string.synced_tabs_connect_to_sync_account
|
||||
SyncedTabsView.ErrorType.SYNC_NEEDS_REAUTHENTICATION -> R.string.synced_tabs_reauth
|
||||
SyncedTabsView.ErrorType.NO_TABS_AVAILABLE -> R.string.synced_tabs_no_tabs
|
||||
}
|
||||
|
||||
sync_tabs_status.text = context.getText(stringResId)
|
||||
|
||||
synced_tabs_list.visibility = View.GONE
|
||||
sync_tabs_status.visibility = View.VISIBLE
|
||||
|
||||
synced_tabs_pull_to_refresh.isEnabled = pullToRefreshEnableState(error)
|
||||
}
|
||||
|
||||
sync_tabs_status.text = context.getText(stringResId)
|
||||
|
||||
synced_tabs_list.visibility = View.GONE
|
||||
sync_tabs_status.visibility = View.VISIBLE
|
||||
|
||||
synced_tabs_pull_to_refresh.isEnabled = pullToRefreshEnableState(error)
|
||||
}
|
||||
|
||||
override fun displaySyncedTabs(syncedTabs: List<SyncedDeviceTabs>) {
|
||||
synced_tabs_list.visibility = View.VISIBLE
|
||||
sync_tabs_status.visibility = View.GONE
|
||||
coroutineScope.launch {
|
||||
synced_tabs_list.visibility = View.VISIBLE
|
||||
sync_tabs_status.visibility = View.GONE
|
||||
|
||||
val allDeviceTabs = emptyList<SyncedTabsAdapter.AdapterItem>().toMutableList()
|
||||
val allDeviceTabs = emptyList<SyncedTabsAdapter.AdapterItem>().toMutableList()
|
||||
|
||||
syncedTabs.forEach { (device, tabs) ->
|
||||
if (tabs.isEmpty()) {
|
||||
return@forEach
|
||||
syncedTabs.forEach { (device, tabs) ->
|
||||
if (tabs.isEmpty()) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val deviceTabs = tabs.map { SyncedTabsAdapter.AdapterItem.Tab(it) }
|
||||
|
||||
allDeviceTabs += listOf(SyncedTabsAdapter.AdapterItem.Device(device)) + deviceTabs
|
||||
}
|
||||
|
||||
val deviceTabs = tabs.map { SyncedTabsAdapter.AdapterItem.Tab(it) }
|
||||
|
||||
allDeviceTabs += listOf(SyncedTabsAdapter.AdapterItem.Device(device)) + deviceTabs
|
||||
adapter.submitList(allDeviceTabs)
|
||||
}
|
||||
|
||||
adapter.submitList(allDeviceTabs)
|
||||
}
|
||||
|
||||
override fun startLoading() {
|
||||
|
@ -83,6 +92,11 @@ class SyncedTabsLayout @JvmOverloads constructor(
|
|||
synced_tabs_pull_to_refresh.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
coroutineScope.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun pullToRefreshEnableState(error: SyncedTabsView.ErrorType) = when (error) {
|
||||
// Disable "pull-to-refresh" when we clearly can't sync tabs, and user needs to take an
|
||||
|
|
|
@ -47,8 +47,8 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item
|
|||
private fun bindHeader(device: AdapterItem.Device) {
|
||||
|
||||
val deviceLogoDrawable = when (device.device.deviceType) {
|
||||
DeviceType.DESKTOP -> { R.drawable.mozac_ic_device_desktop }
|
||||
else -> { R.drawable.mozac_ic_device_mobile }
|
||||
DeviceType.DESKTOP -> R.drawable.mozac_ic_device_desktop
|
||||
else -> R.drawable.mozac_ic_device_mobile
|
||||
}
|
||||
|
||||
itemView.synced_tabs_group_name.text = device.device.displayName
|
||||
|
|
|
@ -19,6 +19,7 @@ import kotlinx.android.synthetic.main.component_tabstray.view.*
|
|||
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.*
|
||||
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.view.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.browser.state.selector.normalTabs
|
||||
|
@ -110,6 +111,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.widget.TextView
|
|||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import mozilla.components.browser.state.state.MediaState
|
||||
import mozilla.components.browser.tabstray.TabViewHolder
|
||||
import mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
|
||||
|
@ -20,7 +21,7 @@ import mozilla.components.concept.tabstray.TabsTray
|
|||
import mozilla.components.feature.media.ext.pauseIfPlaying
|
||||
import mozilla.components.feature.media.ext.playIfPaused
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
import mozilla.components.support.images.ImageRequest
|
||||
import mozilla.components.support.images.ext.loadIntoView
|
||||
import mozilla.components.support.images.loader.ImageLoader
|
||||
import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -72,7 +73,10 @@ class TabTrayViewHolder(
|
|||
if (tab.thumbnail != null) {
|
||||
thumbnailView.setImageBitmap(tab.thumbnail)
|
||||
} else {
|
||||
imageLoader.loadIntoView(thumbnailView, ImageRequest(tab.id))
|
||||
// Make sure we have the view's dimensions so we can load the image at the correct size
|
||||
thumbnailView.doOnNextLayout {
|
||||
imageLoader.loadIntoView(thumbnailView, tab.id)
|
||||
}
|
||||
}
|
||||
|
||||
// Media state
|
||||
|
|
|
@ -5,17 +5,16 @@
|
|||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.UnderlineSpan
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.text.toSpannable
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_exceptions.*
|
||||
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.addUnderline
|
||||
|
||||
/**
|
||||
* Interface for the ExceptionsViewInteractor. This interface is implemented by objects that want
|
||||
|
@ -62,10 +61,7 @@ class ExceptionsView(
|
|||
}
|
||||
|
||||
with(exceptions_learn_more) {
|
||||
val learnMoreText = text
|
||||
text = learnMoreText.toSpannable().apply {
|
||||
setSpan(UnderlineSpan(), 0, learnMoreText.length, 0)
|
||||
}
|
||||
addUnderline()
|
||||
|
||||
movementMethod = LinkMovementMethod.getInstance()
|
||||
setOnClickListener { interactor.onLearnMore() }
|
||||
|
|
|
@ -18,29 +18,21 @@ import org.mozilla.fenix.utils.Settings
|
|||
class SitePermissionsWifiIntegration(
|
||||
private val settings: Settings,
|
||||
private val wifiConnectionMonitor: WifiConnectionMonitor
|
||||
) : LifecycleAwareFeature {
|
||||
) : LifecycleAwareFeature, WifiConnectionMonitor.Observer {
|
||||
|
||||
/**
|
||||
* Adds listener for autoplay setting [AUTOPLAY_ALLOW_ON_WIFI]. Sets all autoplay to allowed when
|
||||
* WIFI is connected, blocked otherwise.
|
||||
*/
|
||||
private val wifiConnectedListener: ((Boolean) -> Unit) by lazy {
|
||||
{ connected: Boolean ->
|
||||
val setting =
|
||||
if (connected) SitePermissionsRules.Action.ALLOWED else SitePermissionsRules.Action.BLOCKED
|
||||
if (settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) == AUTOPLAY_ALLOW_ON_WIFI) {
|
||||
settings.setSitePermissionsPhoneFeatureAction(
|
||||
PhoneFeature.AUTOPLAY_AUDIBLE,
|
||||
setting
|
||||
)
|
||||
settings.setSitePermissionsPhoneFeatureAction(
|
||||
PhoneFeature.AUTOPLAY_INAUDIBLE,
|
||||
setting
|
||||
)
|
||||
} else {
|
||||
// The autoplay setting has changed, we can remove the listener
|
||||
removeWifiConnectedListener()
|
||||
}
|
||||
override fun onWifiConnectionChanged(connected: Boolean) {
|
||||
val setting =
|
||||
if (connected) SitePermissionsRules.Action.ALLOWED else SitePermissionsRules.Action.BLOCKED
|
||||
if (settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) == AUTOPLAY_ALLOW_ON_WIFI) {
|
||||
settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_AUDIBLE, setting)
|
||||
settings.setSitePermissionsPhoneFeatureAction(PhoneFeature.AUTOPLAY_INAUDIBLE, setting)
|
||||
} else {
|
||||
// The autoplay setting has changed, we can remove the listener
|
||||
removeWifiConnectedListener()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,11 +47,11 @@ class SitePermissionsWifiIntegration(
|
|||
}
|
||||
|
||||
fun addWifiConnectedListener() {
|
||||
wifiConnectionMonitor.addOnWifiConnectedChangedListener(wifiConnectedListener)
|
||||
wifiConnectionMonitor.register(this)
|
||||
}
|
||||
|
||||
fun removeWifiConnectedListener() {
|
||||
wifiConnectionMonitor.removeOnWifiConnectedChangedListener(wifiConnectedListener)
|
||||
wifiConnectionMonitor.unregister(this)
|
||||
}
|
||||
|
||||
// Until https://bugzilla.mozilla.org/show_bug.cgi?id=1621825 is fixed, AUTOPLAY_ALLOW_ALL
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
package org.mozilla.fenix.wifi
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
import mozilla.components.support.base.observer.ObserverRegistry
|
||||
|
||||
/**
|
||||
* Attaches itself to the [Application] and listens for WIFI available/not available events. This
|
||||
|
@ -25,30 +26,28 @@ import android.net.NetworkRequest
|
|||
* app.components.wifiConnectionListener.start()
|
||||
* ```
|
||||
*/
|
||||
class WifiConnectionMonitor(app: Application) {
|
||||
private val callbacks = mutableSetOf<(Boolean) -> Unit>()
|
||||
private val connectivityManager = app.getSystemService(Context.CONNECTIVITY_SERVICE) as
|
||||
ConnectivityManager
|
||||
class WifiConnectionMonitor(
|
||||
private val connectivityManager: ConnectivityManager
|
||||
) : Observable<WifiConnectionMonitor.Observer> by ObserverRegistry() {
|
||||
|
||||
private var lastKnownStateWasAvailable: Boolean? = null
|
||||
private var callbackReceived: Boolean = false
|
||||
private var isRegistered = false
|
||||
|
||||
private val frameworkListener = object : ConnectivityManager.NetworkCallback() {
|
||||
override fun onLost(network: Network?) {
|
||||
callbacks.forEach { it(false) }
|
||||
lastKnownStateWasAvailable = false
|
||||
notifyAtLeastOneObserver { onWifiConnectionChanged(connected = false) }
|
||||
callbackReceived = true
|
||||
}
|
||||
|
||||
override fun onAvailable(network: Network?) {
|
||||
callbacks.forEach { it(true) }
|
||||
lastKnownStateWasAvailable = true
|
||||
notifyAtLeastOneObserver { onWifiConnectionChanged(connected = true) }
|
||||
callbackReceived = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the [WifiConnectionMonitor] to the application. After this has been called, callbacks
|
||||
* added via [addOnWifiConnectedChangedListener] will be called until either the app exits, or
|
||||
* [stop] is called.
|
||||
* added via [register] will be called until either the app exits, or [stop] is called.
|
||||
*
|
||||
* Any existing callbacks will be called with the current state when this is called.
|
||||
*/
|
||||
|
@ -62,10 +61,8 @@ class WifiConnectionMonitor(app: Application) {
|
|||
// AFAICT, the framework does not send an event when a new NetworkCallback is registered
|
||||
// while the WIFI is not connected, so we push this manually. If the WIFI is on, it will send
|
||||
// a follow up event shortly
|
||||
val noCallbacksReceivedYet = lastKnownStateWasAvailable == null
|
||||
if (noCallbacksReceivedYet) {
|
||||
lastKnownStateWasAvailable = false
|
||||
callbacks.forEach { it(false) }
|
||||
if (!callbackReceived) {
|
||||
notifyAtLeastOneObserver { onWifiConnectionChanged(connected = false) }
|
||||
}
|
||||
|
||||
connectivityManager.registerNetworkCallback(request, frameworkListener)
|
||||
|
@ -74,7 +71,7 @@ class WifiConnectionMonitor(app: Application) {
|
|||
|
||||
/**
|
||||
* Detatches the [WifiConnectionMonitor] from the app. No callbacks added via
|
||||
* [addOnWifiConnectedChangedListener] will be called after this has been called.
|
||||
* [register] will be called after this has been called.
|
||||
*/
|
||||
fun stop() {
|
||||
// Framework code will throw if an unregistered listener attempts to unregister.
|
||||
|
@ -83,25 +80,7 @@ class WifiConnectionMonitor(app: Application) {
|
|||
isRegistered = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds [onWifiChanged] to a list of listeners that will be called whenever WIFI connects or
|
||||
* disconnects.
|
||||
*
|
||||
* If [onWifiChanged] is successfully added (i.e., it is a new listener), it will be immediately
|
||||
* called with the last known state.
|
||||
*/
|
||||
fun addOnWifiConnectedChangedListener(onWifiChanged: (Boolean) -> Unit) {
|
||||
val lastKnownState = lastKnownStateWasAvailable
|
||||
if (callbacks.add(onWifiChanged) && lastKnownState != null) {
|
||||
onWifiChanged(lastKnownState)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes [onWifiChanged] from the list of listeners to be called whenever WIFI connects or
|
||||
* disconnects.
|
||||
*/
|
||||
fun removeOnWifiConnectedChangedListener(onWifiChanged: (Boolean) -> Unit) {
|
||||
callbacks.remove(onWifiChanged)
|
||||
interface Observer {
|
||||
fun onWifiConnectionChanged(connected: Boolean)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
android:fontFamily="@font/metropolis_bold"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/tracking_protection_item_height"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="@dimen/top_bar_alignment_margin_start"
|
||||
android:layout_marginEnd="@dimen/tracking_protection_item_margin_end"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:text="@string/enhanced_tracking_protection_blocked" />
|
||||
|
|
|
@ -10,9 +10,9 @@
|
|||
android:baselineAligned="false"
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false"
|
||||
android:paddingStart="@dimen/radio_button_preference_horizontal"
|
||||
android:layout_marginStart="@dimen/top_bar_alignment_margin_start"
|
||||
android:layout_marginEnd="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingTop="@dimen/preference_seek_bar_padding"
|
||||
android:paddingEnd="@dimen/radio_button_preference_horizontal"
|
||||
android:paddingBottom="@dimen/preference_seek_bar_padding">
|
||||
|
||||
<TextView
|
||||
|
@ -20,6 +20,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Font size" />
|
||||
|
||||
|
@ -27,11 +28,10 @@
|
|||
android:id="@android:id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="6dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textAlignment="viewStart"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintTop_toBottomOf="@android:id/title"
|
||||
tools:layout_editor_absoluteX="16dp"
|
||||
tools:text="Make text on websites larger or smaller" />
|
||||
|
||||
<SeekBar
|
||||
|
@ -76,6 +76,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="33dp"
|
||||
android:padding="16dp"
|
||||
android:textAlignment="viewStart"
|
||||
android:text="@string/accessibility_text_size_sample_text_1"
|
||||
android:textColor="@color/text_scale_example_text_color"
|
||||
android:textSize="16sp"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp">
|
||||
android:layout_height="@dimen/locale_item_height">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/locale_selected_icon"
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
|
||||
<FrameLayout
|
||||
android:id="@+id/favicon_wrapper"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:padding="4dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_width="@dimen/history_favicon_width_height"
|
||||
android:layout_height="@dimen/history_favicon_width_height"
|
||||
android:padding="@dimen/saved_logins_item_padding"
|
||||
android:layout_marginStart="@dimen/saved_logins_item_margin_start"
|
||||
android:background="@drawable/top_sites_background"
|
||||
android:layout_gravity="center"
|
||||
android:importantForAccessibility="noHideDescendants"
|
||||
|
@ -26,8 +26,8 @@
|
|||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
<ImageView
|
||||
android:id="@+id/favicon_image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_width="@dimen/preference_icon_drawable_size"
|
||||
android:layout_height="@dimen/preference_icon_drawable_size"
|
||||
android:layout_gravity="center"
|
||||
android:adjustViewBounds="true"
|
||||
android:importantForAccessibility="no"
|
||||
|
@ -38,8 +38,8 @@
|
|||
android:id="@+id/webAddressView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:layout_marginStart="@dimen/saved_logins_item_margin_start"
|
||||
android:layout_marginEnd="@dimen/saved_logins_item_margin_end"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
|
@ -55,8 +55,8 @@
|
|||
android:id="@+id/usernameView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="48dp"
|
||||
android:layout_marginStart="@dimen/saved_logins_item_margin_start"
|
||||
android:layout_marginEnd="@dimen/saved_logins_item_margin_end"
|
||||
android:ellipsize="middle"
|
||||
android:singleLine="true"
|
||||
android:textColor="?secondaryText"
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
style="?android:attr/listViewStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="72dp"
|
||||
android:layout_marginStart="@dimen/top_bar_alignment_margin_start"
|
||||
android:layout_marginTop="16dp"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginStart="@dimen/radiobutton_preference_margin_start"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center|start"
|
||||
android:layout_marginEnd="@dimen/radio_button_preference_horizontal"
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
<ImageView
|
||||
android:id="@+id/trackingProtectionCategoryIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_width="@dimen/preference_icon_drawable_size"
|
||||
android:layout_height="@dimen/preference_icon_drawable_size"
|
||||
android:layout_marginStart="@dimen/library_item_icon_margin_horizontal"
|
||||
android:layout_marginTop="@dimen/library_item_icon_margin_vertical"
|
||||
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||
|
@ -27,8 +27,8 @@
|
|||
android:id="@+id/trackingProtectionCategoryTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginStart="@dimen/radio_button_drawable_padding"
|
||||
android:layout_marginTop="@dimen/about_list_margin_top"
|
||||
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||
android:clickable="false"
|
||||
android:textAppearance="@style/ListItemTextStyle"
|
||||
|
@ -44,9 +44,9 @@
|
|||
android:id="@+id/trackingProtectionCategoryItemDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginStart="@dimen/radio_button_drawable_padding"
|
||||
android:layout_marginEnd="@dimen/library_item_icon_margin_horizontal"
|
||||
android:layout_marginBottom="18dp"
|
||||
android:layout_marginBottom="@dimen/tracking_protection_item_margin_bottom"
|
||||
android:clickable="false"
|
||||
android:textColor="?attr/secondaryText"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
|
|
@ -99,6 +99,9 @@
|
|||
<action
|
||||
android:id="@+id/action_global_tabTrayDialogFragment"
|
||||
app:destination="@id/tabTrayDialogFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_savedLoginsAuthFragment"
|
||||
app:destination="@id/savedLoginsAuthFragment" />
|
||||
|
||||
<dialog
|
||||
android:id="@+id/tabTrayDialogFragment"
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
<dimen name="synced_tab_item_min_height">56dp</dimen>
|
||||
<dimen name="component_collection_creation_list_margin">16dp</dimen>
|
||||
<dimen name="exceptions_description_margin">12dp</dimen>
|
||||
<dimen name="preference_seek_bar_padding">24dp</dimen>
|
||||
<dimen name="preference_seek_bar_padding">16dp</dimen>
|
||||
<dimen name="custom_checkbox_alignment_margin">68dp</dimen>
|
||||
|
||||
<dimen name="context_menu_height">48dp</dimen>
|
||||
|
@ -50,10 +50,14 @@
|
|||
|
||||
<!--Preferences-->
|
||||
<dimen name="checkbox_preference_padding_vertical">12dp</dimen>
|
||||
<dimen name="radiobutton_preference_margin_start">8dp</dimen>
|
||||
<dimen name="top_bar_alignment_margin_start">72dp</dimen>
|
||||
|
||||
<!--Quick Settings-->
|
||||
<dimen name="quicksettings_item_height">28dp</dimen>
|
||||
<dimen name="tracking_protection_item_height">48dp</dimen>
|
||||
<dimen name="tracking_protection_item_margin_end">16dp</dimen>
|
||||
<dimen name="tracking_protection_item_margin_bottom">18dp</dimen>
|
||||
|
||||
<dimen name="design_quick_action_sheet_peek_height_min">64dp</dimen>
|
||||
|
||||
|
@ -115,6 +119,7 @@
|
|||
<dimen name="locale_item_text_margin_gone_start">72dp</dimen>
|
||||
<dimen name="locale_item_title_size">16sp</dimen>
|
||||
<dimen name="locale_item_subtitle_size">12sp</dimen>
|
||||
<dimen name="locale_item_height">48dp</dimen>
|
||||
|
||||
<!--Migration Activity-->
|
||||
<dimen name="migration_margin_horizontal_large">20dp</dimen>
|
||||
|
@ -156,6 +161,9 @@
|
|||
<dimen name="saved_logins_sort_menu_dropdown_chevron_icon_size">12dp</dimen>
|
||||
<dimen name="saved_logins_detail_menu_vertical_padding">5dp</dimen>
|
||||
<dimen name="saved_logins_end_icon_drawable_padding">16dp</dimen>
|
||||
<dimen name="saved_logins_item_padding">4dp</dimen>
|
||||
<dimen name="saved_logins_item_margin_start">16dp</dimen>
|
||||
<dimen name="saved_logins_item_margin_end">48dp</dimen>
|
||||
|
||||
<!-- a11y -->
|
||||
<dimen name="accessibility_min_height">48dp</dimen>
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
android:defaultValue="true"
|
||||
android:key="@string/pref_key_accessibility_auto_size"
|
||||
android:summary="@string/preference_accessibility_auto_size_summary"
|
||||
android:title="@string/preference_accessibility_auto_size_2"
|
||||
app:iconSpaceReserved="false" />
|
||||
android:title="@string/preference_accessibility_auto_size_2" />
|
||||
<!-- Custom Preference that scales from 50-200% by steps of 5 represented by 0-30 in steps of 1-->
|
||||
<org.mozilla.fenix.settings.TextPercentageSeekBarPreference
|
||||
android:defaultValue="10"
|
||||
|
@ -29,6 +28,5 @@
|
|||
android:key="@string/pref_key_accessibility_force_enable_zoom"
|
||||
android:summary="@string/preference_accessibility_force_enable_zoom_summary"
|
||||
android:title="@string/preference_accessibility_force_enable_zoom"
|
||||
app:allowDividerAbove="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
app:allowDividerAbove="true" />
|
||||
</PreferenceScreen>
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.content.Context
|
|||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.support.migration.FennecMigrator
|
||||
import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
|
||||
import org.mozilla.fenix.migration.MigrationTelemetryListener
|
||||
|
||||
/**
|
||||
* An application class which knows how to migrate Fennec data.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import mozilla.components.concept.push.PushProcessor
|
||||
import mozilla.components.lib.state.ext.flowScoped
|
||||
|
@ -18,6 +19,8 @@ class MigrationPushRenewer(
|
|||
private val service: PushProcessor?,
|
||||
private val store: MigrationStore
|
||||
) {
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
fun start() {
|
||||
// Observe for migration completed.
|
||||
store.flowScoped { flow ->
|
||||
|
|
|
@ -345,8 +345,7 @@ private fun EngineSession.TrackingProtectionPolicy.assertPolicyEquals(
|
|||
checkPrivacy: Boolean
|
||||
) {
|
||||
assertEquals(this.cookiePolicy, actual.cookiePolicy)
|
||||
// TODO Uncomment this assertion after the fix in AC#6079 lands
|
||||
// assertEquals(this.strictSocialTrackingProtection, actual.strictSocialTrackingProtection)
|
||||
assertEquals(this.strictSocialTrackingProtection, actual.strictSocialTrackingProtection)
|
||||
// E.g., atm, RECOMMENDED == AD + ANALYTICS + SOCIAL + TEST + MOZILLA_SOCIAL + CRYPTOMINING.
|
||||
// If all of these are set manually, the equality check should not fail
|
||||
if (this.trackingCategories.toInt() != actual.trackingCategories.toInt()) {
|
||||
|
|
|
@ -69,7 +69,7 @@ import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|||
import org.mozilla.fenix.home.Tab
|
||||
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class DefaultBrowserToolbarControllerTest {
|
||||
|
||||
|
|
|
@ -9,18 +9,16 @@ import io.mockk.every
|
|||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.concept.engine.Engine
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.tabs.TabsUseCases
|
||||
import org.junit.After
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
|
@ -35,10 +33,12 @@ import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
|
|||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultSessionControlControllerTest {
|
||||
|
||||
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||
|
||||
private val activity: HomeActivity = mockk(relaxed = true)
|
||||
private val fragmentStore: HomeFragmentStore = mockk(relaxed = true)
|
||||
private val navController: NavController = mockk(relaxed = true)
|
||||
|
@ -62,7 +62,6 @@ class DefaultSessionControlControllerTest {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
Dispatchers.setMain(mainThreadSurrogate)
|
||||
mockkStatic("org.mozilla.fenix.ext.ContextKt")
|
||||
every { activity.components.core.engine } returns engine
|
||||
every { activity.components.core.sessionManager } returns sessionManager
|
||||
|
@ -91,12 +90,6 @@ class DefaultSessionControlControllerTest {
|
|||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
|
||||
mainThreadSurrogate.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleCollectionAddTabTapped() {
|
||||
val collection: TabCollection = mockk(relaxed = true)
|
||||
|
|
|
@ -8,17 +8,22 @@ import android.content.Intent
|
|||
import androidx.core.net.toUri
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.Called
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.verify
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
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.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.BuildConfig.DEEP_LINK_SCHEME
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.NavGraphDirections
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.SearchWidgetCreator
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
|
@ -48,7 +53,7 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `return true if scheme is fenix`() {
|
||||
assertTrue(processor.process(testIntent("fenix://test"), navController, out))
|
||||
assertTrue(processor.process(testIntent("test"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController wasNot Called }
|
||||
|
@ -66,16 +71,40 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process home deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://home"), navController, out))
|
||||
assertTrue(processor.process(testIntent("home"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalHome()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process urls_bookmarks deep link`() {
|
||||
assertTrue(processor.process(testIntent("urls_bookmarks"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalBookmarkFragment(BookmarkRoot.Root.id)) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process urls_history deep link`() {
|
||||
assertTrue(processor.process(testIntent("urls_history"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalHistoryFragment()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process home_collections deep link`() {
|
||||
assertTrue(processor.process(testIntent("home_collections"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalHome()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process settings deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://settings"), navController, out))
|
||||
assertTrue(processor.process(testIntent("settings"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalSettingsFragment()) }
|
||||
|
@ -84,7 +113,7 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process turn_on_sync deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://turn_on_sync"), navController, out))
|
||||
assertTrue(processor.process(testIntent("turn_on_sync"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalTurnOnSync()) }
|
||||
|
@ -93,7 +122,7 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process settings_search_engine deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://settings_search_engine"), navController, out))
|
||||
assertTrue(processor.process(testIntent("settings_search_engine"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalSearchEngineFragment()) }
|
||||
|
@ -102,7 +131,7 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process settings_accessibility deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://settings_accessibility"), navController, out))
|
||||
assertTrue(processor.process(testIntent("settings_accessibility"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalAccessibilityFragment()) }
|
||||
|
@ -111,16 +140,48 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process settings_delete_browsing_data deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://settings_delete_browsing_data"), navController, out))
|
||||
assertTrue(processor.process(testIntent("settings_delete_browsing_data"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalDeleteBrowsingDataFragment()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process settings_addon_manager deep link`() {
|
||||
assertTrue(processor.process(testIntent("settings_addon_manager"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalSettingsAddonsManagementFragment()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process settings_logins deep link`() {
|
||||
assertTrue(processor.process(testIntent("settings_logins"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalSavedLoginsAuthFragment()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process settings_tracking_protection deep link`() {
|
||||
assertTrue(processor.process(testIntent("settings_tracking_protection"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalTrackingProtectionFragment()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process settings_privacy deep link`() {
|
||||
assertTrue(processor.process(testIntent("settings_privacy"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalSettingsFragment()) }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process enable_private_browsing deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://enable_private_browsing"), navController, out))
|
||||
assertTrue(processor.process(testIntent("enable_private_browsing"), navController, out))
|
||||
|
||||
verify { activity.browsingModeManager.mode = BrowsingMode.Private }
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalHome()) }
|
||||
|
@ -129,13 +190,13 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process open deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://open"), navController, out))
|
||||
assertTrue(processor.process(testIntent("open"), navController, out))
|
||||
|
||||
verify { activity wasNot Called }
|
||||
verify { navController wasNot Called }
|
||||
verify { out wasNot Called }
|
||||
|
||||
assertTrue(processor.process(testIntent("fenix://open?url=test"), navController, out))
|
||||
assertTrue(processor.process(testIntent("open?url=test"), navController, out))
|
||||
|
||||
verify {
|
||||
activity.openToBrowserAndLoad(
|
||||
|
@ -150,19 +211,31 @@ class DeepLinkIntentProcessorTest {
|
|||
|
||||
@Test
|
||||
fun `process make_default_browser deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://make_default_browser"), navController, out))
|
||||
assertTrue(processor.process(testIntent("make_default_browser"), navController, out))
|
||||
|
||||
verify { navController wasNot Called }
|
||||
verify { out wasNot Called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `process settings_addon_manager deep link`() {
|
||||
assertTrue(processor.process(testIntent("fenix://settings_addon_manager"), navController, out))
|
||||
fun `process settings_notifications deep link`() {
|
||||
assertTrue(processor.process(testIntent("settings_notifications"), navController, out))
|
||||
|
||||
verify { navController.navigate(NavGraphDirections.actionGlobalSettingsAddonsManagementFragment()) }
|
||||
verify { navController wasNot Called }
|
||||
verify { out wasNot Called }
|
||||
verify { activity.startActivity(any()) }
|
||||
}
|
||||
|
||||
private fun testIntent(uri: String) = Intent("", uri.toUri())
|
||||
@Test
|
||||
fun `process install_search_widget deep link`() {
|
||||
mockkObject(SearchWidgetCreator)
|
||||
every { SearchWidgetCreator.createSearchWidget(any()) } returns true
|
||||
assertTrue(processor.process(testIntent("install_search_widget"), navController, out))
|
||||
|
||||
verify { navController wasNot Called }
|
||||
verify { out wasNot Called }
|
||||
verify { activity wasNot Called }
|
||||
}
|
||||
|
||||
private fun testIntent(uri: String) = Intent("", "$DEEP_LINK_SCHEME://$uri".toUri())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* 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.home.sessioncontrol.viewholders
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.private_browsing_description.view.*
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class PrivateBrowsingDescriptionViewHolderTest {
|
||||
|
||||
private lateinit var view: View
|
||||
private lateinit var interactor: TabSessionInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
view = LayoutInflater.from(testContext)
|
||||
.inflate(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID, null)
|
||||
interactor = mockk(relaxed = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call interactor on click`() {
|
||||
PrivateBrowsingDescriptionViewHolder(view, interactor)
|
||||
|
||||
view.private_session_common_myths.performClick()
|
||||
verify { interactor.onPrivateBrowsingLearnMoreClicked() }
|
||||
}
|
||||
}
|
|
@ -4,18 +4,13 @@
|
|||
|
||||
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.onboarding_private_browsing.view.*
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -31,19 +26,10 @@ class OnboardingPrivacyBrowsingViewHolderTest {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
view = LayoutInflater.from(testContext)
|
||||
val context = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||
view = LayoutInflater.from(context)
|
||||
.inflate(OnboardingPrivateBrowsingViewHolder.LAYOUT_ID, null)
|
||||
interactor = mockk(relaxed = true)
|
||||
|
||||
every {
|
||||
any<Resources.Theme>().resolveAttribute(R.attr.onboardingSelected)
|
||||
} returns R.color.onboarding_illustration_selected_normal_theme
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -4,18 +4,13 @@
|
|||
|
||||
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.onboarding_privacy_notice.view.*
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -31,19 +26,10 @@ class OnboardingPrivacyNoticeViewHolderTest {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
view = LayoutInflater.from(testContext)
|
||||
val context = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||
view = LayoutInflater.from(context)
|
||||
.inflate(OnboardingPrivacyNoticeViewHolder.LAYOUT_ID, null)
|
||||
interactor = mockk(relaxed = true)
|
||||
|
||||
every {
|
||||
any<Resources.Theme>().resolveAttribute(R.attr.onboardingSelected)
|
||||
} returns R.color.onboarding_illustration_selected_normal_theme
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -4,16 +4,11 @@
|
|||
|
||||
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import kotlinx.android.synthetic.main.onboarding_tracking_protection.view.*
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -28,18 +23,9 @@ class OnboardingTrackingProtectionViewHolderTest {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
view = LayoutInflater.from(testContext)
|
||||
val context = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||
view = LayoutInflater.from(context)
|
||||
.inflate(OnboardingTrackingProtectionViewHolder.LAYOUT_ID, null)
|
||||
|
||||
every {
|
||||
any<Resources.Theme>().resolveAttribute(R.attr.onboardingSelected)
|
||||
} returns R.color.onboarding_illustration_selected_normal_theme
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/* 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.home.sessioncontrol.viewholders.onboarding
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.text.Spanned
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.onboarding_whats_new.view.*
|
||||
import mozilla.components.support.ktx.android.content.res.resolveAttribute
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class OnboardingWhatsNewViewHolderTest {
|
||||
|
||||
private lateinit var view: View
|
||||
private lateinit var interactor: OnboardingInteractor
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
view = LayoutInflater.from(testContext)
|
||||
.inflate(OnboardingWhatsNewViewHolder.LAYOUT_ID, null)
|
||||
interactor = mockk(relaxed = true)
|
||||
|
||||
every {
|
||||
any<Resources.Theme>().resolveAttribute(R.attr.onboardingSelected)
|
||||
} returns R.color.onboarding_illustration_selected_normal_theme
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sets and styles strings`() {
|
||||
OnboardingWhatsNewViewHolder(view, interactor)
|
||||
|
||||
assertEquals(
|
||||
"Have questions about the redesigned Firefox Preview? Want to know what’s changed?",
|
||||
view.description_text.text
|
||||
)
|
||||
|
||||
val getAnswersHtml = HtmlCompat.toHtml(view.get_answers.text as Spanned, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
|
||||
assertTrue(getAnswersHtml, "<u>Get answers here</u>" in getAnswersHtml)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `call interactor on click`() {
|
||||
OnboardingWhatsNewViewHolder(view, interactor)
|
||||
|
||||
view.get_answers.performClick()
|
||||
verify { interactor.onWhatsNewGetAnswersClicked() }
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@ import org.junit.Test
|
|||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.ext.directionsEq
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
// Robolectric needed for `onShareItem()`
|
||||
|
@ -189,15 +190,12 @@ class HistoryControllerTest {
|
|||
fun onShareItem() {
|
||||
controller.handleShare(historyItem)
|
||||
|
||||
// `verify` checks for referential equality.
|
||||
// This would fail as the NavDirections are created and used in place in the tested method.
|
||||
// Capture the NavDirections and `assert` for structural equality after.
|
||||
verify {
|
||||
navController.navigate(
|
||||
navController.navigate(directionsEq(
|
||||
HistoryFragmentDirections.actionGlobalShareFragment(
|
||||
data = arrayOf(ShareData(url = historyItem.url, title = historyItem.title))
|
||||
)
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/* 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.perf
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.LinearLayout
|
||||
import io.mockk.Called
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.top_site_item.view.*
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
|
||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination
|
||||
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
|
||||
|
||||
class StartupReportFullyDrawnTest {
|
||||
|
||||
@MockK private lateinit var activity: HomeActivity
|
||||
private lateinit var holder: TopSiteItemViewHolder
|
||||
@MockK(relaxed = true) private lateinit var rootContainer: LinearLayout
|
||||
@MockK(relaxed = true) private lateinit var holderItemView: View
|
||||
@MockK(relaxed = true) private lateinit var viewTreeObserver: ViewTreeObserver
|
||||
private lateinit var fullyDrawn: StartupReportFullyDrawn
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
every { activity.findViewById<LinearLayout>(R.id.rootContainer) } returns rootContainer
|
||||
every { holderItemView.context } returns activity
|
||||
every { holderItemView.top_site_item } returns mockk(relaxed = true)
|
||||
holder = TopSiteItemViewHolder(holderItemView, mockk())
|
||||
every { rootContainer.viewTreeObserver } returns viewTreeObserver
|
||||
every { holderItemView.viewTreeObserver } returns viewTreeObserver
|
||||
|
||||
fullyDrawn = StartupReportFullyDrawn()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnActivityCreateEndHome() {
|
||||
// Only APP_LINK destination
|
||||
fullyDrawn.onActivityCreateEndHome(StartupState.Cold(StartupDestination.UNKNOWN), activity)
|
||||
fullyDrawn.onActivityCreateEndHome(StartupState.Cold(StartupDestination.HOMESCREEN), activity)
|
||||
verify { activity wasNot Called }
|
||||
|
||||
// Only run once
|
||||
fullyDrawn.onActivityCreateEndHome(StartupState.Cold(StartupDestination.APP_LINK), activity)
|
||||
verify(exactly = 1) { activity.findViewById<LinearLayout>(R.id.rootContainer) }
|
||||
|
||||
fullyDrawn.onActivityCreateEndHome(StartupState.Cold(StartupDestination.APP_LINK), activity)
|
||||
verify(exactly = 1) { activity.findViewById<LinearLayout>(R.id.rootContainer) }
|
||||
|
||||
every { activity.reportFullyDrawn() } just Runs
|
||||
triggerPreDraw()
|
||||
verify { activity.reportFullyDrawn() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOnTopSitesItemBound() {
|
||||
fullyDrawn.onTopSitesItemBound(StartupState.Cold(StartupDestination.HOMESCREEN), holder)
|
||||
|
||||
every { activity.reportFullyDrawn() } just Runs
|
||||
triggerPreDraw()
|
||||
verify { activity.reportFullyDrawn() }
|
||||
}
|
||||
|
||||
private fun triggerPreDraw() {
|
||||
val listener = slot<ViewTreeObserver.OnPreDrawListener>()
|
||||
verify { viewTreeObserver.addOnPreDrawListener(capture(listener)) }
|
||||
listener.captured.onPreDraw()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* 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.push
|
||||
|
||||
import android.app.Notification
|
||||
import androidx.core.app.NotificationCompat
|
||||
import io.mockk.Called
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class LeanplumNotificationCustomizerTest {
|
||||
|
||||
private val customizer = LeanplumNotificationCustomizer()
|
||||
|
||||
@Test
|
||||
fun `customize adds icon`() {
|
||||
val builder = mockk<NotificationCompat.Builder>(relaxed = true)
|
||||
customizer.customize(builder, mockk())
|
||||
|
||||
verify { builder.setSmallIcon(R.drawable.ic_status_logo) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `customize for BigPictureStyle does nothing`() {
|
||||
val builder = mockk<Notification.Builder>()
|
||||
customizer.customize(builder, mockk(), mockk())
|
||||
|
||||
verify { builder wasNot Called }
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package org.mozilla.fenix.search.toolbar
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
|
@ -21,6 +23,7 @@ import org.junit.Assert.assertTrue
|
|||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
|
@ -28,12 +31,14 @@ class ToolbarViewTest {
|
|||
|
||||
@MockK(relaxed = true) private lateinit var interactor: ToolbarInteractor
|
||||
@MockK private lateinit var engine: Engine
|
||||
private lateinit var context: Context
|
||||
private lateinit var toolbar: BrowserToolbar
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
toolbar = spyk(BrowserToolbar(testContext))
|
||||
context = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||
toolbar = spyk(BrowserToolbar(context))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -68,7 +73,7 @@ class ToolbarViewTest {
|
|||
}
|
||||
|
||||
private fun buildToolbarView(isPrivate: Boolean) = ToolbarView(
|
||||
testContext,
|
||||
context,
|
||||
interactor,
|
||||
historyStorage = null,
|
||||
isPrivate = isPrivate,
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* 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.settings.advanced
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.Locale
|
||||
|
||||
class BaseLocaleViewHolderTest {
|
||||
|
||||
private val selectedLocale = Locale("en", "UK")
|
||||
private val view: View = mockk()
|
||||
private val context: Context = mockk()
|
||||
|
||||
private val localeViewHolder = object : BaseLocaleViewHolder(view, selectedLocale) {
|
||||
override fun bind(locale: Locale) = Unit
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkObject(LocaleManager)
|
||||
every { view.context } returns context
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify other locale checker returns false`() {
|
||||
every { LocaleManager.getCurrentLocale(context) } returns mockk()
|
||||
val otherLocale = mockk<Locale>()
|
||||
|
||||
assertFalse(localeViewHolder.isCurrentLocaleSelected(otherLocale, isDefault = true))
|
||||
assertFalse(localeViewHolder.isCurrentLocaleSelected(otherLocale, isDefault = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify selected locale checker returns true`() {
|
||||
every { LocaleManager.getCurrentLocale(context) } returns mockk()
|
||||
|
||||
assertFalse(localeViewHolder.isCurrentLocaleSelected(selectedLocale, isDefault = true))
|
||||
assertTrue(localeViewHolder.isCurrentLocaleSelected(selectedLocale, isDefault = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify default locale checker returns true`() {
|
||||
every { LocaleManager.getCurrentLocale(context) } returns null
|
||||
|
||||
assertTrue(localeViewHolder.isCurrentLocaleSelected(selectedLocale, isDefault = true))
|
||||
assertFalse(localeViewHolder.isCurrentLocaleSelected(selectedLocale, isDefault = false))
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package org.mozilla.fenix.settings.advanced
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.Locale
|
||||
|
||||
class LocaleAdapterTest {
|
||||
|
||||
private val selectedLocale = Locale("en", "UK")
|
||||
private val view: View = mockk(relaxed = true)
|
||||
private val context: Context = mockk(relaxed = true)
|
||||
|
||||
private val localeViewHolder: BaseLocaleViewHolder =
|
||||
object : BaseLocaleViewHolder(view, selectedLocale) {
|
||||
|
||||
override fun bind(locale: Locale) {
|
||||
// not required
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
every { view.context } returns context
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify selected locale checker returns true`() {
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
every { LocaleManager.isDefaultLocaleSelected(context) } returns false
|
||||
|
||||
assertTrue(localeViewHolder.isCurrentLocaleSelected(selectedLocale, isDefault = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `verify default locale checker returns true`() {
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
every { LocaleManager.isDefaultLocaleSelected(context) } returns true
|
||||
|
||||
assertTrue(localeViewHolder.isCurrentLocaleSelected(selectedLocale, isDefault = true))
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ import android.content.Context
|
|||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.mockkStatic
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
|
@ -24,9 +23,12 @@ import java.util.Locale
|
|||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class LocaleManagerExtensionTest {
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
context = mockk()
|
||||
mockkObject(LocaleManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -44,8 +46,6 @@ class LocaleManagerExtensionTest {
|
|||
@Test
|
||||
@Config(qualifiers = "en-rUS")
|
||||
fun `default locale selected`() {
|
||||
val context: Context = mockk()
|
||||
mockkObject(LocaleManager)
|
||||
every { LocaleManager.getCurrentLocale(context) } returns null
|
||||
|
||||
assertTrue(LocaleManager.isDefaultLocaleSelected(context))
|
||||
|
@ -54,8 +54,6 @@ class LocaleManagerExtensionTest {
|
|||
@Test
|
||||
@Config(qualifiers = "en-rUS")
|
||||
fun `custom locale selected`() {
|
||||
val context: Context = mockk()
|
||||
mockkObject(LocaleManager)
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
every { LocaleManager.getCurrentLocale(context) } returns selectedLocale
|
||||
|
||||
|
@ -65,13 +63,9 @@ class LocaleManagerExtensionTest {
|
|||
@Test
|
||||
@Config(qualifiers = "en-rUS")
|
||||
fun `match current stored locale string with a Locale from our list`() {
|
||||
val context: Context = mockk()
|
||||
mockkObject(LocaleManager)
|
||||
val otherLocale = Locale("fr")
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
val localeList = ArrayList<Locale>()
|
||||
localeList.add(otherLocale)
|
||||
localeList.add(selectedLocale)
|
||||
val localeList = listOf(otherLocale, selectedLocale)
|
||||
|
||||
every { LocaleManager.getCurrentLocale(context) } returns selectedLocale
|
||||
|
||||
|
@ -81,13 +75,9 @@ class LocaleManagerExtensionTest {
|
|||
@Test
|
||||
@Config(qualifiers = "en-rUS")
|
||||
fun `match null stored locale with the default Locale from our list`() {
|
||||
val context: Context = mockk()
|
||||
mockkObject(LocaleManager)
|
||||
val firstLocale = Locale("fr")
|
||||
val secondLocale = Locale("en", "UK")
|
||||
val localeList = ArrayList<Locale>()
|
||||
localeList.add(firstLocale)
|
||||
localeList.add(secondLocale)
|
||||
val localeList = listOf(firstLocale, secondLocale)
|
||||
|
||||
every { LocaleManager.getCurrentLocale(context) } returns null
|
||||
|
||||
|
|
|
@ -5,14 +5,15 @@
|
|||
package org.mozilla.fenix.settings.advanced
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyAll
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
@ -20,118 +21,113 @@ import java.util.Locale
|
|||
|
||||
class LocaleSettingsControllerTest {
|
||||
|
||||
private val context: Context = mockk<Activity>(relaxed = true)
|
||||
private val activity = mockk<Activity>(relaxed = true)
|
||||
private val localeSettingsStore: LocaleSettingsStore = mockk(relaxed = true)
|
||||
private val mockState = LocaleSettingsState(mockk(), mockk(), mockk())
|
||||
|
||||
private lateinit var controller: LocaleSettingsController
|
||||
private lateinit var controller: DefaultLocaleSettingsController
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
controller = DefaultLocaleSettingsController(context, localeSettingsStore)
|
||||
controller = spyk(DefaultLocaleSettingsController(activity, localeSettingsStore))
|
||||
|
||||
mockkObject(LocaleManager)
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `don't set locale if same locale is chosen`() {
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
every { localeSettingsStore.state } returns LocaleSettingsState(
|
||||
mockk(),
|
||||
mockk(),
|
||||
selectedLocale
|
||||
)
|
||||
mockkObject(LocaleManager)
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
every { LocaleManager.getCurrentLocale(context) } returns mockk()
|
||||
every { LocaleManager.isDefaultLocaleSelected(context) } returns false
|
||||
every { localeSettingsStore.state } returns mockState.copy(selectedLocale = selectedLocale)
|
||||
every { LocaleManager.getCurrentLocale(activity) } returns mockk()
|
||||
|
||||
controller.handleLocaleSelected(selectedLocale)
|
||||
|
||||
verify(
|
||||
inverse = true,
|
||||
verifyBlock = { localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale)) })
|
||||
verify(
|
||||
inverse = true,
|
||||
verifyBlock = { LocaleManager.setNewLocale(context, selectedLocale.toLanguageTag()) })
|
||||
verify(
|
||||
inverse = true,
|
||||
verifyBlock = { LocaleManager.updateBaseConfiguration(context, selectedLocale) })
|
||||
verify(inverse = true, verifyBlock = { (context as Activity).recreate() })
|
||||
verifyAll(inverse = true) {
|
||||
localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale))
|
||||
LocaleManager.setNewLocale(activity, selectedLocale.toLanguageTag())
|
||||
activity.recreate()
|
||||
}
|
||||
with(controller) {
|
||||
verify(inverse = true) { LocaleManager.updateBaseConfiguration(activity, selectedLocale) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `set a new locale from the list`() {
|
||||
fun `set a new locale from the list if other locale is chosen`() {
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
val otherLocale: Locale = mockk()
|
||||
every { localeSettingsStore.state } returns LocaleSettingsState(
|
||||
mockk(),
|
||||
mockk(),
|
||||
otherLocale
|
||||
)
|
||||
mockkObject(LocaleManager)
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
every { LocaleManager.updateBaseConfiguration(context, selectedLocale) } just Runs
|
||||
every {
|
||||
LocaleManager.setNewLocale(
|
||||
context,
|
||||
selectedLocale.toLanguageTag()
|
||||
)
|
||||
} returns context
|
||||
every { localeSettingsStore.state } returns mockState.copy(selectedLocale = otherLocale)
|
||||
every { LocaleManager.setNewLocale(activity, selectedLocale.toLanguageTag()) } returns activity
|
||||
with(controller) {
|
||||
every { LocaleManager.updateBaseConfiguration(activity, selectedLocale) } just Runs
|
||||
}
|
||||
|
||||
controller.handleLocaleSelected(selectedLocale)
|
||||
|
||||
verify { localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale)) }
|
||||
verify { LocaleManager.setNewLocale(context, selectedLocale.toLanguageTag()) }
|
||||
verify { LocaleManager.updateBaseConfiguration(context, selectedLocale) }
|
||||
verify { (context as Activity).recreate() }
|
||||
verify { LocaleManager.setNewLocale(activity, selectedLocale.toLanguageTag()) }
|
||||
verify { activity.recreate() }
|
||||
with(controller) {
|
||||
verify { LocaleManager.updateBaseConfiguration(activity, selectedLocale) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `set a new locale from the list if default locale is not selected`() {
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
every { localeSettingsStore.state } returns mockState.copy(selectedLocale = selectedLocale)
|
||||
every { LocaleManager.getCurrentLocale(activity) } returns null
|
||||
every { LocaleManager.setNewLocale(activity, selectedLocale.toLanguageTag()) } returns activity
|
||||
with(controller) {
|
||||
every { LocaleManager.updateBaseConfiguration(activity, selectedLocale) } just Runs
|
||||
}
|
||||
|
||||
controller.handleLocaleSelected(selectedLocale)
|
||||
|
||||
verify { localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale)) }
|
||||
verify { LocaleManager.setNewLocale(activity, selectedLocale.toLanguageTag()) }
|
||||
verify { activity.recreate() }
|
||||
with(controller) {
|
||||
verify { LocaleManager.updateBaseConfiguration(activity, selectedLocale) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `don't set default locale if default locale is already chosen`() {
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
val localeList = ArrayList<Locale>()
|
||||
localeList.add(selectedLocale)
|
||||
every { localeSettingsStore.state } returns LocaleSettingsState(
|
||||
localeList,
|
||||
mockk(),
|
||||
mockk()
|
||||
)
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
every { LocaleManager.isDefaultLocaleSelected(context) } returns true
|
||||
every { localeSettingsStore.state } returns mockState.copy(localeList = listOf(selectedLocale))
|
||||
every { LocaleManager.getCurrentLocale(activity) } returns null
|
||||
|
||||
controller.handleDefaultLocaleSelected()
|
||||
|
||||
verify(
|
||||
inverse = true,
|
||||
verifyBlock = { localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale)) })
|
||||
verify(
|
||||
inverse = true,
|
||||
verifyBlock = { LocaleManager.resetToSystemDefault(context) })
|
||||
verify(
|
||||
inverse = true,
|
||||
verifyBlock = { LocaleManager.updateBaseConfiguration(context, selectedLocale) })
|
||||
verify(inverse = true, verifyBlock = { (context as Activity).recreate() })
|
||||
verifyAll(inverse = true) {
|
||||
localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale))
|
||||
LocaleManager.resetToSystemDefault(activity)
|
||||
activity.recreate()
|
||||
with(controller) {
|
||||
LocaleManager.updateBaseConfiguration(activity, selectedLocale)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `set the default locale as the new locale`() {
|
||||
val selectedLocale = Locale("en", "UK")
|
||||
val localeList = ArrayList<Locale>()
|
||||
localeList.add(selectedLocale)
|
||||
every { localeSettingsStore.state } returns LocaleSettingsState(
|
||||
localeList,
|
||||
mockk(),
|
||||
mockk()
|
||||
)
|
||||
mockkObject(LocaleManager)
|
||||
mockkStatic("org.mozilla.fenix.settings.advanced.LocaleManagerExtensionKt")
|
||||
every { LocaleManager.resetToSystemDefault(context) } just Runs
|
||||
every { LocaleManager.updateBaseConfiguration(context, selectedLocale) } just Runs
|
||||
every { localeSettingsStore.state } returns mockState.copy(localeList = listOf(selectedLocale))
|
||||
every { LocaleManager.resetToSystemDefault(activity) } just Runs
|
||||
with(controller) {
|
||||
every { LocaleManager.updateBaseConfiguration(activity, selectedLocale) } just Runs
|
||||
}
|
||||
|
||||
controller.handleDefaultLocaleSelected()
|
||||
|
||||
verify { localeSettingsStore.dispatch(LocaleSettingsAction.Select(selectedLocale)) }
|
||||
verify { LocaleManager.resetToSystemDefault(context) }
|
||||
verify { LocaleManager.updateBaseConfiguration(context, selectedLocale) }
|
||||
verify { (context as Activity).recreate() }
|
||||
verify { LocaleManager.resetToSystemDefault(activity) }
|
||||
verify { activity.recreate() }
|
||||
with(controller) {
|
||||
verify { LocaleManager.updateBaseConfiguration(activity, selectedLocale) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -18,10 +18,11 @@ class LocaleSettingsStoreTest {
|
|||
|
||||
@Before
|
||||
fun setup() {
|
||||
val localeList = ArrayList<Locale>()
|
||||
localeList.add(Locale("fr")) // default
|
||||
localeList.add(otherLocale)
|
||||
localeList.add(selectedLocale)
|
||||
val localeList = listOf(
|
||||
Locale("fr"), // default
|
||||
otherLocale,
|
||||
selectedLocale
|
||||
)
|
||||
|
||||
localeSettingsStore =
|
||||
LocaleSettingsStore(LocaleSettingsState(localeList, localeList, selectedLocale))
|
||||
|
|
|
@ -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 org.mozilla.fenix.settings.advanced
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.locale_settings_item.view.*
|
||||
import mozilla.components.support.locale.LocaleManager
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
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.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import java.util.Locale
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class LocaleViewHoldersTest {
|
||||
|
||||
private val selectedLocale = Locale("en", "US")
|
||||
private lateinit var view: View
|
||||
private lateinit var interactor: LocaleSettingsViewInteractor
|
||||
private lateinit var localeViewHolder: LocaleViewHolder
|
||||
private lateinit var systemLocaleViewHolder: SystemLocaleViewHolder
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
mockkObject(LocaleManager)
|
||||
every { LocaleManager.getCurrentLocale(any()) } returns null
|
||||
|
||||
view = LayoutInflater.from(testContext)
|
||||
.inflate(R.layout.locale_settings_item, null)
|
||||
interactor = mockk()
|
||||
|
||||
localeViewHolder = LocaleViewHolder(view, selectedLocale, interactor)
|
||||
systemLocaleViewHolder = SystemLocaleViewHolder(view, selectedLocale, interactor)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bind LocaleViewHolder`() {
|
||||
localeViewHolder.bind(selectedLocale)
|
||||
|
||||
assertEquals("English (United States)", view.locale_title_text.text)
|
||||
assertEquals("English (United States)", view.locale_subtitle_text.text)
|
||||
assertFalse(view.locale_selected_icon.isVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LocaleViewHolder calls interactor on click`() {
|
||||
localeViewHolder.bind(selectedLocale)
|
||||
|
||||
every { interactor.onLocaleSelected(selectedLocale) } just Runs
|
||||
view.performClick()
|
||||
verify { interactor.onLocaleSelected(selectedLocale) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bind SystemLocaleViewHolder`() {
|
||||
systemLocaleViewHolder.bind(selectedLocale)
|
||||
|
||||
assertEquals("Follow device language", view.locale_title_text.text)
|
||||
assertFalse(view.locale_subtitle_text.isVisible)
|
||||
assertTrue(view.locale_selected_icon.isVisible)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `SystemLocaleViewHolder calls interactor on click`() {
|
||||
systemLocaleViewHolder.bind(selectedLocale)
|
||||
|
||||
every { interactor.onDefaultLocaleSelected() } just Runs
|
||||
view.performClick()
|
||||
verify { interactor.onDefaultLocaleSelected() }
|
||||
}
|
||||
}
|
|
@ -10,39 +10,30 @@ import io.mockk.every
|
|||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import mozilla.components.concept.engine.Engine
|
||||
import org.junit.After
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.ext.components
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultDeleteBrowsingDataControllerTest {
|
||||
|
||||
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||
|
||||
private val context: Context = mockk(relaxed = true)
|
||||
private lateinit var controller: DefaultDeleteBrowsingDataController
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
Dispatchers.setMain(mainThreadSurrogate)
|
||||
|
||||
every { context.components.core.engine.clearData(any()) } just Runs
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
|
||||
mainThreadSurrogate.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteTabs() = runBlockingTest {
|
||||
controller = DefaultDeleteBrowsingDataController(context, coroutineContext)
|
||||
|
|
|
@ -6,24 +6,21 @@
|
|||
|
||||
package org.mozilla.fenix.settings.deletebrowsingdata
|
||||
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyOrder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
||||
import mozilla.components.concept.engine.Engine
|
||||
import mozilla.components.feature.tabs.TabsUseCases
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.After
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
|
@ -31,13 +28,15 @@ import org.mozilla.fenix.components.FenixSnackbar
|
|||
import org.mozilla.fenix.components.PermissionStorage
|
||||
import org.mozilla.fenix.ext.clearAndCommit
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class DeleteAndQuitTest {
|
||||
|
||||
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||
|
||||
private var activity: HomeActivity = mockk(relaxed = true)
|
||||
lateinit var settings: Settings
|
||||
|
@ -54,8 +53,6 @@ class DeleteAndQuitTest {
|
|||
clear()
|
||||
}
|
||||
|
||||
Dispatchers.setMain(mainThreadSurrogate)
|
||||
|
||||
every { activity.components.core.historyStorage } returns historyStorage
|
||||
every { activity.components.core.permissionStorage } returns permissionStorage
|
||||
every { activity.components.useCases.tabsUseCases } returns tabUseCases
|
||||
|
@ -63,12 +60,6 @@ class DeleteAndQuitTest {
|
|||
every { activity.components.core.engine } returns engine
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
|
||||
mainThreadSurrogate.close()
|
||||
}
|
||||
|
||||
private fun Settings.clear() {
|
||||
preferences.clearAndCommit()
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import mozilla.components.support.test.ext.joinBlocking
|
|||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
class LoginsFragmentStoreTest {
|
||||
|
@ -123,4 +124,19 @@ class LoginsFragmentStoreTest {
|
|||
assertEquals("example", store.state.searchedForText)
|
||||
assertEquals(listOf(exampleLogin), store.state.filteredItems)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LoginSelected action`() {
|
||||
val store = LoginsFragmentStore(baseState.copy(
|
||||
isLoading = false,
|
||||
loginList = listOf(mockk()),
|
||||
filteredItems = listOf(mockk())
|
||||
))
|
||||
|
||||
store.dispatch(LoginsAction.LoginSelected(mockk())).joinBlocking()
|
||||
|
||||
assertTrue(store.state.isLoading)
|
||||
assertTrue(store.state.loginList.isEmpty())
|
||||
assertTrue(store.state.filteredItems.isEmpty())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,6 @@ class LoginsListViewHolderTest {
|
|||
holder.bind(baseLogin)
|
||||
|
||||
view.performClick()
|
||||
verify { interactor.itemClicked(baseLogin) }
|
||||
verify { interactor.onItemClicked(baseLogin) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,20 +4,30 @@
|
|||
|
||||
package org.mozilla.fenix.settings.logins
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyAll
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class SavedLoginsControllerTest {
|
||||
private val store: LoginsFragmentStore = mockk(relaxed = true)
|
||||
private val navController: NavController = mockk(relaxed = true)
|
||||
private val browserNavigator: (String, Boolean, BrowserDirection) -> Unit = mockk(relaxed = true)
|
||||
|
||||
private val settings: Settings = mockk(relaxed = true)
|
||||
private val metrics: MetricController = mockk(relaxed = true)
|
||||
private val sortingStrategy: SortingStrategy = SortingStrategy.Alphabetically(testContext)
|
||||
private val controller = SavedLoginsController(store, settings)
|
||||
private val controller = SavedLoginsController(store, navController, browserNavigator, settings, metrics)
|
||||
|
||||
@Test
|
||||
fun `GIVEN a sorting strategy, WHEN handleSort is called on the controller, THEN the correct action should be dispatched and the strategy saved in sharedPref`() {
|
||||
|
@ -34,4 +44,32 @@ class SavedLoginsControllerTest {
|
|||
settings.savedLoginsSortingStrategy = sortingStrategy
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a SavedLogin, WHEN handleItemClicked is called for it, THEN LoginsAction$LoginSelected should be emitted`() {
|
||||
val login: SavedLogin = mockk(relaxed = true)
|
||||
|
||||
controller.handleItemClicked(login)
|
||||
|
||||
verifyAll {
|
||||
store.dispatch(LoginsAction.LoginSelected(login))
|
||||
metrics.track(Event.OpenOneLogin)
|
||||
navController.navigate(
|
||||
SavedLoginsFragmentDirections.actionSavedLoginsFragmentToLoginDetailFragment(login.guid)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the learn more option, WHEN handleLearnMoreClicked is called for it, then we should open the right support webpage`() {
|
||||
controller.handleLearnMoreClicked()
|
||||
|
||||
verify {
|
||||
browserNavigator.invoke(
|
||||
SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.SYNC_SETUP),
|
||||
true,
|
||||
BrowserDirection.FromSavedLoginsFragment
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
package org.mozilla.fenix.settings.logins
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyAll
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
@ -15,32 +15,35 @@ import kotlin.random.Random
|
|||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class SavedLoginsInteractorTest {
|
||||
private val controller: SavedLoginsController = mockk(relaxed = true)
|
||||
private val savedLoginClicked: (SavedLogin) -> Unit = mockk(relaxed = true)
|
||||
private val learnMore: () -> Unit = mockk(relaxed = true)
|
||||
private val interactor = SavedLoginsInteractor(
|
||||
controller,
|
||||
savedLoginClicked,
|
||||
learnMore
|
||||
)
|
||||
private val interactor = SavedLoginsInteractor(controller)
|
||||
|
||||
@Test
|
||||
fun itemClicked() {
|
||||
fun `GIVEN a SavedLogin being clicked, WHEN the interactor is called for it, THEN it should just delegate the controller`() {
|
||||
val item = SavedLogin("mozilla.org", "username", "password", "id", Random.nextLong())
|
||||
interactor.itemClicked(item)
|
||||
interactor.onItemClicked(item)
|
||||
|
||||
verify {
|
||||
savedLoginClicked.invoke(item)
|
||||
verifyAll {
|
||||
controller.handleItemClicked(item)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a sorting strategy, WHEN sort method is called on the interactor, THEN controller should call handleSort with the same parameter`() {
|
||||
fun `GIVEN a change in sorting strategy, WHEN the interactor is called for it, THEN it should just delegate the controller`() {
|
||||
val sortingStrategy: SortingStrategy = SortingStrategy.Alphabetically(testContext)
|
||||
|
||||
interactor.sort(sortingStrategy)
|
||||
interactor.onSortingStrategyChanged(sortingStrategy)
|
||||
|
||||
verify {
|
||||
verifyAll {
|
||||
controller.handleSort(sortingStrategy)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN the learn more option is clicked, WHEN the interactor is called for it, THEN it should just delegate the controller`() {
|
||||
interactor.onLearnMoreClicked()
|
||||
|
||||
verifyAll {
|
||||
controller.handleLearnMoreClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.sync
|
||||
|
||||
import android.content.Context
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import mozilla.components.concept.sync.AccountObserver
|
||||
import mozilla.components.feature.syncedtabs.storage.SyncedTabsStorage
|
||||
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.FenixApplication
|
||||
|
||||
class SyncedTabsIntegrationTest {
|
||||
|
||||
@MockK private lateinit var context: Context
|
||||
@MockK private lateinit var syncedTabsStorage: SyncedTabsStorage
|
||||
@MockK private lateinit var accountManager: FxaAccountManager
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
every { syncedTabsStorage.stop() } just Runs
|
||||
every { accountManager.register(any(), owner = any(), autoPause = true) } just Runs
|
||||
every { context.applicationContext } returns mockk<FenixApplication> {
|
||||
every { components } returns mockk {
|
||||
every { backgroundServices.syncedTabsStorage } returns syncedTabsStorage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `starts and stops syncedTabsStorage on user authentication`() {
|
||||
val observer = slot<AccountObserver>()
|
||||
SyncedTabsIntegration(context, accountManager).launch()
|
||||
verify { accountManager.register(capture(observer), owner = any(), autoPause = true) }
|
||||
|
||||
every { syncedTabsStorage.start() } just Runs
|
||||
observer.captured.onAuthenticated(mockk(), mockk())
|
||||
verify { syncedTabsStorage.start() }
|
||||
|
||||
every { syncedTabsStorage.stop() } just Runs
|
||||
observer.captured.onLoggedOut()
|
||||
verify { syncedTabsStorage.stop() }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/* 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.sync
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.android.synthetic.main.sync_tabs_list_item.view.*
|
||||
import kotlinx.android.synthetic.main.view_synced_tabs_group.view.*
|
||||
import mozilla.components.browser.storage.sync.Tab
|
||||
import mozilla.components.browser.storage.sync.TabEntry
|
||||
import mozilla.components.concept.sync.Device
|
||||
import mozilla.components.concept.sync.DeviceType
|
||||
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.mozilla.fenix.R
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class SyncedTabsViewHolderTest {
|
||||
|
||||
private lateinit var tabViewHolder: SyncedTabsViewHolder.TabViewHolder
|
||||
private lateinit var tabView: View
|
||||
private lateinit var deviceViewHolder: SyncedTabsViewHolder.DeviceViewHolder
|
||||
private lateinit var deviceView: View
|
||||
private lateinit var deviceViewGroupName: TextView
|
||||
|
||||
private val tab = Tab(
|
||||
history = listOf(
|
||||
mockk(),
|
||||
TabEntry(
|
||||
title = "Firefox",
|
||||
url = "https://firefox.com",
|
||||
iconUrl = "https://firefox.com/favicon.ico"
|
||||
),
|
||||
mockk()
|
||||
),
|
||||
active = 1,
|
||||
lastUsed = 0L
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
val inflater = LayoutInflater.from(testContext)
|
||||
|
||||
tabView = inflater.inflate(SyncedTabsViewHolder.TabViewHolder.LAYOUT_ID, null)
|
||||
tabViewHolder = SyncedTabsViewHolder.TabViewHolder(tabView)
|
||||
|
||||
deviceViewGroupName = mockk(relaxUnitFun = true)
|
||||
deviceView = mockk {
|
||||
every { synced_tabs_group_name } returns deviceViewGroupName
|
||||
}
|
||||
deviceViewHolder = SyncedTabsViewHolder.DeviceViewHolder(deviceView)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `TabViewHolder binds active tab`() {
|
||||
tabViewHolder.bind(SyncedTabsAdapter.AdapterItem.Tab(tab), mockk())
|
||||
|
||||
assertEquals("Firefox", tabView.synced_tab_item_title.text)
|
||||
assertEquals("https://firefox.com", tabView.synced_tab_item_url.text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `TabViewHolder calls interactor on click`() {
|
||||
val interactor = mockk<(Tab) -> Unit>(relaxed = true)
|
||||
tabViewHolder.bind(SyncedTabsAdapter.AdapterItem.Tab(tab), interactor)
|
||||
|
||||
tabView.performClick()
|
||||
verify { interactor(tab) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeviceViewHolder binds desktop device`() {
|
||||
val device = mockk<Device> {
|
||||
every { displayName } returns "Charcoal"
|
||||
every { deviceType } returns DeviceType.DESKTOP
|
||||
}
|
||||
deviceViewHolder.bind(SyncedTabsAdapter.AdapterItem.Device(device), mockk())
|
||||
|
||||
verify { deviceViewGroupName.text = "Charcoal" }
|
||||
verify {
|
||||
deviceViewGroupName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.mozac_ic_device_desktop, 0, 0, 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeviceViewHolder binds mobile device`() {
|
||||
val device = mockk<Device> {
|
||||
every { displayName } returns "Emerald"
|
||||
every { deviceType } returns DeviceType.MOBILE
|
||||
}
|
||||
deviceViewHolder.bind(SyncedTabsAdapter.AdapterItem.Device(device), mockk())
|
||||
|
||||
verify { deviceViewGroupName.text = "Emerald" }
|
||||
verify {
|
||||
deviceViewGroupName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.mozac_ic_device_mobile, 0, 0, 0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/* 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.wifi
|
||||
|
||||
import io.mockk.Called
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.settings.PhoneFeature.AUTOPLAY_AUDIBLE
|
||||
import org.mozilla.fenix.settings.PhoneFeature.AUTOPLAY_INAUDIBLE
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_ALLOW_ALL
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_ALLOW_ON_WIFI
|
||||
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class SitePermissionsWifiIntegrationTest {
|
||||
|
||||
private lateinit var settings: Settings
|
||||
private lateinit var wifiConnectionMonitor: WifiConnectionMonitor
|
||||
private lateinit var wifiIntegration: SitePermissionsWifiIntegration
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
settings = mockk()
|
||||
wifiConnectionMonitor = mockk(relaxed = true)
|
||||
wifiIntegration = SitePermissionsWifiIntegration(settings, wifiConnectionMonitor)
|
||||
|
||||
every { settings.setSitePermissionsPhoneFeatureAction(any(), any()) } just Runs
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `add and remove wifi connected listener`() {
|
||||
wifiIntegration.addWifiConnectedListener()
|
||||
verify { wifiConnectionMonitor.register(any()) }
|
||||
|
||||
wifiIntegration.removeWifiConnectedListener()
|
||||
verify { wifiConnectionMonitor.unregister(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `start and stop wifi connection monitor`() {
|
||||
wifiIntegration.start()
|
||||
verify { wifiConnectionMonitor.start() }
|
||||
|
||||
wifiIntegration.stop()
|
||||
verify { wifiConnectionMonitor.stop() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `add only if autoplay is only allowed on wifi`() {
|
||||
every { settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) } returns AUTOPLAY_ALLOW_ALL
|
||||
wifiIntegration.maybeAddWifiConnectedListener()
|
||||
verify { wifiConnectionMonitor wasNot Called }
|
||||
|
||||
every { settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) } returns AUTOPLAY_ALLOW_ON_WIFI
|
||||
wifiIntegration.maybeAddWifiConnectedListener()
|
||||
verify { wifiConnectionMonitor.register(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `listener removes itself if autoplay is not only allowed on wifi`() {
|
||||
every { settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) } returns AUTOPLAY_ALLOW_ALL
|
||||
wifiIntegration.onWifiConnectionChanged(connected = true)
|
||||
verify { wifiConnectionMonitor.unregister(any()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `listener sets audible and inaudible settings to allowed on connect`() {
|
||||
every { settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) } returns AUTOPLAY_ALLOW_ON_WIFI
|
||||
wifiIntegration.onWifiConnectionChanged(connected = true)
|
||||
verify { settings.setSitePermissionsPhoneFeatureAction(AUTOPLAY_AUDIBLE, Action.ALLOWED) }
|
||||
verify { settings.setSitePermissionsPhoneFeatureAction(AUTOPLAY_INAUDIBLE, Action.ALLOWED) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `listener sets audible and inaudible settings to blocked on disconnected`() {
|
||||
every { settings.getAutoplayUserSetting(default = AUTOPLAY_BLOCK_ALL) } returns AUTOPLAY_ALLOW_ON_WIFI
|
||||
wifiIntegration.onWifiConnectionChanged(connected = false)
|
||||
verify { settings.setSitePermissionsPhoneFeatureAction(AUTOPLAY_AUDIBLE, Action.BLOCKED) }
|
||||
verify { settings.setSitePermissionsPhoneFeatureAction(AUTOPLAY_INAUDIBLE, Action.BLOCKED) }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue