diff --git a/.taskcluster.yml b/.taskcluster.yml index b175ab5da..aaf457d5e 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -17,15 +17,17 @@ tasks: $if: 'tasks_for in ["cron", "action"]' then: '${tasks_for}@noreply.mozilla.org' else: - $if: 'tasks_for == "github-push"' - then: '${event.pusher.email}' - # Assume Pull Request + $if: 'event.sender.login == "bors[bot]"' + then: 'skaspari+mozlando@mozilla.com' # It must match what's in bors.toml else: - $if: 'tasks_for == "github-pull-request"' - then: '${event.pull_request.user.login}@users.noreply.github.com' + $if: 'tasks_for == "github-push"' + then: '${event.pusher.email}' else: - $if: 'tasks_for == "github-release"' - then: '${event.sender.login}@users.noreply.github.com' + $if: 'tasks_for == "github-pull-request"' + then: '${event.pull_request.user.login}@users.noreply.github.com' + else: + $if: 'tasks_for == "github-release"' + then: '${event.sender.login}@users.noreply.github.com' baseRepoUrl: $if: 'tasks_for in ["github-push", "github-release"]' then: '${event.repository.html_url}' @@ -101,7 +103,7 @@ tasks: $if: > tasks_for in ["action", "cron"] || (tasks_for == "github-pull-request" && pullRequestAction in ["opened", "reopened", "synchronize"]) - || (tasks_for == "github-push" && head_branch[:10] != "refs/tags/") + || (tasks_for == "github-push" && head_branch[:10] != "refs/tags/") && (head_branch != "staging.tmp") && (head_branch != "trying.tmp") || (tasks_for == "github-release" && releaseAction == "published") then: $let: diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt index 036273f24..12b74d038 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/SettingsAddonsTest.kt @@ -1,5 +1,6 @@ package org.mozilla.fenix.ui +import android.view.View import androidx.test.espresso.IdlingRegistry import org.mozilla.fenix.helpers.TestAssetHelper @@ -11,12 +12,12 @@ import okhttp3.mockwebserver.MockWebServer import org.junit.Rule import org.junit.Before import org.junit.After -import org.junit.Ignore import org.junit.Test import org.mozilla.fenix.R import org.mozilla.fenix.helpers.AndroidAssetDispatcher import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.RecyclerViewIdlingResource +import org.mozilla.fenix.helpers.ViewVisibilityIdlingResource import org.mozilla.fenix.ui.robots.homeScreen import org.mozilla.fenix.ui.robots.navigationToolbar @@ -30,6 +31,7 @@ class SettingsAddonsTest { private lateinit var mockWebServer: MockWebServer private var addonsListIdlingResource: RecyclerViewIdlingResource? = null + private var addonContainerIdlingResource: ViewVisibilityIdlingResource? = null @get:Rule val activityTestRule = HomeActivityTestRule() @@ -49,6 +51,10 @@ class SettingsAddonsTest { if (addonsListIdlingResource != null) { IdlingRegistry.getInstance().unregister(addonsListIdlingResource!!) } + + if (addonContainerIdlingResource != null) { + IdlingRegistry.getInstance().unregister(addonContainerIdlingResource!!) + } } // Walks through settings add-ons menu to ensure all items are present @@ -89,7 +95,6 @@ class SettingsAddonsTest { } } - @Ignore("Failing intermittently on Firebase: https://github.com/mozilla-mobile/fenix/issues/13829") // Opens the addons settings menu, installs an addon, then uninstalls @Test fun verifyAddonsCanBeUninstalled() { @@ -107,8 +112,13 @@ class SettingsAddonsTest { clickInstallAddon(addonName) acceptInstallAddon() verifyDownloadAddonPrompt(addonName, activityTestRule) + IdlingRegistry.getInstance().unregister(addonsListIdlingResource!!) }.openDetailedMenuForAddon(addonName) { - verifyCurrentAddonMenu() + addonContainerIdlingResource = ViewVisibilityIdlingResource( + activityTestRule.activity.findViewById(R.id.addon_container), + View.VISIBLE + ) + IdlingRegistry.getInstance().register(addonContainerIdlingResource!!) }.removeAddon { } } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt index aa7cc34ff..86f5c929f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/StrictEnhancedTrackingProtectionTest.kt @@ -8,7 +8,6 @@ import androidx.test.platform.app.InstrumentationRegistry import okhttp3.mockwebserver.MockWebServer import org.junit.After import org.junit.Before -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.mozilla.fenix.ext.settings @@ -77,7 +76,6 @@ class StrictEnhancedTrackingProtectionTest { } } - @Ignore("Failing on AC 58 update: https://github.com/mozilla-mobile/fenix/issues/14524") @Test fun testStrictVisitContentNotification() { val trackingProtectionTest = @@ -91,7 +89,6 @@ class StrictEnhancedTrackingProtectionTest { }.closeNotificationPopup {} } - @Ignore("Failing on AC 58 update: https://github.com/mozilla-mobile/fenix/issues/14524") @Test fun testStrictVisitContentShield() { val trackingProtectionTest = @@ -109,7 +106,6 @@ class StrictEnhancedTrackingProtectionTest { } } - @Ignore("Failing on AC 58 update: https://github.com/mozilla-mobile/fenix/issues/14524") @Test fun testStrictVisitProtectionSheet() { val trackingProtectionTest = @@ -129,7 +125,6 @@ class StrictEnhancedTrackingProtectionTest { } } - @Ignore("Failing on AC 58 update: https://github.com/mozilla-mobile/fenix/issues/14524") @Test fun testStrictVisitDisable() { val trackingProtectionTest = @@ -161,7 +156,6 @@ class StrictEnhancedTrackingProtectionTest { } } - @Ignore("Failing on AC 58 update: https://github.com/mozilla-mobile/fenix/issues/14524") @Test fun testStrictVisitDisableExceptionToggle() { val trackingProtectionTest = @@ -194,7 +188,6 @@ class StrictEnhancedTrackingProtectionTest { } } - @Ignore("Failing on AC 58 update: https://github.com/mozilla-mobile/fenix/issues/14524") @Test fun testStrictVisitSheetDetails() { val trackingProtectionTest = diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.kt index 499861c30..b228c42b7 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SettingsSubMenuAddonsManagerAddonDetailedMenuRobot.kt @@ -16,8 +16,6 @@ import org.mozilla.fenix.helpers.click class SettingsSubMenuAddonsManagerAddonDetailedMenuRobot { - fun verifyCurrentAddonMenu() = assertAddonMenuItems() - class Transition { fun goBack(interact: SettingsSubMenuAddonsManagerRobot.() -> Unit): SettingsSubMenuAddonsManagerRobot.Transition { fun goBackButton() = onView(allOf(withContentDescription("Navigate up"))) @@ -28,33 +26,14 @@ class SettingsSubMenuAddonsManagerAddonDetailedMenuRobot { } fun removeAddon(interact: SettingsSubMenuAddonsManagerRobot.() -> Unit): SettingsSubMenuAddonsManagerRobot.Transition { + removeAddonButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) removeAddonButton().click() SettingsSubMenuAddonsManagerRobot().interact() return SettingsSubMenuAddonsManagerRobot.Transition() } } - - private fun assertAddonMenuItems() { - enableSwitchButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - settingsButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - detailsButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - permissionsButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - removeAddonButton().check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) - } } -private fun enableSwitchButton() = - onView(withId(R.id.enable_switch)) - -private fun settingsButton() = - onView(withId(R.id.settings)) - -private fun detailsButton() = - onView(withId(R.id.details)) - -private fun permissionsButton() = - onView(withId(R.id.permissions)) - private fun removeAddonButton() = onView(withId(R.id.remove_add_on)) diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 152018600..0d33c7c90 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -45,4 +45,9 @@ object FeatureFlags { * Enables swipe to delete in bookmarks */ val bookmarkSwipeToDelete = Config.channel.isNightlyOrDebug + + /** + * Enables ETP cookie purging + */ + val etpCookiePurging = Config.channel.isNightlyOrDebug } diff --git a/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt b/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt index 5ff1932bb..b624b097f 100644 --- a/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt +++ b/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt @@ -16,16 +16,24 @@ object StrictModeManager { /*** * Enables strict mode for debug purposes. meant to be run only in the main process. - * @param setPenaltyDialog boolean value to decide setting the dialog box as a penalty. + * @param setPenaltyDeath boolean value to decide setting the penaltyDeath as a penalty. + * @param setPenaltyDialog boolean value to decide setting the dialog box as a penalty. + * Note: dialog penalty cannot be set with penaltyDeath */ - fun enableStrictMode(setPenaltyDialog: Boolean) { + fun enableStrictMode(setPenaltyDeath: Boolean, setPenaltyDialog: Boolean = false) { if (Config.channel.isDebug) { val threadPolicy = StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() - if (setPenaltyDialog && Build.MANUFACTURER !in strictModeExceptionList) { + if (setPenaltyDeath && Build.MANUFACTURER !in strictModeExceptionList) { + threadPolicy.penaltyDeath() + } + + // dialog penalty cannot be set with penaltyDeath + if (!setPenaltyDeath && setPenaltyDialog) { threadPolicy.penaltyDialog() } + StrictMode.setThreadPolicy(threadPolicy.build()) val builder = StrictMode.VmPolicy.Builder() @@ -39,7 +47,7 @@ object StrictModeManager { builder.detectContentUriWithoutPermission() } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - if (setPenaltyDialog) { + if (setPenaltyDeath || setPenaltyDialog) { builder.permitNonSdkApiUsage() } else { builder.detectNonSdkApiUsage() @@ -50,14 +58,15 @@ object StrictModeManager { } /** - * Revert strict mode to disable penalty dialog. Tied to fragment lifecycle since strict mode + * Revert strict mode to disable penalty. Tied to fragment lifecycle since strict mode * needs to switch to penalty logs. Using the fragment life cycle allows decoupling from any * specific fragment. */ fun changeStrictModePolicies(fragmentManager: FragmentManager) { - fragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { + fragmentManager.registerFragmentLifecycleCallbacks(object : + FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { - enableStrictMode(false) + enableStrictMode(setPenaltyDeath = false, setPenaltyDialog = false) fm.unregisterFragmentLifecycleCallbacks(this) } }, false) diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index c85545e95..0c843d5cc 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -255,7 +255,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session ) }, onCloseTab = { closedSession -> - val tab = store.state.findTab(closedSession.id) ?: return@DefaultBrowserToolbarController + val tab = store.state.findTab(closedSession.id) + ?: return@DefaultBrowserToolbarController val isSelected = tab.id == context.components.core.store.state.selectedTabId val snackbarMessage = if (tab.content.private) { @@ -372,11 +373,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session view = view ) - val shouldForwardToThirdParties = - PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - context.getPreferenceKey(R.string.pref_key_external_download_manager), false - ) - val downloadFeature = DownloadsFeature( context.applicationContext, store = store, @@ -388,7 +384,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session store, DownloadService::class ), - shouldForwardToThirdParties = { shouldForwardToThirdParties }, + shouldForwardToThirdParties = { + PreferenceManager.getDefaultSharedPreferences(context).getBoolean( + context.getPreferenceKey(R.string.pref_key_external_download_manager), false + ) + }, promptsStyling = DownloadsFeature.PromptsStyling( gravity = Gravity.BOTTOM, shouldWidthMatchParent = true, @@ -877,7 +877,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } override fun onBackLongPressed(): Boolean { - findNavController().navigate(R.id.action_global_tabHistoryDialogFragment) + findNavController().navigate( + NavGraphDirections.actionGlobalTabHistoryDialogFragment( + activeSessionId = customTabSessionId + ) + ) return true } @@ -1120,7 +1124,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session private fun didFirstContentfulHappen() = if (components.settings.waitToShowPageUntilFirstPaint) { - val tab = components.core.store.state.findTabOrCustomTabOrSelectedTab(customTabSessionId) + val tab = + components.core.store.state.findTabOrCustomTabOrSelectedTab(customTabSessionId) tab?.content?.firstContentfulPaint ?: false } else { true diff --git a/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt b/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt index 7b951c9b1..82e6e5aea 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/OpenInAppOnboardingObserver.kt @@ -36,10 +36,12 @@ class OpenInAppOnboardingObserver( } } + @Suppress("ComplexCondition") override fun onLoadingStateChanged(session: Session, loading: Boolean) { val appLink = appLinksUseCases.appLinkRedirect if (!loading && + !settings.openLinksInExternalApp && settings.shouldShowOpenInAppBanner && appLink(session.url).hasExternalApp() ) { diff --git a/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt b/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt index a7bfe9d4d..6c975a075 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/ToolbarGestureHandler.kt @@ -12,8 +12,6 @@ import android.graphics.PointF import android.graphics.Rect import android.view.View import android.view.ViewConfiguration -import androidx.annotation.Dimension -import androidx.annotation.Dimension.DP import androidx.core.animation.doOnEnd import androidx.core.graphics.contains import androidx.core.graphics.toPoint @@ -21,8 +19,8 @@ import androidx.core.view.isVisible import androidx.interpolator.view.animation.LinearOutSlowInInterpolator import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager -import mozilla.components.support.ktx.android.util.dpToPx import mozilla.components.support.ktx.android.view.getRectWithViewLocation +import org.mozilla.fenix.R import org.mozilla.fenix.ext.getRectWithScreenLocation import org.mozilla.fenix.ext.getWindowInsets import org.mozilla.fenix.ext.isKeyboardVisible @@ -57,7 +55,8 @@ class ToolbarGestureHandler( private val windowWidth: Int get() = activity.resources.displayMetrics.widthPixels - private val previewOffset = PREVIEW_OFFSET.dpToPx(activity.resources.displayMetrics) + private val previewOffset = + activity.resources.getDimensionPixelSize(R.dimen.browser_fragment_gesture_preview_offset) private val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop private val minimumFlingVelocity = ViewConfiguration.get(activity).scaledMinimumFlingVelocity @@ -304,12 +303,6 @@ class ToolbarGestureHandler( */ private const val OVERSCROLL_HIDE_PERCENT = 0.20 - /** - * The size of the gap between the tab preview and content layout. - */ - @Dimension(unit = DP) - private const val PREVIEW_OFFSET = 48 - /** * Animation duration when switching to another tab */ diff --git a/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt b/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt index bd7a4c569..0cf872a25 100644 --- a/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt +++ b/app/src/main/java/org/mozilla/fenix/components/FenixSnackbar.kt @@ -119,8 +119,8 @@ class FenixSnackbar private constructor( val callback = FenixSnackbarCallback(content) val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar - val toolbarHeight = view.context.resources - .getDimensionPixelSize(R.dimen.browser_toolbar_height) + val toolbarHeight = view.resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) + val dynamicToolbarEnabled = view.context.settings().isDynamicToolbarEnabled return FenixSnackbar(parent, content, callback, isError).also { it.duration = durationOrAccessibleDuration @@ -134,7 +134,7 @@ class FenixSnackbar private constructor( // can't intelligently position the snackbar on the upper most view. // Ideally we should not pass ContentFrameLayout in, but it's the only // way to display snackbars through a fragment transition. - view is ContentFrameLayout + (view is ContentFrameLayout || !dynamicToolbarEnabled) ) { toolbarHeight } else { diff --git a/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt b/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt index 9a0853e43..bc614aef1 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactory.kt @@ -8,6 +8,7 @@ import androidx.annotation.VisibleForTesting import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicyForSessionTypes import org.mozilla.fenix.Config +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.utils.Settings /** @@ -102,6 +103,6 @@ internal fun TrackingProtectionPolicyForSessionTypes.adaptPolicyToChannel(): Tra trackingCategories = trackingCategories, cookiePolicy = cookiePolicy, strictSocialTrackingProtection = strictSocialTrackingProtection, - cookiePurging = Config.channel.isNightlyOrDebug + cookiePurging = FeatureFlags.etpCookiePurging ) } diff --git a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt index 149620a54..b8335346d 100644 --- a/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt +++ b/app/src/main/java/org/mozilla/fenix/components/searchengine/FenixSearchEngineProvider.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.components.searchengine import android.content.Context import androidx.annotation.VisibleForTesting import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.async @@ -32,16 +33,15 @@ import java.util.Locale open class FenixSearchEngineProvider( private val context: Context ) : SearchEngineProvider, CoroutineScope by CoroutineScope(Job() + Dispatchers.IO) { - private val locationService = with(MozillaLocationService( - context, - context.components.core.client, - BuildConfig.MLS_TOKEN - )) { - if (Config.channel.isDebug || !this.hasRegionCached()) { - LocationService.dummy() - } else { - this - } + private val shouldMockMLS = Config.channel.isDebug || BuildConfig.MLS_TOKEN.isNullOrEmpty() + private val locationService: LocationService = if (shouldMockMLS) { + LocationService.dummy() + } else { + MozillaLocationService( + context, + context.components.core.client, + BuildConfig.MLS_TOKEN + ) } // We have two search engine types: one based on MLS reported region, one based only on Locale. @@ -94,6 +94,17 @@ open class FenixSearchEngineProvider( private var loadedSearchEngines = refreshAsync() + // https://github.com/mozilla-mobile/fenix/issues/9935 + // Create new getter that will return the fallback SearchEngineList if + // the main one hasn't completed yet + private val searchEngines: Deferred + get() = + if (isRegionCachedByLocationService) { + loadedSearchEngines + } else { + fallbackEngines + } + fun getDefaultEngine(context: Context): SearchEngine { val engines = installedSearchEngines(context) val selectedName = context.settings().defaultSearchEngineName @@ -107,7 +118,7 @@ open class FenixSearchEngineProvider( */ fun installedSearchEngines(context: Context): SearchEngineList = runBlocking { val installedIdentifiers = installedSearchEngineIdentifiers(context) - val engineList = loadedSearchEngines.await() + val engineList = searchEngines.await() engineList.copy( list = engineList.list.filter { @@ -178,11 +189,7 @@ open class FenixSearchEngineProvider( } private fun refreshAsync() = async { - val engineList = if (isRegionCachedByLocationService) { - baseSearchEngines.await() - } else { - fallbackEngines.await() - } + val engineList = baseSearchEngines.await() val bundledList = bundledSearchEngines.await().list val customList = customSearchEngines.await().list diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt index 46692b0a4..4fd69b08e 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarMenuController.kt @@ -87,7 +87,9 @@ class DefaultBrowserToolbarMenuController( is ToolbarMenu.Item.Back -> { if (item.viewHistory) { navController.navigate( - BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment() + BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment( + activeSessionId = customTabSession?.id + ) ) } else { sessionUseCases.goBack.invoke(currentSession) @@ -96,7 +98,9 @@ class DefaultBrowserToolbarMenuController( is ToolbarMenu.Item.Forward -> { if (item.viewHistory) { navController.navigate( - BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment() + BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment( + activeSessionId = customTabSession?.id + ) ) } else { sessionUseCases.goForward.invoke(currentSession) diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt index c067ed7a4..86ad63e72 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt @@ -27,7 +27,6 @@ import mozilla.components.browser.state.state.ExternalAppType import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior import mozilla.components.browser.toolbar.display.DisplayToolbar -import mozilla.components.support.ktx.android.util.dpToFloat import mozilla.components.support.utils.URLStringUtils import org.mozilla.fenix.R import org.mozilla.fenix.customtabs.CustomTabToolbarIntegration @@ -111,7 +110,7 @@ class BrowserToolbarView( view.apply { setScrollFlags() - elevation = TOOLBAR_ELEVATION.dpToFloat(resources.displayMetrics) + elevation = resources.getDimension(R.dimen.browser_fragment_toolbar_elevation) if (!isCustomTabSession) { display.setUrlBackground(getDrawable(R.drawable.search_url_background)) diff --git a/app/src/main/java/org/mozilla/fenix/components/topsheet/TopSheetBehavior.kt b/app/src/main/java/org/mozilla/fenix/components/topsheet/TopSheetBehavior.kt index 5d70b18f2..8de28ea2f 100644 --- a/app/src/main/java/org/mozilla/fenix/components/topsheet/TopSheetBehavior.kt +++ b/app/src/main/java/org/mozilla/fenix/components/topsheet/TopSheetBehavior.kt @@ -155,7 +155,7 @@ class TopSheetBehavior a.hasValue(R.styleable.BottomSheetBehavior_Layout_shapeAppearance) createMaterialShapeDrawable(context, attrs!!) createShapeValueAnimator() - peekHeight = context.resources.displayMetrics.heightPixels * PEEK_HEIGHT_RATIO + peekHeight = (context.resources.displayMetrics.heightPixels * PEEK_HEIGHT_RATIO).toInt() isHideable = a.getBoolean( R.styleable.BottomSheetBehavior_Layout_behavior_hideable, false @@ -821,7 +821,7 @@ class TopSheetBehavior private const val CORNER_ANIMATION_DURATION = 500 private val DEF_STYLE_RES = R.style.Widget_Design_BottomSheet_Modal - private const val PEEK_HEIGHT_RATIO = 3 / 4 + private const val PEEK_HEIGHT_RATIO = 0.75 private const val VELOCITY_UNITS = 1000 } } diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index 2387b9c58..f5accb0e4 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -8,6 +8,7 @@ import android.content.Context import android.content.Intent import android.os.SystemClock import android.view.View +import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.isVisible import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.component_browser_top_toolbar.* @@ -81,7 +82,8 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler components.core.store, customTabSessionId ) { uri -> - val intent = Intent.parseUri("${BuildConfig.DEEP_LINK_SCHEME}://open?url=$uri", 0) + val intent = + Intent.parseUri("${BuildConfig.DEEP_LINK_SCHEME}://open?url=$uri", 0) if (intent.action == Intent.ACTION_VIEW) { intent.addCategory(Intent.CATEGORY_BROWSABLE) intent.component = null @@ -103,7 +105,12 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler ) { toolbarVisible -> browserToolbarView.view.isVisible = toolbarVisible webAppToolbarShouldBeVisible = toolbarVisible - if (!toolbarVisible) { engineView.setDynamicToolbarMaxHeight(0) } + if (!toolbarVisible) { + engineView.setDynamicToolbarMaxHeight(0) + val browserEngine = + swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams + browserEngine.bottomMargin = 0 + } }, owner = this, view = toolbar diff --git a/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt b/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt index 8a8de7dce..8d85530ba 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/SpannableString.kt @@ -9,10 +9,13 @@ import android.text.Spannable import android.text.SpannableString import android.text.style.AbsoluteSizeSpan import android.text.style.ForegroundColorSpan +import androidx.annotation.AttrRes +import androidx.annotation.Dimension +import androidx.annotation.Dimension.DP import mozilla.components.support.ktx.android.content.getColorFromAttr import mozilla.components.support.ktx.android.util.dpToPx -fun SpannableString.setTextSize(context: Context, textSize: Int) = +fun SpannableString.setTextSize(context: Context, @Dimension(unit = DP) textSize: Int) = this.setSpan( AbsoluteSizeSpan(textSize.dpToPx(context.resources.displayMetrics)), 0, @@ -20,11 +23,9 @@ fun SpannableString.setTextSize(context: Context, textSize: Int) = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) -fun SpannableString.setTextColor(context: Context, colorResId: Int) = +fun SpannableString.setTextColor(context: Context, @AttrRes colorResId: Int) = this.setSpan( - ForegroundColorSpan( - context.getColorFromAttr(colorResId) - ), + ForegroundColorSpan(context.getColorFromAttr(colorResId)), 0, this.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE diff --git a/app/src/main/java/org/mozilla/fenix/ext/TextView.kt b/app/src/main/java/org/mozilla/fenix/ext/TextView.kt index 262f3ce7e..c3ae660ed 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/TextView.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/TextView.kt @@ -11,9 +11,9 @@ import androidx.core.text.toSpannable /** * Adds an underline effect to the text displayed in the TextView. */ -fun TextView.addUnderline() { +fun TextView.addUnderline(start: Int = 0, end: Int = this.text.length, flags: Int = 0) { val currentText = text text = currentText.toSpannable().apply { - setSpan(UnderlineSpan(), 0, currentText.length, 0) + setSpan(UnderlineSpan(), start, end, flags) } } diff --git a/app/src/main/java/org/mozilla/fenix/ext/View.kt b/app/src/main/java/org/mozilla/fenix/ext/View.kt index 683d9b65d..069f60ad2 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/View.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/View.kt @@ -9,11 +9,13 @@ import android.os.Build import android.view.TouchDelegate import android.view.View import androidx.annotation.Dimension +import androidx.annotation.Dimension.DP import androidx.annotation.VisibleForTesting import androidx.core.view.WindowInsetsCompat import mozilla.components.support.ktx.android.util.dpToPx +import org.mozilla.fenix.R -fun View.increaseTapArea(extraDps: Int) { +fun View.increaseTapArea(@Dimension(unit = DP) extraDps: Int) { val dips = extraDps.dpToPx(resources.displayMetrics) val parent = this.parent as View parent.post { @@ -73,7 +75,7 @@ fun View.isKeyboardVisible(): Boolean { val minimumKeyboardHeight = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 0 } else { - MINIMUM_KEYBOARD_HEIGHT.dpToPx(resources.displayMetrics) + resources.getDimensionPixelSize(R.dimen.minimum_keyboard_height) } return getKeyboardHeight() > minimumKeyboardHeight } diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index bc392ac14..932f38a98 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -20,6 +20,7 @@ import android.widget.Button import android.widget.LinearLayout import android.widget.PopupWindow import androidx.appcompat.app.AlertDialog +import androidx.appcompat.content.res.AppCompatResources import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM @@ -69,7 +70,7 @@ import mozilla.components.feature.top.sites.TopSitesConfig import mozilla.components.feature.top.sites.TopSitesFeature import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.support.base.feature.ViewBoundFeatureWrapper -import mozilla.components.support.ktx.android.util.dpToPx +import mozilla.components.support.ktx.android.content.res.resolveAttribute import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.HomeActivity @@ -313,13 +314,13 @@ class HomeFragment : Fragment() { applyTo(view.toolbarLayout) } - view.bottom_bar.background = resources.getDrawable( - ThemeManager.resolveAttribute(R.attr.bottomBarBackgroundTop, requireContext()), - null + view.bottom_bar.background = AppCompatResources.getDrawable( + view.context, + view.context.theme.resolveAttribute(R.attr.bottomBarBackgroundTop) ) view.homeAppBar.updateLayoutParams { - topMargin = HEADER_MARGIN.dpToPx(resources.displayMetrics) + topMargin = resources.getDimensionPixelSize(R.dimen.home_fragment_top_toolbar_header_margin) } } ToolbarPosition.BOTTOM -> { @@ -1002,8 +1003,5 @@ class HomeFragment : Fragment() { private const val ANIM_SNACKBAR_DELAY = 100L private const val CFR_WIDTH_DIVIDER = 1.7 private const val CFR_Y_OFFSET = -20 - - // Layout - private const val HEADER_MARGIN = 60 } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt index 6725adfe7..b2886d6e7 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlAdapter.kt @@ -46,7 +46,7 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) { data class TopSitePager(val topSites: List) : AdapterItem(TopSitePagerViewHolder.LAYOUT_ID) { override fun sameAs(other: AdapterItem): Boolean { val newTopSites = (other as? TopSitePager) ?: return false - return newTopSites.topSites == this.topSites + return newTopSites.topSites.size == this.topSites.size } override fun contentsSameAs(other: AdapterItem): Boolean { @@ -54,7 +54,7 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) { if (newTopSites.topSites.size != this.topSites.size) return false val newSitesSequence = newTopSites.topSites.asSequence() val oldTopSites = this.topSites.asSequence() - return newSitesSequence.zip(oldTopSites).all { (new, old) -> new.title == old.title } + return newSitesSequence.zip(oldTopSites).all { (new, old) -> new == old } } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt index 4cc6a0d85..dc0329227 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt @@ -18,7 +18,7 @@ class PrivateBrowsingDescriptionViewHolder( ) : RecyclerView.ViewHolder(view) { init { - val resources = view.context.resources + val resources = view.resources val appName = resources.getString(R.string.app_name) view.private_session_description.text = resources.getString( R.string.private_browsing_placeholder_description_2, appName diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt index 9fbb41e71..20a7665d5 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TabInCollectionViewHolder.kt @@ -11,14 +11,13 @@ import androidx.appcompat.content.res.AppCompatResources import kotlinx.android.synthetic.main.list_element.* import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.support.ktx.android.content.getColorFromAttr -import mozilla.components.support.ktx.android.util.dpToFloat import org.mozilla.fenix.R -import org.mozilla.fenix.utils.view.ViewHolder import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.increaseTapArea import org.mozilla.fenix.ext.loadIntoView import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor +import org.mozilla.fenix.utils.view.ViewHolder import mozilla.components.feature.tab.collections.Tab as ComponentTab class TabInCollectionViewHolder( @@ -42,7 +41,7 @@ class TabInCollectionViewHolder( 0, view.width, view.height, - FAV_ICON_BORDER_RADIUS_IN_DP.dpToFloat(view.context.resources.displayMetrics) + view.resources.getDimension(R.dimen.tab_tray_favicon_border_radius) ) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt index 06affb026..23e59a081 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/TopSitePagerViewHolder.kt @@ -44,7 +44,8 @@ class TopSitePagerViewHolder( } fun bind(topSites: List) { - topSitesPagerAdapter.updateData(topSites) + val chunkedTopSites = topSites.chunked(TOP_SITES_PER_PAGE) + topSitesPagerAdapter.submitList(chunkedTopSites) // Don't show any page indicator if there is only 1 page. val numPages = if (topSites.size > TOP_SITES_PER_PAGE) { diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt index b335e57de..e1d1d571f 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/topsites/TopSitesPagerAdapter.kt @@ -6,21 +6,16 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.topsites import android.view.LayoutInflater import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import kotlinx.android.synthetic.main.component_top_sites.view.* import mozilla.components.feature.top.sites.TopSite import org.mozilla.fenix.home.sessioncontrol.TopSiteInteractor import org.mozilla.fenix.home.sessioncontrol.viewholders.TopSiteViewHolder class TopSitesPagerAdapter( private val interactor: TopSiteInteractor -) : RecyclerView.Adapter() { - - private var topSites: List> = listOf() - - fun updateData(topSites: List) { - this.topSites = topSites.chunked(TOP_SITES_PER_PAGE) - notifyDataSetChanged() - } +) : ListAdapter, TopSiteViewHolder>(DiffCallback) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TopSiteViewHolder { val view = LayoutInflater.from(parent.context) @@ -29,12 +24,17 @@ class TopSitesPagerAdapter( } override fun onBindViewHolder(holder: TopSiteViewHolder, position: Int) { - holder.bind(this.topSites[position]) + val adapter = holder.itemView.top_sites_list.adapter as TopSitesAdapter + adapter.submitList(getItem(position)) } - override fun getItemCount(): Int = this.topSites.size + private object DiffCallback : DiffUtil.ItemCallback>() { + override fun areItemsTheSame(oldItem: List, newItem: List): Boolean { + return oldItem.size == newItem.size + } - companion object { - const val TOP_SITES_PER_PAGE = 8 + override fun areContentsTheSame(oldItem: List, newItem: List): Boolean { + return newItem.zip(oldItem).all { (new, old) -> new == old } + } } } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt index 2843bc443..f6f6a368d 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkAdapter.kt @@ -26,16 +26,30 @@ class BookmarkAdapter(private val emptyView: View, private val interactor: Bookm private var isFirstRun = true fun updateData(tree: BookmarkNode?, mode: BookmarkFragmentState.Mode) { + // Display folders above all other bookmarks. + val allNodes = tree?.children.orEmpty() + val folders: MutableList = mutableListOf() + val notFolders: MutableList = mutableListOf() + allNodes.forEach { + if (it.type == BookmarkNodeType.FOLDER) { + folders.add(it) + } else { + notFolders.add(it) + } + } + val newTree = folders + notFolders + val diffUtil = DiffUtil.calculateDiff( BookmarkDiffUtil( this.tree, - tree?.children.orEmpty(), + newTree, this.mode, mode ) ) - this.tree = tree?.children.orEmpty() + this.tree = newTree + isFirstRun = if (isFirstRun) false else { emptyView.isVisible = this.tree.isEmpty() false diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt index b31b99fa6..5edf2c26f 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/BookmarkTouchHelper.kt @@ -30,8 +30,12 @@ class BookmarkTouchCallback( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { + // Swiping separators is currently not supported. + if (viewHolder is BookmarkSeparatorViewHolder) { + return 0 + } val item = (viewHolder as BookmarkNodeViewHolder).item - return if (viewHolder is BookmarkSeparatorViewHolder || item?.inRoots() == true) { + return if (item?.inRoots() == true) { 0 } else { super.getSwipeDirs(recyclerView, viewHolder) @@ -72,7 +76,7 @@ class BookmarkTouchCallback( R.drawable.swipe_delete_background )!! val margin = - SwipeToDeleteCallback.MARGIN.dpToPx(recyclerView.context.resources.displayMetrics) + SwipeToDeleteCallback.MARGIN.dpToPx(recyclerView.resources.displayMetrics) val cellHeight = viewHolder.itemView.bottom - viewHolder.itemView.top val iconTop = viewHolder.itemView.top + (cellHeight - icon.intrinsicHeight) / 2 val iconBottom = iconTop + icon.intrinsicHeight diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/DesktopFolders.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/DesktopFolders.kt index f53ce4b91..a34dbf0da 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/DesktopFolders.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/DesktopFolders.kt @@ -11,36 +11,15 @@ import org.mozilla.fenix.R import org.mozilla.fenix.ext.components class DesktopFolders( - context: Context, + private val context: Context, private val showMobileRoot: Boolean ) { - private val bookmarksStorage = context.components.core.bookmarksStorage - private val bookmarksTitle = context.getString(R.string.library_bookmarks) - /** * Map of [BookmarkNode.title] to translated strings. */ - private val rootTitles: Map = if (showMobileRoot) { - mapOf( - "root" to bookmarksTitle, - "mobile" to bookmarksTitle, - "menu" to context.getString(R.string.library_desktop_bookmarks_menu), - "toolbar" to context.getString(R.string.library_desktop_bookmarks_toolbar), - "unfiled" to context.getString(R.string.library_desktop_bookmarks_unfiled) - ) - } else { - mapOf( - "root" to context.getString(R.string.library_desktop_bookmarks_root), - "menu" to context.getString(R.string.library_desktop_bookmarks_menu), - "toolbar" to context.getString(R.string.library_desktop_bookmarks_toolbar), - "unfiled" to context.getString(R.string.library_desktop_bookmarks_unfiled) - ) - } - - fun withRootTitle(node: BookmarkNode): BookmarkNode = - if (rootTitles.containsKey(node.title)) node.copy(title = rootTitles[node.title]) else node + private val rootTitles = rootTitles(context, showMobileRoot) suspend fun withOptionalDesktopFolders(node: BookmarkNode): BookmarkNode { return when (node.guid) { @@ -103,7 +82,7 @@ class DesktopFolders( return listOf( mobileRoot.copy( children = mobileChildren, - title = bookmarksTitle + title = context.getString(R.string.library_bookmarks) ) ) } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/Utils.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/Utils.kt new file mode 100644 index 000000000..354b80bac --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/Utils.kt @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.library.bookmarks + +import android.content.Context +import mozilla.components.concept.storage.BookmarkNode +import org.mozilla.fenix.R + +fun rootTitles(context: Context, withMobileRoot: Boolean): Map = if (withMobileRoot) { + mapOf( + "root" to context.getString(R.string.library_bookmarks), + "mobile" to context.getString(R.string.library_bookmarks), + "menu" to context.getString(R.string.library_desktop_bookmarks_menu), + "toolbar" to context.getString(R.string.library_desktop_bookmarks_toolbar), + "unfiled" to context.getString(R.string.library_desktop_bookmarks_unfiled) + ) +} else { + mapOf( + "root" to context.getString(R.string.library_desktop_bookmarks_root), + "menu" to context.getString(R.string.library_desktop_bookmarks_menu), + "toolbar" to context.getString(R.string.library_desktop_bookmarks_toolbar), + "unfiled" to context.getString(R.string.library_desktop_bookmarks_unfiled) + ) +} + +fun friendlyRootTitle( + context: Context, + node: BookmarkNode, + withMobileRoot: Boolean = true, + rootTitles: Map = rootTitles(context, withMobileRoot) +) = when { + !node.inRoots() -> node.title + rootTitles.containsKey(node.title) -> rootTitles[node.title] + else -> node.title +} diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/addfolder/AddBookmarkFolderFragment.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/addfolder/AddBookmarkFolderFragment.kt index aa7475c90..f5485cd52 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/addfolder/AddBookmarkFolderFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/addfolder/AddBookmarkFolderFragment.kt @@ -29,6 +29,7 @@ import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.library.bookmarks.BookmarksSharedViewModel +import org.mozilla.fenix.library.bookmarks.friendlyRootTitle /** * Menu to create a new bookmark folder. @@ -58,19 +59,19 @@ class AddBookmarkFolderFragment : Fragment(R.layout.fragment_edit_bookmark) { showToolbar(getString(R.string.bookmark_add_folder_fragment_label)) viewLifecycleOwner.lifecycleScope.launch(Main) { + val context = requireContext() sharedViewModel.selectedFolder = withContext(IO) { sharedViewModel.selectedFolder - ?: requireComponents.core.bookmarksStorage.getTree(BookmarkRoot.Mobile.id) + ?: requireComponents.core.bookmarksStorage.getBookmark(BookmarkRoot.Mobile.id) } - bookmarkParentFolderSelector.text = sharedViewModel.selectedFolder!!.title + bookmarkParentFolderSelector.text = friendlyRootTitle(context, sharedViewModel.selectedFolder!!) bookmarkParentFolderSelector.setOnClickListener { nav( R.id.bookmarkAddFolderFragment, AddBookmarkFolderFragmentDirections .actionBookmarkAddFolderFragmentToBookmarkSelectFolderFragment( - BookmarkRoot.Root.id, - true + allowCreatingNewFolder = true ) ) } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt index 27ecc25d4..c7de31c64 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/edit/EditBookmarkFragment.kt @@ -45,7 +45,7 @@ import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.setToolbarColors import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.library.bookmarks.BookmarksSharedViewModel -import org.mozilla.fenix.library.bookmarks.DesktopFolders +import org.mozilla.fenix.library.bookmarks.friendlyRootTitle /** * Menu to edit the name, URL, and location of a bookmark item. @@ -58,6 +58,7 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) { } private var bookmarkNode: BookmarkNode? = null private var bookmarkParent: BookmarkNode? = null + private var initialParentGuid: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -71,14 +72,23 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) { viewLifecycleOwner.lifecycleScope.launch(Main) { val context = requireContext() val bookmarkNodeBeforeReload = bookmarkNode + val bookmarksStorage = context.components.core.bookmarksStorage - withContext(IO) { - val bookmarksStorage = context.components.core.bookmarksStorage - bookmarkNode = bookmarksStorage.getTree(args.guidToEdit) - bookmarkParent = sharedViewModel.selectedFolder - ?: bookmarkNode?.parentGuid - ?.let { bookmarksStorage.getTree(it) } - ?.let { DesktopFolders(context, showMobileRoot = true).withRootTitle(it) } + bookmarkNode = withContext(IO) { + bookmarksStorage.getBookmark(args.guidToEdit) + } + + if (initialParentGuid == null) { + initialParentGuid = bookmarkNode?.parentGuid + } + + bookmarkParent = withContext(IO) { + // Use user-selected parent folder if it's set, or node's current parent otherwise. + if (sharedViewModel.selectedFolder != null) { + sharedViewModel.selectedFolder + } else { + bookmarkNode?.parentGuid?.let { bookmarksStorage.getBookmark(it) } + } } when (bookmarkNode?.type) { @@ -100,15 +110,23 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) { } bookmarkParent?.let { node -> - bookmarkParentFolderSelector.text = node.title - bookmarkParentFolderSelector.setOnClickListener { - sharedViewModel.selectedFolder = null - nav( - R.id.bookmarkEditFragment, - EditBookmarkFragmentDirections - .actionBookmarkEditFragmentToBookmarkSelectFolderFragment(null) - ) - } + bookmarkParentFolderSelector.text = friendlyRootTitle(context, node) + } + + bookmarkParentFolderSelector.setOnClickListener { + sharedViewModel.selectedFolder = null + nav( + R.id.bookmarkEditFragment, + EditBookmarkFragmentDirections + .actionBookmarkEditFragmentToBookmarkSelectFolderFragment( + allowCreatingNewFolder = false, + // Don't allow moving folders into themselves. + hideFolderGuid = when (bookmarkNode!!.type) { + BookmarkNodeType.FOLDER -> bookmarkNode!!.guid + else -> null + } + ) + ) } view.bookmarkNameEdit.apply { @@ -209,14 +227,18 @@ class EditBookmarkFragment : Fragment(R.layout.fragment_edit_bookmark) { if (title != bookmarkNode?.title || url != bookmarkNode?.url) { components.analytics.metrics.track(Event.EditedBookmark) } - if (sharedViewModel.selectedFolder != null) { + val parentGuid = sharedViewModel.selectedFolder?.guid ?: bookmarkNode!!.parentGuid + val parentChanged = initialParentGuid != parentGuid + // Only track the 'moved' event if new parent was selected. + if (parentChanged) { components.analytics.metrics.track(Event.MovedBookmark) } components.core.bookmarksStorage.updateNode( args.guidToEdit, BookmarkInfo( - sharedViewModel.selectedFolder?.guid ?: bookmarkNode!!.parentGuid, - bookmarkNode?.position, + parentGuid, + // Setting position to 'null' is treated as a 'move to the end' by the storage API. + if (parentChanged) null else bookmarkNode?.position, title, if (bookmarkNode?.type == BookmarkNodeType.ITEM) url else null ) diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt index d9064ef29..7eb347bd3 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderAdapter.kt @@ -15,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView import kotlinx.android.extensions.LayoutContainer import mozilla.components.concept.storage.BookmarkNode import mozilla.components.concept.storage.BookmarkNodeType -import mozilla.components.support.ktx.android.util.dpToPx import org.mozilla.fenix.R import org.mozilla.fenix.library.LibrarySiteItemView import org.mozilla.fenix.library.bookmarks.BookmarksSharedViewModel @@ -25,12 +24,19 @@ import org.mozilla.fenix.library.bookmarks.selectfolder.SelectBookmarkFolderAdap class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedViewModel) : ListAdapter(DiffCallback) { - fun updateData(tree: BookmarkNode?) { + fun updateData(tree: BookmarkNode?, hideFolderGuid: String?) { val updatedData = tree ?.convertToFolderDepthTree() ?.drop(1) .orEmpty() - submitList(updatedData) + + val filteredData = if (hideFolderGuid != null && updatedData.isNotEmpty()) { + updatedData.filter { it.node.guid != hideFolderGuid } + } else { + updatedData + } + + submitList(filteredData) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookmarkFolderViewHolder { @@ -85,8 +91,8 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi view.setOnClickListener { onSelect(folder.node) } - val pxToIndent = dpsToIndent.dpToPx(view.context.resources.displayMetrics) - val padding = pxToIndent * if (folder.depth > maxDepth) maxDepth else folder.depth + val pxToIndent = view.resources.getDimensionPixelSize(R.dimen.bookmark_select_folder_indent) + val padding = pxToIndent * minOf(MAX_DEPTH, folder.depth) view.updatePaddingRelative(start = padding) } @@ -117,8 +123,7 @@ class SelectBookmarkFolderAdapter(private val sharedViewModel: BookmarksSharedVi this == sharedViewModel.selectedFolder companion object { - private const val maxDepth = 10 - private const val dpsToIndent = 10 + private const val MAX_DEPTH = 10 } } diff --git a/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderFragment.kt b/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderFragment.kt index 86902eb41..e5022c4dc 100644 --- a/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/library/bookmarks/selectfolder/SelectBookmarkFolderFragment.kt @@ -50,6 +50,8 @@ class SelectBookmarkFolderFragment : Fragment() { super.onResume() showToolbar(getString(R.string.bookmark_select_folder_fragment_label)) + val args: SelectBookmarkFolderFragmentArgs by navArgs() + viewLifecycleOwner.lifecycleScope.launch(Main) { bookmarkNode = withContext(IO) { val context = requireContext() @@ -59,13 +61,13 @@ class SelectBookmarkFolderFragment : Fragment() { } val adapter = SelectBookmarkFolderAdapter(sharedViewModel) recylerViewBookmarkFolders.adapter = adapter - adapter.updateData(bookmarkNode) + adapter.updateData(bookmarkNode, args.hideFolderGuid) } } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { val args: SelectBookmarkFolderFragmentArgs by navArgs() - if (!args.visitedAddBookmark) { + if (!args.allowCreatingNewFolder) { inflater.inflate(R.menu.bookmarks_select_folder, menu) } } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt b/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt index c309a4fb9..3bfb5076a 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/HistoryView.kt @@ -12,8 +12,10 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.SimpleItemAnimator import kotlinx.android.synthetic.main.component_history.* import kotlinx.android.synthetic.main.component_history.view.* +import kotlinx.android.synthetic.main.recently_closed_nav_item.* import mozilla.components.support.base.feature.UserInteractionHandler import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components import org.mozilla.fenix.library.LibraryPageView import org.mozilla.fenix.library.SelectionInteractor import org.mozilla.fenix.theme.ThemeManager @@ -164,6 +166,19 @@ class HistoryView( fun updateEmptyState(userHasHistory: Boolean) { history_list.isVisible = userHasHistory history_empty_view.isVisible = !userHasHistory + recently_closed_nav_empty.apply { + setOnClickListener { + interactor.onRecentlyClosedClicked() + } + val numRecentTabs = view.context.components.core.store.state.closedTabs.size + recently_closed_tabs_description.text = String.format( + view.context.getString( + if (numRecentTabs == 1) + R.string.recently_closed_tab else R.string.recently_closed_tabs + ), numRecentTabs + ) + isVisible = !userHasHistory + } if (!userHasHistory) { history_empty_view.announceForAccessibility(context.getString(R.string.history_empty_message)) } diff --git a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt index 03016716e..82a6e0eaa 100644 --- a/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/library/history/viewholders/HistoryListItemViewHolder.kt @@ -5,10 +5,12 @@ package org.mozilla.fenix.library.history.viewholders import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.history_list_item.view.* import kotlinx.android.synthetic.main.library_site_item.view.* +import kotlinx.android.synthetic.main.recently_closed_nav_item.view.* import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.hideAndDisable @@ -41,7 +43,7 @@ class HistoryListItemViewHolder( } } - itemView.recently_closed.setOnClickListener { + itemView.findViewById(R.id.recently_closed_nav).setOnClickListener { historyInteractor.onRecentlyClosedClicked() } } @@ -96,10 +98,11 @@ class HistoryListItemViewHolder( showTopContent: Boolean, isNormalMode: Boolean ) { + itemView.delete_button.isVisible = showTopContent + itemView.findViewById(R.id.recently_closed_nav).isVisible = showTopContent + if (showTopContent) { itemView.delete_button.run { - visibility = View.VISIBLE - if (isNormalMode) { isEnabled = true alpha = 1f @@ -115,10 +118,15 @@ class HistoryListItemViewHolder( R.string.recently_closed_tab else R.string.recently_closed_tabs ), numRecentTabs ) - itemView.recently_closed.isVisible = true - } else { - itemView.recently_closed.visibility = View.GONE - itemView.delete_button.visibility = View.GONE + itemView.findViewById(R.id.recently_closed_nav).run { + if (isNormalMode) { + isEnabled = true + alpha = 1f + } else { + isEnabled = false + alpha = DELETE_BUTTON_DISABLED_ALPHA + } + } } } diff --git a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt index b38cbdc8e..74bad1b8c 100644 --- a/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt +++ b/app/src/main/java/org/mozilla/fenix/search/awesomebar/AwesomeBarView.kt @@ -19,6 +19,7 @@ import mozilla.components.feature.awesomebar.provider.SearchActionProvider import mozilla.components.feature.awesomebar.provider.SearchSuggestionProvider import mozilla.components.feature.awesomebar.provider.SessionSuggestionProvider import mozilla.components.feature.search.SearchUseCases +import mozilla.components.feature.syncedtabs.DeviceIndicators import mozilla.components.feature.session.SessionUseCases import mozilla.components.feature.syncedtabs.SyncedTabsStorageSuggestionProvider import mozilla.components.feature.tabs.TabsUseCases @@ -130,7 +131,12 @@ class AwesomeBarView( SyncedTabsStorageSuggestionProvider( components.backgroundServices.syncedTabsStorage, components.useCases.tabsUseCases.addTab, - components.core.icons + components.core.icons, + DeviceIndicators( + getDrawable(activity, R.drawable.ic_search_results_device_desktop), + getDrawable(activity, R.drawable.ic_search_results_device_mobile), + getDrawable(activity, R.drawable.ic_search_results_device_tablet) + ) ) val searchBitmap = getDrawable(activity, R.drawable.ic_search)!!.apply { diff --git a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt index 5080db6d3..ef15773d5 100644 --- a/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/searchdialog/SearchDialogFragment.kt @@ -234,6 +234,8 @@ class SearchDialogFragment : AppCompatDialogFragment(), UserInteractionHandler { } fill_link_from_clipboard.setOnClickListener { + view.hideKeyboard() + toolbarView.view.clearFocus() (activity as HomeActivity) .openToBrowserAndLoad( searchTermOrURL = requireContext().components.clipboardHandler.url ?: "", diff --git a/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt index 732f89671..a9f56e8b6 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/account/AccountSettingsFragment.kt @@ -35,7 +35,6 @@ import mozilla.components.service.fxa.sync.SyncReason import mozilla.components.service.fxa.sync.SyncStatusObserver import mozilla.components.service.fxa.sync.getLastSynced import mozilla.components.support.ktx.android.content.getColorFromAttr -import mozilla.components.support.ktx.android.util.dpToPx import org.mozilla.fenix.R import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.StoreProvider @@ -162,7 +161,7 @@ class AccountSettingsFragment : PreferenceFragmentCompat() { } setOnBindEditTextListener { editText -> editText.filters = arrayOf(InputFilter.LengthFilter(DEVICE_NAME_MAX_LENGTH)) - editText.minHeight = DEVICE_NAME_EDIT_TEXT_MIN_HEIGHT_DP.dpToPx(resources.displayMetrics) + editText.minHeight = resources.getDimensionPixelSize(R.dimen.account_settings_device_name_min_height) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt index 7f42aaf1d..67ca920ed 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/account/TurnOnSyncFragment.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.settings.account import android.Manifest import android.os.Bundle +import android.text.Spanned import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -25,6 +26,7 @@ import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.R import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.addUnderline import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar @@ -64,6 +66,10 @@ class TurnOnSyncFragment : Fragment(), AccountObserver { requireComponents.analytics.metrics.track(Event.SyncAuthScanPairing) } + private val createAccountClickListener = View.OnClickListener { + navigateToPairWithEmail() + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requireComponents.analytics.metrics.track(Event.SyncAuthOpened) @@ -85,7 +91,8 @@ class TurnOnSyncFragment : Fragment(), AccountObserver { override fun onResume() { super.onResume() if (pairWithEmailStarted || - requireComponents.backgroundServices.accountManager.authenticatedAccount() != null) { + requireComponents.backgroundServices.accountManager.authenticatedAccount() != null + ) { findNavController().popBackStack() return @@ -118,6 +125,20 @@ class TurnOnSyncFragment : Fragment(), AccountObserver { DefaultSyncController(activity = activity as HomeActivity) ) + val createAccountActionText = getString(R.string.sign_in_create_account_link) + val fullText = getString(R.string.sign_in_create_account_text, createAccountActionText) + val spanStart = fullText.indexOf(createAccountActionText, 0, false) + val spanEnd = spanStart + createAccountActionText.length + + view.createAccount.apply { + text = fullText + addUnderline( + spanStart, + spanEnd, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + setOnClickListener(createAccountClickListener) + } return view } diff --git a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt index 4549f570e..a48d6d264 100644 --- a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsViewHolder.kt @@ -17,7 +17,6 @@ import kotlinx.android.synthetic.main.view_synced_tabs_group.view.* import kotlinx.android.synthetic.main.view_synced_tabs_title.view.* import mozilla.components.concept.sync.DeviceType import mozilla.components.feature.syncedtabs.view.SyncedTabsView -import mozilla.components.support.ktx.android.util.dpToPx import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.R import org.mozilla.fenix.sync.SyncedTabsAdapter.AdapterItem @@ -135,13 +134,8 @@ sealed class SyncedTabsViewHolder(itemView: View) : RecyclerView.ViewHolder(item LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT ) - val displayMetrics = itemView.context.resources.displayMetrics - val margin = ERROR_MARGIN.dpToPx(displayMetrics) + val margin = itemView.resources.getDimensionPixelSize(R.dimen.synced_tabs_error_margin) lp.setMargins(margin, margin, margin, 0) itemView.layoutParams = lp } - - companion object { - private const val ERROR_MARGIN = 20 - } } diff --git a/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryController.kt b/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryController.kt index 2d6ecc784..91710a0bb 100644 --- a/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryController.kt @@ -5,8 +5,8 @@ package org.mozilla.fenix.tabhistory import androidx.navigation.NavController +import mozilla.components.browser.session.SessionManager import mozilla.components.feature.session.SessionUseCases -import org.mozilla.fenix.R interface TabHistoryController { fun handleGoToHistoryItem(item: TabHistoryItem) @@ -14,11 +14,16 @@ interface TabHistoryController { class DefaultTabHistoryController( private val navController: NavController, - private val goToHistoryIndexUseCase: SessionUseCases.GoToHistoryIndexUseCase + private val goToHistoryIndexUseCase: SessionUseCases.GoToHistoryIndexUseCase, + private val customTabId: String? = null, + private val sessionManager: SessionManager ) : TabHistoryController { override fun handleGoToHistoryItem(item: TabHistoryItem) { - navController.popBackStack(R.id.browserFragment, false) - goToHistoryIndexUseCase.invoke(item.index) + navController.navigateUp() + goToHistoryIndexUseCase.invoke( + item.index, + session = customTabId?.let { sessionManager.findSessionById(customTabId) } + ?: sessionManager.selectedSession) } } diff --git a/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryDialogFragment.kt index 5ae534b73..b723d97be 100644 --- a/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryDialogFragment.kt @@ -12,10 +12,16 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import kotlinx.android.synthetic.main.activity_home.* import kotlinx.android.synthetic.main.fragment_tab_history_dialog.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import mozilla.components.lib.state.ext.consumeFrom +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.mapNotNull +import mozilla.components.browser.state.selector.findCustomTabOrSelectedTab +import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import org.mozilla.fenix.R +import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.requireComponents class TabHistoryDialogFragment : BottomSheetDialogFragment() { @@ -25,6 +31,8 @@ class TabHistoryDialogFragment : BottomSheetDialogFragment() { setStyle(STYLE_NO_TITLE, R.style.BottomSheet) } + var customTabSessionId: String? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -35,9 +43,13 @@ class TabHistoryDialogFragment : BottomSheetDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + customTabSessionId = requireArguments().getString(EXTRA_SESSION_ID) + val controller = DefaultTabHistoryController( navController = findNavController(), - goToHistoryIndexUseCase = requireComponents.useCases.sessionUseCases.goToHistoryIndex + goToHistoryIndexUseCase = requireComponents.useCases.sessionUseCases.goToHistoryIndex, + customTabId = customTabSessionId, + sessionManager = container.requireContext().components.core.sessionManager ) val tabHistoryView = TabHistoryView( container = tabHistoryLayout, @@ -45,12 +57,20 @@ class TabHistoryDialogFragment : BottomSheetDialogFragment() { interactor = TabHistoryInteractor(controller) ) - consumeFrom(requireComponents.core.store) { - tabHistoryView.updateState(it) + requireComponents.core.store.flowScoped(viewLifecycleOwner) { flow -> + flow.mapNotNull { state -> state.findCustomTabOrSelectedTab(customTabSessionId)?.content?.history } + .ifChanged() + .collect { historyState -> + tabHistoryView.updateState(historyState) + } } } private fun expand() { (dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED } + + companion object { + const val EXTRA_SESSION_ID = "activeSessionId" + } } diff --git a/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryView.kt b/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryView.kt index f74da7e4d..789db3d60 100644 --- a/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabhistory/TabHistoryView.kt @@ -11,8 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.component_tabhistory.* -import mozilla.components.browser.state.selector.selectedTab -import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.state.content.HistoryState import org.mozilla.fenix.R interface TabHistoryViewInteractor { @@ -66,18 +65,16 @@ class TabHistoryView( tabHistoryRecyclerView.layoutManager = layoutManager } - fun updateState(state: BrowserState) { - state.selectedTab?.content?.history?.let { historyState -> - currentIndex = historyState.currentIndex - val items = historyState.items.mapIndexed { index, historyItem -> - TabHistoryItem( - title = historyItem.title, - url = historyItem.uri, - index = index, - isSelected = index == historyState.currentIndex - ) - } - adapter.submitList(items) + fun updateState(historyState: HistoryState) { + currentIndex = historyState.currentIndex + val items = historyState.items.mapIndexed { index, historyItem -> + TabHistoryItem( + title = historyItem.title, + url = historyItem.uri, + index = index, + isSelected = index == currentIndex + ) } + adapter.submitList(items) } } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt index f80e48406..41fba7213 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/CollectionsAdapter.kt @@ -8,10 +8,10 @@ import android.view.LayoutInflater import android.view.ViewGroup import android.widget.CheckedTextView import androidx.annotation.VisibleForTesting -import androidx.core.content.ContextCompat +import androidx.appcompat.content.res.AppCompatResources import androidx.core.view.updatePaddingRelative import androidx.recyclerview.widget.RecyclerView -import mozilla.components.support.ktx.android.util.dpToPx +import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds import org.mozilla.fenix.R internal class CollectionsAdapter( @@ -36,15 +36,17 @@ internal class CollectionsAdapter( override fun onBindViewHolder(holder: CollectionItemViewHolder, position: Int) { if (position == 0) { - val displayMetrics = holder.textView.context.resources.displayMetrics - holder.textView.updatePaddingRelative(start = NEW_COLLECTION_PADDING_START.dpToPx(displayMetrics)) + val resources = holder.textView.resources + holder.textView.updatePaddingRelative( + start = resources.getDimensionPixelSize(R.dimen.tab_tray_new_collection_padding_start) + ) holder.textView.compoundDrawablePadding = - NEW_COLLECTION_DRAWABLE_PADDING.dpToPx(displayMetrics) - holder.textView.setCompoundDrawablesWithIntrinsicBounds( - ContextCompat.getDrawable( + resources.getDimensionPixelSize(R.dimen.tab_tray_new_collection_drawable_padding) + holder.textView.putCompoundDrawablesRelativeWithIntrinsicBounds( + start = AppCompatResources.getDrawable( holder.textView.context, R.drawable.ic_new - ), null, null, null + ) ) } else { holder.textView.isChecked = checkedPosition == position @@ -65,9 +67,4 @@ internal class CollectionsAdapter( override fun getItemCount() = collections.size fun getSelectedCollection() = checkedPosition - 1 - - companion object { - private const val NEW_COLLECTION_PADDING_START = 24 - private const val NEW_COLLECTION_DRAWABLE_PADDING = 28 - } } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt index c0cd9f1f5..2d58a2bb8 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt @@ -54,7 +54,6 @@ import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.feature.syncedtabs.SyncedTabsFeature import mozilla.components.support.base.feature.ViewBoundFeatureWrapper -import mozilla.components.support.ktx.android.util.dpToPx import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.toolbar.TabCounter.Companion.INFINITE_CHAR_PADDING_BOTTOM @@ -568,20 +567,29 @@ class TabTrayView( ) ) - val displayMetrics = view.context.resources.displayMetrics - view.handle.updateLayoutParams { - height = - if (multiselect) MULTISELECT_HANDLE_HEIGHT.dpToPx(displayMetrics) else NORMAL_HANDLE_HEIGHT.dpToPx( - displayMetrics - ) + height = view.resources.getDimensionPixelSize( + if (multiselect) { + R.dimen.tab_tray_multiselect_handle_height + } else { + R.dimen.tab_tray_normal_handle_height + } + ) if (useTopTabsTray) { - bottomMargin = if (multiselect) 0.dpToPx(displayMetrics) else NORMAL_BOTTOM_MARGIN.dpToPx( - displayMetrics + bottomMargin = view.resources.getDimensionPixelSize( + if (multiselect) { + R.dimen.tab_tray_multiselect_handle_bottom_margin + } else { + R.dimen.tab_tray_normal_handle_bottom_margin + } ) } else { - topMargin = if (multiselect) 0.dpToPx(displayMetrics) else NORMAL_TOP_MARGIN.dpToPx( - displayMetrics + topMargin = view.resources.getDimensionPixelSize( + if (multiselect) { + R.dimen.tab_tray_multiselect_handle_top_margin + } else { + R.dimen.tab_tray_normal_handle_top_margin + } ) } } @@ -672,7 +680,7 @@ class TabTrayView( val topOffset = if (landscape) { 0 } else { - view.context.resources.getDimension(R.dimen.tab_tray_top_offset).toInt() + view.context.resources.getDimensionPixelSize(R.dimen.tab_tray_top_offset) } if (!useTopTabsTray) { @@ -689,11 +697,11 @@ class TabTrayView( if (private) { fabView.new_tab_button.extend() fabView.new_tab_button.contentDescription = - view.context.resources.getString(R.string.add_private_tab) + view.context.getString(R.string.add_private_tab) } else { fabView.new_tab_button.shrink() fabView.new_tab_button.contentDescription = - view.context.resources.getString(R.string.add_tab) + view.context.getString(R.string.add_tab) } } @@ -734,10 +742,6 @@ class TabTrayView( private const val EXPAND_AT_SIZE = 3 private const val SLIDE_OFFSET = 0 private const val SELECTION_DELAY = 500 - private const val MULTISELECT_HANDLE_HEIGHT = 11 - private const val NORMAL_HANDLE_HEIGHT = 3 - private const val NORMAL_TOP_MARGIN = 8 - private const val NORMAL_BOTTOM_MARGIN = 8 private const val NORMAL_HANDLE_PERCENT_WIDTH = 0.1F private const val COLUMN_WIDTH_DP = 190 } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt index 03fe38d43..b9189c555 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt @@ -80,7 +80,7 @@ class TouchCallback( val iconLeft: Int val iconRight: Int val margin = - SwipeToDeleteCallback.MARGIN.dpToPx(recyclerView.context.resources.displayMetrics) + SwipeToDeleteCallback.MARGIN.dpToPx(recyclerView.resources.displayMetrics) val iconWidth = icon.intrinsicWidth val iconHeight = icon.intrinsicHeight val cellHeight = itemView.bottom - itemView.top diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionBlockingFragment.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionBlockingFragment.kt index 0bacaebfd..bbc8832f4 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionBlockingFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionBlockingFragment.kt @@ -10,6 +10,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_tracking_protection_blocking.* +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.showToolbar @@ -22,6 +23,7 @@ class TrackingProtectionBlockingFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + category_redirect_trackers.isVisible = FeatureFlags.etpCookiePurging when (args.protectionMode) { TrackingProtectionMode.STANDARD -> { diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionCategoryItem.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionCategoryItem.kt index 15d4a52bb..9a40e612a 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionCategoryItem.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionCategoryItem.kt @@ -7,7 +7,6 @@ package org.mozilla.fenix.trackingprotection import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater -import androidx.appcompat.content.res.AppCompatResources import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.withStyledAttributes import kotlinx.android.synthetic.main.tracking_protection_category.view.* @@ -27,11 +26,6 @@ class TrackingProtectionCategoryItem @JvmOverloads constructor( defStyleAttr, 0 ) { - val id = getResourceId( - R.styleable.TrackingProtectionCategory_categoryItemIcon, - R.drawable.ic_cryptominers - ) - trackingProtectionCategoryIcon?.background = AppCompatResources.getDrawable(context, id) trackingProtectionCategoryTitle?.text = resources.getString( getResourceId( R.styleable.TrackingProtectionCategory_categoryItemTitle, diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt index 79e42c13a..624a76526 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt @@ -149,9 +149,14 @@ class TrackingProtectionPanelView( } } + /** + * Checks whether the permission was allowed or blocked when they were last used based on + * visibility, where "..._loaded" titles correspond to "Allowed" permissions and the other + * corresponds to "Blocked" permissions for each category. + */ private fun getLastUsedCategoryView(categoryTitle: String) = when (categoryTitle) { CROSS_SITE_TRACKING_COOKIES.name -> { - cross_site_tracking + if (cross_site_tracking.isGone) cross_site_tracking_loaded else cross_site_tracking } SOCIAL_MEDIA_TRACKERS.name -> { if (social_media_trackers.isGone) social_media_trackers_loaded else social_media_trackers @@ -171,11 +176,14 @@ class TrackingProtectionPanelView( private fun updateCategoryVisibility() { cross_site_tracking.isGone = bucketedTrackers.get(CROSS_SITE_TRACKING_COOKIES, true).isEmpty() - social_media_trackers.isGone = bucketedTrackers.get(SOCIAL_MEDIA_TRACKERS, true).isEmpty() + social_media_trackers.isGone = + bucketedTrackers.get(SOCIAL_MEDIA_TRACKERS, true).isEmpty() fingerprinters.isGone = bucketedTrackers.get(FINGERPRINTERS, true).isEmpty() tracking_content.isGone = bucketedTrackers.get(TRACKING_CONTENT, true).isEmpty() cryptominers.isGone = bucketedTrackers.get(CRYPTOMINERS, true).isEmpty() + cross_site_tracking_loaded.isGone = + bucketedTrackers.get(CROSS_SITE_TRACKING_COOKIES, false).isEmpty() social_media_trackers_loaded.isGone = bucketedTrackers.get(SOCIAL_MEDIA_TRACKERS, false).isEmpty() fingerprinters_loaded.isGone = bucketedTrackers.get(FINGERPRINTERS, false).isEmpty() @@ -189,6 +197,7 @@ class TrackingProtectionPanelView( cross_site_tracking.setOnClickListener(this) tracking_content.setOnClickListener(this) cryptominers.setOnClickListener(this) + cross_site_tracking_loaded.setOnClickListener(this) social_media_trackers_loaded.setOnClickListener(this) fingerprinters_loaded.setOnClickListener(this) tracking_content_loaded.setOnClickListener(this) @@ -251,7 +260,7 @@ class TrackingProtectionPanelView( private fun getCategory(v: View) = when (v.id) { R.id.social_media_trackers, R.id.social_media_trackers_loaded -> SOCIAL_MEDIA_TRACKERS R.id.fingerprinters, R.id.fingerprinters_loaded -> FINGERPRINTERS - R.id.cross_site_tracking -> CROSS_SITE_TRACKING_COOKIES + R.id.cross_site_tracking, R.id.cross_site_tracking_loaded -> CROSS_SITE_TRACKING_COOKIES R.id.tracking_content, R.id.tracking_content_loaded -> TRACKING_CONTENT R.id.cryptominers, R.id.cryptominers_loaded -> CRYPTOMINERS else -> null @@ -262,6 +271,7 @@ class TrackingProtectionPanelView( */ private fun isLoaded(v: View) = when (v.id) { R.id.social_media_trackers_loaded, + R.id.cross_site_tracking_loaded, R.id.fingerprinters_loaded, R.id.tracking_content_loaded, R.id.cryptominers_loaded -> true diff --git a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt index 543e2b257..26dbf59ee 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt @@ -68,8 +68,7 @@ fun CoroutineScope.allowUndo( } val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar - val toolbarHeight = view.context.resources - .getDimensionPixelSize(R.dimen.browser_toolbar_height) + val toolbarHeight = view.resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) snackbar.view.updatePadding( bottom = if ( diff --git a/app/src/main/res/layout/component_history.xml b/app/src/main/res/layout/component_history.xml index 3452a6c5f..180f69a8b 100644 --- a/app/src/main/res/layout/component_history.xml +++ b/app/src/main/res/layout/component_history.xml @@ -2,48 +2,58 @@ - + + + android:id="@+id/progress_bar" + style="@style/Widget.AppCompat.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="8dp" + android:indeterminate="true" + android:translationY="-3dp" + android:visibility="gone" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + + android:id="@+id/history_empty_view" + android:layout_width="0dp" + android:layout_height="0dp" + android:gravity="center" + android:text="@string/history_empty_message" + android:textColor="?secondaryText" + android:textSize="16sp" + android:visibility="gone" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/recently_closed_nav_empty" /> + android:layout_height="0dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/recently_closed_nav_empty"> + android:visibility="gone" + tools:listitem="@layout/history_list_item" /> diff --git a/app/src/main/res/layout/component_tracking_protection_panel.xml b/app/src/main/res/layout/component_tracking_protection_panel.xml index 27383a466..e363deb9f 100644 --- a/app/src/main/res/layout/component_tracking_protection_panel.xml +++ b/app/src/main/res/layout/component_tracking_protection_panel.xml @@ -61,7 +61,6 @@ android:layout_height="@dimen/tracking_protection_item_height" android:text="@string/etp_cookies_title" android:visibility="gone" - app:drawableStartCompat="@drawable/ic_cookies" app:layout_constraintTop_toBottomOf="@id/blocking_header" /> + + + app:layout_constraintTop_toBottomOf="@id/cross_site_tracking_loaded" /> + + diff --git a/app/src/main/res/layout/fragment_turn_on_sync.xml b/app/src/main/res/layout/fragment_turn_on_sync.xml index 83b8714fd..c933921d4 100644 --- a/app/src/main/res/layout/fragment_turn_on_sync.xml +++ b/app/src/main/res/layout/fragment_turn_on_sync.xml @@ -71,7 +71,19 @@ android:layout_margin="0dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/signInScanButton" /> + app:layout_constraintTop_toBottomOf="@id/signInScanButton" + app:layout_constraintBottom_toTopOf="@id/createAccount"/> + + diff --git a/app/src/main/res/layout/history_list_item.xml b/app/src/main/res/layout/history_list_item.xml index 147d44820..98d8cace0 100644 --- a/app/src/main/res/layout/history_list_item.xml +++ b/app/src/main/res/layout/history_list_item.xml @@ -3,7 +3,6 @@ - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/share_close.xml b/app/src/main/res/layout/share_close.xml index e374a3628..1221af606 100644 --- a/app/src/main/res/layout/share_close.xml +++ b/app/src/main/res/layout/share_close.xml @@ -22,7 +22,6 @@ android:contentDescription="@string/content_description_close_button" android:padding="12dp" app:iconTint="@color/neutral_text" - app:layout_constraintEnd_toStartOf="@id/title" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@drawable/mozac_ic_close" /> diff --git a/app/src/main/res/layout/tracking_protection_category.xml b/app/src/main/res/layout/tracking_protection_category.xml index 324f4d028..a7ca6623f 100644 --- a/app/src/main/res/layout/tracking_protection_category.xml +++ b/app/src/main/res/layout/tracking_protection_category.xml @@ -9,25 +9,11 @@ android:layout_height="wrap_content" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> - - + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/trackingProtectionCategoryTitle" /> diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index a91a57421..1d3033460 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -298,10 +298,6 @@ android:name="org.mozilla.fenix.library.bookmarks.selectfolder.SelectBookmarkFolderFragment" android:label="@string/bookmark_select_folder_fragment_label" tools:layout="@layout/fragment_select_bookmark_folder"> - @@ -309,9 +305,14 @@ android:id="@+id/action_bookmarkSelectFolderFragment_to_bookmarkAddFolderFragment" app:destination="@id/bookmarkAddFolderFragment" /> + + tools:layout="@layout/fragment_tab_history_dialog"> + + Ubrir vinclos en una pestanya privada Permitir capturas de pantalla en navegación privada + + Si ye permitiu, las pestanyas privadas seran visibles quan multiples aplicacions sigan ubiertas Adhibir acceso directo a navegación privada @@ -282,6 +284,8 @@ Tema Inicio + + Cenyos Personalizar @@ -316,8 +320,12 @@ Buscar historial de navegación Buscar marcapachinas + + Mirar en pestanyas sincronizadas Achustes d’a cuenta + + Autocompletar URLs Ubrir vinclos en aplicacions @@ -452,6 +460,12 @@ Seguir tema d’o dispositivo + + + Estirar pa refrescar + + Desplazar pa amagar la barra de ferramientas + Sesions @@ -1031,10 +1045,6 @@ Encomienza la sincronización d\'as adrezas d\'interés, las contrasenyas y muito mas con a tuya cuenta d\'o Firefox. Saber-ne mas - - Ya has iniciau sesión como %s en unatro navegador Firefox d’este telefono. Quiers iniciar sesión con esta cuenta? Sí, iniciar sesión @@ -1272,8 +1282,6 @@ Continar a lo puesto web Nombre d’acceso directo - - Puetz anyadir facilment este puesto web a la tuya pachina d’inicio pa tener-ie acceso instantanio y navegar rapidament como si estase una aplicación. Inicios de sesión y claus @@ -1496,7 +1504,7 @@ Ya existe un inicio de sesión con ixe nombre d’usuario - + Connectar unatro dispositivo. Per favor, torna a autentificar-te. @@ -1515,8 +1523,6 @@ S’ha arribau a lo limite de puestos principals. - - Pa anyadir un nuevo puesto principal, has de borrar-ne belatro. Mantiene pretau lo puesto y selecciona borrar. Vale, entendiu @@ -1525,7 +1531,7 @@ Eliminar - Quita-le lo millor provecho a %s. @@ -1533,4 +1539,9 @@ Colecciona las cosetas que t’importan Agrupa busquedas, puestos y pestanyas semellants pa un acceso rapido mas tarde. + + Ya has iniciau sesión como %s en unatro navegador Firefox d’este telefono. Quiers iniciar sesión con esta cuenta? + + Puetz anyadir facilment este puesto web a la tuya pachina d’inicio pa tener-ie acceso instantanio y navegar rapidament como si estase una aplicación. + diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 3d215ba78..0a03c69e6 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -318,6 +318,8 @@ Buscar nel historial de restolar Buscar nos marcadores + + Buscar llingüetes sincronizaes Axustes de la cuenta @@ -600,7 +602,7 @@ - Perdona pero %1$s nun pue cargar esa páxina. + Perdona mas %1$s nun pue cargar esa páxina. Pues tentar de restaurar o zarrar esta llingüeta embaxo. @@ -1012,7 +1014,7 @@ Estricta - Bloquia más rastrexadores, anuncios y ventanos emerxentes. Los páxines van cargar más rápido pero quiciabes dalgunes funcionalidaes nun funcionen. + Bloquia más rastrexadores, anuncios y ventanos emerxentes. Los páxines van cargar más rápido mas dalgunes funcionalidaes quiciabes nun funcionen. @@ -1078,9 +1080,9 @@ Usar una direición de corréu - Firefox va dexar de sincronizase cola to cuenta pero nun va desaniciar nengún datu d\'esti preséu. + Firefox va dexar de sincronizase cola to cuenta mas nun va desaniciar nengún datu d\'esti preséu. - %s va dexar de sincronizase cola to cuenta pero nun va desaniciar nengún datu d\'esti preséu. + %s va dexar de sincronizase cola to cuenta mas nun va desaniciar nengún datu d\'esti preséu. Desconeutar @@ -1110,7 +1112,7 @@ Estricta - Bloquia más rastrexadores, anuncios y ventanos emerxentes. Les páxines van cargar más rápido pero quiciabes dalgunes funcionalidaes nun funcionen. + Bloquia más rastrexadores, anuncios y ventanos emerxentes. Les páxines van cargar más rápido mas dalgunes funcionalidaes quiciabes nun funcionen. Lo que la proteición estricta escontra\'l rastrexu bloquia @@ -1472,7 +1474,7 @@ Amosar los sitios más visitaos - Aprovecha %s al máximu. diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml index c958e618f..c471c14d6 100644 --- a/app/src/main/res/values-be/strings.xml +++ b/app/src/main/res/values-be/strings.xml @@ -321,6 +321,8 @@ Налады ўліковага запісу Адкрываць спасылкі ў праграмах + + Вонкавы менеджар сцягванняў Дадаткі @@ -351,6 +353,8 @@ Сінхранізацыя… Памылка сінхранізацыі. Апошняе паспяховае сінхранаванне: %s + + Памылка сінхранізацыі. Апошняе сінхранаванне: ніколі Апошняя сінхранізацыя: %s diff --git a/app/src/main/res/values-co/strings.xml b/app/src/main/res/values-co/strings.xml index 978cf2589..b87301ee5 100644 --- a/app/src/main/res/values-co/strings.xml +++ b/app/src/main/res/values-co/strings.xml @@ -149,6 +149,8 @@ Installà Unghjette sincrunizate + + Risincrunizà Circà in a pagina @@ -271,6 +273,8 @@ Apre i liami in un’unghjetta privata Permette e catture di screnu durante a navigazione privata + + S’ella hè permessa, l’unghjette private seranu ancu videvule quandu parechje appiecazioni sò aperte Aghjunghje un accurtatoghjiu per a navigazione privata @@ -291,6 +295,8 @@ Tema Accolta + + Mosse Persunalizà @@ -326,9 +332,13 @@ Circà in l’indette + + Ricercà in l’unghjette sincrunizate Preferenze di u contu + + Compie autumaticamente l’URL Apre i liami in appiecazioni @@ -465,6 +475,17 @@ Seguità u tema di l’apparechju + + + Tirà per attualizà + + Sfilà per piattà a barra d’attrezzi + + + Fà sfilà a barra d’attrezzi lateralmente per cambià d’unghjetta + + Fà sfilà a barra d’attrezzi insù per apre unghjette + Sessioni @@ -1061,7 +1082,7 @@ - Site cunnettu cum’è %s nant’à un altru navigatore Firefox cù stu telefonu. Vulete cunnettavvi cù stu contu ? + Site cunnettu cum’è %s nant’à un altru navigatore Firefox cù st’apparechju. Vulete cunnettavvi cù stu contu ? Iè, cunnettatemi @@ -1306,7 +1327,7 @@ Nome di l’accurtatoghju - Hè faciule d’aghjunghje stu situ nant’à u screnu d’accolta di u vostru telefonu per accedeci direttamente è navigà più prestu cum’è s’ella fussi un’appiecazione. + Hè faciule d’aghjunghje stu situ nant’à u screnu d’accolta di u vostru apparechju per accedeci direttamente è navigà più prestu cum’è s’ella fussi un’appiecazione. Identificazioni è parolle d’entrata @@ -1378,8 +1399,12 @@ Situ cupiatu ver di u preme’papei Cupià a parolla d’entrata + + Squassà a parolla d’entrata Cupià u nome d’utilizatore + + Squassà u nome d’utilizatore Cupià u situ @@ -1537,7 +1562,7 @@ L’identificazione di cunnessione cù stu nome d’utilizatore esiste dighjà - + Cunnettate un altru apparechju. Ci vole à autenticassi torna. @@ -1559,7 +1584,7 @@ Cunfina di siti principale tocca - Per aghjunghje un novu situ principale, ci vole à cacciane unu. Effettuate una longa incalcata nant’à u situ è selezziunate Caccià. + Per aghjunghje un novu situ principale, ci vole à cacciane un’altru. Effettuate una longa incalcata nant’à u situ è selezziunate Caccià. Iè, aghju capitu @@ -1569,7 +1594,7 @@ Caccià - Ottene u più bellu da %s. @@ -1577,4 +1602,9 @@ Racuglite ciò chì conta per voi Gruppate inseme ricerche simile, i siti è l’unghjette per un accessu future più prestu. + + Site cunnettu cum’è %s nant’à un altru navigatore Firefox cù stu telefonu. Vulete cunnettavvi cù stu contu ? + + Hè faciule d’aghjunghje stu situ nant’à u screnu d’accolta di u vostru telefonu per accedeci direttamente è navigà più prestu cum’è s’ella fussi un’appiecazione. + diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 88bd72994..701f63e17 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -145,6 +145,8 @@ Gosod Tabiau wedi’u cydweddu + + Ailgydweddu Canfod yn y dudalen diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 729763d07..7ead58dc5 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -148,6 +148,8 @@ Installieren Synchronisierte Tabs + + Neu synchronisieren In Seite suchen diff --git a/app/src/main/res/values-dsb/strings.xml b/app/src/main/res/values-dsb/strings.xml index f2d2d0a09..26e9371a7 100644 --- a/app/src/main/res/values-dsb/strings.xml +++ b/app/src/main/res/values-dsb/strings.xml @@ -146,6 +146,8 @@ Instalěrowaś Synchronizěrowane rejtariki + + Znowego synchronizěrowaś Na boku pytaś diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index c86df2352..a6eba5850 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -139,6 +139,8 @@ Εγκατάσταση Συγχρονισμένες καρτέλες + + Επανασυγχρονισμός Εύρεση στη σελίδα @@ -320,6 +322,8 @@ Αναζήτηση ιστορικού περιήγησης Αναζήτηση σελιδοδεικτών + + Αναζήτηση συγχρονισμένων καρτελών Ρυθμίσεις λογαριασμού diff --git a/app/src/main/res/values-en-rCA/strings.xml b/app/src/main/res/values-en-rCA/strings.xml index 6a4d2d402..b9f8406b4 100644 --- a/app/src/main/res/values-en-rCA/strings.xml +++ b/app/src/main/res/values-en-rCA/strings.xml @@ -145,6 +145,8 @@ Install Synced tabs + + Resync Find in page diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index 4c5420667..9ac54abc6 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -146,6 +146,8 @@ Instalar Pestañas sincronizadas + + Resincronizar Buscar en la página diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml index 826f96a8e..829553b6b 100644 --- a/app/src/main/res/values-es-rCL/strings.xml +++ b/app/src/main/res/values-es-rCL/strings.xml @@ -324,6 +324,8 @@ Buscar historial de navegación Buscar marcadores + + Buscar pestañas sincronizadas Ajustes de la cuenta diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 20ea0046e..e2d9d896a 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -147,6 +147,8 @@ Instalar Pestañas sincronizadas + + Resincronizar Buscar en la página @@ -331,6 +333,8 @@ Buscar historial de navegación Buscar marcadores + + Buscar pestañas sincronizadas Ajustes de la cuenta diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 1562209d9..558c1db48 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -72,12 +72,19 @@ Descartar + + Ir a ajustes Descartar Configura que las pestañas abiertas se cierren automáticamente si no han sido vistas en el último día, semana o mes. + + Ver opciones + + Descartar + Nueva pestaña @@ -122,6 +129,8 @@ Instalar Pestañas sincronizadas + + Resincronizar Buscar en la página @@ -264,6 +273,8 @@ Barra de herramientas Tema + + Inicio Gestos @@ -300,14 +311,23 @@ Buscar historial de navegación Buscar marcadores + + Buscar pestañas sincronizadas Configuración de la cuenta + + Autocompletar URLs Abrir enlaces en aplicaciones + + Administrador de descargas externo Complementos + + Notificaciones + Sincronizar ahora @@ -437,6 +457,9 @@ Seguir el tema del dispositivo + + + Arrastrar para actualizar Desplazar para ocultar la barra @@ -476,6 +499,31 @@ Cerrar + + Pestañas cerradas recientemente + + Mostrar historial completo + + %d pestañas + + %d pestaña + + No hay pestañas recientemente cerradas + + + + Cerrar pestañas + + Manualmente + + Después de un día + + Después de una semana + + Después de un mes + Pestañas abiertas @@ -495,6 +543,8 @@ Guardar en colección Compartir todas las pestañas + + Pestañas cerradas recientemente Cerrar todas las pestañas @@ -542,6 +592,8 @@ Eliminar + + Eliminar del historial %1$s (modo privado) @@ -587,6 +639,9 @@ No hay ningún historial + + + No hay descargas aquí %1$d seleccionado @@ -1326,8 +1381,12 @@ Sitio copiado al portapapeles Copiar contraseña + + Borrar contraseña Copiar nombre de usuario + + Borrar nombre de usuario Copiar sitio @@ -1506,7 +1565,13 @@ Vale, entendido - + Mostrar los sitios más visitados + + + Eliminar + + Sácale el máximo provecho a %s. diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index a4c8a3a7d..2c59ac64c 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -146,6 +146,8 @@ Asenna Synkronoidut välilehdet + + Synkronoi uudelleen Etsi sivulta @@ -330,6 +332,8 @@ Etsi selaushistoriasta Etsi kirjanmerkeistä + + Etsi synkronoiduista välilehdistä Tilin asetukset @@ -478,7 +482,7 @@ Vedä päivittääksesi - Viertä piilottaaksesi työkalurivin + Vieritä piilottaaksesi työkalurivin Pyyhkäise työkaluriviä sivuttain vaihtaaksesi välilehtiä diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 76e85bd47..06553b266 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -146,6 +146,8 @@ Installer Onglets synchronisés + + Resynchroniser Rechercher dans la page @@ -478,9 +480,17 @@ Suivre le thème de l’appareil + + + Tirer pour actualiser Masquer la barre d’outils au défilement + + Changer d’onglet en glissant la barre d’outils latéralement + + Ouvrir des onglets en glissant la barre d’outils vers le haut + Sessions diff --git a/app/src/main/res/values-fy-rNL/strings.xml b/app/src/main/res/values-fy-rNL/strings.xml index 598b1cf91..6bc3feeff 100644 --- a/app/src/main/res/values-fy-rNL/strings.xml +++ b/app/src/main/res/values-fy-rNL/strings.xml @@ -148,6 +148,8 @@ Ynstallearje Syngronisearre ljepblêden + + Opnij syngronisearje Sykje op side diff --git a/app/src/main/res/values-gn/strings.xml b/app/src/main/res/values-gn/strings.xml index afe9fea06..61e9ebc74 100644 --- a/app/src/main/res/values-gn/strings.xml +++ b/app/src/main/res/values-gn/strings.xml @@ -149,6 +149,8 @@ Mohenda Tendayke mbojuehepyre + + Mbojuajujey Eheka kuatiaroguépe @@ -330,6 +332,8 @@ Eheka kundahára rembiasakue Eheka techaukaha + + Eheka tendayke mbojuehepyre Mba’ete ñemboheko diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index a675fa334..9638ae8d2 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -144,6 +144,8 @@ Instaliraj Sinkronizirane kartice + + Ponovna sinkronizacija Pronađi na stranici @@ -325,6 +327,8 @@ Pretraži povijest pregledavanja Pretraži zabilješke + + Pretraži sinkronizirane kartice Postavke računa @@ -1166,7 +1170,7 @@ Prijavi se pomoću kamere - Umjesto toga koristi e-mail + Umjesto toga koristi e-poštu Firefox će prestati sinkronizirati s tvojim računom, ali neće izbrisati podatke o tvom pregledavanju na ovom uređaju. diff --git a/app/src/main/res/values-hsb/strings.xml b/app/src/main/res/values-hsb/strings.xml index 92c7e0cb4..4baecf054 100644 --- a/app/src/main/res/values-hsb/strings.xml +++ b/app/src/main/res/values-hsb/strings.xml @@ -146,6 +146,8 @@ Instalować Synchronizowane rajtarki + + Znowa synchronizować Na stronje pytać diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index c11c150ad..dac694860 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -145,6 +145,8 @@ Telepítés Szinkronizált lapok + + Újraszinkronizálás Keresés az oldalon diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 55b437366..5dcd282e2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -145,6 +145,8 @@ Installa Schede sincronizzate + + Risincronizza Trova nella pagina @@ -331,6 +333,8 @@ Cerca nella cronologia Cerca nei segnalibri + + Cerca nelle schede sincronizzate Impostazioni account diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 7a0092cff..41eeb1c36 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -144,6 +144,8 @@ התקנה לשוניות מסונכרנות + + סנכרון מחדש חיפוש בדף diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index d06538d0f..6fc93f90f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -84,7 +84,7 @@ - アプリでリンクを自動的に開くように Firefox を設定できます。 + アプリ内のリンクを Firefox で自動的に開くように設定できます。 設定を開く @@ -148,6 +148,8 @@ インストール 同期したタブ + + 再同期 ページ内検索 @@ -340,7 +342,7 @@ 自動補完 URL - リンクをアプリで開く + アプリ内のリンクを開く 外部のダウンロードマネージャー diff --git a/app/src/main/res/values-kk/strings.xml b/app/src/main/res/values-kk/strings.xml index a716d09de..8601523ce 100644 --- a/app/src/main/res/values-kk/strings.xml +++ b/app/src/main/res/values-kk/strings.xml @@ -142,6 +142,8 @@ Орнату Синхрондалған беттер + + Қайта синхрондау Беттен табу @@ -285,6 +287,8 @@ Тема Үйге + + Ым қимылдар Баптау @@ -319,8 +323,12 @@ Шолу тарихынан іздеу Бетбелгілерден іздеу + + Синхрондалған беттерден іздеу Тіркелгі баптаулары + + URL адрестерін автотолықтыру Сілтемелерді қолданбаларда ашу @@ -456,6 +464,16 @@ Құрылғы темасына сүйену + + + Жаңарту үшін тартыңыз + + Құралдар тақтасын жасыру үшін айналдырыңыз + + Беттерді ауыстыру үшін құралдар тақтасын сырғытыңыз + + Беттерді ашу үшін құралдар тақтасын жоғары қарай сырғытыңыз + Сессиялар @@ -1044,6 +1062,10 @@ Firefox тіркелгісімен бетбелгілер, парольдер және т.б. синхрондауды бастаңыз. Көбірек білу + + Сіз осы құрылғыда басқа Firefox браузерінде %s ретінде кірдіңіз. Осы тіркелгімен кіргіңіз келе ме? Иә, авторизациялау @@ -1288,6 +1310,9 @@ Жарлық атауы + + Бұл веб-сайтты жылдам қатынау және қолданба тектес режимде жылдам шолу мақсатымен құрылғыңыздың үй бетіңізге қосуға болады. + Логиндер және парольдер @@ -1357,8 +1382,12 @@ Сайт алмасу буферіне көшірілді Парольді көшіріп алу + + Парольді тазарту Пайдаланушы атын көшіріп алу + + Пайдаланушы атын тазарту Сайтты көшіріп алу @@ -1532,6 +1561,8 @@ Үздік сайттар саны шегіне жетті + + Жаңа үздік сайтын қосу үшін, біреуін өшіріңіз. Сайтқа басып, басулы ұстаңыз және өшіру таңдаңыз. Жақсы, түсіндім diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 674f00715..b47cab6ec 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -152,6 +152,8 @@ 설치 동기화된 탭 + + 다시 동기화 페이지에서 찾기 diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index a39f99ad3..37d78a947 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -148,6 +148,8 @@ Installer Synkroniserte faner + + Synkroniser på nytt Finn på siden @@ -253,7 +255,7 @@ Passord - Kredittkort og adresser + Betalingskort og adresser Velg som standardnettleser diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 06d678eb2..6a692d417 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -150,6 +150,8 @@ Installeren Gesynchroniseerde tabbladen + + Opnieuw synchroniseren Zoeken op pagina diff --git a/app/src/main/res/values-nn-rNO/strings.xml b/app/src/main/res/values-nn-rNO/strings.xml index e2120962e..746e17f9d 100644 --- a/app/src/main/res/values-nn-rNO/strings.xml +++ b/app/src/main/res/values-nn-rNO/strings.xml @@ -148,6 +148,8 @@ Installer Synkroniserte faner + + Synkroniser på nytt Finn på sida diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index 1dafdb785..9a014ca6c 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -186,7 +186,7 @@ - Escanerizar + Numerizar Motor de recèrca @@ -1110,7 +1110,7 @@ TORNAR ENSAJAR - Escanerizatz lo còdi QR + Numerizatz lo còdi QR https://firefox.com/pair]]> diff --git a/app/src/main/res/values-pa-rIN/strings.xml b/app/src/main/res/values-pa-rIN/strings.xml index 735d24251..ebc183d39 100644 --- a/app/src/main/res/values-pa-rIN/strings.xml +++ b/app/src/main/res/values-pa-rIN/strings.xml @@ -141,6 +141,8 @@ ਇੰਸਟਾਲ ਕਰੋ ਸਿੰਕ ਕੀਤੀਆਂ ਟੈਬਾਂ + + ਮੁੜ-ਸਿੰਕ ਕਰੋ ਸਫ਼ੇ ‘ਚ ਲੱਭੋ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 0fd89ff61..a1f5c3cea 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -146,6 +146,8 @@ Zainstaluj Karty z innych urządzeń + + Synchronizuj ponownie Znajdź na stronie @@ -328,6 +330,8 @@ Historia Zakładki + + Karty z innych urządzeń Ustawienia konta diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 2db9c4767..df7ff3f34 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -145,6 +145,8 @@ Instalar Abas sincronizadas + + Ressincronizar Procurar na página diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index c0443e09b..39e6acdc9 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -146,6 +146,8 @@ Instalar Separadores sincronizados + + Ressincronizar Encontrar na página diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index a988808c3..7d7e04ff1 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -143,6 +143,8 @@ Инсталирај Синхронизовани језичци + + Поново синхронизујте Нађи на страници @@ -267,6 +269,8 @@ Отварај везе у приватном језичку Дозволи снимке екрана у приватном прегледању + + Ако је дозвољено, приватни језичци биће видљиви и кад је отворено више апликација Додајте пречицу за приватно прегледање @@ -287,6 +291,8 @@ Тема Почетна + + Гестикулације Прилагоди @@ -321,8 +327,12 @@ Претражи историјат прегледања Претражи забелешке + + Претражи синхронизоване језичке Подешавања налога + + Аутоматско завршавање адреса Отвори везе у апликацијама @@ -460,6 +470,18 @@ Прати тему уређаја + + + Повуците да освежите + + + Скролујте да сакријете траку с алаткама + + + Превуците траку с алаткама у страну да промените језичке + + Превуците траку с алаткама на горе да отворите језичке + Сесије @@ -1051,7 +1073,7 @@ - Пријављени сте као %s у другом Firefox прегледачу, на овом телефону. Желите ли да се пријавите са тим налогом? + Пријављени сте као %s на другом Firefox прегледачу на овом уређају. Желите ли да се пријавите са овим налогом? Да, пријави ме @@ -1300,7 +1322,7 @@ Назив пречице - Ову страницу можете лако додати на почетни екран вашег телефона за тренутни приступ и брже прегледање. + Овај сајт можете лако додати на почетни екран уређаја за директан приступ и брже прегледање, баш као што користите апликацију. Пријаве и лозинке @@ -1370,8 +1392,12 @@ Адреса странице копирана у оставу Копирај лозинку + + Обриши лозинку Копирај корисничко + + Обриши корисничко име Копирај страницу @@ -1528,7 +1554,7 @@ Пријава са овим корисничким именом већ постоји - + Повежите други уређај. Поново потврдите идентитет. @@ -1549,7 +1575,7 @@ Достигнуто је ограничење популарних страница - Да бисте додали нову популарну страницу, избришите постојећу. Дуже притисните страницу и одаберите уклањање. + За додавање новог омиљеног сајта, прво уклоните један. Дуго притисните и изаберите уклони. Важи, разумем @@ -1559,7 +1585,7 @@ Уклони - Искористите %s у потпуности. @@ -1567,4 +1593,9 @@ Сакупљајте ствари које су вам важне Групишите сличне претраге, странице и језичке за брз приступ касније. - + + Пријављени сте као %s у другом Firefox прегледачу, на овом телефону. Желите ли да се пријавите са тим налогом? + + Ову страницу можете лако додати на почетни екран вашег телефона за тренутни приступ и брже прегледање. + + diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 05ebf75b3..19281efbe 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -147,6 +147,8 @@ Installera Synkade flikar + + Omsynkronisering Hitta på sidan diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 30441b67f..70755ca4f 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -144,6 +144,8 @@ ติดตั้ง แท็บที่ซิงค์ + + ซิงค์ใหม่ ค้นหาในหน้า @@ -325,6 +327,8 @@ ค้นหาประวัติการเรียกดู ค้นหาที่คั่นหน้า + + ค้นหาแท็บที่ซิงค์ การตั้งค่าบัญชี diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c145ad747..c1c228dfa 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -145,6 +145,8 @@ Yükle Eşitlenmiş sekmeler + + Yeniden eşitle Sayfada bul diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d18133ce2..e9c83525d 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -146,6 +146,8 @@ Встановити Синхронізовані вкладки + + Синхронізувати повторно Знайти на сторінці diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 73957372c..b8f1e02ef 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -146,6 +146,8 @@ Cài đặt Các thẻ đã đồng bộ + + Đồng bộ hóa lại Tìm trong trang diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b74b498b5..a953f900d 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -149,6 +149,8 @@ 安装 受同步的标签页 + + 重新同步 在页面中查找 @@ -565,7 +567,7 @@ 新建隐私标签页 - 私密 + 隐私标签页 打开的标签页 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a478f6ebf..3c455dc1b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -146,6 +146,8 @@ 安裝 同步的分頁 + + 重新同步 在頁面中搜尋 diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 2edab8d15..73450133e 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -72,7 +72,6 @@ - diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 4b188525c..2401f4222 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -35,11 +35,12 @@ 14sp 18sp 56dp - 56dp 16dp 12dp 16dp 68dp + + 100dp 48dp 8dp @@ -81,8 +82,17 @@ 256dp - + 48dp + 10dp + + + 60dp + + + + 48dp + 16dp 56dp @@ -160,9 +170,17 @@ 40dp 125dp 130dp - 92dp 69dp + 4dp + 11dp + 3dp + 0dp + 8dp + 0dp + 8dp + 24dp + 28dp 10dp @@ -173,6 +191,12 @@ 16dp 48dp + + 20dp + + + 48dp + 40dp 4dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 84612045c..63c2472c8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1166,6 +1166,11 @@ Sign in with your camera Use email instead + + No account? %s to sync Firefox between devices. + + Create one Firefox will stop syncing with your account, but won’t delete any of your browsing data on this device. @@ -1272,6 +1277,10 @@ %s | OSS Libraries + + Redirect Trackers + + Clears cookies set by redirects to known tracking websites. Support diff --git a/app/src/test/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactoryTest.kt b/app/src/test/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactoryTest.kt index 9289859d2..518690cc3 100644 --- a/app/src/test/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactoryTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/TrackingProtectionPolicyFactoryTest.kt @@ -3,7 +3,6 @@ package org.mozilla.fenix.components import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject -import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertArrayEquals @@ -13,6 +12,7 @@ import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.Config +import org.mozilla.fenix.FeatureFlags import org.mozilla.fenix.R import org.mozilla.fenix.ReleaseChannel import org.mozilla.fenix.utils.Settings @@ -23,7 +23,7 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `WHEN useStrictMode is true then SHOULD return strict mode`() { - val expected = EngineSession.TrackingProtectionPolicy.strict() + val expected = TrackingProtectionPolicy.strict() val factory = TrackingProtectionPolicyFactory(mockSettings(useStrict = true)) @@ -35,12 +35,12 @@ class TrackingProtectionPolicyFactoryTest { expected.assertPolicyEquals(privateOnly, checkPrivacy = false) expected.assertPolicyEquals(normalOnly, checkPrivacy = false) expected.assertPolicyEquals(always, checkPrivacy = false) - EngineSession.TrackingProtectionPolicy.none().assertPolicyEquals(none, checkPrivacy = false) + TrackingProtectionPolicy.none().assertPolicyEquals(none, checkPrivacy = false) } @Test fun `WHEN neither use strict nor use custom is true SHOULD return recommended mode`() { - val expected = EngineSession.TrackingProtectionPolicy.recommended() + val expected = TrackingProtectionPolicy.recommended() val factory = TrackingProtectionPolicyFactory(mockSettings(useStrict = false, useCustom = false)) @@ -52,13 +52,13 @@ class TrackingProtectionPolicyFactoryTest { expected.assertPolicyEquals(privateOnly, checkPrivacy = false) expected.assertPolicyEquals(normalOnly, checkPrivacy = false) expected.assertPolicyEquals(always, checkPrivacy = false) - EngineSession.TrackingProtectionPolicy.none().assertPolicyEquals(none, checkPrivacy = false) + TrackingProtectionPolicy.none().assertPolicyEquals(none, checkPrivacy = false) } @Test fun `GIVEN custom policy WHEN should not block cookies THEN tracking policy should not block cookies`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_ALL, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_ALL, trackingCategories = allTrackingCategories ) @@ -75,8 +75,8 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN cookie policy block all THEN tracking policy should have cookie policy allow none`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, trackingCategories = allTrackingCategories ) @@ -91,34 +91,6 @@ class TrackingProtectionPolicyFactoryTest { expected.assertPolicyEquals(always, checkPrivacy = false) } - @Test - fun `cookiePurging must be available ONLY in nightly or debug`() { - mockkObject(Config) - for (channel in ReleaseChannel.values()) { - every { Config.channel } returns channel - - val shouldCookiePurgingActive = channel.isNightlyOrDebug - val customSetting = - settingsForCustom(shouldBlockCookiesInCustom = true, blockCookiesSelection = "all") - val stringSetting = mockSettings(useStrict = true) - val recommendedSetting = mockSettings(useTrackingProtection = true) - - for (setting in arrayOf(recommendedSetting, stringSetting, customSetting)) { - val factory = TrackingProtectionPolicyFactory(setting) - val privateOnly = - factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true) - val normalOnly = - factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false) - val always = - factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true) - - assertEquals(shouldCookiePurgingActive, privateOnly.cookiePurging) - assertEquals(shouldCookiePurgingActive, normalOnly.cookiePurging) - assertEquals(shouldCookiePurgingActive, always.cookiePurging) - } - } - } - @Test fun `adaptPolicyToChannel MUST only update properties that have changed per given channel`() { mockkObject(Config) @@ -131,20 +103,17 @@ class TrackingProtectionPolicyFactoryTest { for (channel in ReleaseChannel.values()) { every { Config.channel } returns channel - val shouldCookiePurgingActive = channel.isNightlyOrDebug - for (policy in policies) { val adaptedPolicy = policy.adaptPolicyToChannel() policy.assertPolicyEquals(adaptedPolicy, checkPrivacy = false) - assertEquals(shouldCookiePurgingActive, adaptedPolicy.cookiePurging) } } } @Test fun `GIVEN custom policy WHEN cookie policy social THEN tracking policy should have cookie policy allow non-trackers`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NON_TRACKERS, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NON_TRACKERS, trackingCategories = allTrackingCategories ) @@ -161,8 +130,8 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN cookie policy accept visited THEN tracking policy should have cookie policy allow visited`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_VISITED, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_VISITED, trackingCategories = allTrackingCategories ) @@ -179,8 +148,8 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN cookie policy block third party THEN tracking policy should have cookie policy allow first party`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_ONLY_FIRST_PARTY, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_ONLY_FIRST_PARTY, trackingCategories = allTrackingCategories ) @@ -197,8 +166,8 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN cookie policy unrecognized THEN tracking policy should have cookie policy block all`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, trackingCategories = allTrackingCategories ) @@ -261,7 +230,7 @@ class TrackingProtectionPolicyFactoryTest { useETPFactory = TrackingProtectionPolicyFactory(mockSettings(useTrackingProtection = false)) policy = useETPFactory.createTrackingProtectionPolicy() - assertEquals(policy, EngineSession.TrackingProtectionPolicy.none()) + assertEquals(policy, TrackingProtectionPolicy.none()) } @Test @@ -306,14 +275,14 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN default tracking policies THEN tracking policies should match default`() { val defaultTrackingCategories = arrayOf( - EngineSession.TrackingProtectionPolicy.TrackingCategory.AD, - EngineSession.TrackingProtectionPolicy.TrackingCategory.ANALYTICS, - EngineSession.TrackingProtectionPolicy.TrackingCategory.SOCIAL, - EngineSession.TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL + TrackingProtectionPolicy.TrackingCategory.AD, + TrackingProtectionPolicy.TrackingCategory.ANALYTICS, + TrackingProtectionPolicy.TrackingCategory.SOCIAL, + TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL ) - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, trackingCategories = defaultTrackingCategories ) @@ -332,8 +301,8 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN all tracking policies THEN tracking policies should match all`() { - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, trackingCategories = allTrackingCategories ) @@ -353,15 +322,15 @@ class TrackingProtectionPolicyFactoryTest { @Test fun `GIVEN custom policy WHEN some tracking policies THEN tracking policies should match passed policies`() { val someTrackingCategories = arrayOf( - EngineSession.TrackingProtectionPolicy.TrackingCategory.AD, - EngineSession.TrackingProtectionPolicy.TrackingCategory.ANALYTICS, - EngineSession.TrackingProtectionPolicy.TrackingCategory.SOCIAL, - EngineSession.TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL, - EngineSession.TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING + TrackingProtectionPolicy.TrackingCategory.AD, + TrackingProtectionPolicy.TrackingCategory.ANALYTICS, + TrackingProtectionPolicy.TrackingCategory.SOCIAL, + TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL, + TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING ) - val expected = EngineSession.TrackingProtectionPolicy.select( - cookiePolicy = EngineSession.TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, trackingCategories = someTrackingCategories ) @@ -377,6 +346,55 @@ class TrackingProtectionPolicyFactoryTest { expected.assertPolicyEquals(actual, checkPrivacy = false) } + + @Test + fun `GIVEN custom policy WHEN some tracking policies THEN purge cookies`() { + val expected = TrackingProtectionPolicy.select( + cookiePolicy = TrackingProtectionPolicy.CookiePolicy.ACCEPT_NONE, + trackingCategories = allTrackingCategories, + cookiePurging = FeatureFlags.etpCookiePurging + ) + + val factory = TrackingProtectionPolicyFactory(settingsForCustom(shouldBlockCookiesInCustom = true)) + + val privateOnly = factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true) + val normalOnly = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false) + val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true) + + expected.assertPolicyEquals(privateOnly, checkPrivacy = false) + expected.assertPolicyEquals(normalOnly, checkPrivacy = false) + expected.assertPolicyEquals(always, checkPrivacy = false) + } + + @Test + fun `GIVEN strict policy WHEN some tracking policies THEN purge cookies`() { + val expected = TrackingProtectionPolicy.strict() + + val factory = TrackingProtectionPolicyFactory(mockSettings(useStrict = true, useTrackingProtection = true)) + + val privateOnly = factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true) + val normalOnly = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false) + val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true) + + assertEquals(privateOnly.cookiePurging, expected.cookiePurging) + assertEquals(normalOnly.cookiePurging, expected.cookiePurging) + assertEquals(always.cookiePurging, expected.cookiePurging) + } + + @Test + fun `GIVEN standard policy WHEN some tracking policies THEN purge cookies`() { + val expected = TrackingProtectionPolicy.recommended() + + val factory = TrackingProtectionPolicyFactory(mockSettings(useStrict = false, useCustom = false, useTrackingProtection = true)) + + val privateOnly = factory.createTrackingProtectionPolicy(normalMode = false, privateMode = true) + val normalOnly = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = false) + val always = factory.createTrackingProtectionPolicy(normalMode = true, privateMode = true) + + assertEquals(privateOnly.cookiePurging, expected.cookiePurging) + assertEquals(normalOnly.cookiePurging, expected.cookiePurging) + assertEquals(always.cookiePurging, expected.cookiePurging) + } } private fun mockSettings( @@ -408,8 +426,8 @@ private fun settingsForCustom( every { blockCryptominersInCustomTrackingProtection } returns blockCryptominers } -private fun EngineSession.TrackingProtectionPolicy.assertPolicyEquals( - actual: EngineSession.TrackingProtectionPolicy, +private fun TrackingProtectionPolicy.assertPolicyEquals( + actual: TrackingProtectionPolicy, checkPrivacy: Boolean ) { assertEquals(this.cookiePolicy, actual.cookiePolicy) @@ -426,16 +444,16 @@ private fun EngineSession.TrackingProtectionPolicy.assertPolicyEquals( } } -private fun Array.toInt(): Int { +private fun Array.toInt(): Int { return fold(initial = 0) { acc, next -> acc + next.id } } private val allTrackingCategories = arrayOf( - EngineSession.TrackingProtectionPolicy.TrackingCategory.AD, - EngineSession.TrackingProtectionPolicy.TrackingCategory.ANALYTICS, - EngineSession.TrackingProtectionPolicy.TrackingCategory.SOCIAL, - EngineSession.TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL, - EngineSession.TrackingProtectionPolicy.TrackingCategory.SCRIPTS_AND_SUB_RESOURCES, - EngineSession.TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING, - EngineSession.TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING + TrackingProtectionPolicy.TrackingCategory.AD, + TrackingProtectionPolicy.TrackingCategory.ANALYTICS, + TrackingProtectionPolicy.TrackingCategory.SOCIAL, + TrackingProtectionPolicy.TrackingCategory.MOZILLA_SOCIAL, + TrackingProtectionPolicy.TrackingCategory.SCRIPTS_AND_SUB_RESOURCES, + TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING, + TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING ) diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt index 11d1fa383..89e1a295a 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/DefaultBrowserToolbarMenuControllerTest.kt @@ -137,7 +137,7 @@ class DefaultBrowserToolbarMenuControllerTest { val controller = createController(scope = this) controller.handleToolbarItemInteraction(item) - val directions = BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment() + val directions = BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment(null) verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.BACK)) } verify { navController.navigate(directions) } @@ -161,7 +161,7 @@ class DefaultBrowserToolbarMenuControllerTest { val controller = createController(scope = this) controller.handleToolbarItemInteraction(item) - val directions = BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment() + val directions = BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment(null) verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.FORWARD)) } verify { navController.navigate(directions) } diff --git a/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt b/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt index 003312d3d..b4fadbaa9 100644 --- a/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/ext/ViewTest.kt @@ -21,10 +21,12 @@ import io.mockk.mockkStatic import io.mockk.slot import io.mockk.verify import mozilla.components.support.ktx.android.util.dpToPx +import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mozilla.fenix.R import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.robolectric.annotation.Config @@ -41,6 +43,9 @@ class ViewTest { mockkStatic("mozilla.components.support.ktx.android.util.DisplayMetricsKt") mockkStatic("org.mozilla.fenix.ext.ViewKt") + every { view.resources.getDimensionPixelSize(any()) } answers { + testContext.resources.getDimensionPixelSize(firstArg()) + } every { view.resources.displayMetrics } returns displayMetrics every { view.parent } returns parent every { parent.touchDelegate = any() } just Runs @@ -119,7 +124,7 @@ class ViewTest { @Config(sdk = [Build.VERSION_CODES.LOLLIPOP, Build.VERSION_CODES.LOLLIPOP_MR1]) @Test fun `isKeyboardVisible returns false when the keyboard height is less than or equal to the minimum threshold`() { - val threshold = MINIMUM_KEYBOARD_HEIGHT.dpToPx(displayMetrics) + val threshold = testContext.resources.getDimensionPixelSize(R.dimen.minimum_keyboard_height) every { view.getKeyboardHeight() } returns threshold - 1 assertEquals(false, view.isKeyboardVisible()) @@ -131,7 +136,7 @@ class ViewTest { @Config(sdk = [Build.VERSION_CODES.LOLLIPOP, Build.VERSION_CODES.LOLLIPOP_MR1]) @Test fun `isKeyboardVisible returns true when the keyboard height is greater than the minimum threshold`() { - val threshold = MINIMUM_KEYBOARD_HEIGHT.dpToPx(displayMetrics) + val threshold = testContext.resources.getDimensionPixelSize(R.dimen.minimum_keyboard_height) every { view.getKeyboardHeight() } returns threshold + 1 assertEquals(true, view.isKeyboardVisible()) diff --git a/app/src/test/java/org/mozilla/fenix/library/bookmarks/DesktopFoldersTest.kt b/app/src/test/java/org/mozilla/fenix/library/bookmarks/DesktopFoldersTest.kt index 603048a28..59edae653 100644 --- a/app/src/test/java/org/mozilla/fenix/library/bookmarks/DesktopFoldersTest.kt +++ b/app/src/test/java/org/mozilla/fenix/library/bookmarks/DesktopFoldersTest.kt @@ -45,24 +45,20 @@ class DesktopFoldersTest { @Test fun `withRootTitle and do showMobileRoot`() { - val desktopFolders = DesktopFolders(context, showMobileRoot = true) - - assertEquals(testContext.getString(R.string.library_bookmarks), desktopFolders.withRootTitle(mockNodeWithTitle("root")).title) - assertEquals(testContext.getString(R.string.library_bookmarks), desktopFolders.withRootTitle(mockNodeWithTitle("mobile")).title) - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_menu), desktopFolders.withRootTitle(mockNodeWithTitle("menu")).title) - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_toolbar), desktopFolders.withRootTitle(mockNodeWithTitle("toolbar")).title) - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_unfiled), desktopFolders.withRootTitle(mockNodeWithTitle("unfiled")).title) + assertEquals(testContext.getString(R.string.library_bookmarks), friendlyRootTitle(context, mockNodeWithTitle("root"))) + assertEquals(testContext.getString(R.string.library_bookmarks), friendlyRootTitle(context, mockNodeWithTitle("mobile"))) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_menu), friendlyRootTitle(context, mockNodeWithTitle("menu"))) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_toolbar), friendlyRootTitle(context, mockNodeWithTitle("toolbar"))) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_unfiled), friendlyRootTitle(context, mockNodeWithTitle("unfiled"))) } @Test fun `withRootTitle and do not showMobileRoot`() { - val desktopFolders = DesktopFolders(context, showMobileRoot = false) - - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_root), desktopFolders.withRootTitle(mockNodeWithTitle("root")).title) - assertEquals(mockNodeWithTitle("mobile"), desktopFolders.withRootTitle(mockNodeWithTitle("mobile"))) - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_menu), desktopFolders.withRootTitle(mockNodeWithTitle("menu")).title) - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_toolbar), desktopFolders.withRootTitle(mockNodeWithTitle("toolbar")).title) - assertEquals(testContext.getString(R.string.library_desktop_bookmarks_unfiled), desktopFolders.withRootTitle(mockNodeWithTitle("unfiled")).title) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_root), friendlyRootTitle(context, mockNodeWithTitle("root"), false)) + assertEquals(mockNodeWithTitle("mobile").title, friendlyRootTitle(context, mockNodeWithTitle("mobile"), false)) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_menu), friendlyRootTitle(context, mockNodeWithTitle("menu"), false)) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_toolbar), friendlyRootTitle(context, mockNodeWithTitle("toolbar"), false)) + assertEquals(testContext.getString(R.string.library_desktop_bookmarks_unfiled), friendlyRootTitle(context, mockNodeWithTitle("unfiled"), false)) } @Test diff --git a/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt index 0502b803f..41c5d3e73 100644 --- a/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabhistory/TabHistoryControllerTest.kt @@ -5,13 +5,14 @@ package org.mozilla.fenix.tabhistory import androidx.navigation.NavController +import io.mockk.every import io.mockk.mockk import io.mockk.verify +import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager import mozilla.components.browser.state.store.BrowserStore import mozilla.components.feature.session.SessionUseCases import org.junit.Test -import org.mozilla.fenix.R class TabHistoryControllerTest { @@ -19,10 +20,12 @@ class TabHistoryControllerTest { private val sessionManager: SessionManager = mockk(relaxed = true) private val navController: NavController = mockk(relaxed = true) private val sessionUseCases = SessionUseCases(store, sessionManager) + private val goToHistoryIndexUseCase = sessionUseCases.goToHistoryIndex private val controller = DefaultTabHistoryController( navController = navController, - goToHistoryIndexUseCase = goToHistoryIndexUseCase + goToHistoryIndexUseCase = goToHistoryIndexUseCase, + sessionManager = sessionManager ) private val currentItem = TabHistoryItem( @@ -33,10 +36,31 @@ class TabHistoryControllerTest { ) @Test - fun handleGoToHistoryIndex() { + fun handleGoToHistoryIndexNormalBrowsing() { + val session: Session = mockk(relaxed = true) + every { sessionManager.selectedSession } returns session + controller.handleGoToHistoryItem(currentItem) - verify { navController.popBackStack(R.id.browserFragment, false) } - verify { goToHistoryIndexUseCase.invoke(currentItem.index) } + verify { navController.navigateUp() } + verify { goToHistoryIndexUseCase.invoke(currentItem.index, session) } + } + + @Test + fun handleGoToHistoryIndexCustomTab() { + val customTabController = DefaultTabHistoryController( + navController = navController, + goToHistoryIndexUseCase = goToHistoryIndexUseCase, + customTabId = "custom-id", + sessionManager = sessionManager + ) + val customTabSession: Session = mockk(relaxed = true) + + every { sessionManager.findSessionById(any()) } returns customTabSession + + customTabController.handleGoToHistoryItem(currentItem) + + verify { navController.navigateUp() } + verify { goToHistoryIndexUseCase.invoke(currentItem.index, customTabSession) } } } diff --git a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelViewTest.kt b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelViewTest.kt index 7bcdafe36..eb56f1a02 100644 --- a/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelViewTest.kt +++ b/app/src/test/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelViewTest.kt @@ -19,6 +19,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mozilla.fenix.R import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.CROSS_SITE_TRACKING_COOKIES import org.mozilla.fenix.trackingprotection.TrackingProtectionCategory.SOCIAL_MEDIA_TRACKERS @RunWith(FenixRobolectricTestRunner::class) @@ -97,4 +98,13 @@ class TrackingProtectionPanelViewTest { view.social_media_trackers_loaded.performClick() verify { interactor.openDetails(SOCIAL_MEDIA_TRACKERS, categoryBlocked = false) } } + + @Test + fun testCrossSiteTrackerClick() { + view.cross_site_tracking.performClick() + verify { interactor.openDetails(CROSS_SITE_TRACKING_COOKIES, categoryBlocked = true) } + + view.cross_site_tracking_loaded.performClick() + verify { interactor.openDetails(CROSS_SITE_TRACKING_COOKIES, categoryBlocked = false) } + } } diff --git a/bors.toml b/bors.toml new file mode 100644 index 000000000..7000995c9 --- /dev/null +++ b/bors.toml @@ -0,0 +1,13 @@ +status = [] +block_labels = [ + "needs:data-review", + "do not land", + "pr:work-in-progress", + "pr:needs-changes" +] +required_approvals = 0 +delete_merged_branches = true +cut_body_after = "---" +[committer] +name = "MozLando" +email = "skaspari+mozlando@mozilla.com" diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 8e8ea9494..36b4427cd 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "60.0.20200917130150" + const val VERSION = "60.0.20200921130100" }