Run ktlintFormat to adapt to latest formatting rules.

upstream-sync
Sebastian Kaspari 3 years ago committed by mergify[bot]
parent 94f9c23721
commit 971b419d77

@ -79,9 +79,11 @@ class BaselinePingTest {
@JvmStatic @JvmStatic
@OptIn(DelicateCoroutinesApi::class) // GlobalScope usage @OptIn(DelicateCoroutinesApi::class) // GlobalScope usage
fun setupOnce() { fun setupOnce() {
val httpClient = ConceptFetchHttpUploader(lazy { val httpClient = ConceptFetchHttpUploader(
GeckoViewFetchClient(ApplicationProvider.getApplicationContext()) lazy {
}) GeckoViewFetchClient(ApplicationProvider.getApplicationContext())
}
)
// Fenix does not initialize the Glean SDK in tests/debug builds, but this test // Fenix does not initialize the Glean SDK in tests/debug builds, but this test
// requires Glean to be initialized so we need to do it manually. Additionally, // requires Glean to be initialized so we need to do it manually. Additionally,
@ -151,8 +153,11 @@ class BaselinePingTest {
// sending the ping that was submitted on background. This can go away once bug 1634375 // sending the ping that was submitted on background. This can go away once bug 1634375
// is fixed. // is fixed.
device.pressRecentApps() device.pressRecentApps()
device.findObject(UiSelector().descriptionContains( device.findObject(
ApplicationProvider.getApplicationContext<Context>().getString(R.string.app_name))) UiSelector().descriptionContains(
ApplicationProvider.getApplicationContext<Context>().getString(R.string.app_name)
)
)
.click() .click()
// Validate the received data. // Validate the received data.

@ -83,7 +83,7 @@ object TestHelper {
fun getPermissionAllowID(): String { fun getPermissionAllowID(): String {
return when return when
(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
true -> "com.android.permissioncontroller" true -> "com.android.permissioncontroller"
false -> "com.android.packageinstaller" false -> "com.android.packageinstaller"
} }

@ -58,7 +58,7 @@ private val failureMsgRecyclerViewConstraintLayoutChildren = getErrorMessage(
private val failureMsgNumberOfInflation = getErrorMessage( private val failureMsgNumberOfInflation = getErrorMessage(
shortName = "Number of inflation on start up doesn't match expected count", shortName = "Number of inflation on start up doesn't match expected count",
implications = "The number of inflation can negatively impact start up time. Having more inflations" + implications = "The number of inflation can negatively impact start up time. Having more inflations" +
"will most likely mean we're adding extra work on the UI thread." "will most likely mean we're adding extra work on the UI thread."
) )
/** /**
* A performance test to limit the number of StrictMode suppressions and number of runBlocking used * A performance test to limit the number of StrictMode suppressions and number of runBlocking used

@ -83,14 +83,14 @@ class MenuScreenShotTest : ScreenshotTest() {
}.openLanguageSubMenu { }.openLanguageSubMenu {
Screengrab.screenshot("SettingsSubMenuAccessibilityRobot_settings-language") Screengrab.screenshot("SettingsSubMenuAccessibilityRobot_settings-language")
}.goBack { }.goBack {
// From about here we need to scroll up to ensure all settings options are visible. // From about here we need to scroll up to ensure all settings options are visible.
}.openSetDefaultBrowserSubMenu { }.openSetDefaultBrowserSubMenu {
Screengrab.screenshot("SettingsSubMenuDefaultBrowserRobot_settings-default-browser") Screengrab.screenshot("SettingsSubMenuDefaultBrowserRobot_settings-default-browser")
}.goBack { }.goBack {
// Disabled for Pixel 2 // Disabled for Pixel 2
// }.openEnhancedTrackingProtectionSubMenu { // }.openEnhancedTrackingProtectionSubMenu {
// Screengrab.screenshot("settings-enhanced-tp") // Screengrab.screenshot("settings-enhanced-tp")
// }.goBack { // }.goBack {
}.openLoginsAndPasswordSubMenu { }.openLoginsAndPasswordSubMenu {
Screengrab.screenshot("SettingsSubMenuLoginsAndPasswords-settings-logins-passwords") Screengrab.screenshot("SettingsSubMenuLoginsAndPasswords-settings-logins-passwords")
}.goBack { }.goBack {
@ -176,7 +176,7 @@ class MenuScreenShotTest : ScreenshotTest() {
@Test @Test
fun saveLoginPromptTest() { fun saveLoginPromptTest() {
val saveLoginTest = val saveLoginTest =
TestAssetHelper.getSaveLoginAsset(mockWebServer) TestAssetHelper.getSaveLoginAsset(mockWebServer)
navigationToolbar { navigationToolbar {
}.enterURLAndEnterToBrowser(saveLoginTest.url) { }.enterURLAndEnterToBrowser(saveLoginTest.url) {
verifySaveLoginPromptIsShownNotSave() verifySaveLoginPromptIsShownNotSave()

@ -173,9 +173,11 @@ class SyncIntegrationTest {
// Useful functions for the tests // Useful functions for the tests
fun typeEmail() { fun typeEmail() {
val emailInput = mDevice.findObject(UiSelector() val emailInput = mDevice.findObject(
UiSelector()
.instance(0) .instance(0)
.className(EditText::class.java)) .className(EditText::class.java)
)
emailInput.waitForExists(TestAssetHelper.waitingTime) emailInput.waitForExists(TestAssetHelper.waitingTime)
val emailAddress = javaClass.classLoader!!.getResource("email.txt").readText() val emailAddress = javaClass.classLoader!!.getResource("email.txt").readText()
@ -188,9 +190,11 @@ class SyncIntegrationTest {
} }
fun typePassword() { fun typePassword() {
val passwordInput = mDevice.findObject(UiSelector() val passwordInput = mDevice.findObject(
UiSelector()
.instance(0) .instance(0)
.className(EditText::class.java)) .className(EditText::class.java)
)
val passwordValue = javaClass.classLoader!!.getResource("password.txt").readText() val passwordValue = javaClass.classLoader!!.getResource("password.txt").readText()
passwordInput.setText(passwordValue) passwordInput.setText(passwordValue)

@ -96,7 +96,7 @@ class SettingsAddonsTest {
acceptInstallAddon() acceptInstallAddon()
verifyDownloadAddonPrompt(addonName, activityTestRule) verifyDownloadAddonPrompt(addonName, activityTestRule)
} }
} }
// Opens the addons settings menu, installs an addon, then uninstalls // Opens the addons settings menu, installs an addon, then uninstalls

@ -44,19 +44,19 @@ private fun deviceName() = Espresso.onView(CoreMatchers.allOf(ViewMatchers.withT
private fun disconnectButton() = Espresso.onView(CoreMatchers.allOf(ViewMatchers.withId(R.id.signOutDisconnect))) private fun disconnectButton() = Espresso.onView(CoreMatchers.allOf(ViewMatchers.withId(R.id.signOutDisconnect)))
private fun assertBookmarksCheckbox() = bookmarksCheckbox().check( private fun assertBookmarksCheckbox() = bookmarksCheckbox().check(
ViewAssertions.matches( ViewAssertions.matches(
ViewMatchers.withEffectiveVisibility( ViewMatchers.withEffectiveVisibility(
ViewMatchers.Visibility.VISIBLE ViewMatchers.Visibility.VISIBLE
)
) )
)
) )
private fun assertHistoryCheckbox() = historyCheckbox().check( private fun assertHistoryCheckbox() = historyCheckbox().check(
ViewAssertions.matches( ViewAssertions.matches(
ViewMatchers.withEffectiveVisibility( ViewMatchers.withEffectiveVisibility(
ViewMatchers.Visibility.VISIBLE ViewMatchers.Visibility.VISIBLE
)
) )
)
) )
private fun assertSignOutButton() = signOutButton().check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) private fun assertSignOutButton() = signOutButton().check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))

@ -91,8 +91,12 @@ fun addToHomeScreen(interact: AddToHomeScreenRobot.() -> Unit): AddToHomeScreenR
private fun shortcutNameField() = onView(withId(R.id.shortcut_text)) private fun shortcutNameField() = onView(withId(R.id.shortcut_text))
private fun assertShortcutNameField(expectedText: String) { private fun assertShortcutNameField(expectedText: String) {
onView(allOf(withId(R.id.shortcut_text), onView(
withText(expectedText))) allOf(
withId(R.id.shortcut_text),
withText(expectedText)
)
)
.check(matches(isCompletelyDisplayed())) .check(matches(isCompletelyDisplayed()))
} }

@ -105,8 +105,10 @@ class BookmarksRobot {
fun verifySelectDefaultFolderSnackBarText() = assertSnackBarText("Cant edit default folders") fun verifySelectDefaultFolderSnackBarText() = assertSnackBarText("Cant edit default folders")
fun verifyCurrentFolderTitle(title: String) { fun verifyCurrentFolderTitle(title: String) {
mDevice.findObject(UiSelector().resourceId("$packageName:id/navigationToolbar") mDevice.findObject(
.textContains(title)) UiSelector().resourceId("$packageName:id/navigationToolbar")
.textContains(title)
)
.waitForExists(waitingTime) .waitForExists(waitingTime)
onView( onView(
@ -119,8 +121,10 @@ class BookmarksRobot {
} }
fun waitForBookmarksFolderContentToExist(parentFolderName: String, childFolderName: String) { fun waitForBookmarksFolderContentToExist(parentFolderName: String, childFolderName: String) {
mDevice.findObject(UiSelector().resourceId("$packageName:id/navigationToolbar") mDevice.findObject(
.textContains(parentFolderName)) UiSelector().resourceId("$packageName:id/navigationToolbar")
.textContains(parentFolderName)
)
.waitForExists(waitingTime) .waitForExists(waitingTime)
mDevice.waitNotNull(Until.findObject(By.text(childFolderName)), waitingTime) mDevice.waitNotNull(Until.findObject(By.text(childFolderName)), waitingTime)

@ -509,8 +509,10 @@ class BrowserRobot {
fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
mDevice.waitForIdle(waitingTime) mDevice.waitForIdle(waitingTime)
tabsCounter().click() tabsCounter().click()
mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout")), mDevice.waitNotNull(
waitingTime) Until.findObject(By.res("$packageName:id/tab_layout")),
waitingTime
)
TabDrawerRobot().interact() TabDrawerRobot().interact()
return TabDrawerRobot.Transition() return TabDrawerRobot.Transition()

@ -58,8 +58,10 @@ class DownloadRobot {
} }
fun waitForDownloadsListToExist() = fun waitForDownloadsListToExist() =
assertTrue(mDevice.findObject(UiSelector().resourceId("$packageName:id/download_list")) assertTrue(
.waitForExists(waitingTime)) mDevice.findObject(UiSelector().resourceId("$packageName:id/download_list"))
.waitForExists(waitingTime)
)
class Transition { class Transition {
fun clickDownload(interact: DownloadRobot.() -> Unit): Transition { fun clickDownload(interact: DownloadRobot.() -> Unit): Transition {

@ -179,8 +179,8 @@ private fun assertBasicLevelTrackingContentBlocked() {
withText( withText(
containsString( containsString(
"social-track-digest256.dummytracker.org\n" + "social-track-digest256.dummytracker.org\n" +
"ads-track-digest256.dummytracker.org\n" + "ads-track-digest256.dummytracker.org\n" +
"analytics-track-digest256.dummytracker.org" "analytics-track-digest256.dummytracker.org"
) )
) )
) )

@ -94,7 +94,7 @@ class HistoryRobot {
} }
fun openThreeDotMenu(interact: ThreeDotMenuHistoryItemRobot.() -> Unit): fun openThreeDotMenu(interact: ThreeDotMenuHistoryItemRobot.() -> Unit):
ThreeDotMenuHistoryItemRobot.Transition { ThreeDotMenuHistoryItemRobot.Transition {
threeDotMenu().click() threeDotMenu().click()

@ -61,9 +61,9 @@ import org.mozilla.fenix.ui.util.STRING_ONBOARDING_TRACKING_PROTECTION_HEADER
class HomeScreenRobot { class HomeScreenRobot {
val privateSessionMessage = val privateSessionMessage =
"$appName clears your search and browsing history from private tabs when you close them" + "$appName clears your search and browsing history from private tabs when you close them" +
" or quit the app. While this doesnt make you anonymous to websites or your internet" + " or quit the app. While this doesnt make you anonymous to websites or your internet" +
" service provider, it makes it easier to keep what you do online private from anyone" + " service provider, it makes it easier to keep what you do online private from anyone" +
" else who uses this device." " else who uses this device."
fun verifyNavigationToolbar() = assertNavigationToolbar() fun verifyNavigationToolbar() = assertNavigationToolbar()
fun verifyFocusedNavigationToolbar() = assertFocusedNavigationToolbar() fun verifyFocusedNavigationToolbar() = assertFocusedNavigationToolbar()
@ -207,7 +207,7 @@ class HomeScreenRobot {
} }
fun triggerPrivateBrowsingShortcutPrompt(interact: AddToHomeScreenRobot.() -> Unit): AddToHomeScreenRobot.Transition { fun triggerPrivateBrowsingShortcutPrompt(interact: AddToHomeScreenRobot.() -> Unit): AddToHomeScreenRobot.Transition {
// Loop to press the PB icon for 5 times to display the Add the Private Browsing Shortcut CFR // Loop to press the PB icon for 5 times to display the Add the Private Browsing Shortcut CFR
for (i in 1..5) { for (i in 1..5) {
mDevice.findObject(UiSelector().resourceId("$packageName:id/privateBrowsingButton")) mDevice.findObject(UiSelector().resourceId("$packageName:id/privateBrowsingButton"))
.waitForExists( .waitForExists(
@ -356,7 +356,7 @@ private fun assertFocusedNavigationToolbar() =
private fun assertHomeScreen() { private fun assertHomeScreen() {
mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout")).waitForExists(waitingTime) mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout")).waitForExists(waitingTime)
onView(ViewMatchers.withResourceName("homeLayout")) onView(ViewMatchers.withResourceName("homeLayout"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertHomeMenu() = onView(ViewMatchers.withResourceName("menuButton")) private fun assertHomeMenu() = onView(ViewMatchers.withResourceName("menuButton"))
@ -383,7 +383,8 @@ private fun assertCollectionsHeader() =
private fun assertNoCollectionsText() = private fun assertNoCollectionsText() =
onView( onView(
withText( withText(
containsString("Collect the things that matter to you.\n" + containsString(
"Collect the things that matter to you.\n" +
"Group together similar searches, sites, and tabs for quick access later." "Group together similar searches, sites, and tabs for quick access later."
) )
) )

@ -58,7 +58,8 @@ class LibrarySubMenusMultipleSelectionToolbarRobot {
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject( Until.findObject(
By.text("ALL ACTIONS") By.text("ALL ACTIONS")
), waitingTime ),
waitingTime
) )
} }
@ -68,7 +69,8 @@ class LibrarySubMenusMultipleSelectionToolbarRobot {
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject( Until.findObject(
By.text("ALL ACTIONS") By.text("ALL ACTIONS")
), waitingTime ),
waitingTime
) )
} }

@ -70,8 +70,10 @@ class NavigationToolbarRobot {
fun typeSearchTerm(searchTerm: String) = awesomeBar().perform(typeText(searchTerm)) fun typeSearchTerm(searchTerm: String) = awesomeBar().perform(typeText(searchTerm))
fun toggleReaderView() { fun toggleReaderView() {
mDevice.findObject(UiSelector() mDevice.findObject(
.resourceId("$packageName:id/mozac_browser_toolbar_page_actions")) UiSelector()
.resourceId("$packageName:id/mozac_browser_toolbar_page_actions")
)
.waitForExists(waitingTime) .waitForExists(waitingTime)
readerViewToggle().click() readerViewToggle().click()
@ -133,7 +135,8 @@ class NavigationToolbarRobot {
when (etpEnabled) { when (etpEnabled) {
true -> true ->
try { try {
assertTrue("Onboarding message not displayed", assertTrue(
"Onboarding message not displayed",
onboardingDisplayed onboardingDisplayed
) )
} catch (e: AssertionError) { } catch (e: AssertionError) {
@ -184,8 +187,10 @@ class NavigationToolbarRobot {
fun openTabTray(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { fun openTabTray(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
mDevice.waitForIdle(waitingTime) mDevice.waitForIdle(waitingTime)
tabTrayButton().click() tabTrayButton().click()
mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout")), mDevice.waitNotNull(
waitingTime) Until.findObject(By.res("$packageName:id/tab_layout")),
waitingTime
)
TabDrawerRobot().interact() TabDrawerRobot().interact()
return TabDrawerRobot.Transition() return TabDrawerRobot.Transition()
@ -251,7 +256,8 @@ class NavigationToolbarRobot {
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>( RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant( hasDescendant(
withText("Close tab") withText("Close tab")
), ViewActions.click() ),
ViewActions.click()
) )
) )
@ -267,7 +273,8 @@ class NavigationToolbarRobot {
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>( RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant( hasDescendant(
withText("New tab") withText("New tab")
), ViewActions.click() ),
ViewActions.click()
) )
) )
@ -283,7 +290,8 @@ class NavigationToolbarRobot {
RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>( RecyclerViewActions.actionOnItem<RecyclerView.ViewHolder>(
hasDescendant( hasDescendant(
withText("New private tab") withText("New private tab")
), ViewActions.click() ),
ViewActions.click()
) )
) )
@ -352,8 +360,10 @@ private fun readerViewToggle() =
onView(withParent(withId(R.id.mozac_browser_toolbar_page_actions))) onView(withParent(withId(R.id.mozac_browser_toolbar_page_actions)))
private fun assertReaderViewDetected(visible: Boolean) { private fun assertReaderViewDetected(visible: Boolean) {
mDevice.findObject(UiSelector() mDevice.findObject(
.description("Reader view")) UiSelector()
.description("Reader view")
)
.waitForExists(waitingTime) .waitForExists(waitingTime)
onView( onView(
@ -368,8 +378,10 @@ private fun assertReaderViewDetected(visible: Boolean) {
} }
private fun assertCloseReaderViewDetected(visible: Boolean) { private fun assertCloseReaderViewDetected(visible: Boolean) {
mDevice.findObject(UiSelector() mDevice.findObject(
.description("Close reader view")) UiSelector()
.description("Close reader view")
)
.waitForExists(waitingTime) .waitForExists(waitingTime)
onView( onView(

@ -55,9 +55,9 @@ class ReaderViewRobot {
val prefs = InstrumentationRegistry.getInstrumentation() val prefs = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences( .targetContext.getSharedPreferences(
"mozac_feature_reader_view", "mozac_feature_reader_view",
Context.MODE_PRIVATE Context.MODE_PRIVATE
) )
assertEquals(fontType, prefs.getString(fontTypeKey, "")) assertEquals(fontType, prefs.getString(fontTypeKey, ""))
} }
@ -67,9 +67,9 @@ class ReaderViewRobot {
val prefs = InstrumentationRegistry.getInstrumentation() val prefs = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences( .targetContext.getSharedPreferences(
"mozac_feature_reader_view", "mozac_feature_reader_view",
Context.MODE_PRIVATE Context.MODE_PRIVATE
) )
val fontSizeKeyValue = prefs.getInt(fontSizeKey, 3) val fontSizeKeyValue = prefs.getInt(fontSizeKey, 3)
@ -81,9 +81,9 @@ class ReaderViewRobot {
val prefs = InstrumentationRegistry.getInstrumentation() val prefs = InstrumentationRegistry.getInstrumentation()
.targetContext.getSharedPreferences( .targetContext.getSharedPreferences(
"mozac_feature_reader_view", "mozac_feature_reader_view",
Context.MODE_PRIVATE Context.MODE_PRIVATE
) )
assertEquals(expectedColorScheme, prefs.getString(colorSchemeKey, "")) assertEquals(expectedColorScheme, prefs.getString(colorSchemeKey, ""))
} }

@ -94,7 +94,8 @@ private fun assertRecentlyClosedTabsMenuView() {
) )
) )
.check( .check(
matches(withEffectiveVisibility(Visibility.VISIBLE))) matches(withEffectiveVisibility(Visibility.VISIBLE))
)
} }
private fun assertEmptyRecentlyClosedTabsList() = private fun assertEmptyRecentlyClosedTabsList() =
@ -105,7 +106,8 @@ private fun assertEmptyRecentlyClosedTabsList() =
) )
) )
.check( .check(
matches(withText("No recently closed tabs here"))) matches(withText("No recently closed tabs here"))
)
private fun assertPageUrl(expectedUrl: Uri) = onView( private fun assertPageUrl(expectedUrl: Uri) = onView(
allOf( allOf(
@ -116,7 +118,8 @@ private fun assertPageUrl(expectedUrl: Uri) = onView(
) )
) )
.check( .check(
matches(withText(Matchers.containsString(expectedUrl.toString())))) matches(withText(Matchers.containsString(expectedUrl.toString())))
)
private fun recentlyClosedTabsPageTitle() = onView( private fun recentlyClosedTabsPageTitle() = onView(
allOf( allOf(
@ -128,9 +131,11 @@ private fun recentlyClosedTabsPageTitle() = onView(
private fun assertRecentlyClosedTabsPageTitle(title: String) { private fun assertRecentlyClosedTabsPageTitle(title: String) {
recentlyClosedTabsPageTitle() recentlyClosedTabsPageTitle()
.check( .check(
matches(withEffectiveVisibility(Visibility.VISIBLE))) matches(withEffectiveVisibility(Visibility.VISIBLE))
)
.check( .check(
matches(withText(title))) matches(withText(title))
)
} }
private fun recentlyClosedTabsThreeDotButton() = private fun recentlyClosedTabsThreeDotButton() =
@ -138,45 +143,50 @@ private fun recentlyClosedTabsThreeDotButton() =
allOf( allOf(
withId(R.id.overflow_menu), withId(R.id.overflow_menu),
withEffectiveVisibility( withEffectiveVisibility(
Visibility.VISIBLE Visibility.VISIBLE
)
) )
) )
)
private fun assertRecentlyClosedTabsMenuCopy() = private fun assertRecentlyClosedTabsMenuCopy() =
onView(withText("Copy")) onView(withText("Copy"))
.check( .check(
matches( matches(
withEffectiveVisibility(Visibility.VISIBLE))) withEffectiveVisibility(Visibility.VISIBLE)
)
)
private fun assertRecentlyClosedTabsMenuShare() = private fun assertRecentlyClosedTabsMenuShare() =
onView(withText("Share")) onView(withText("Share"))
.check( .check(
matches( matches(
withEffectiveVisibility(Visibility.VISIBLE))) withEffectiveVisibility(Visibility.VISIBLE)
)
)
private fun assertRecentlyClosedTabsOverlayNewTab() = private fun assertRecentlyClosedTabsOverlayNewTab() =
onView(withText("Open in new tab")) onView(withText("Open in new tab"))
.check( .check(
matches( matches(
withEffectiveVisibility(Visibility.VISIBLE)) withEffectiveVisibility(Visibility.VISIBLE)
) )
)
private fun assertRecentlyClosedTabsMenuPrivateTab() = private fun assertRecentlyClosedTabsMenuPrivateTab() =
onView(withText("Open in private tab")) onView(withText("Open in private tab"))
.check( .check(
matches( matches(
withEffectiveVisibility(Visibility.VISIBLE) withEffectiveVisibility(Visibility.VISIBLE)
)
) )
)
private fun assertRecentlyClosedTabsMenuDelete() = private fun assertRecentlyClosedTabsMenuDelete() =
onView(withText("Delete")) onView(withText("Delete"))
.check( .check(
matches( matches(
withEffectiveVisibility(Visibility.VISIBLE) withEffectiveVisibility(Visibility.VISIBLE)
) )
) )
private fun recentlyClosedTabsCopyButton() = onView(withText("Copy")) private fun recentlyClosedTabsCopyButton() = onView(withText("Copy"))
@ -185,26 +195,31 @@ private fun copySnackBarText() = onView(withId(R.id.snackbar_text))
private fun assertCopySnackBarText() = copySnackBarText() private fun assertCopySnackBarText() = copySnackBarText()
.check( .check(
matches matches
(withText("URL copied"))) (withText("URL copied"))
)
private fun recentlyClosedTabsShareButton() = onView(withText("Share")) private fun recentlyClosedTabsShareButton() = onView(withText("Share"))
private fun assertRecentlyClosedShareOverlay() = private fun assertRecentlyClosedShareOverlay() =
onView(withId(R.id.shareWrapper)) onView(withId(R.id.shareWrapper))
.check( .check(
matches(ViewMatchers.isDisplayed())) matches(ViewMatchers.isDisplayed())
)
private fun assetRecentlyClosedShareTitle(title: String) = private fun assetRecentlyClosedShareTitle(title: String) =
onView(withId(R.id.share_tab_title)) onView(withId(R.id.share_tab_title))
.check( .check(
matches(ViewMatchers.isDisplayed())) matches(ViewMatchers.isDisplayed())
)
.check( .check(
matches(withText(title))) matches(withText(title))
)
private fun assertRecentlyClosedShareFavicon() = private fun assertRecentlyClosedShareFavicon() =
onView(withId(R.id.share_tab_favicon)) onView(withId(R.id.share_tab_favicon))
.check( .check(
matches(ViewMatchers.isDisplayed())) matches(ViewMatchers.isDisplayed())
)
private fun assertRecentlyClosedShareUrl(expectedUrl: Uri) = private fun assertRecentlyClosedShareUrl(expectedUrl: Uri) =
onView( onView(
@ -214,7 +229,8 @@ private fun assertRecentlyClosedShareUrl(expectedUrl: Uri) =
) )
) )
.check( .check(
matches(withText(Matchers.containsString(expectedUrl.toString())))) matches(withText(Matchers.containsString(expectedUrl.toString())))
)
private fun recentlyClosedTabsNewTabButton() = onView(withText("Open in new tab")) private fun recentlyClosedTabsNewTabButton() = onView(withText("Open in new tab"))

@ -73,8 +73,10 @@ class SearchRobot {
selectDefaultSearchEngine(searchEngineName) selectDefaultSearchEngine(searchEngineName)
fun clickSearchEngineShortcutButton() { fun clickSearchEngineShortcutButton() {
val searchEnginesShortcutButton = mDevice.findObject(UiSelector() val searchEnginesShortcutButton = mDevice.findObject(
.resourceId("$packageName:id/search_engines_shortcut_button")) UiSelector()
.resourceId("$packageName:id/search_engines_shortcut_button")
)
searchEnginesShortcutButton.waitForExists(waitingTime) searchEnginesShortcutButton.waitForExists(waitingTime)
searchEnginesShortcutButton.click() searchEnginesShortcutButton.click()
} }
@ -270,7 +272,8 @@ private fun assertKeyboardVisibility(isExpectedToBeVisible: Boolean) = {
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject( Until.findObject(
By.text("Search Engine") By.text("Search Engine")
), waitingTime ),
waitingTime
) )
assertEquals( assertEquals(
isExpectedToBeVisible, isExpectedToBeVisible,

@ -111,7 +111,7 @@ class SettingsRobot {
} }
fun openAboutFirefoxPreview(interact: SettingsSubMenuAboutRobot.() -> Unit): fun openAboutFirefoxPreview(interact: SettingsSubMenuAboutRobot.() -> Unit):
SettingsSubMenuAboutRobot.Transition { SettingsSubMenuAboutRobot.Transition {
assertAboutFirefoxPreview().click() assertAboutFirefoxPreview().click()
@ -120,7 +120,7 @@ class SettingsRobot {
} }
fun openSearchSubMenu(interact: SettingsSubMenuSearchRobot.() -> Unit): fun openSearchSubMenu(interact: SettingsSubMenuSearchRobot.() -> Unit):
SettingsSubMenuSearchRobot.Transition { SettingsSubMenuSearchRobot.Transition {
fun searchEngineButton() = onView(withText("Search")) fun searchEngineButton() = onView(withText("Search"))
searchEngineButton().click() searchEngineButton().click()
@ -401,7 +401,7 @@ private fun assertNotificationsButton() {
private fun assertDataCollectionButton() { private fun assertDataCollectionButton() {
scrollToElementByText("Data collection") scrollToElementByText("Data collection")
onView(withText("Data collection")) onView(withText("Data collection"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun openLinksInAppsButton() = onView(withText("Open links in apps")) private fun openLinksInAppsButton() = onView(withText("Open links in apps"))

@ -117,8 +117,8 @@ private fun assertCurrentTimestamp() {
// Currently UI tests run against debug builds, which display a hard-coded string 'debug build' // Currently UI tests run against debug builds, which display a hard-coded string 'debug build'
// instead of the date. See https://github.com/mozilla-mobile/fenix/pull/10812#issuecomment-633746833 // instead of the date. See https://github.com/mozilla-mobile/fenix/pull/10812#issuecomment-633746833
.check(matches(withText(containsString("debug build")))) .check(matches(withText(containsString("debug build"))))
// This assertion should be valid for non-debug build types. // This assertion should be valid for non-debug build types.
// .check(BuildDateAssertion.isDisplayedDateAccurate()) // .check(BuildDateAssertion.isDisplayedDateAccurate())
} }
private fun assertWhatIsNewInFirefoxPreview() { private fun assertWhatIsNewInFirefoxPreview() {

@ -59,8 +59,11 @@ private fun goBackButton() =
onView(withContentDescription("Navigate up")) onView(withContentDescription("Navigate up"))
private fun assertNavigationToolBarHeader() = onView( private fun assertNavigationToolBarHeader() = onView(
allOf(withParent(withId(R.id.navigationToolbar)), allOf(
withText(R.string.preferences_data_collection))) withParent(withId(R.id.navigationToolbar)),
withText(R.string.preferences_data_collection)
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDataCollectionOptions() { private fun assertDataCollectionOptions() {

@ -62,18 +62,27 @@ class SettingsSubMenuDeleteBrowsingDataOnQuitRobot {
private fun goBackButton() = onView(withContentDescription("Navigate up")) private fun goBackButton() = onView(withContentDescription("Navigate up"))
private fun assertNavigationToolBarHeader() = onView(allOf(withId(R.id.navigationToolbar), private fun assertNavigationToolBarHeader() = onView(
withChild(withText(R.string.preferences_delete_browsing_data_on_quit)))) allOf(
withId(R.id.navigationToolbar),
withChild(withText(R.string.preferences_delete_browsing_data_on_quit))
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun deleteBrowsingOnQuitButton() = onView(allOf(withParentIndex(0), private fun deleteBrowsingOnQuitButton() = onView(
withChild(withText(R.string.preferences_delete_browsing_data_on_quit)))) allOf(
withParentIndex(0),
withChild(withText(R.string.preferences_delete_browsing_data_on_quit))
)
)
private fun assertDeleteBrowsingOnQuitButton() = deleteBrowsingOnQuitButton() private fun assertDeleteBrowsingOnQuitButton() = deleteBrowsingOnQuitButton()
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDeleteBrowsingOnQuitButtonSummary() = onView( private fun assertDeleteBrowsingOnQuitButtonSummary() = onView(
withText(R.string.preference_summary_delete_browsing_data_on_quit_2)) withText(R.string.preference_summary_delete_browsing_data_on_quit_2)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertDeleteBrowsingOnQuitButtonSwitchDefault() = onView(withResourceName("switch_widget")) private fun assertDeleteBrowsingOnQuitButtonSwitchDefault() = onView(withResourceName("switch_widget"))

@ -86,8 +86,12 @@ private fun goBackButton() =
onView(allOf(withContentDescription("Navigate up"))) onView(allOf(withContentDescription("Navigate up")))
private fun assertNavigationToolBarHeader() { private fun assertNavigationToolBarHeader() {
onView(allOf(withId(R.id.navigationToolbar), onView(
withChild(withText(R.string.preferences_delete_browsing_data)))) allOf(
withId(R.id.navigationToolbar),
withChild(withText(R.string.preferences_delete_browsing_data))
)
)
.check((matches(withEffectiveVisibility(Visibility.VISIBLE)))) .check((matches(withEffectiveVisibility(Visibility.VISIBLE))))
} }

@ -111,8 +111,12 @@ class SettingsSubMenuEnhancedTrackingProtectionRobot {
} }
private fun assertNavigationToolBarHeader() { private fun assertNavigationToolBarHeader() {
onView(allOf(withParent(withId(org.mozilla.fenix.R.id.navigationToolbar)), onView(
withText("Enhanced Tracking Protection"))) allOf(
withParent(withId(org.mozilla.fenix.R.id.navigationToolbar)),
withText("Enhanced Tracking Protection")
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
@ -122,21 +126,32 @@ private fun assertEnhancedTrackingProtectionHeader() {
} }
private fun assertEnhancedTrackingProtectionHeaderDescription() { private fun assertEnhancedTrackingProtectionHeaderDescription() {
onView(allOf(withParent(withParentIndex(0)), onView(
withText("Keep your data to yourself. $appName protects you from many of the most common trackers that follow what you do online."))) allOf(
withParent(withParentIndex(0)),
withText("Keep your data to yourself. $appName protects you from many of the most common trackers that follow what you do online.")
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertLearnMoreText() { private fun assertLearnMoreText() {
onView(allOf(withParent(withParentIndex(0)), onView(
withText("Learn more"))) allOf(
withParent(withParentIndex(0)),
withText("Learn more")
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
private fun assertEnhancedTrackingProtectionTextWithSwitchWidget() { private fun assertEnhancedTrackingProtectionTextWithSwitchWidget() {
onView(allOf( onView(
allOf(
withParentIndex(1), withParentIndex(1),
withChild(withText("Enhanced Tracking Protection")))) withChild(withText("Enhanced Tracking Protection"))
)
)
.check(matches(withEffectiveVisibility(Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
} }
@ -195,7 +210,8 @@ private fun assertTrackingProtectionSwitchEnabled() {
} }
private fun assertRadioButtonDefaults() { private fun assertRadioButtonDefaults() {
onView(withText("Strict") onView(
withText("Strict")
).assertIsChecked(false) ).assertIsChecked(false)
onView( onView(
@ -205,7 +221,8 @@ private fun assertRadioButtonDefaults() {
) )
).assertIsChecked(true) ).assertIsChecked(true)
onView(withText("Custom") onView(
withText("Custom")
).assertIsChecked(false) ).assertIsChecked(false)
} }

@ -18,10 +18,10 @@ import org.hamcrest.CoreMatchers
class SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot { class SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot {
fun verifySaveLoginsOptionsView() { fun verifySaveLoginsOptionsView() {
onView(ViewMatchers.withText("Ask to save")) onView(ViewMatchers.withText("Ask to save"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
onView(ViewMatchers.withText("Never save")) onView(ViewMatchers.withText("Never save"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
} }
class Transition { class Transition {
@ -35,4 +35,4 @@ class SettingsSubMenuLoginsAndPasswordOptionsToSaveRobot {
} }
private fun goBackButton() = private fun goBackButton() =
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))

@ -92,10 +92,10 @@ fun settingsSubMenuLoginsAndPassword(interact: SettingsSubMenuLoginsAndPasswordR
} }
private fun goBackButton() = private fun goBackButton() =
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
private fun assertDefaultView() = onView(ViewMatchers.withText("Sync logins across devices")) private fun assertDefaultView() = onView(ViewMatchers.withText("Sync logins across devices"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun assertDefaultValueAutofillLogins() = onView(ViewMatchers.withText("Autofill")) private fun assertDefaultValueAutofillLogins() = onView(ViewMatchers.withText("Autofill"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
@ -104,4 +104,4 @@ private fun assertDefaultValueExceptions() = onView(ViewMatchers.withText("Excep
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun assertDefaultValueSyncLogins() = onView(ViewMatchers.withText("Sign in to Sync")) private fun assertDefaultValueSyncLogins() = onView(ViewMatchers.withText("Sign in to Sync"))
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))

@ -46,13 +46,13 @@ class SettingsTurnOnSyncRobot {
} }
private fun goBackButton() = private fun goBackButton() =
Espresso.onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up"))) Espresso.onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
private fun assertUseEmailField() = Espresso.onView(ViewMatchers.withText("Use email instead")) private fun assertUseEmailField() = Espresso.onView(ViewMatchers.withText("Use email instead"))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun assertReadyToScan() = Espresso.onView(ViewMatchers.withText("Ready to scan")) private fun assertReadyToScan() = Espresso.onView(ViewMatchers.withText("Ready to scan"))
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) .check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
private fun useEmailButton() = Espresso.onView(ViewMatchers.withText("Use email instead")) private fun useEmailButton() = Espresso.onView(ViewMatchers.withText("Use email instead"))

@ -39,7 +39,8 @@ fun systemSettings(interact: SystemSettingsRobot.() -> Unit): SystemSettingsRobo
private fun assertSystemNotificationsView() { private fun assertSystemNotificationsView() {
mDevice.findObject(UiSelector().resourceId("com.android.settings:id/list")) mDevice.findObject(UiSelector().resourceId("com.android.settings:id/list"))
.waitForExists(waitingTime) .waitForExists(waitingTime)
assertTrue(mDevice.findObject(UiSelector().textContains("Show notifications")) assertTrue(
.waitForExists(waitingTime) mDevice.findObject(UiSelector().textContains("Show notifications"))
.waitForExists(waitingTime)
) )
} }

@ -230,8 +230,10 @@ class TabDrawerRobot {
fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition { fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
mDevice.waitForIdle(waitingTime) mDevice.waitForIdle(waitingTime)
tabsCounter().click() tabsCounter().click()
mDevice.waitNotNull(Until.findObject(By.res("$packageName:id/tab_layout")), mDevice.waitNotNull(
waitingTime) Until.findObject(By.res("$packageName:id/tab_layout")),
waitingTime
)
TabDrawerRobot().interact() TabDrawerRobot().interact()
return TabDrawerRobot.Transition() return TabDrawerRobot.Transition()
@ -338,7 +340,7 @@ class TabDrawerRobot {
} }
fun openRecentlyClosedTabs(interact: RecentlyClosedTabsRobot.() -> Unit): fun openRecentlyClosedTabs(interact: RecentlyClosedTabsRobot.() -> Unit):
RecentlyClosedTabsRobot.Transition { RecentlyClosedTabsRobot.Transition {
threeDotMenu().click() threeDotMenu().click()
@ -356,7 +358,7 @@ class TabDrawerRobot {
} }
fun clickSaveCollection(interact: CollectionRobot.() -> Unit): fun clickSaveCollection(interact: CollectionRobot.() -> Unit):
CollectionRobot.Transition { CollectionRobot.Transition {
saveTabsToCollectionButton().click() saveTabsToCollectionButton().click()
CollectionRobot().interact() CollectionRobot().interact()
@ -395,9 +397,11 @@ private fun threeDotMenu() = onView(withId(R.id.tab_tray_overflow))
private fun assertExistingOpenTabs(title: String) { private fun assertExistingOpenTabs(title: String) {
try { try {
mDevice.findObject(UiSelector() mDevice.findObject(
.resourceId("$packageName:id/mozac_browser_tabstray_title") UiSelector()
.textContains(title)) .resourceId("$packageName:id/mozac_browser_tabstray_title")
.textContains(title)
)
.waitForExists(waitingTime) .waitForExists(waitingTime)
tab(title).check(matches(isDisplayed())) tab(title).check(matches(isDisplayed()))

@ -34,7 +34,8 @@ class ThreeDotMenuHistoryItemRobot {
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject( Until.findObject(
By.text("ALL ACTIONS") By.text("ALL ACTIONS")
), TestAssetHelper.waitingTime ),
TestAssetHelper.waitingTime
) )
LibrarySubMenusMultipleSelectionToolbarRobot().interact() LibrarySubMenusMultipleSelectionToolbarRobot().interact()

@ -512,7 +512,8 @@ private fun tabSettingsButton() =
private fun assertTabSettingsButton() { private fun assertTabSettingsButton() {
tabSettingsButton() tabSettingsButton()
.check( .check(
matches(isDisplayed())) matches(isDisplayed())
)
} }
private fun recentlyClosedTabsButton() = private fun recentlyClosedTabsButton() =
@ -521,7 +522,8 @@ private fun recentlyClosedTabsButton() =
private fun assertRecentlyClosedTabsButton() { private fun assertRecentlyClosedTabsButton() {
recentlyClosedTabsButton() recentlyClosedTabsButton()
.check( .check(
matches(isDisplayed())) matches(isDisplayed())
)
} }
private fun shareAllTabsButton() = private fun shareAllTabsButton() =
@ -530,7 +532,8 @@ private fun shareAllTabsButton() =
private fun assertShareAllTabsButton() { private fun assertShareAllTabsButton() {
shareAllTabsButton() shareAllTabsButton()
.check( .check(
matches(isDisplayed())) matches(isDisplayed())
)
} }
private fun assertNewTabButton() = onView(withText("New tab")).check(matches(isDisplayed())) private fun assertNewTabButton() = onView(withText("New tab")).check(matches(isDisplayed()))

@ -140,7 +140,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
channel = BuildConfig.BUILD_TYPE, channel = BuildConfig.BUILD_TYPE,
httpClient = ConceptFetchHttpUploader( httpClient = ConceptFetchHttpUploader(
lazy(LazyThreadSafetyMode.NONE) { components.core.client } lazy(LazyThreadSafetyMode.NONE) { components.core.client }
)), )
),
uploadEnabled = telemetryEnabled, uploadEnabled = telemetryEnabled,
buildInfo = GleanBuildInfo.buildInfo buildInfo = GleanBuildInfo.buildInfo
) )
@ -392,15 +393,17 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
logger.info("onTrimMemory(), level=$level, main=${isMainProcess()}") logger.info("onTrimMemory(), level=$level, main=${isMainProcess()}")
components.analytics.crashReporter.recordCrashBreadcrumb(Breadcrumb( components.analytics.crashReporter.recordCrashBreadcrumb(
category = "Memory", Breadcrumb(
message = "onTrimMemory()", category = "Memory",
data = mapOf( message = "onTrimMemory()",
"level" to level.toString(), data = mapOf(
"main" to isMainProcess().toString() "level" to level.toString(),
), "main" to isMainProcess().toString()
level = Breadcrumb.Level.INFO ),
)) level = Breadcrumb.Level.INFO
)
)
runOnlyInMainProcess { runOnlyInMainProcess {
components.core.icons.onTrimMemory(level) components.core.icons.onTrimMemory(level)
@ -473,22 +476,24 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
components.core.store, components.core.store,
onNewTabOverride = { onNewTabOverride = {
_, engineSession, url -> _, engineSession, url ->
val shouldCreatePrivateSession = val shouldCreatePrivateSession =
components.core.store.state.selectedTab?.content?.private components.core.store.state.selectedTab?.content?.private
?: components.settings.openLinksInAPrivateTab ?: components.settings.openLinksInAPrivateTab
components.useCases.tabsUseCases.addTab( components.useCases.tabsUseCases.addTab(
url = url, url = url,
selectTab = true, selectTab = true,
engineSession = engineSession, engineSession = engineSession,
private = shouldCreatePrivateSession private = shouldCreatePrivateSession
) )
}, },
onCloseTabOverride = { onCloseTabOverride = {
_, sessionId -> components.useCases.tabsUseCases.removeTab(sessionId) _, sessionId ->
components.useCases.tabsUseCases.removeTab(sessionId)
}, },
onSelectTabOverride = { onSelectTabOverride = {
_, sessionId -> components.useCases.tabsUseCases.selectTab(sessionId) _, sessionId ->
components.useCases.tabsUseCases.selectTab(sessionId)
}, },
onExtensionsLoaded = { extensions -> onExtensionsLoaded = { extensions ->
components.addonUpdater.registerForFutureUpdates(extensions) components.addonUpdater.registerForFutureUpdates(extensions)

@ -964,9 +964,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
// First time opening this activity in the task. // First time opening this activity in the task.
// Cold start / start from Recents after back press. // Cold start / start from Recents after back press.
return activityIcicle == null && return activityIcicle == null &&
// Activity was restarted from Recents after it was destroyed by Android while in background // Activity was restarted from Recents after it was destroyed by Android while in background
// in cases of memory pressure / "Don't keep activities". // in cases of memory pressure / "Don't keep activities".
startingIntent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0 startingIntent.flags and Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY == 0
} }
/** /**

@ -18,7 +18,8 @@ import org.mozilla.fenix.ext.showToolbar
/** /**
* A fragment to show the permissions of an add-on. * A fragment to show the permissions of an add-on.
*/ */
class AddonPermissionsDetailsFragment : Fragment(R.layout.fragment_add_on_permissions), class AddonPermissionsDetailsFragment :
Fragment(R.layout.fragment_add_on_permissions),
AddonPermissionsDetailsInteractor { AddonPermissionsDetailsInteractor {
private val args by navArgs<AddonPermissionsDetailsFragmentArgs>() private val args by navArgs<AddonPermissionsDetailsFragmentArgs>()

@ -45,7 +45,8 @@ abstract class AddonPopupBaseFragment : Fragment(), EngineSession.Observer, User
fragmentManager = parentFragmentManager, fragmentManager = parentFragmentManager,
onNeedToRequestPermissions = { permissions -> onNeedToRequestPermissions = { permissions ->
requestPermissions(permissions, REQUEST_CODE_PROMPT_PERMISSIONS) requestPermissions(permissions, REQUEST_CODE_PROMPT_PERMISSIONS)
}), }
),
owner = this, owner = this,
view = view view = view
) )

@ -108,8 +108,9 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
val addons = requireContext().components.addonManager.getAddons(allowCache = allowCache) val addons = requireContext().components.addonManager.getAddons(allowCache = allowCache)
// Add-ons that should be excluded in Mozilla Online builds // Add-ons that should be excluded in Mozilla Online builds
val excludedAddonIDs = if (Config.channel.isMozillaOnline && val excludedAddonIDs = if (Config.channel.isMozillaOnline &&
!BuildConfig.MOZILLA_ONLINE_ADDON_EXCLUSIONS.isNullOrEmpty()) { !BuildConfig.MOZILLA_ONLINE_ADDON_EXCLUSIONS.isNullOrEmpty()
BuildConfig.MOZILLA_ONLINE_ADDON_EXCLUSIONS.toList() ) {
BuildConfig.MOZILLA_ONLINE_ADDON_EXCLUSIONS.toList()
} else { } else {
emptyList<String>() emptyList<String>()
} }
@ -200,7 +201,7 @@ class AddonsManagementFragment : Fragment(R.layout.fragment_add_ons_management)
private fun hasExistingAddonInstallationDialogFragment(): Boolean { private fun hasExistingAddonInstallationDialogFragment(): Boolean {
return parentFragmentManager.findFragmentByTag(INSTALLATION_DIALOG_FRAGMENT_TAG) return parentFragmentManager.findFragmentByTag(INSTALLATION_DIALOG_FRAGMENT_TAG)
as? AddonInstallationDialogFragment != null as? AddonInstallationDialogFragment != null
} }
@VisibleForTesting @VisibleForTesting

@ -148,8 +148,12 @@ import mozilla.components.feature.session.behavior.ToolbarPosition as MozacToolb
*/ */
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
@Suppress("TooManyFunctions", "LargeClass") @Suppress("TooManyFunctions", "LargeClass")
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, ActivityResultHandler, abstract class BaseBrowserFragment :
OnBackLongPressedListener, AccessibilityManager.AccessibilityStateChangeListener { Fragment(),
UserInteractionHandler,
ActivityResultHandler,
OnBackLongPressedListener,
AccessibilityManager.AccessibilityStateChangeListener {
private lateinit var browserFragmentStore: BrowserFragmentStore private lateinit var browserFragmentStore: BrowserFragmentStore
private lateinit var browserAnimator: BrowserAnimator private lateinit var browserAnimator: BrowserAnimator
@ -235,25 +239,25 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
} }
final override fun onViewCreated(view: View, savedInstanceState: Bundle?) = final override fun onViewCreated(view: View, savedInstanceState: Bundle?) =
PerfStartup.baseBfragmentOnViewCreated.measureNoInline { // weird indentation to avoid breaking blame. PerfStartup.baseBfragmentOnViewCreated.measureNoInline { // weird indentation to avoid breaking blame.
initializeUI(view) initializeUI(view)
if (customTabSessionId == null) { if (customTabSessionId == null) {
// We currently only need this observer to navigate to home // We currently only need this observer to navigate to home
// in case all tabs have been removed on startup. No need to // in case all tabs have been removed on startup. No need to
// this if we have a known session to display. // this if we have a known session to display.
observeRestoreComplete(requireComponents.core.store, findNavController()) observeRestoreComplete(requireComponents.core.store, findNavController())
} }
observeTabSelection(requireComponents.core.store) observeTabSelection(requireComponents.core.store)
if (!onboarding.userHasBeenOnboarded()) { if (!onboarding.userHasBeenOnboarded()) {
observeTabSource(requireComponents.core.store) observeTabSource(requireComponents.core.store)
} }
requireContext().accessibilityManager.addAccessibilityStateChangeListener(this) requireContext().accessibilityManager.addAccessibilityStateChangeListener(this)
Unit Unit
} }
private fun initializeUI(view: View) { private fun initializeUI(view: View) {
val tab = getCurrentTab() val tab = getCurrentTab()
@ -823,15 +827,17 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
internal fun expandToolbarOnNavigation(store: BrowserStore) { internal fun expandToolbarOnNavigation(store: BrowserStore) {
consumeFlow(store) { flow -> consumeFlow(store) { flow ->
flow.mapNotNull { flow.mapNotNull {
state -> state.findCustomTabOrSelectedTab(customTabSessionId) state ->
} state.findCustomTabOrSelectedTab(customTabSessionId)
.ifAnyChanged {
tab -> arrayOf(tab.content.url, tab.content.loadRequest)
}
.collect {
findInPageIntegration.onBackPressed()
browserToolbarView.expand()
} }
.ifAnyChanged {
tab ->
arrayOf(tab.content.url, tab.content.loadRequest)
}
.collect {
findInPageIntegration.onBackPressed()
browserToolbarView.expand()
}
} }
} }
@ -907,8 +913,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
@VisibleForTesting @VisibleForTesting
internal fun shouldPullToRefreshBeEnabled(inFullScreen: Boolean): Boolean { internal fun shouldPullToRefreshBeEnabled(inFullScreen: Boolean): Boolean {
return FeatureFlags.pullToRefreshEnabled && return FeatureFlags.pullToRefreshEnabled &&
requireContext().settings().isPullToRefreshEnabledInBrowser && requireContext().settings().isPullToRefreshEnabledInBrowser &&
!inFullScreen !inFullScreen
} }
@VisibleForTesting @VisibleForTesting
@ -981,12 +987,12 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
flow.ifChanged { flow.ifChanged {
it.selectedTabId it.selectedTabId
} }
.mapNotNull { .mapNotNull {
it.selectedTab it.selectedTab
} }
.collect { .collect {
handleTabSelected(it) handleTabSelected(it)
} }
} }
} }
@ -998,14 +1004,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
state.selectedTab state.selectedTab
} }
.collect { .collect {
if (!onboarding.userHasBeenOnboarded() && if (!onboarding.userHasBeenOnboarded() &&
it.content.loadRequest?.triggeredByRedirect != true && it.content.loadRequest?.triggeredByRedirect != true &&
it.source !in intentSourcesList && it.source !in intentSourcesList &&
it.content.url !in onboardingLinksList it.content.url !in onboardingLinksList
) { ) {
onboarding.finish() onboarding.finish()
}
} }
}
} }
} }
@ -1070,10 +1076,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
@CallSuper @CallSuper
override fun onBackPressed(): Boolean { override fun onBackPressed(): Boolean {
return findInPageIntegration.onBackPressed() || return findInPageIntegration.onBackPressed() ||
fullScreenFeature.onBackPressed() || fullScreenFeature.onBackPressed() ||
promptsFeature.onBackPressed() || promptsFeature.onBackPressed() ||
sessionFeature.onBackPressed() || sessionFeature.onBackPressed() ||
removeSessionIfNeeded() removeSessionIfNeeded()
} }
override fun onBackLongPressed(): Boolean { override fun onBackLongPressed(): Boolean {

@ -28,7 +28,8 @@ class FenixSnackbarDelegate(private val view: View) : ContextMenuCandidate.Snack
.setAction(view.context.getString(action)) { listener.invoke(view) } .setAction(view.context.getString(action)) { listener.invoke(view) }
.show() .show()
} else { } else {
FenixSnackbar.make(view, FenixSnackbar.make(
view,
duration = FenixSnackbar.LENGTH_SHORT, duration = FenixSnackbar.LENGTH_SHORT,
isDisplayedWithBrowserToolbar = true isDisplayedWithBrowserToolbar = true
) )

@ -58,22 +58,23 @@ class OpenInAppOnboardingObserver(
flow.mapNotNull { state -> flow.mapNotNull { state ->
state.selectedTab state.selectedTab
} }
.ifAnyChanged { .ifAnyChanged {
tab -> arrayOf(tab.content.url, tab.content.loading) tab ->
} arrayOf(tab.content.url, tab.content.loading)
.collect { tab -> }
if (tab.content.url != currentUrl) { .collect { tab ->
sessionDomainForDisplayedBanner?.let { if (tab.content.url != currentUrl) {
if (tab.content.url.tryGetHostFromUrl() != it) { sessionDomainForDisplayedBanner?.let {
infoBanner?.dismiss() if (tab.content.url.tryGetHostFromUrl() != it) {
infoBanner?.dismiss()
}
} }
currentUrl = tab.content.url
} else {
// Loading state has changed
maybeShowOpenInAppBanner(tab.content.url, tab.content.loading)
} }
currentUrl = tab.content.url
} else {
// Loading state has changed
maybeShowOpenInAppBanner(tab.content.url, tab.content.loading)
} }
}
} }
} }

@ -214,8 +214,10 @@ class ToolbarGestureHandler(
val reverseFling = val reverseFling =
abs(velocityX) >= minimumFlingVelocity && !velocityMatchesDirection abs(velocityX) >= minimumFlingVelocity && !velocityMatchesDirection
return !reverseFling && (previewWidth / windowWidth >= GESTURE_FINISH_PERCENT || return !reverseFling && (
abs(velocityX) >= minimumFlingVelocity) previewWidth / windowWidth >= GESTURE_FINISH_PERCENT ||
abs(velocityX) >= minimumFlingVelocity
)
} }
private fun getAnimator(finalContextX: Float, duration: Long): ValueAnimator { private fun getAnimator(finalContextX: Float, duration: Long): ValueAnimator {

@ -170,9 +170,12 @@ class CollectionCreationView(
setOnClickListener { setOnClickListener {
name_collection_edittext.hideKeyboard() name_collection_edittext.hideKeyboard()
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
handler.postDelayed({ handler.postDelayed(
interactor.onBackPressed(SaveCollectionStep.NameCollection) {
}, TRANSITION_DURATION) interactor.onBackPressed(SaveCollectionStep.NameCollection)
},
TRANSITION_DURATION
)
} }
} }
@ -216,9 +219,12 @@ class CollectionCreationView(
setOnClickListener { setOnClickListener {
name_collection_edittext.hideKeyboard() name_collection_edittext.hideKeyboard()
val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
handler.postDelayed({ handler.postDelayed(
interactor.onBackPressed(SaveCollectionStep.RenameCollection) {
}, TRANSITION_DURATION) interactor.onBackPressed(SaveCollectionStep.RenameCollection)
},
TRANSITION_DURATION
)
} }
} }
transition.addListener(object : Transition.TransitionListener { transition.addListener(object : Transition.TransitionListener {

@ -129,7 +129,8 @@ class Components(private val context: Context) {
@Suppress("MagicNumber") @Suppress("MagicNumber")
val supportedAddonsChecker by lazyMonitored { val supportedAddonsChecker by lazyMonitored {
DefaultSupportedAddonsChecker(context, SupportedAddonsChecker.Frequency(12, TimeUnit.HOURS), DefaultSupportedAddonsChecker(
context, SupportedAddonsChecker.Frequency(12, TimeUnit.HOURS),
onNotificationClickIntent = Intent(context, HomeActivity::class.java).apply { onNotificationClickIntent = Intent(context, HomeActivity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK

@ -105,7 +105,7 @@ class Core(
val defaultSettings = DefaultSettings( val defaultSettings = DefaultSettings(
requestInterceptor = requestInterceptor, requestInterceptor = requestInterceptor,
remoteDebuggingEnabled = context.settings().isRemoteDebuggingEnabled && remoteDebuggingEnabled = context.settings().isRemoteDebuggingEnabled &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M,
testingModeEnabled = false, testingModeEnabled = false,
trackingProtectionPolicy = trackingProtectionPolicyFactory.createTrackingProtectionPolicy(), trackingProtectionPolicy = trackingProtectionPolicyFactory.createTrackingProtectionPolicy(),
historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage), historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage),
@ -432,7 +432,7 @@ class Core(
fun getPreferredColorScheme(): PreferredColorScheme { fun getPreferredColorScheme(): PreferredColorScheme {
val inDark = val inDark =
(context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) ==
Configuration.UI_MODE_NIGHT_YES Configuration.UI_MODE_NIGHT_YES
return when { return when {
context.settings().shouldUseDarkTheme -> PreferredColorScheme.Dark context.settings().shouldUseDarkTheme -> PreferredColorScheme.Dark
context.settings().shouldUseLightTheme -> PreferredColorScheme.Light context.settings().shouldUseLightTheme -> PreferredColorScheme.Light

@ -39,10 +39,10 @@ enum class IntentProcessorType {
fun IntentProcessors.getType(processor: IntentProcessor?) = when { fun IntentProcessors.getType(processor: IntentProcessor?) = when {
migrationIntentProcessor == processor -> IntentProcessorType.MIGRATION migrationIntentProcessor == processor -> IntentProcessorType.MIGRATION
externalAppIntentProcessors.contains(processor) || externalAppIntentProcessors.contains(processor) ||
customTabIntentProcessor == processor || customTabIntentProcessor == processor ||
privateCustomTabIntentProcessor == processor -> IntentProcessorType.EXTERNAL_APP privateCustomTabIntentProcessor == processor -> IntentProcessorType.EXTERNAL_APP
intentProcessor == processor || intentProcessor == processor ||
privateIntentProcessor == processor || privateIntentProcessor == processor ||
fennecPageShortcutIntentProcessor == processor -> IntentProcessorType.NEW_TAB fennecPageShortcutIntentProcessor == processor -> IntentProcessorType.NEW_TAB
else -> IntentProcessorType.OTHER else -> IntentProcessorType.OTHER
} }

@ -38,15 +38,17 @@ object PrivateShortcutCreateManager {
) )
) )
.setIcon(icon) .setIcon(icon)
.setIntent(Intent(context, HomeActivity::class.java).apply { .setIntent(
action = Intent.ACTION_VIEW Intent(context, HomeActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK action = Intent.ACTION_VIEW
putExtra(HomeActivity.PRIVATE_BROWSING_MODE, true) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
putExtra( putExtra(HomeActivity.PRIVATE_BROWSING_MODE, true)
HomeActivity.OPEN_TO_SEARCH, putExtra(
StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT HomeActivity.OPEN_TO_SEARCH,
) StartSearchIntentProcessor.PRIVATE_BROWSING_PINNED_SHORTCUT
}) )
}
)
.build() .build()
val homeScreenIntent = Intent(Intent.ACTION_MAIN) val homeScreenIntent = Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME) .addCategory(Intent.CATEGORY_HOME)

@ -41,7 +41,8 @@ class Services(
interceptLinkClicks = true, interceptLinkClicks = true,
launchInApp = { launchInApp = {
PreferenceManager.getDefaultSharedPreferences(context).getBoolean( PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
context.getPreferenceKey(R.string.pref_key_open_links_in_external_app), false) context.getPreferenceKey(R.string.pref_key_open_links_in_external_app), false
)
} }
) )
} }

@ -25,7 +25,8 @@ class ActivationPing(private val context: Context) {
private val prefs: SharedPreferences by lazy { private val prefs: SharedPreferences by lazy {
context.getSharedPreferences( context.getSharedPreferences(
"${this.javaClass.canonicalName}.prefs", Context.MODE_PRIVATE) "${this.javaClass.canonicalName}.prefs", Context.MODE_PRIVATE
)
} }
/** /**

@ -27,7 +27,7 @@ class SecurePrefsTelemetry(
experiments.withExperiment(FeatureId.ANDROID_KEYSTORE) { experimentBranch -> experiments.withExperiment(FeatureId.ANDROID_KEYSTORE) { experimentBranch ->
// .. and this device is not in the control group. // .. and this device is not in the control group.
if (experimentBranch == ExperimentBranch.TREATMENT) { if (experimentBranch == ExperimentBranch.TREATMENT) {
SecurePrefsReliabilityExperiment(appContext)() SecurePrefsReliabilityExperiment(appContext)()
} }
} }
} }

@ -59,7 +59,7 @@ class MasterPasswordTipProvider(
override val shouldDisplay: Boolean by lazy { override val shouldDisplay: Boolean by lazy {
context.settings().shouldDisplayMasterPasswordMigrationTip && context.settings().shouldDisplayMasterPasswordMigrationTip &&
fennecLoginsMPImporter?.hasMasterPassword() == true fennecLoginsMPImporter?.hasMasterPassword() == true
} }
private fun masterPasswordMigrationTip(): Tip = private fun masterPasswordMigrationTip(): Tip =

@ -92,9 +92,11 @@ class MigrationTipProvider(private val context: Context) : TipProvider {
return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) { return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) {
context.startActivity(context.packageManager.getLaunchIntentForPackage(FENIX.productName)) context.startActivity(context.packageManager.getLaunchIntentForPackage(FENIX.productName))
} else { } else {
context.startActivity(Intent( context.startActivity(
Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_NIGHTLY_PLAY_STORE_URL) Intent(
)) Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_NIGHTLY_PLAY_STORE_URL)
)
)
} }
} }

@ -180,8 +180,10 @@ private fun BrowserStore.updateSearchTermsOfSelectedSession(
) { ) {
val selectedTabId = state.selectedTabId ?: return val selectedTabId = state.selectedTabId ?: return
dispatch(ContentAction.UpdateSearchTermsAction( dispatch(
selectedTabId, ContentAction.UpdateSearchTermsAction(
searchTerms selectedTabId,
)) searchTerms
)
)
} }

@ -118,13 +118,15 @@ class DefaultBrowserToolbarMenuController(
customTabUseCases.migrate(customTabSessionId, select = true) customTabUseCases.migrate(customTabSessionId, select = true)
// Switch to the actual browser which should now display our new selected session // Switch to the actual browser which should now display our new selected session
activity.startActivity(openInFenixIntent.apply { activity.startActivity(
// We never want to launch the browser in the same task as the external app openInFenixIntent.apply {
// activity. So we force a new task here. IntentReceiverActivity will do the // We never want to launch the browser in the same task as the external app
// right thing and take care of routing to an already existing browser and avoid // activity. So we force a new task here. IntentReceiverActivity will do the
// cloning a new one. // right thing and take care of routing to an already existing browser and avoid
flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK // cloning a new one.
}) flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK
}
)
// Close this activity (and the task) since it is no longer displaying any session // Close this activity (and the task) since it is no longer displaying any session
activity.finishAndRemoveTask() activity.finishAndRemoveTask()

@ -68,7 +68,7 @@ class BrowserToolbarView(
@VisibleForTesting @VisibleForTesting
internal val isPwaTabOrTwaTab: Boolean internal val isPwaTabOrTwaTab: Boolean
get() = customTabSession?.config?.externalAppType == ExternalAppType.PROGRESSIVE_WEB_APP || get() = customTabSession?.config?.externalAppType == ExternalAppType.PROGRESSIVE_WEB_APP ||
customTabSession?.config?.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY customTabSession?.config?.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY
init { init {
val isCustomTabSession = customTabSession != null val isCustomTabSession = customTabSession != null

@ -158,12 +158,12 @@ open class DefaultToolbarMenu(
@VisibleForTesting(otherwise = PRIVATE) @VisibleForTesting(otherwise = PRIVATE)
fun canAddToHomescreen(): Boolean = fun canAddToHomescreen(): Boolean =
selectedSession != null && isPinningSupported && selectedSession != null && isPinningSupported &&
!context.components.useCases.webAppUseCases.isInstallable() !context.components.useCases.webAppUseCases.isInstallable()
@VisibleForTesting(otherwise = PRIVATE) @VisibleForTesting(otherwise = PRIVATE)
fun canInstall(): Boolean = fun canInstall(): Boolean =
selectedSession != null && isPinningSupported && selectedSession != null && isPinningSupported &&
context.components.useCases.webAppUseCases.isInstallable() context.components.useCases.webAppUseCases.isInstallable()
@VisibleForTesting(otherwise = PRIVATE) @VisibleForTesting(otherwise = PRIVATE)
fun shouldShowOpenInApp(): Boolean = selectedSession?.let { session -> fun shouldShowOpenInApp(): Boolean = selectedSession?.let { session ->

@ -134,7 +134,7 @@ class DefaultToolbarIntegration(
DisplayToolbar.Indicators.HIGHLIGHT DisplayToolbar.Indicators.HIGHLIGHT
) )
} }
context.settings().shouldUseTrackingProtection context.settings().shouldUseTrackingProtection
toolbar.display.icons = toolbar.display.icons.copy( toolbar.display.icons = toolbar.display.icons.copy(
emptyIcon = null, emptyIcon = null,
@ -155,11 +155,11 @@ class DefaultToolbarIntegration(
interactor.onTabCounterMenuItemTapped(it) interactor.onTabCounterMenuItemTapped(it)
}, },
iconColor = iconColor =
if (isPrivate) { if (isPrivate) {
ContextCompat.getColor(context, R.color.primary_text_private_theme) ContextCompat.getColor(context, R.color.primary_text_private_theme)
} else { } else {
null null
} }
).also { ).also {
it.updateMenu(context.settings().toolbarPosition) it.updateMenu(context.settings().toolbarPosition)
} }

@ -37,11 +37,13 @@ fun Activity.enterToImmersiveMode() {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// This will be addressed on https://github.com/mozilla-mobile/fenix/issues/17804 // This will be addressed on https://github.com/mozilla-mobile/fenix/issues/17804
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION window.decorView.systemUiVisibility = (
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
)
} }
fun Activity.breadcrumb( fun Activity.breadcrumb(

@ -18,7 +18,7 @@ fun ConnectivityManager.isOnline(network: Network? = null): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getNetworkCapabilities(network ?: activeNetwork)?.let { getNetworkCapabilities(network ?: activeNetwork)?.let {
it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
} ?: false } ?: false
} else { } else {
// for devices below android M, there's no better way to get this. // for devices below android M, there's no better way to get this.

@ -31,9 +31,15 @@ fun TabCollection.getIconColor(context: Context): Int {
* Then get the numbers from all these default names, compute the maximum number and add one. * Then get the numbers from all these default names, compute the maximum number and add one.
*/ */
fun List<TabCollection>.getDefaultCollectionNumber(): Int { fun List<TabCollection>.getDefaultCollectionNumber(): Int {
return (this return (
.map { it.title } this
.filter { it.matches(Regex("Collection\\s\\d+")) } .map { it.title }
.map { Integer.valueOf(it.split(" ")[DefaultCollectionCreationController.DEFAULT_COLLECTION_NUMBER_POSITION]) } .filter { it.matches(Regex("Collection\\s\\d+")) }
.maxOrNull() ?: 0) + DefaultCollectionCreationController.DEFAULT_INCREMENT_VALUE .map {
Integer.valueOf(
it.split(" ")[DefaultCollectionCreationController.DEFAULT_COLLECTION_NUMBER_POSITION]
)
}
.maxOrNull() ?: 0
) + DefaultCollectionCreationController.DEFAULT_INCREMENT_VALUE
} }

@ -77,16 +77,19 @@ object GeckoProvider {
.getHashUrl(CN_GET_HASH_URL) .getHashUrl(CN_GET_HASH_URL)
.build() .build()
runtimeSettings.contentBlocking.setSafeBrowsingProviders(mozcn, runtimeSettings.contentBlocking.setSafeBrowsingProviders(
mozcn,
// Keep the existing configuration // Keep the existing configuration
ContentBlocking.GOOGLE_SAFE_BROWSING_PROVIDER, ContentBlocking.GOOGLE_SAFE_BROWSING_PROVIDER,
ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER) ContentBlocking.GOOGLE_LEGACY_SAFE_BROWSING_PROVIDER
)
runtimeSettings.contentBlocking.setSafeBrowsingPhishingTable( runtimeSettings.contentBlocking.setSafeBrowsingPhishingTable(
"m6eb-phish-shavar", "m6eb-phish-shavar",
"m6ib-phish-shavar", "m6ib-phish-shavar",
// Existing configuration // Existing configuration
"goog-phish-proto") "goog-phish-proto"
)
} }
val geckoRuntime = GeckoRuntime.create(context, runtimeSettings) val geckoRuntime = GeckoRuntime.create(context, runtimeSettings)

@ -92,7 +92,7 @@ class HistoryMetadataMiddleware(
// When history state is ready, we can record metadata for this page. // When history state is ready, we can record metadata for this page.
val knownHistoryMetadata = tab.historyMetadata val knownHistoryMetadata = tab.historyMetadata
val metadataPresentForUrl = knownHistoryMetadata != null && val metadataPresentForUrl = knownHistoryMetadata != null &&
knownHistoryMetadata.url == tab.content.url knownHistoryMetadata.url == tab.content.url
// Record metadata for tab if there is no metadata present, or if url of the // Record metadata for tab if there is no metadata present, or if url of the
// tab changes since we last recorded metadata. // tab changes since we last recorded metadata.
if (!metadataPresentForUrl) { if (!metadataPresentForUrl) {

@ -198,7 +198,8 @@ class HomeFragment : Fragment() {
if (!onboarding.userHasBeenOnboarded() && if (!onboarding.userHasBeenOnboarded() &&
requireContext().settings().shouldShowPrivacyPopWindow && requireContext().settings().shouldShowPrivacyPopWindow &&
Config.channel.isMozillaOnline) { Config.channel.isMozillaOnline
) {
showPrivacyPopWindow(requireContext(), requireActivity()) showPrivacyPopWindow(requireContext(), requireActivity())
} }
} }
@ -665,22 +666,25 @@ class HomeFragment : Fragment() {
currentMode, currentMode,
owner = this@HomeFragment.viewLifecycleOwner owner = this@HomeFragment.viewLifecycleOwner
) )
requireComponents.backgroundServices.accountManager.register(object : AccountObserver { requireComponents.backgroundServices.accountManager.register(
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { object : AccountObserver {
if (authType != AuthType.Existing) { override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
view?.let { if (authType != AuthType.Existing) {
FenixSnackbar.make( view?.let {
view = it, FenixSnackbar.make(
duration = Snackbar.LENGTH_SHORT, view = it,
isDisplayedWithBrowserToolbar = false duration = Snackbar.LENGTH_SHORT,
) isDisplayedWithBrowserToolbar = false
.setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on)) )
.setAnchorView(toolbarLayout) .setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on))
.show() .setAnchorView(toolbarLayout)
.show()
}
} }
} }
} },
}, owner = this@HomeFragment.viewLifecycleOwner) owner = this@HomeFragment.viewLifecycleOwner
)
} }
if (browsingModeManager.mode.isPrivate && if (browsingModeManager.mode.isPrivate &&
@ -701,7 +705,8 @@ class HomeFragment : Fragment() {
private fun navToSavedLogins() { private fun navToSavedLogins() {
findNavController().navigate( findNavController().navigate(
HomeFragmentDirections.actionGlobalSavedLoginsAuthFragment()) HomeFragmentDirections.actionGlobalSavedLoginsAuthFragment()
)
} }
private fun dispatchModeChanges(mode: Mode) { private fun dispatchModeChanges(mode: Mode) {

@ -181,7 +181,8 @@ class HomeMenu(
// We don't want to cause its initialization just for this check. // We don't want to cause its initialization just for this check.
val accountAuthItem = val accountAuthItem =
if (context.components.backgroundServices.accountManagerAvailableQueue.isReady() && if (context.components.backgroundServices.accountManagerAvailableQueue.isReady() &&
context.components.backgroundServices.accountManager.accountNeedsReauth()) { context.components.backgroundServices.accountManager.accountNeedsReauth()
) {
reconnectToSyncItem reconnectToSyncItem
} else { } else {
null null
@ -220,37 +221,40 @@ class HomeMenu(
if (lifecycleOwner.lifecycle.currentState == Lifecycle.State.DESTROYED) { if (lifecycleOwner.lifecycle.currentState == Lifecycle.State.DESTROYED) {
return@runIfReadyOrQueue return@runIfReadyOrQueue
} }
context.components.backgroundServices.accountManager.register(object : AccountObserver { context.components.backgroundServices.accountManager.register(
override fun onAuthenticationProblems() { object : AccountObserver {
lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { override fun onAuthenticationProblems() {
onMenuBuilderChanged( lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
BrowserMenuBuilder( onMenuBuilderChanged(
menuItems BrowserMenuBuilder(
menuItems
)
) )
) }
} }
}
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
onMenuBuilderChanged( onMenuBuilderChanged(
BrowserMenuBuilder( BrowserMenuBuilder(
menuItems menuItems
)
) )
) }
} }
}
override fun onLoggedOut() { override fun onLoggedOut() {
lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
onMenuBuilderChanged( onMenuBuilderChanged(
BrowserMenuBuilder( BrowserMenuBuilder(
menuItems menuItems
)
) )
) }
} }
} },
}, lifecycleOwner) lifecycleOwner
)
} }
} }
} }

@ -29,21 +29,31 @@ fun showPrivacyPopWindow(context: Context, activity: Activity) {
val clickableSpan2 = PrivacyContentSpan(Position.POS2, context) val clickableSpan2 = PrivacyContentSpan(Position.POS2, context)
val clickableSpan3 = PrivacyContentSpan(Position.POS3, context) val clickableSpan3 = PrivacyContentSpan(Position.POS3, context)
messageSpannable.setSpan(clickableSpan1, content.indexOf(messageClickable1), messageSpannable.setSpan(
content.indexOf(messageClickable1) + messageClickable1.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) clickableSpan1, content.indexOf(messageClickable1),
messageSpannable.setSpan(clickableSpan2, content.indexOf(messageClickable2), content.indexOf(messageClickable1) + messageClickable1.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE
content.indexOf(messageClickable2) + messageClickable2.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) )
messageSpannable.setSpan(clickableSpan3, content.indexOf(messageClickable3), messageSpannable.setSpan(
content.indexOf(messageClickable3) + messageClickable3.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE) clickableSpan2, content.indexOf(messageClickable2),
content.indexOf(messageClickable2) + messageClickable2.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
messageSpannable.setSpan(
clickableSpan3, content.indexOf(messageClickable3),
content.indexOf(messageClickable3) + messageClickable3.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
// Users can only use fenix after they agree with the privacy notice // Users can only use fenix after they agree with the privacy notice
val builder = AlertDialog.Builder(activity) val builder = AlertDialog.Builder(activity)
.setPositiveButton(context.getString(R.string.privacy_notice_positive_button), .setPositiveButton(
context.getString(R.string.privacy_notice_positive_button),
DialogInterface.OnClickListener { _, _ -> DialogInterface.OnClickListener { _, _ ->
context.settings().shouldShowPrivacyPopWindow = false context.settings().shouldShowPrivacyPopWindow = false
}) }
.setNeutralButton(context.getString(R.string.privacy_notice_neutral_button), )
DialogInterface.OnClickListener { _, _ -> exitProcess(0) }) .setNeutralButton(
context.getString(R.string.privacy_notice_neutral_button),
DialogInterface.OnClickListener { _, _ -> exitProcess(0) }
)
.setTitle(context.getString(R.string.privacy_notice_title)) .setTitle(context.getString(R.string.privacy_notice_title))
.setMessage(messageSpannable) .setMessage(messageSpannable)
.setCancelable(false) .setCancelable(false)

@ -197,28 +197,28 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
data class RecentBookmarks(val recentBookmarks: List<BookmarkNode>) : data class RecentBookmarks(val recentBookmarks: List<BookmarkNode>) :
AdapterItem(RecentBookmarksViewHolder.LAYOUT_ID) { AdapterItem(RecentBookmarksViewHolder.LAYOUT_ID) {
override fun sameAs(other: AdapterItem): Boolean { override fun sameAs(other: AdapterItem): Boolean {
val newBookmarks = (other as? RecentBookmarks) ?: return false val newBookmarks = (other as? RecentBookmarks) ?: return false
if (newBookmarks.recentBookmarks.size != this.recentBookmarks.size) { if (newBookmarks.recentBookmarks.size != this.recentBookmarks.size) {
return false return false
} }
return recentBookmarks.zip(newBookmarks.recentBookmarks).all { (new, old) -> return recentBookmarks.zip(newBookmarks.recentBookmarks).all { (new, old) ->
new.guid == old.guid new.guid == old.guid
}
} }
}
override fun contentsSameAs(other: AdapterItem): Boolean { override fun contentsSameAs(other: AdapterItem): Boolean {
val newBookmarks = (other as? RecentBookmarks) ?: return false val newBookmarks = (other as? RecentBookmarks) ?: return false
val newBookmarksSequence = newBookmarks.recentBookmarks.asSequence() val newBookmarksSequence = newBookmarks.recentBookmarks.asSequence()
val oldBookmarksList = this.recentBookmarks.asSequence() val oldBookmarksList = this.recentBookmarks.asSequence()
return newBookmarksSequence.zip(oldBookmarksList).all { (new, old) -> return newBookmarksSequence.zip(oldBookmarksList).all { (new, old) ->
new == old new == old
}
} }
} }
}
data class HistoryMetadataItem(val historyMetadata: HistoryMetadata) : AdapterItem( data class HistoryMetadataItem(val historyMetadata: HistoryMetadata) : AdapterItem(
HistoryMetadataViewHolder.LAYOUT_ID HistoryMetadataViewHolder.LAYOUT_ID

@ -317,7 +317,7 @@ class DefaultSessionControlController(
dismissSearchDialogIfDisplayed() dismissSearchDialogIfDisplayed()
activity.openToBrowserAndLoad( activity.openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
(SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS), (SupportUtils.SumoTopic.PRIVATE_BROWSING_MYTHS),
newTab = true, newTab = true,
from = BrowserDirection.FromHome from = BrowserDirection.FromHome
) )
@ -401,9 +401,11 @@ class DefaultSessionControlController(
val searchAccessPoint = Event.PerformedSearch.SearchAccessPoint.TOPSITE val searchAccessPoint = Event.PerformedSearch.SearchAccessPoint.TOPSITE
val event = val event =
availableEngines.firstOrNull { availableEngines.firstOrNull {
engine -> engine.resultUrls.firstOrNull { it.contains(url) } != null engine ->
engine.resultUrls.firstOrNull { it.contains(url) } != null
}?.let { }?.let {
searchEngine -> searchAccessPoint.let { sap -> searchEngine ->
searchAccessPoint.let { sap ->
MetricsUtils.createSearchEvent(searchEngine, store, sap) MetricsUtils.createSearchEvent(searchEngine, store, sap)
} }
} }
@ -424,7 +426,7 @@ class DefaultSessionControlController(
@VisibleForTesting @VisibleForTesting
internal fun getAvailableSearchEngines() = internal fun getAvailableSearchEngines() =
activity.components.core.store.state.search.searchEngines + activity.components.core.store.state.search.searchEngines +
activity.components.core.store.state.search.availableSearchEngines activity.components.core.store.state.search.availableSearchEngines
/** /**
* Append a search attribution query to any provided search engine URL based on the * Append a search attribution query to any provided search engine URL based on the

@ -222,9 +222,16 @@ class SessionControlInteractor(
private val recentTabController: RecentTabController, private val recentTabController: RecentTabController,
private val recentBookmarksController: RecentBookmarksController, private val recentBookmarksController: RecentBookmarksController,
private val historyMetadataController: HistoryMetadataController private val historyMetadataController: HistoryMetadataController
) : CollectionInteractor, OnboardingInteractor, TopSiteInteractor, TipInteractor, ) : CollectionInteractor,
TabSessionInteractor, ToolbarInteractor, ExperimentCardInteractor, RecentTabInteractor, OnboardingInteractor,
RecentBookmarksInteractor, HistoryMetadataInteractor { TopSiteInteractor,
TipInteractor,
TabSessionInteractor,
ToolbarInteractor,
ExperimentCardInteractor,
RecentTabInteractor,
RecentBookmarksInteractor,
HistoryMetadataInteractor {
override fun onCollectionAddTabTapped(collection: TabCollection) { override fun onCollectionAddTabTapped(collection: TabCollection) {
controller.handleCollectionAddTabTapped(collection) controller.handleCollectionAddTabTapped(collection)

@ -43,7 +43,7 @@ class OnboardingPrivateBrowsingViewHolder(
val text = SpannableString(view.context.getString(R.string.onboarding_private_browsing_description1)).apply { val text = SpannableString(view.context.getString(R.string.onboarding_private_browsing_description1)).apply {
val spanStartIndex = indexOf(IMAGE_PLACEHOLDER) val spanStartIndex = indexOf(IMAGE_PLACEHOLDER)
setSpan( setSpan(
inlineIcon, inlineIcon,
spanStartIndex, spanStartIndex,
spanStartIndex + IMAGE_PLACEHOLDER.length, spanStartIndex + IMAGE_PLACEHOLDER.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE Spanned.SPAN_EXCLUSIVE_EXCLUSIVE

@ -73,8 +73,8 @@ class BookmarkAdapter(private val emptyView: View, private val interactor: Bookm
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldMode::class == newMode::class && oldMode::class == newMode::class &&
old[oldItemPosition] in oldMode.selectedItems == new[newItemPosition] in newMode.selectedItems && old[oldItemPosition] in oldMode.selectedItems == new[newItemPosition] in newMode.selectedItems &&
old[oldItemPosition] == new[newItemPosition] old[oldItemPosition] == new[newItemPosition]
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = old[oldItemPosition] val oldItem = old[oldItemPosition]

@ -43,6 +43,6 @@ class BookmarkDeselectNavigationListener(
*/ */
private fun differentFromSelectedFolder(arguments: Bundle?): Boolean { private fun differentFromSelectedFolder(arguments: Bundle?): Boolean {
return arguments != null && return arguments != null &&
BookmarkFragmentArgs.fromBundle(arguments).currentRoot != viewModel.selectedFolder?.guid BookmarkFragmentArgs.fromBundle(arguments).currentRoot != viewModel.selectedFolder?.guid
} }
} }

@ -295,10 +295,12 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
} }
private fun deleteMulti(selected: Set<BookmarkNode>, eventType: Event = Event.RemoveBookmarks) { private fun deleteMulti(selected: Set<BookmarkNode>, eventType: Event = Event.RemoveBookmarks) {
selected.iterator().forEach { if (it.type == BookmarkNodeType.FOLDER) { selected.iterator().forEach {
showRemoveFolderDialog(selected) if (it.type == BookmarkNodeType.FOLDER) {
return showRemoveFolderDialog(selected)
} } return
}
}
updatePendingBookmarksToDelete(selected) updatePendingBookmarksToDelete(selected)
pendingBookmarkDeletionJob = getDeleteOperation(eventType) pendingBookmarkDeletionJob = getDeleteOperation(eventType)
@ -320,9 +322,11 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
viewLifecycleOwner.lifecycleScope.allowUndo( viewLifecycleOwner.lifecycleScope.allowUndo(
requireView(), message, requireView(), message,
getString(R.string.bookmark_undo_deletion), { getString(R.string.bookmark_undo_deletion),
{
undoPendingDeletion(selected) undoPendingDeletion(selected)
}, operation = getDeleteOperation(eventType) },
operation = getDeleteOperation(eventType)
) )
} }

@ -89,8 +89,8 @@ private fun bookmarkFragmentStateReducer(
) )
} }
is BookmarkFragmentAction.Select -> state.copy( is BookmarkFragmentAction.Select -> state.copy(
mode = BookmarkFragmentState.Mode.Selecting(state.mode.selectedItems + action.item) mode = BookmarkFragmentState.Mode.Selecting(state.mode.selectedItems + action.item)
) )
is BookmarkFragmentAction.Deselect -> { is BookmarkFragmentAction.Deselect -> {
val items = state.mode.selectedItems - action.item val items = state.mode.selectedItems - action.item
val mode = if (items.isEmpty()) { val mode = if (items.isEmpty()) {

@ -77,8 +77,12 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi
containerView.context, containerView.context,
R.drawable.ic_folder_icon R.drawable.ic_folder_icon
)?.apply { )?.apply {
setTint(ContextCompat.getColor(containerView.context, setTint(
R.color.primary_text_light_theme)) ContextCompat.getColor(
containerView.context,
R.color.primary_text_light_theme
)
)
} }
) )
view.titleView.text = folder.node.title view.titleView.text = folder.node.title

@ -231,7 +231,8 @@ class DownloadFragment : LibraryPageFragment<DownloadItem>(), UserInteractionHan
String.format( String.format(
requireContext().getString( requireContext().getString(
R.string.download_delete_single_item_snackbar R.string.download_delete_single_item_snackbar
), downloadItems.first().fileName ),
downloadItems.first().fileName
) )
} }
} }

@ -122,9 +122,12 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
requireComponents.core.historyStorage.createSynchronousPagedHistoryProvider() requireComponents.core.historyStorage.createSynchronousPagedHistoryProvider()
) )
viewModel.userHasHistory.observe(this, Observer { viewModel.userHasHistory.observe(
historyView.updateEmptyState(it) this,
}) Observer {
historyView.updateEmptyState(it)
}
)
requireComponents.analytics.metrics.track(Event.HistoryOpened) requireComponents.analytics.metrics.track(Event.HistoryOpened)
@ -154,9 +157,12 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
historyView.update(it) historyView.update(it)
} }
viewModel.history.observe(viewLifecycleOwner, Observer { viewModel.history.observe(
historyView.historyAdapter.submitList(it) viewLifecycleOwner,
}) Observer {
historyView.historyAdapter.submitList(it)
}
)
} }
override fun onResume() { override fun onResume() {
@ -229,7 +235,8 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
String.format( String.format(
requireContext().getString( requireContext().getString(
R.string.history_delete_single_item_snackbar R.string.history_delete_single_item_snackbar
), historyItems.first().url.toShortUrl(requireComponents.publicSuffixList) ),
historyItems.first().url.toShortUrl(requireComponents.publicSuffixList)
) )
} }
} }

@ -175,7 +175,8 @@ class HistoryView(
view.context.getString( view.context.getString(
if (numRecentTabs == 1) if (numRecentTabs == 1)
R.string.recently_closed_tab else R.string.recently_closed_tabs R.string.recently_closed_tab else R.string.recently_closed_tabs
), numRecentTabs ),
numRecentTabs
) )
isVisible = !userHasHistory isVisible = !userHasHistory
} }

@ -116,7 +116,8 @@ class HistoryListItemViewHolder(
itemView.context.getString( itemView.context.getString(
if (numRecentTabs == 1) if (numRecentTabs == 1)
R.string.recently_closed_tab else R.string.recently_closed_tabs R.string.recently_closed_tab else R.string.recently_closed_tabs
), numRecentTabs ),
numRecentTabs
) )
itemView.findViewById<ConstraintLayout>(R.id.recently_closed_nav).run { itemView.findViewById<ConstraintLayout>(R.id.recently_closed_nav).run {
if (isNormalMode) { if (isNormalMode) {

@ -62,9 +62,11 @@ class DefaultBrowserNotificationWorker(
return NotificationCompat.Builder(this, channelId) return NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_status_logo) .setSmallIcon(R.drawable.ic_status_logo)
.setContentTitle( .setContentTitle(
applicationContext.getString(R.string.notification_default_browser_title, appName)) applicationContext.getString(R.string.notification_default_browser_title, appName)
)
.setContentText( .setContentText(
applicationContext.getString(R.string.notification_default_browser_text, appName)) applicationContext.getString(R.string.notification_default_browser_text, appName)
)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL) .setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setColor(ContextCompat.getColor(this, R.color.primary_text_light_theme)) .setColor(ContextCompat.getColor(this, R.color.primary_text_light_theme))
.setPriority(NotificationCompat.PRIORITY_DEFAULT) .setPriority(NotificationCompat.PRIORITY_DEFAULT)

@ -49,7 +49,7 @@ object Performance {
val batteryStatus = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED)) val batteryStatus = context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
batteryStatus?.let { batteryStatus?.let {
val isPhonePlugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) == val isPhonePlugged = it.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) ==
BatteryManager.BATTERY_PLUGGED_USB BatteryManager.BATTERY_PLUGGED_USB
val isAdbEnabled = AndroidSettings.Global.getInt( val isAdbEnabled = AndroidSettings.Global.getInt(
context.contentResolver, context.contentResolver,
AndroidSettings.Global.ADB_ENABLED, 0 AndroidSettings.Global.ADB_ENABLED, 0

@ -15,9 +15,9 @@ import java.lang.reflect.Modifier.PRIVATE
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
private val classPrefixList = arrayOf( private val classPrefixList = arrayOf(
"android.widget.", "android.widget.",
"android.webkit.", "android.webkit.",
"android.app." "android.app."
) )
/** /**
* Counts the number of inflations fenix does. This class behaves only as an inflation counter since * Counts the number of inflations fenix does. This class behaves only as an inflation counter since

@ -52,13 +52,15 @@ class StartupActivityLog {
return return
} }
val transformedEntries = log.map { when (it) { val transformedEntries = log.map {
is LogEntry.AppStarted -> "App-STARTED" when (it) {
is LogEntry.AppStopped -> "App-STOPPED" is LogEntry.AppStarted -> "App-STARTED"
is LogEntry.ActivityCreated -> "${it.activityClass.simpleName}-CREATED" is LogEntry.AppStopped -> "App-STOPPED"
is LogEntry.ActivityStarted -> "${it.activityClass.simpleName}-STARTED" is LogEntry.ActivityCreated -> "${it.activityClass.simpleName}-CREATED"
is LogEntry.ActivityStopped -> "${it.activityClass.simpleName}-STOPPED" is LogEntry.ActivityStarted -> "${it.activityClass.simpleName}-STARTED"
} } is LogEntry.ActivityStopped -> "${it.activityClass.simpleName}-STOPPED"
}
}
loggerArg.debug(transformedEntries.toString()) loggerArg.debug(transformedEntries.toString())
} }

@ -33,7 +33,8 @@ class StartupReportFullyDrawn {
*/ */
fun onActivityCreateEndHome(state: StartupState, activity: HomeActivity) { fun onActivityCreateEndHome(state: StartupState, activity: HomeActivity) {
if (!isInstrumented && if (!isInstrumented &&
state is StartupState.Cold && state.destination == APP_LINK) { state is StartupState.Cold && state.destination == APP_LINK
) {
// Instrumenting the first frame drawn should be good enough for app link for now. // Instrumenting the first frame drawn should be good enough for app link for now.
isInstrumented = true isInstrumented = true
attachReportFullyDrawn(activity, activity.findViewById(R.id.rootContainer)) attachReportFullyDrawn(activity, activity.findViewById(R.id.rootContainer))
@ -50,7 +51,8 @@ class StartupReportFullyDrawn {
*/ */
fun onTopSitesItemBound(state: StartupState, holder: TopSiteItemViewHolder) { fun onTopSitesItemBound(state: StartupState, holder: TopSiteItemViewHolder) {
if (!isInstrumented && if (!isInstrumented &&
state is StartupState.Cold && state.destination == HOMESCREEN) { state is StartupState.Cold && state.destination == HOMESCREEN
) {
isInstrumented = true isInstrumented = true
// Ideally we wouldn't cast to HomeActivity but we want to save implementation time. // Ideally we wouldn't cast to HomeActivity but we want to save implementation time.

@ -25,9 +25,12 @@ class VisualCompletenessQueue(val queue: RunWhenReadyQueue) {
containerWeakReference.get()?.doOnPreDraw { containerWeakReference.get()?.doOnPreDraw {
// This delay is temporary. We are delaying 5 seconds until the performance // This delay is temporary. We are delaying 5 seconds until the performance
// team can locate the real point of visual completeness. // team can locate the real point of visual completeness.
it.postDelayed({ it.postDelayed(
queue.ready() {
}, delay) queue.ready()
},
delay
)
} }
} }
} }

@ -81,7 +81,8 @@ internal class WebPushEngineDelegate(
}, },
onSubscribe = { subscription -> onSubscribe = { subscription ->
onSubscribe(subscription.toEnginePushSubscription()) onSubscribe(subscription.toEnginePushSubscription())
}) }
)
} }
override fun onUnsubscribe(scope: String, onUnsubscribe: (Boolean) -> Unit) { override fun onUnsubscribe(scope: String, onUnsubscribe: (Boolean) -> Unit) {
@ -93,7 +94,8 @@ internal class WebPushEngineDelegate(
}, },
onUnsubscribe = { result -> onUnsubscribe = { result ->
onUnsubscribe(result) onUnsubscribe(result)
}) }
)
} }
} }

@ -132,15 +132,15 @@ class SearchDialogController(
fragmentStore.dispatch( fragmentStore.dispatch(
SearchFragmentAction.ShowSearchShortcutEnginePicker( SearchFragmentAction.ShowSearchShortcutEnginePicker(
(textMatchesCurrentUrl || textMatchesCurrentSearch || text.isEmpty()) && (textMatchesCurrentUrl || textMatchesCurrentSearch || text.isEmpty()) &&
settings.shouldShowSearchShortcuts settings.shouldShowSearchShortcuts
) )
) )
fragmentStore.dispatch( fragmentStore.dispatch(
SearchFragmentAction.AllowSearchSuggestionsInPrivateModePrompt( SearchFragmentAction.AllowSearchSuggestionsInPrivateModePrompt(
text.isNotEmpty() && text.isNotEmpty() &&
activity.browsingModeManager.mode.isPrivate && activity.browsingModeManager.mode.isPrivate &&
!settings.shouldShowSearchSuggestionsInPrivate && !settings.shouldShowSearchSuggestionsInPrivate &&
!settings.showSearchSuggestionsInPrivateOnboardingFinished !settings.showSearchSuggestionsInPrivateOnboardingFinished
) )
) )
} }
@ -244,7 +244,7 @@ class SearchDialogController(
dismissDialog() dismissDialog()
} }
setPositiveButton(R.string.camera_permissions_needed_positive_button_text) { setPositiveButton(R.string.camera_permissions_needed_positive_button_text) {
dialog: DialogInterface, _ -> dialog: DialogInterface, _ ->
val intent: Intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val intent: Intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS) Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
} else { } else {

@ -508,8 +508,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private fun updateSearchSuggestionsHintVisibility(state: SearchFragmentState) { private fun updateSearchSuggestionsHintVisibility(state: SearchFragmentState) {
view?.apply { view?.apply {
val showHint = state.showSearchSuggestionsHint && val showHint = state.showSearchSuggestionsHint &&
!state.showSearchShortcuts && !state.showSearchShortcuts &&
state.url != state.query state.url != state.query
findViewById<View>(R.id.search_suggestions_hint)?.isVisible = showHint findViewById<View>(R.id.search_suggestions_hint)?.isVisible = showHint
search_suggestions_hint_divider?.isVisible = showHint search_suggestions_hint_divider?.isVisible = showHint
@ -522,8 +522,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
val isVisible = val isVisible =
searchEngine?.id?.contains("google") == true && searchEngine?.id?.contains("google") == true &&
isSpeechAvailable() && isSpeechAvailable() &&
requireContext().settings().shouldShowVoiceSearch requireContext().settings().shouldShowVoiceSearch
if (isVisible) { if (isVisible) {
toolbarView.view.addEditAction( toolbarView.view.addEditAction(
@ -559,8 +559,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler {
private fun updateClipboardSuggestion(searchState: SearchFragmentState, clipboardUrl: String?) { private fun updateClipboardSuggestion(searchState: SearchFragmentState, clipboardUrl: String?) {
val shouldShowView = searchState.showClipboardSuggestions && val shouldShowView = searchState.showClipboardSuggestions &&
searchState.query.isEmpty() && searchState.query.isEmpty() &&
!clipboardUrl.isNullOrEmpty() && !searchState.showSearchShortcuts !clipboardUrl.isNullOrEmpty() && !searchState.showSearchShortcuts
fill_link_from_clipboard.isVisible = shouldShowView fill_link_from_clipboard.isVisible = shouldShowView
fill_link_divider.isVisible = shouldShowView fill_link_divider.isVisible = shouldShowView

@ -34,9 +34,9 @@ class PerformanceActivityLifecycleCallbacks(
// They have been whitelisted in case new activities are added to the application // They have been whitelisted in case new activities are added to the application
// to ensure these new activities would not crash the application. // to ensure these new activities would not crash the application.
return isTransientActivityInMigrationVariant(activity) || return isTransientActivityInMigrationVariant(activity) ||
(activity is IntentReceiverActivity) || (activity is IntentReceiverActivity) ||
(activity is VoiceSearchActivity) || (activity is VoiceSearchActivity) ||
(activity is AuthIntentReceiverActivity) (activity is AuthIntentReceiverActivity)
} }
/** /**

@ -116,16 +116,20 @@ class CustomizationFragment : PreferenceFragmentCompat() {
private fun setupToolbarCategory() { private fun setupToolbarCategory() {
val topPreference = requirePreference<RadioButtonPreference>(R.string.pref_key_toolbar_top) val topPreference = requirePreference<RadioButtonPreference>(R.string.pref_key_toolbar_top)
topPreference.onClickListener { topPreference.onClickListener {
requireContext().components.analytics.metrics.track(Event.ToolbarPositionChanged( requireContext().components.analytics.metrics.track(
Event.ToolbarPositionChanged.Position.TOP Event.ToolbarPositionChanged(
)) Event.ToolbarPositionChanged.Position.TOP
)
)
} }
val bottomPreference = requirePreference<RadioButtonPreference>(R.string.pref_key_toolbar_bottom) val bottomPreference = requirePreference<RadioButtonPreference>(R.string.pref_key_toolbar_bottom)
bottomPreference.onClickListener { bottomPreference.onClickListener {
requireContext().components.analytics.metrics.track(Event.ToolbarPositionChanged( requireContext().components.analytics.metrics.track(
Event.ToolbarPositionChanged.Position.BOTTOM Event.ToolbarPositionChanged(
)) Event.ToolbarPositionChanged.Position.BOTTOM
)
)
} }
val toolbarPosition = requireContext().settings().toolbarPosition val toolbarPosition = requireContext().settings().toolbarPosition

@ -20,8 +20,10 @@ fun SitePermissions.toggle(featurePhone: PhoneFeature): SitePermissions {
fun SitePermissions.get(field: PhoneFeature) = when (field) { fun SitePermissions.get(field: PhoneFeature) = when (field) {
PhoneFeature.AUTOPLAY -> PhoneFeature.AUTOPLAY ->
throw IllegalAccessException("AUTOPLAY can't be accessed via get try " + throw IllegalAccessException(
"using AUTOPLAY_AUDIBLE and AUTOPLAY_INAUDIBLE") "AUTOPLAY can't be accessed via get try " +
"using AUTOPLAY_AUDIBLE and AUTOPLAY_INAUDIBLE"
)
PhoneFeature.CAMERA -> camera PhoneFeature.CAMERA -> camera
PhoneFeature.LOCATION -> location PhoneFeature.LOCATION -> location
PhoneFeature.MICROPHONE -> microphone PhoneFeature.MICROPHONE -> microphone
@ -33,8 +35,10 @@ fun SitePermissions.get(field: PhoneFeature) = when (field) {
} }
fun SitePermissions.update(field: PhoneFeature, value: SitePermissions.Status) = when (field) { fun SitePermissions.update(field: PhoneFeature, value: SitePermissions.Status) = when (field) {
PhoneFeature.AUTOPLAY -> throw IllegalAccessException("AUTOPLAY can't be accessed via update " + PhoneFeature.AUTOPLAY -> throw IllegalAccessException(
"try using AUTOPLAY_AUDIBLE and AUTOPLAY_INAUDIBLE") "AUTOPLAY can't be accessed via update " +
"try using AUTOPLAY_AUDIBLE and AUTOPLAY_INAUDIBLE"
)
PhoneFeature.CAMERA -> copy(camera = value) PhoneFeature.CAMERA -> copy(camera = value)
PhoneFeature.LOCATION -> copy(location = value) PhoneFeature.LOCATION -> copy(location = value)
PhoneFeature.MICROPHONE -> copy(microphone = value) PhoneFeature.MICROPHONE -> copy(microphone = value)

@ -71,7 +71,8 @@ class PairFragment : Fragment(R.layout.fragment_pair), UserInteractionHandler {
}, },
scanMessage = scanMessage =
if (requireContext().settings().allowDomesticChinaFxaServer && if (requireContext().settings().allowDomesticChinaFxaServer &&
org.mozilla.fenix.Config.channel.isMozillaOnline) org.mozilla.fenix.Config.channel.isMozillaOnline
)
R.string.pair_instructions_2_cn R.string.pair_instructions_2_cn
else R.string.pair_instructions_2 else R.string.pair_instructions_2
), ),

@ -55,7 +55,8 @@ private fun DebugInfo() {
) )
Text( Text(
text = store.state.search.region?.home ?: "Unknown", text = store.state.search.region?.home ?: "Unknown",
modifier = Modifier.padding(4.dp)) modifier = Modifier.padding(4.dp)
)
Text( Text(
text = stringResource(R.string.debug_info_region_current), text = stringResource(R.string.debug_info_region_current),
style = MaterialTheme.typography.h6, style = MaterialTheme.typography.h6,

@ -370,9 +370,12 @@ class SettingsFragment : PreferenceFragmentCompat() {
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed(
exitProcess(0) {
}, AMO_COLLECTION_OVERRIDE_EXIT_DELAY) exitProcess(0)
},
AMO_COLLECTION_OVERRIDE_EXIT_DELAY
)
} }
binding.customAmoCollection.setText(context.settings().overrideAmoCollection) binding.customAmoCollection.setText(context.settings().overrideAmoCollection)
@ -435,9 +438,12 @@ class SettingsFragment : PreferenceFragmentCompat() {
getString(R.string.toast_override_fxa_sync_server_done), getString(R.string.toast_override_fxa_sync_server_done),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed(
exitProcess(0) {
}, FXA_SYNC_OVERRIDE_EXIT_DELAY) exitProcess(0)
},
FXA_SYNC_OVERRIDE_EXIT_DELAY
)
} }
} }
} }
@ -538,8 +544,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
findPreference<Preference>(getPreferenceKey(R.string.pref_key_override_sync_tokenserver)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_override_sync_tokenserver))
val settings = requireContext().settings() val settings = requireContext().settings()
val show = settings.overrideFxAServer.isNotEmpty() || val show = settings.overrideFxAServer.isNotEmpty() ||
settings.overrideSyncTokenServer.isNotEmpty() || settings.overrideSyncTokenServer.isNotEmpty() ||
settings.showSecretDebugMenuThisSession settings.showSecretDebugMenuThisSession
// Only enable changes to these prefs when the user isn't connected to an account. // Only enable changes to these prefs when the user isn't connected to an account.
val enabled = val enabled =
requireComponents.backgroundServices.accountManager.authenticatedAccount() == null requireComponents.backgroundServices.accountManager.authenticatedAccount() == null
@ -560,9 +566,11 @@ class SettingsFragment : PreferenceFragmentCompat() {
val preferenceAmoCollectionOverride = val preferenceAmoCollectionOverride =
findPreference<Preference>(getPreferenceKey(R.string.pref_key_override_amo_collection)) findPreference<Preference>(getPreferenceKey(R.string.pref_key_override_amo_collection))
val show = (Config.channel.isNightlyOrDebug && ( val show = (
settings.amoCollectionOverrideConfigured() || settings.showSecretDebugMenuThisSession) Config.channel.isNightlyOrDebug && (
) settings.amoCollectionOverrideConfigured() || settings.showSecretDebugMenuThisSession
)
)
preferenceAmoCollectionOverride?.apply { preferenceAmoCollectionOverride?.apply {
isVisible = show isVisible = show
summary = settings.overrideAmoCollection.ifEmpty { null } summary = settings.overrideAmoCollection.ifEmpty { null }
@ -589,9 +597,12 @@ class SettingsFragment : PreferenceFragmentCompat() {
getString(R.string.toast_override_fxa_sync_server_done), getString(R.string.toast_override_fxa_sync_server_done),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
Handler(Looper.getMainLooper()).postDelayed({ Handler(Looper.getMainLooper()).postDelayed(
exitProcess(0) {
}, FXA_SYNC_OVERRIDE_EXIT_DELAY) exitProcess(0)
},
FXA_SYNC_OVERRIDE_EXIT_DELAY
)
} }
} }
} }

@ -29,11 +29,11 @@ object SupportUtils {
const val GOOGLE_URL = "https://www.google.com/" const val GOOGLE_URL = "https://www.google.com/"
const val BAIDU_URL = "https://m.baidu.com/?from=1000969a" const val BAIDU_URL = "https://m.baidu.com/?from=1000969a"
const val JD_URL = "https://union-click.jd.com/jdc" + const val JD_URL = "https://union-click.jd.com/jdc" +
"?e=&p=AyIGZRprFDJWWA1FBCVbV0IUWVALHFRBEwQAQB1AWQkFVUVXfFkAF14lRFRbJXstVWR3WQ1rJ08AZnhS" + "?e=&p=AyIGZRprFDJWWA1FBCVbV0IUWVALHFRBEwQAQB1AWQkFVUVXfFkAF14lRFRbJXstVWR3WQ1rJ08AZnhS" +
"HDJBYh4LZR9eEAMUBlccWCUBEQZRGFoXCxc3ZRteJUl8BmUZWhQ" + "HDJBYh4LZR9eEAMUBlccWCUBEQZRGFoXCxc3ZRteJUl8BmUZWhQ" +
"AEwdRGF0cMhIAVB5ZFAETBVAaXRwyFQdcKydLSUpaCEtYFAIXN2UrWCUyIgdVK1slXVZaCCtZFAMWDg%3D%3D" "AEwdRGF0cMhIAVB5ZFAETBVAaXRwyFQdcKydLSUpaCEtYFAIXN2UrWCUyIgdVK1slXVZaCCtZFAMWDg%3D%3D"
const val PDD_URL = "https://mobile.yangkeduo.com/duo_cms_mall.html?pid=13289095_194240604&" + const val PDD_URL = "https://mobile.yangkeduo.com/duo_cms_mall.html?pid=13289095_194240604&" +
"cpsSign=CM_210309_13289095_194240604_8bcfd56d5db3c43d983014d2658ec26e&duoduo_type=2" "cpsSign=CM_210309_13289095_194240604_8bcfd56d5db3c43d983014d2658ec26e&duoduo_type=2"
const val GOOGLE_US_URL = "https://www.google.com/webhp?client=firefox-b-1-m&channel=ts" const val GOOGLE_US_URL = "https://www.google.com/webhp?client=firefox-b-1-m&channel=ts"
const val GOOGLE_XX_URL = "https://www.google.com/webhp?client=firefox-b-m&channel=ts" const val GOOGLE_XX_URL = "https://www.google.com/webhp?client=firefox-b-m&channel=ts"

@ -44,19 +44,22 @@ class SyncPreferenceView(
) { ) {
init { init {
accountManager.register(object : AccountObserver { accountManager.register(
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) { object : AccountObserver {
MainScope().launch { updateSyncPreferenceStatus() } override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
} MainScope().launch { updateSyncPreferenceStatus() }
}
override fun onLoggedOut() {
MainScope().launch { updateSyncPreferenceNeedsLogin() } override fun onLoggedOut() {
} MainScope().launch { updateSyncPreferenceNeedsLogin() }
}
override fun onAuthenticationProblems() {
MainScope().launch { updateSyncPreferenceNeedsReauth() } override fun onAuthenticationProblems() {
} MainScope().launch { updateSyncPreferenceNeedsReauth() }
}, owner = lifecycleOwner) }
},
owner = lifecycleOwner
)
val accountExists = accountManager.authenticatedAccount() != null val accountExists = accountManager.authenticatedAccount() != null
val needsReauth = accountManager.accountNeedsReauth() val needsReauth = accountManager.accountNeedsReauth()

@ -353,23 +353,23 @@ class TextPercentageSeekBarPreference @JvmOverloads constructor(
} }
mSeekBar?.setAccessibilityDelegate(object : mSeekBar?.setAccessibilityDelegate(object :
View.AccessibilityDelegate() { View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo( override fun onInitializeAccessibilityNodeInfo(
host: View?, host: View?,
info: AccessibilityNodeInfo? info: AccessibilityNodeInfo?
) { ) {
super.onInitializeAccessibilityNodeInfo(host, info) super.onInitializeAccessibilityNodeInfo(host, info)
val initialInfo = info?.rangeInfo val initialInfo = info?.rangeInfo
info?.rangeInfo = initialInfo?.let { info?.rangeInfo = initialInfo?.let {
AccessibilityNodeInfo.RangeInfo.obtain( AccessibilityNodeInfo.RangeInfo.obtain(
RANGE_TYPE_PERCENT, RANGE_TYPE_PERCENT,
MIN_VALUE.toFloat(), MIN_VALUE.toFloat(),
SEEK_BAR_MAX.toFloat(), SEEK_BAR_MAX.toFloat(),
convertCurrentValue(it.current) convertCurrentValue(it.current)
) )
}
} }
} })
})
} }
/** /**

@ -76,7 +76,7 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
learnMorePreference.setOnPreferenceClickListener { learnMorePreference.setOnPreferenceClickListener {
(activity as HomeActivity).openToBrowserAndLoad( (activity as HomeActivity).openToBrowserAndLoad(
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
(SupportUtils.SumoTopic.TRACKING_PROTECTION), (SupportUtils.SumoTopic.TRACKING_PROTECTION),
newTab = true, newTab = true,
from = BrowserDirection.FromTrackingProtection from = BrowserDirection.FromTrackingProtection
) )

@ -138,13 +138,15 @@ class AboutFragment : Fragment(), AboutPageListener {
AboutItem.ExternalLink( AboutItem.ExternalLink(
WHATS_NEW, WHATS_NEW,
SupportUtils.getWhatsNewUrl(context) SupportUtils.getWhatsNewUrl(context)
), getString(R.string.about_whats_new, getString(R.string.app_name)) ),
getString(R.string.about_whats_new, getString(R.string.app_name))
), ),
AboutPageItem( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
SUPPORT, SUPPORT,
SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.HELP) SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.HELP)
), getString(R.string.about_support) ),
getString(R.string.about_support)
), ),
AboutPageItem( AboutPageItem(
AboutItem.Crashes, AboutItem.Crashes,
@ -154,13 +156,15 @@ class AboutFragment : Fragment(), AboutPageListener {
AboutItem.ExternalLink( AboutItem.ExternalLink(
PRIVACY_NOTICE, PRIVACY_NOTICE,
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE) SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE)
), getString(R.string.about_privacy_notice) ),
getString(R.string.about_privacy_notice)
), ),
AboutPageItem( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
RIGHTS, RIGHTS,
SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS) SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS)
), getString(R.string.about_know_your_rights) ),
getString(R.string.about_know_your_rights)
), ),
AboutPageItem( AboutPageItem(
AboutItem.ExternalLink(LICENSING_INFO, ABOUT_LICENSE_URL), AboutItem.ExternalLink(LICENSING_INFO, ABOUT_LICENSE_URL),

@ -24,7 +24,8 @@ class DefaultLocaleSettingsController(
override fun handleLocaleSelected(locale: Locale) { override fun handleLocaleSelected(locale: Locale) {
if (localeSettingsStore.state.selectedLocale == locale && if (localeSettingsStore.state.selectedLocale == locale &&
!LocaleManager.isDefaultLocaleSelected(activity)) { !LocaleManager.isDefaultLocaleSelected(activity)
) {
return return
} }
localeSettingsStore.dispatch(LocaleSettingsAction.Select(locale)) localeSettingsStore.dispatch(LocaleSettingsAction.Select(locale))

@ -17,14 +17,18 @@ fun LocaleManager.getSupportedLocales(): List<Locale> {
val resultLocaleList: MutableList<Locale> = ArrayList() val resultLocaleList: MutableList<Locale> = ArrayList()
resultLocaleList.add(0, getSystemDefault()) resultLocaleList.add(0, getSystemDefault())
resultLocaleList.addAll(BuildConfig.SUPPORTED_LOCALE_ARRAY resultLocaleList.addAll(
.toList() BuildConfig.SUPPORTED_LOCALE_ARRAY
.map { .toList()
it.toLocale() .map {
}.sortedWith(compareBy( it.toLocale()
{ it.displayLanguage }, }.sortedWith(
{ it.displayCountry } compareBy(
))) { it.displayLanguage },
{ it.displayCountry }
)
)
)
return resultLocaleList return resultLocaleList
} }

@ -64,8 +64,8 @@ private fun localeSettingsStateReducer(
is LocaleSettingsAction.Search -> { is LocaleSettingsAction.Search -> {
val searchedItems = state.localeList.filter { val searchedItems = state.localeList.filter {
it.getDisplayLanguage(it).startsWith(action.query, ignoreCase = true) || it.getDisplayLanguage(it).startsWith(action.query, ignoreCase = true) ||
it.displayLanguage.startsWith(action.query, ignoreCase = true) || it.displayLanguage.startsWith(action.query, ignoreCase = true) ||
it === state.localeList[0] it === state.localeList[0]
} }
state.copy(searchedLocaleList = searchedItems) state.copy(searchedLocaleList = searchedItems)
} }

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save