diff --git a/.github/ISSUE_TEMPLATE/---webcontent-issue-report.md b/.github/ISSUE_TEMPLATE/---webcontent-issue-report.md index 9c533dd02..827d1e6b3 100644 --- a/.github/ISSUE_TEMPLATE/---webcontent-issue-report.md +++ b/.github/ISSUE_TEMPLATE/---webcontent-issue-report.md @@ -13,9 +13,7 @@ assignees: '' ### Actual behavior -### Does toggling Tracking Protection fix the issue? (Press the lock icon in the toolbar while on the site to see toggle) - -### Can you reproduce in [Firefox for Android](https://play.google.com/store/apps/details?id=org.mozilla.firefox)? +### Does toggling Tracking Protection fix the issue? (Press the shield icon in the toolbar while on the site to see toggle) ### Can you reproduce in Chrome (or other non-Mozilla browser)? diff --git a/README.md b/README.md index 1f3d952c6..b792aef75 100644 --- a/README.md +++ b/README.md @@ -83,9 +83,6 @@ Therefore, everyone should feel free to open issues and pull requests. Join the Developers are especially welcome, wanted, and needed. -We are also [looking for maintainers](https://github.com/interfect/fenix/issues/23). - - ## I want to open a Pull Request! We encourage you to participate in this open source project. We love Pull Requests, Bug Reports, ideas, (security) code reviews or any other kind of positive contribution. diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt index 4add929b9..19464c018 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/HistoryTest.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.ui import android.content.Context import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu +import androidx.test.espresso.IdlingRegistry import kotlinx.coroutines.runBlocking import mozilla.components.browser.storage.sync.PlacesHistoryStorage import okhttp3.mockwebserver.MockWebServer @@ -13,8 +14,10 @@ import org.junit.After import org.junit.Before import org.junit.Rule 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.TestAssetHelper import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem import org.mozilla.fenix.ui.robots.historyMenu @@ -30,6 +33,7 @@ import org.mozilla.fenix.ui.robots.navigationToolbar class HistoryTest { /* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping. private lateinit var mockWebServer: MockWebServer + private var historyListIdlingResource: RecyclerViewIdlingResource? = null @get:Rule val activityTestRule = HomeActivityTestRule() @@ -52,6 +56,10 @@ class HistoryTest { runBlocking { historyStorage.deleteEverything() } + + if (historyListIdlingResource != null) { + IdlingRegistry.getInstance().unregister(historyListIdlingResource!!) + } } @Test @@ -74,6 +82,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) verifyHistoryMenuView() verifyVisitedTimeTitle() verifyFirstTestPageTitle("Test_Page_1") @@ -90,6 +101,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) }.openThreeDotMenu { }.clickCopy { verifyCopySnackBarText() @@ -105,6 +119,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) }.openThreeDotMenu { }.clickShare { verifyShareOverlay() @@ -123,6 +140,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) }.openThreeDotMenu { }.clickOpenInNormalTab { verifyUrl(firstWebPage.url.toString()) @@ -140,6 +160,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) }.openThreeDotMenu { }.clickOpenInPrivateTab { verifyUrl(firstWebPage.url.toString()) @@ -157,7 +180,11 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) }.openThreeDotMenu { + IdlingRegistry.getInstance().unregister(historyListIdlingResource!!) }.clickDelete { verifyDeleteSnackbarText("Deleted") verifyEmptyHistoryView() @@ -173,7 +200,11 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) clickDeleteHistoryButton() + IdlingRegistry.getInstance().unregister(historyListIdlingResource!!) verifyDeleteConfirmationMessage() confirmDeleteAllHistory() verifyDeleteSnackbarText("Browsing data deleted") @@ -190,6 +221,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) longTapSelectItem(firstWebPage.url) } @@ -216,6 +250,9 @@ class HistoryTest { homeScreen { }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) longTapSelectItem(firstWebPage.url) openActionBarOverflowOrOptionsMenu(activityTestRule.activity) } @@ -236,6 +273,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) longTapSelectItem(firstWebPage.url) openActionBarOverflowOrOptionsMenu(activityTestRule.activity) } @@ -259,9 +299,13 @@ class HistoryTest { }.submitQuery(secondWebPage.url.toString()) { }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) longTapSelectItem(firstWebPage.url) longTapSelectItem(secondWebPage.url) openActionBarOverflowOrOptionsMenu(activityTestRule.activity) + IdlingRegistry.getInstance().unregister(historyListIdlingResource!!) } multipleSelectionToolbar { @@ -282,6 +326,9 @@ class HistoryTest { mDevice.waitForIdle() }.openThreeDotMenu { }.openHistory { + historyListIdlingResource = + RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list)) + IdlingRegistry.getInstance().register(historyListIdlingResource!!) longTapSelectItem(firstWebPage.url) } diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt index 14d463209..8069ccb38 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/MediaNotificationTest.kt @@ -8,7 +8,6 @@ import androidx.test.uiautomator.UiSelector 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.helpers.AndroidAssetDispatcher @@ -61,6 +60,7 @@ class MediaNotificationTest { }.enterURLAndEnterToBrowser(videoTestPage.url) { mDevice.waitForIdle() clickMediaPlayerPlayButton() + waitForPlaybackToStart() }.openNotificationShade { verifySystemNotificationExists(videoTestPage.title) clickMediaSystemNotificationControlButton("Pause") @@ -136,7 +136,6 @@ class MediaNotificationTest { } } - @Ignore("Flaky test, temp disabled: https://github.com/mozilla-mobile/fenix/issues/12645") @Test fun mediaSystemNotificationInPrivateModeTest() { val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer) @@ -160,6 +159,7 @@ class MediaNotificationTest { verifyMediaIsPaused() }.openTabDrawer { closeTab() + verifySnackBarText("Private tab closed") } mDevice.openNotification() diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt index e07d7ba8c..771b4fddf 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt @@ -12,14 +12,12 @@ import android.net.Uri import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.pressBack import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.BundleMatchers import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.RootMatchers.isDialog import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.Visibility import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed @@ -33,7 +31,6 @@ import androidx.test.uiautomator.By.text import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until -import androidx.test.uiautomator.Until.hasObject import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.containsString import org.junit.Assert.assertTrue @@ -328,14 +325,7 @@ class BrowserRobot { } fun clickMediaPlayerPlayButton() { - mDevice.waitNotNull( - hasObject( - By - .clazz("android.widget.Button") - .textContains("Play") - ), - waitingTime - ) + mediaPlayerPlayButton().waitForExists(waitingTime) mediaPlayerPlayButton().click() } @@ -459,9 +449,9 @@ private fun tabsCounter() = onView(withId(R.id.counter_box)) private fun mediaPlayerPlayButton() = mDevice.findObject( - By - .clazz("android.widget.Button") - .textContains("Play") + UiSelector() + .className("android.widget.Button") + .text("Play") ) private fun assertBlueDot() { diff --git a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt index f25e14492..85f67ec7f 100644 --- a/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt +++ b/app/src/androidTest/java/org/mozilla/fenix/ui/robots/SearchRobot.kt @@ -29,10 +29,12 @@ import androidx.test.uiautomator.UiObject import androidx.test.uiautomator.UiSelector import androidx.test.uiautomator.Until import org.hamcrest.CoreMatchers.allOf +import org.hamcrest.CoreMatchers.anyOf import org.hamcrest.CoreMatchers.startsWith import org.hamcrest.Matchers import org.junit.Assert.assertEquals import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.SessionLoadedIdlingResource import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime import org.mozilla.fenix.helpers.click @@ -122,6 +124,7 @@ class SearchRobot { class Transition { val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + private lateinit var sessionLoadedIdlingResource: SessionLoadedIdlingResource fun dismiss(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { mDevice.waitForIdle() @@ -139,9 +142,20 @@ class SearchRobot { } fun submitQuery(query: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition { + sessionLoadedIdlingResource = SessionLoadedIdlingResource() mDevice.waitForIdle() browserToolbarEditView().perform(typeText(query + "\n")) + runWithIdleRes(sessionLoadedIdlingResource) { + onView( + anyOf( + ViewMatchers.withResourceName("browserLayout"), + ViewMatchers.withResourceName("onboarding_message") // Req ETP dialog + ) + ) + .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) + } + BrowserRobot().interact() return BrowserRobot.Transition() } diff --git a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt index 1db3b7c2c..410356205 100644 --- a/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt +++ b/app/src/main/java/org/mozilla/fenix/FeatureFlags.kt @@ -29,7 +29,7 @@ object FeatureFlags { /** * Enables showing the top frequently visited sites */ - val topFrecentSite = Config.channel.isNightlyOrDebug + const val topFrecentSite = true /** * Enables wait til first contentful paint diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index a40e9afde..aaa99af95 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -452,8 +452,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { order["org.mozilla.geckoview.COPY"] = 2 order["CUSTOM_CONTEXT_MENU_SEARCH"] = 3 order["CUSTOM_CONTEXT_MENU_SEARCH_PRIVATELY"] = 4 - order["org.mozilla.geckoview.SELECT_ALL"] = 5 - order["CUSTOM_CONTEXT_MENU_SHARE"] = 6 + order["org.mozilla.geckoview.PASTE"] = 5 + order["org.mozilla.geckoview.SELECT_ALL"] = 6 + order["CUSTOM_CONTEXT_MENU_SHARE"] = 7 return actions.sortedBy { actionName -> // Sort the actions in our preferred order, putting "other" actions unsorted at the end 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 38f51747d..63d82b9e3 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -38,6 +38,7 @@ import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager import mozilla.components.browser.state.action.ContentAction +import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab import mozilla.components.browser.state.state.SessionState import mozilla.components.browser.state.state.content.DownloadState @@ -253,13 +254,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session BrowserFragmentDirections.actionGlobalTabTrayDialogFragment() ) }, - onCloseTab = { - val snapshot = sessionManager.createSessionSnapshot(it) - val state = snapshot.engineSession?.saveState() - val isSelected = - it.id == context.components.core.store.state.selectedTabId ?: false + onCloseTab = { closedSession -> + val tab = store.state.findTab(closedSession.id) ?: return@DefaultBrowserToolbarController + val isSelected = tab.id == context.components.core.store.state.selectedTabId - val snackbarMessage = if (snapshot.session.private) { + val snackbarMessage = if (tab.content.private) { requireContext().getString(R.string.snackbar_private_tab_closed) } else { requireContext().getString(R.string.snackbar_tab_closed) @@ -271,9 +270,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session requireContext().getString(R.string.snackbar_deleted_undo), { sessionManager.add( - snapshot.session, + closedSession, isSelected, - engineSessionState = state + engineSessionState = tab.engineState.engineSessionState ) }, operation = { } @@ -295,7 +294,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session openInFenixIntent = openInFenixIntent, bookmarkTapped = { viewLifecycleOwner.lifecycleScope.launch { bookmarkTapped(it) } }, scope = viewLifecycleOwner.lifecycleScope, - tabCollectionStorage = requireComponents.core.tabCollectionStorage + tabCollectionStorage = requireComponents.core.tabCollectionStorage, + topSitesStorage = requireComponents.core.topSitesStorage ) _browserInteractor = BrowserInteractor( @@ -659,9 +659,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } } - view.swipeRefresh.isEnabled = FeatureFlags.pullToRefreshEnabled - @Suppress("ConstantConditionIf") - if (FeatureFlags.pullToRefreshEnabled) { + view.swipeRefresh.isEnabled = + FeatureFlags.pullToRefreshEnabled && context.settings().isPullToRefreshEnabledInBrowser + if (view.swipeRefresh.isEnabled) { val primaryTextColor = ThemeManager.resolveAttribute(R.attr.primaryText, context) view.swipeRefresh.setColorSchemeColors(primaryTextColor) @@ -964,11 +964,9 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session private fun showQuickSettingsDialog() { val session = getSessionById() ?: return viewLifecycleOwner.lifecycleScope.launch(Main) { - val sitePermissions: SitePermissions? = withContext(IO) { - session.url.toUri().host?.let { host -> - val storage = requireContext().components.core.permissionStorage - storage.findSitePermissionsBy(host) - } + val sitePermissions: SitePermissions? = session.url.toUri().host?.let { host -> + val storage = requireComponents.core.permissionStorage + storage.findSitePermissionsBy(host) } view?.let { diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index 22564c5e9..56964573b 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -72,15 +72,17 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { val components = context.components return super.initializeUI(view)?.also { - gestureLayout.addGestureListener( - ToolbarGestureHandler( - activity = requireActivity(), - contentLayout = browserLayout, - tabPreview = tabPreview, - toolbarLayout = browserToolbarView.view, - sessionManager = components.core.sessionManager + if (context.settings().isSwipeToolbarToSwitchTabsEnabled) { + gestureLayout.addGestureListener( + ToolbarGestureHandler( + activity = requireActivity(), + contentLayout = browserLayout, + tabPreview = tabPreview, + toolbarLayout = browserToolbarView.view, + sessionManager = components.core.sessionManager + ) ) - ) + } val readerModeAction = BrowserToolbar.ToggleButton( diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationController.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationController.kt index b93775caa..0429f6b7c 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationController.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationController.kt @@ -69,7 +69,7 @@ fun List.toSessionBundle(sessionManager: SessionManager): List { * @param metrics Controller that handles telemetry events. * @param tabCollectionStorage Storage used to save tab collections to disk. * @param sessionManager Used to query and serialize tabs. - * @param ioScope Coroutine scope that launches on the IO thread. + * @param scope Coroutine scope to launch coroutines. */ class DefaultCollectionCreationController( private val store: CollectionCreationStore, @@ -77,7 +77,7 @@ class DefaultCollectionCreationController( private val metrics: MetricController, private val tabCollectionStorage: TabCollectionStorage, private val sessionManager: SessionManager, - private val ioScope: CoroutineScope + private val scope: CoroutineScope ) : CollectionCreationController { companion object { @@ -89,7 +89,7 @@ class DefaultCollectionCreationController( dismiss() val sessionBundle = tabs.toSessionBundle(sessionManager) - ioScope.launch { + scope.launch { tabCollectionStorage.createCollection(name, sessionBundle) } @@ -100,7 +100,7 @@ class DefaultCollectionCreationController( override fun renameCollection(collection: TabCollection, name: String) { dismiss() - ioScope.launch { + scope.launch { tabCollectionStorage.renameCollection(collection, name) } metrics.track(Event.CollectionRenamed) @@ -130,7 +130,7 @@ class DefaultCollectionCreationController( override fun selectCollection(collection: TabCollection, tabs: List) { dismiss() val sessionBundle = tabs.toList().toSessionBundle(sessionManager) - ioScope.launch { + scope.launch { tabCollectionStorage .addTabsToCollection(collection, sessionBundle) } diff --git a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt index 264426c27..4e15537f7 100644 --- a/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/collections/CollectionCreationFragment.kt @@ -14,9 +14,7 @@ import androidx.fragment.app.DialogFragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_create_collection.view.* -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.plus import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState @@ -80,7 +78,7 @@ class CollectionCreationFragment : DialogFragment() { requireComponents.analytics.metrics, requireComponents.core.tabCollectionStorage, requireComponents.core.sessionManager, - ioScope = lifecycleScope + Dispatchers.IO + scope = lifecycleScope ) ) collectionCreationView = CollectionCreationView( diff --git a/app/src/main/java/org/mozilla/fenix/components/Components.kt b/app/src/main/java/org/mozilla/fenix/components/Components.kt index f065d9445..c5036195f 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Components.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Components.kt @@ -54,7 +54,7 @@ class Components(private val context: Context) { core.store, search.searchEngineManager, core.webAppShortcutManager, - core.topSiteStorage + core.topSitesStorage ) } val intentProcessors by lazy { diff --git a/app/src/main/java/org/mozilla/fenix/components/Core.kt b/app/src/main/java/org/mozilla/fenix/components/Core.kt index ec21fee58..79fedd99f 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Core.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Core.kt @@ -265,7 +265,7 @@ class Core(private val context: Context, private val crashReporter: CrashReporti val pinnedSiteStorage by lazy { PinnedSiteStorage(context) } - val topSiteStorage by lazy { + val topSitesStorage by lazy { val defaultTopSites = mutableListOf>() StrictMode.allowThreadDiskReads().resetPoliciesAfter { diff --git a/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt b/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt index 5d31c5a1e..8535cc7fc 100644 --- a/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/PermissionStorage.kt @@ -6,6 +6,8 @@ package org.mozilla.fenix.components import android.content.Context import androidx.paging.DataSource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissions.Status import mozilla.components.feature.sitepermissions.SitePermissionsStorage @@ -38,11 +40,11 @@ class PermissionStorage(private val context: Context) { return sitePermissions } - fun findSitePermissionsBy(origin: String): SitePermissions? { - return permissionsStorage.findSitePermissionsBy(origin) + suspend fun findSitePermissionsBy(origin: String): SitePermissions? = withContext(Dispatchers.IO) { + permissionsStorage.findSitePermissionsBy(origin) } - fun updateSitePermissions(sitePermissions: SitePermissions) { + suspend fun updateSitePermissions(sitePermissions: SitePermissions) = withContext(Dispatchers.IO) { permissionsStorage.update(sitePermissions) } @@ -50,11 +52,11 @@ class PermissionStorage(private val context: Context) { return permissionsStorage.getSitePermissionsPaged() } - fun deleteSitePermissions(sitePermissions: SitePermissions) { + suspend fun deleteSitePermissions(sitePermissions: SitePermissions) = withContext(Dispatchers.IO) { permissionsStorage.remove(sitePermissions) } - fun deleteAllSitePermissions() { + suspend fun deleteAllSitePermissions() = withContext(Dispatchers.IO) { permissionsStorage.removeAll() } } diff --git a/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt b/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt index 6fe835e20..214012cba 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt @@ -9,6 +9,9 @@ import android.os.StrictMode import androidx.lifecycle.LiveData import androidx.lifecycle.asLiveData import androidx.paging.DataSource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager import mozilla.components.feature.tab.collections.Tab @@ -49,6 +52,7 @@ class TabCollectionStorage( fun onCollectionRenamed(tabCollection: TabCollection, title: String) = Unit } + private val ioScope = CoroutineScope(Dispatchers.IO) var cachedTabCollections = listOf() private val collectionStorage by lazy { @@ -57,15 +61,15 @@ class TabCollectionStorage( } } - fun createCollection(title: String, sessions: List) { + suspend fun createCollection(title: String, sessions: List) = ioScope.launch { collectionStorage.createCollection(title, sessions) notifyObservers { onCollectionCreated(title, sessions) } - } + }.join() - fun addTabsToCollection(tabCollection: TabCollection, sessions: List) { + suspend fun addTabsToCollection(tabCollection: TabCollection, sessions: List) = ioScope.launch { collectionStorage.addTabsToCollection(tabCollection, sessions) notifyObservers { onTabsAdded(tabCollection, sessions) } - } + }.join() fun getTabCollectionsCount(): Int { return collectionStorage.getTabCollectionsCount() @@ -79,18 +83,18 @@ class TabCollectionStorage( return collectionStorage.getCollectionsPaged() } - fun removeCollection(tabCollection: TabCollection) { + suspend fun removeCollection(tabCollection: TabCollection) = ioScope.launch { collectionStorage.removeCollection(tabCollection) - } + }.join() - fun removeTabFromCollection(tabCollection: TabCollection, tab: Tab) { + suspend fun removeTabFromCollection(tabCollection: TabCollection, tab: Tab) = ioScope.launch { collectionStorage.removeTabFromCollection(tabCollection, tab) - } + }.join() - fun renameCollection(tabCollection: TabCollection, title: String) { + suspend fun renameCollection(tabCollection: TabCollection, title: String) = ioScope.launch { collectionStorage.renameCollection(tabCollection, title) notifyObservers { onCollectionRenamed(tabCollection, title) } - } + }.join() } fun TabCollection.description(context: Context): String { 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 ddb6cb0d5..46692b0a4 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 @@ -6,6 +6,7 @@ package org.mozilla.fenix.components.toolbar import android.content.Intent import androidx.annotation.VisibleForTesting +import androidx.appcompat.app.AlertDialog import androidx.navigation.NavController import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.snackbar.Snackbar @@ -19,6 +20,8 @@ import mozilla.components.browser.session.SessionManager import mozilla.components.concept.engine.EngineSession.LoadUrlFlags import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.feature.session.SessionFeature +import mozilla.components.feature.top.sites.DefaultTopSitesStorage +import mozilla.components.feature.top.sites.TopSite import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.NavGraphDirections @@ -62,7 +65,8 @@ class DefaultBrowserToolbarMenuController( private val openInFenixIntent: Intent, private val bookmarkTapped: (Session) -> Unit, private val scope: CoroutineScope, - private val tabCollectionStorage: TabCollectionStorage + private val tabCollectionStorage: TabCollectionStorage, + private val topSitesStorage: DefaultTopSitesStorage ) : BrowserToolbarMenuController { private val currentSession @@ -124,23 +128,38 @@ class DefaultBrowserToolbarMenuController( ) ToolbarMenu.Item.AddToTopSites -> { scope.launch { - ioScope.launch { - currentSession?.let { - with(activity.components.useCases.topSitesUseCase) { - addPinnedSites(it.title, it.url) + val context = swipeRefresh.context + val numPinnedSites = + topSitesStorage.cachedTopSites.filter { it.type != TopSite.Type.FRECENT }.size + + if (numPinnedSites >= settings.topSitesMaxLimit) { + AlertDialog.Builder(swipeRefresh.context).apply { + setTitle(R.string.top_sites_max_limit_title) + setMessage(R.string.top_sites_max_limit_content_2) + setPositiveButton(R.string.top_sites_max_limit_confirmation_button) { dialog, _ -> + dialog.dismiss() } - } - }.join() + create() + }.show() + } else { + ioScope.launch { + currentSession?.let { + with(activity.components.useCases.topSitesUseCase) { + addPinnedSites(it.title, it.url) + } + } + }.join() - FenixSnackbar.make( - view = swipeRefresh, - duration = Snackbar.LENGTH_SHORT, - isDisplayedWithBrowserToolbar = true - ) - .setText( - swipeRefresh.context.getString(R.string.snackbar_added_to_top_sites) + FenixSnackbar.make( + view = swipeRefresh, + duration = Snackbar.LENGTH_SHORT, + isDisplayedWithBrowserToolbar = true ) - .show() + .setText( + context.getString(R.string.snackbar_added_to_top_sites) + ) + .show() + } } } ToolbarMenu.Item.AddToHomeScreen, ToolbarMenu.Item.InstallToHomeScreen -> { 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 510b067f9..4f0673f2f 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 @@ -214,7 +214,8 @@ class BrowserToolbarView( when (settings.toolbarPosition) { ToolbarPosition.BOTTOM -> { (view.layoutParams as CoordinatorLayout.LayoutParams).apply { - (behavior as BrowserToolbarBottomBehavior).forceExpand(view) + // behavior can be null if the "Scroll to hide toolbar" setting is toggled off. + (behavior as? BrowserToolbarBottomBehavior)?.forceExpand(view) } } ToolbarPosition.TOP -> { @@ -225,7 +226,8 @@ class BrowserToolbarView( /** * Dynamically sets scroll flags for the toolbar when the user does not have a screen reader enabled - * Note that the bottom toolbar has a feature flag for being dynamic, so it may not get flags set. + * Note that the toolbar will have the flags set and be able to be hidden + * only if the user didn't disabled this behavior in app's settings. */ fun setScrollFlags(shouldDisableScroll: Boolean = false) { when (settings.toolbarPosition) { @@ -236,7 +238,10 @@ class BrowserToolbarView( } ToolbarPosition.TOP -> { view.updateLayoutParams { - scrollFlags = if (settings.shouldUseFixedTopToolbar || shouldDisableScroll) { + scrollFlags = + if (settings.shouldUseFixedTopToolbar || + !settings.isDynamicToolbarEnabled || + shouldDisableScroll) { // Force expand the toolbar so the user is not stuck with a hidden toolbar expand() 0 diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterMenu.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterMenu.kt index 6076dbc54..6d6bebe5a 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterMenu.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterMenu.kt @@ -81,20 +81,41 @@ class TabCounterMenu( } @VisibleForTesting - internal fun menuItems(showOnly: BrowsingMode?): List { + internal fun menuItems(showOnly: BrowsingMode): List { return when (showOnly) { BrowsingMode.Normal -> listOf(newTabItem) BrowsingMode.Private -> listOf(newPrivateTabItem) - null -> listOf( - newTabItem, - newPrivateTabItem, - DividerMenuCandidate(), - closeTabItem - ) } } - fun updateMenu(showOnly: BrowsingMode? = null) { + @VisibleForTesting + internal fun menuItems(toolbarPosition: ToolbarPosition): List { + val items = listOf( + newTabItem, + newPrivateTabItem, + DividerMenuCandidate(), + closeTabItem + ) + + return when (toolbarPosition) { + ToolbarPosition.BOTTOM -> items.reversed() + ToolbarPosition.TOP -> items + } + } + + /** + * Update the displayed menu items. + * @param showOnly Show only the new tab item corresponding to the given [BrowsingMode]. + */ + fun updateMenu(showOnly: BrowsingMode) { menuController.submitList(menuItems(showOnly)) } + + /** + * Update the displayed menu items. + * @param toolbarPosition Return a list that is ordered based on the given [ToolbarPosition]. + */ + fun updateMenu(toolbarPosition: ToolbarPosition) { + menuController.submitList(menuItems(toolbarPosition)) + } } diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt index 0b743e744..118c062ff 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/TabCounterToolbarButton.kt @@ -34,6 +34,7 @@ class TabCounterToolbarButton( override fun createView(parent: ViewGroup): View { val store = parent.context.components.core.store val metrics = parent.context.components.analytics.metrics + val settings = parent.context.components.settings store.flowScoped(lifecycleOwner) { flow -> flow.map { state -> state.getNormalOrPrivateTabs(isPrivate).size } @@ -42,7 +43,7 @@ class TabCounterToolbarButton( } val menu = TabCounterMenu(parent.context, metrics, onItemTapped) - menu.updateMenu() + menu.updateMenu(settings.toolbarPosition) val view = TabCounter(parent.context).apply { reference = WeakReference(this) 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 26db79972..5ddd53818 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -55,6 +55,7 @@ import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.menu.view.MenuButton import mozilla.components.browser.session.Session import mozilla.components.browser.session.SessionManager +import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.selector.getNormalOrPrivateTabs import mozilla.components.browser.state.selector.normalTabs import mozilla.components.browser.state.selector.privateTabs @@ -84,7 +85,6 @@ import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.tips.FenixTipManager import org.mozilla.fenix.components.tips.Tip import org.mozilla.fenix.components.tips.providers.MasterPasswordTipProvider -import org.mozilla.fenix.components.tips.providers.MigrationTipProvider import org.mozilla.fenix.components.toolbar.TabCounterMenu import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.components @@ -199,7 +199,7 @@ class HomeFragment : Fragment() { collections = components.core.tabCollectionStorage.cachedTabCollections, expandedCollections = emptySet(), mode = currentMode.getCurrentMode(), - topSites = components.core.topSiteStorage.cachedTopSites, + topSites = components.core.topSitesStorage.cachedTopSites, tip = StrictMode.allowThreadDiskReads().resetPoliciesAfter { FenixTipManager( listOf( @@ -207,8 +207,7 @@ class HomeFragment : Fragment() { requireContext(), ::navToSavedLogins, ::dismissTip - ), - MigrationTipProvider(requireContext()) + ) ) ).getTip() }, @@ -220,7 +219,7 @@ class HomeFragment : Fragment() { topSitesFeature.set( feature = TopSitesFeature( view = DefaultTopSitesView(homeFragmentStore), - storage = components.core.topSiteStorage, + storage = components.core.topSitesStorage, config = ::getTopSitesConfig ), owner = this, @@ -471,16 +470,10 @@ class HomeFragment : Fragment() { private fun removeAllTabsAndShowSnackbar(sessionCode: String) { val tabs = sessionManager.sessionsOfType(private = sessionCode == ALL_PRIVATE_TABS).toList() val selectedIndex = sessionManager - .selectedSession?.let { sessionManager.sessions.indexOf(it) } ?: 0 + .selectedSession?.let { sessionManager.sessions.indexOf(it) } ?: SessionManager.NO_SELECTION val snapshot = tabs .map(sessionManager::createSessionSnapshot) - .map { - it.copy( - engineSession = null, - engineSessionState = it.engineSession?.saveState() - ) - } .let { SessionManager.Snapshot(it, selectedIndex) } tabs.forEach { @@ -508,7 +501,7 @@ class HomeFragment : Fragment() { private fun removeTabAndShowSnackbar(sessionId: String) { sessionManager.findSessionById(sessionId)?.let { session -> val snapshot = sessionManager.createSessionSnapshot(session) - val state = snapshot.engineSession?.saveState() + val state = store.state.findTab(sessionId)?.engineState?.engineSessionState val isSelected = session.id == requireComponents.core.store.state.selectedTabId ?: false @@ -559,7 +552,7 @@ class HomeFragment : Fragment() { HomeFragmentAction.Change( collections = components.core.tabCollectionStorage.cachedTabCollections, mode = currentMode.getCurrentMode(), - topSites = components.core.topSiteStorage.cachedTopSites, + topSites = components.core.topSitesStorage.cachedTopSites, tip = StrictMode.allowThreadDiskReads().resetPoliciesAfter { FenixTipManager( listOf( @@ -567,8 +560,7 @@ class HomeFragment : Fragment() { requireContext(), ::navToSavedLogins, ::dismissTip - ), - MigrationTipProvider(requireContext()) + ) ) ).getTip() }, diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt index 7c927d2c5..db4ada0e0 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/SessionControlController.kt @@ -240,7 +240,7 @@ class DefaultSessionControlController( handleSwipedItemDeletionCancel ) } else { - viewLifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch { tabCollectionStorage.removeTabFromCollection(collection, tab) } } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivateBrowsingViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivateBrowsingViewHolder.kt index c0d5ff877..6f52c8341 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivateBrowsingViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingPrivateBrowsingViewHolder.kt @@ -11,10 +11,12 @@ import android.text.SpannableString import android.text.Spanned import android.text.style.ImageSpan import android.view.View +import androidx.annotation.ColorInt import androidx.annotation.DrawableRes -import androidx.appcompat.content.res.AppCompatResources import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.onboarding_private_browsing.view.* +import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.ktx.android.content.getDrawableWithTint import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components @@ -33,7 +35,8 @@ class OnboardingPrivateBrowsingViewHolder( val inlineIcon = PrivateBrowsingImageSpan( view.context, R.drawable.ic_private_browsing, - view.description_text_once.lineHeight + tint = view.context.getColorFromAttr(R.attr.primaryText), + size = view.description_text_once.lineHeight ) val text = SpannableString(view.context.getString(R.string.onboarding_private_browsing_description1)).apply { @@ -57,9 +60,10 @@ class OnboardingPrivateBrowsingViewHolder( class PrivateBrowsingImageSpan( context: Context, @DrawableRes drawableId: Int, + @ColorInt tint: Int, size: Int ) : ImageSpan( - AppCompatResources.getDrawable(context, drawableId)!!.apply { setBounds(size) } + context.getDrawableWithTint(drawableId, tint)!!.apply { setBounds(size) } ) { override fun draw( canvas: Canvas, diff --git a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt index 59ed9af37..581b5171f 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/CustomizationFragment.kt @@ -58,6 +58,7 @@ class CustomizationFragment : PreferenceFragmentCompat() { setupTabsTrayCategory() setupFabCategory() setupHomeCategory() + setupGesturesCategory() setupAddonsCustomizationCategory() } @@ -225,6 +226,23 @@ class CustomizationFragment : PreferenceFragmentCompat() { } } + private fun setupGesturesCategory() { + requirePreference(R.string.pref_key_website_pull_to_refresh).apply { + isVisible = FeatureFlags.pullToRefreshEnabled + isChecked = context.settings().isPullToRefreshEnabledInBrowser + onPreferenceChangeListener = SharedPreferenceUpdater() + } + requirePreference(R.string.pref_key_dynamic_toolbar).apply { + isChecked = context.settings().isDynamicToolbarEnabled + onPreferenceChangeListener = SharedPreferenceUpdater() + } + requirePreference(R.string.pref_key_swipe_toolbar_switch_tabs).apply { + isChecked = context.settings().isSwipeToolbarToSwitchTabsEnabled + onPreferenceChangeListener = SharedPreferenceUpdater() + } + + } + private fun setupAddonsCustomizationCategory() { requirePreference(R.string.pref_key_addons_custom_account).apply { text = context.settings().customAddonsAccount diff --git a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt index 8f01a1e3b..9f80cf5e6 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/about/AboutFragment.kt @@ -41,7 +41,7 @@ class AboutFragment : Fragment(), AboutPageListener { private lateinit var headerAppName: String private lateinit var appName: String - private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this) + private var aboutPageAdapter: AboutPageAdapter? = AboutPageAdapter(this) override fun onCreateView( inflater: LayoutInflater, @@ -58,6 +58,10 @@ class AboutFragment : Fragment(), AboutPageListener { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (aboutPageAdapter == null) { + aboutPageAdapter = AboutPageAdapter(this) + } + about_list.run { adapter = aboutPageAdapter addItemDecoration( @@ -76,7 +80,12 @@ class AboutFragment : Fragment(), AboutPageListener { ) populateAboutHeader() - aboutPageAdapter.submitList(populateAboutList()) + aboutPageAdapter?.submitList(populateAboutList()) + } + + override fun onDestroyView() { + super.onDestroyView() + aboutPageAdapter = null } private fun populateAboutHeader() { diff --git a/app/src/main/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteBrowsingDataFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteBrowsingDataFragment.kt index 36081afcc..733ae3ba2 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteBrowsingDataFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteBrowsingDataFragment.kt @@ -15,11 +15,14 @@ import kotlinx.android.synthetic.main.fragment_delete_browsing_data.* import kotlinx.android.synthetic.main.fragment_delete_browsing_data.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Dispatchers.IO +import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import mozilla.components.lib.state.ext.flowScoped import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import org.mozilla.fenix.R @@ -139,7 +142,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da private fun deleteSelected() { startDeletion() - viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleOwner.lifecycleScope.launch(IO) { getCheckboxes().mapIndexed { i, v -> if (v.isChecked) { when (i) { @@ -152,7 +155,7 @@ class DeleteBrowsingDataFragment : Fragment(R.layout.fragment_delete_browsing_da } } - launch(Dispatchers.Main) { + withContext(Main) { finishDeletion() requireComponents.analytics.metrics.track(Event.ClearedPrivateData) } diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt index b9de6f84d..9ad2a5f1a 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsDetailsExceptionsFragment.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.withContext import mozilla.components.feature.sitepermissions.SitePermissions import org.mozilla.fenix.R import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.settings.PhoneFeature import org.mozilla.fenix.settings.PhoneFeature.CAMERA @@ -44,13 +45,10 @@ class SitePermissionsDetailsExceptionsFragment : PreferenceFragmentCompat() { override fun onResume() { super.onResume() showToolbar(sitePermissions.origin) - viewLifecycleOwner.lifecycleScope.launch(IO) { - val context = requireContext() + viewLifecycleOwner.lifecycleScope.launch(Main) { sitePermissions = - requireNotNull(context.components.core.permissionStorage.findSitePermissionsBy(sitePermissions.origin)) - withContext(Main) { - bindCategoryPhoneFeatures() - } + requireNotNull(requireComponents.core.permissionStorage.findSitePermissionsBy(sitePermissions.origin)) + bindCategoryPhoneFeatures() } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsExceptionsFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsExceptionsFragment.kt index f941ce29b..2b73d4da2 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsExceptionsFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsExceptionsFragment.kt @@ -24,7 +24,6 @@ import androidx.paging.PagedListAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import mozilla.components.feature.sitepermissions.SitePermissions @@ -108,13 +107,12 @@ class SitePermissionsExceptionsFragment : } private fun deleteAllSitePermissions() { - viewLifecycleOwner.lifecycleScope.launch(IO) { + viewLifecycleOwner.lifecycleScope.launch(Main) { requireContext().components.core.permissionStorage.deleteAllSitePermissions() - launch(Main) { - showEmptyListMessage() - // Reload the selected session. - requireContext().components.useCases.sessionUseCases.reload() - } + + showEmptyListMessage() + // Reload the selected session. + requireContext().components.useCases.sessionUseCases.reload() } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt index b8ad9d170..110fc2bda 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/sitepermissions/SitePermissionsManageExceptionsPhoneFeatureFragment.kt @@ -18,7 +18,6 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.navArgs -import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import mozilla.components.feature.sitepermissions.SitePermissions @@ -142,11 +141,9 @@ class SitePermissionsManageExceptionsPhoneFeatureFragment : Fragment() { private fun updatedSitePermissions(status: SitePermissions.Status) { val updatedSitePermissions = args.sitePermissions.update(args.phoneFeature, status) - viewLifecycleOwner.lifecycleScope.launch(IO) { + viewLifecycleOwner.lifecycleScope.launch(Main) { requireComponents.core.permissionStorage.updateSitePermissions(updatedSitePermissions) - launch(Main) { - requireComponents.tryReloadTabBy(updatedSitePermissions.origin) - } + requireComponents.tryReloadTabBy(updatedSitePermissions.origin) } } } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/FenixTabsAdapter.kt b/app/src/main/java/org/mozilla/fenix/tabtray/FenixTabsAdapter.kt index bdd97d2e5..1ab54bfe3 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/FenixTabsAdapter.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/FenixTabsAdapter.kt @@ -8,6 +8,7 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import androidx.core.view.isVisible +import kotlinx.android.synthetic.main.checkbox_item.view.* import kotlinx.android.synthetic.main.tab_tray_item.view.* import mozilla.components.browser.tabstray.TabViewHolder import mozilla.components.browser.tabstray.TabsAdapter @@ -67,7 +68,11 @@ class FenixTabsAdapter( override fun onBindViewHolder(holder: TabViewHolder, position: Int) { super.onBindViewHolder(holder, position) val newIndex = tabCount - position - 1 - (holder as TabTrayViewHolder).updateAccessibilityRowIndex(holder.itemView, newIndex) + (holder as TabTrayViewHolder).updateAccessibilityRowInfo( + holder.itemView, + newIndex, + selectedItems.contains(holder.tab) + ) holder.tab?.let { tab -> showCheckedIfSelected(tab, holder.itemView) @@ -105,7 +110,6 @@ class FenixTabsAdapter( private fun showCheckedIfSelected(tab: Tab, view: View) { val shouldBeChecked = mode is TabTrayDialogFragmentState.Mode.MultiSelect && selectedItems.contains(tab) - view.checkmark.isVisible = shouldBeChecked view.selected_mask.isVisible = shouldBeChecked view.mozac_browser_tabstray_close.isVisible = mode is TabTrayDialogFragmentState.Mode.Normal } diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt index 20c110afc..e8507d767 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt @@ -30,6 +30,7 @@ import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import mozilla.components.browser.session.Session +import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.thumbnails.loader.ThumbnailLoader import mozilla.components.feature.tab.collections.TabCollection @@ -260,26 +261,22 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler } private fun showUndoSnackbarForTab(sessionId: String) { - val sessionManager = view?.context?.components?.core?.sessionManager + val store = requireComponents.core.store + val sessionManager = requireComponents.core.sessionManager - val snapshot = sessionManager - ?.findSessionById(sessionId)?.let { - sessionManager.createSessionSnapshot(it) - } ?: return + val tab = requireComponents.core.store.state.findTab(sessionId) ?: return + val session = sessionManager.findSessionById(sessionId) ?: return // Check if this is the last tab of this session type - val isLastOpenTab = - sessionManager.sessions.filter { snapshot.session.private == it.private }.size == 1 - + val isLastOpenTab = store.state.tabs.filter { it.content.private == tab.content.private }.size == 1 if (isLastOpenTab) { dismissTabTrayAndNavigateHome(sessionId) return } - val state = snapshot.engineSession?.saveState() val isSelected = sessionId == requireComponents.core.store.state.selectedTabId ?: false - val snackbarMessage = if (snapshot.session.private) { + val snackbarMessage = if (tab.content.private) { getString(R.string.snackbar_private_tab_closed) } else { getString(R.string.snackbar_tab_closed) @@ -290,8 +287,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler snackbarMessage, getString(R.string.snackbar_deleted_undo), { - sessionManager.add(snapshot.session, isSelected, engineSessionState = state) - _tabTrayView?.scrollToTab(snapshot.session.id) + sessionManager.add(session, isSelected, engineSessionState = tab.engineState.engineSessionState) + _tabTrayView?.scrollToTab(session.id) }, operation = { }, elevation = ELEVATION, @@ -378,7 +375,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler val selectedCollection = (list.adapter as CollectionsAdapter).getSelectedCollection() val collection = tabCollectionStorage.cachedTabCollections[selectedCollection] - viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleOwner.lifecycleScope.launch(Main) { tabCollectionStorage.addTabsToCollection(collection, sessionList) it.metrics.track( Event.CollectionTabsAdded( @@ -386,10 +383,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment(), UserInteractionHandler sessionList.size ) ) - launch(Main) { - tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode) - dialog.dismiss() - } + tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode) + dialog.dismiss() } }.setNegativeButton(android.R.string.cancel) { dialog, _ -> tabTrayDialogStore.dispatch(TabTrayDialogFragmentAction.ExitMultiSelectMode) 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 4128a4357..ba4e167eb 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayView.kt @@ -9,6 +9,8 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.accessibility.AccessibilityEvent +import android.view.accessibility.AccessibilityNodeInfo +import android.view.accessibility.AccessibilityNodeInfo.CollectionInfo import androidx.annotation.IdRes import androidx.cardview.widget.CardView import androidx.constraintlayout.widget.ConstraintLayout @@ -266,7 +268,7 @@ class TabTrayView( // WARNING: Merging the upstream fix for this will cause lot of conflicts! // // if (hasAccessibilityEnabled) { - // tabsAdapter.notifyDataSetChanged() + // tabsAdapter.notifyItemRangeChanged(0, tabs.size) // } if (!hasLoaded) { @@ -633,6 +635,22 @@ class TabTrayView( } else { view.context?.getString(R.string.open_tab_tray_plural, count.toString()) } + + view.tabsTray.accessibilityDelegate = object : View.AccessibilityDelegate() { + override fun onInitializeAccessibilityNodeInfo( + host: View?, + info: AccessibilityNodeInfo? + ) { + super.onInitializeAccessibilityNodeInfo(host, info) + info?.let { + info.collectionInfo = CollectionInfo.obtain( + tabsAdapter.tabCount, + 1, + false + ) + } + } + } } private fun updateTabCounter(count: Int): String { diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt index ad1a1f1a1..7b71b339d 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayViewHolder.kt @@ -201,25 +201,22 @@ class TabTrayViewHolder( imageLoader.loadIntoView(thumbnailView, ImageLoadRequest(id, thumbnailSize)) } - internal fun updateAccessibilityRowIndex(item: View, newIndex: Int) { + internal fun updateAccessibilityRowInfo(item: View, newIndex: Int, isSelected: Boolean) { item.accessibilityDelegate = object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo( host: View?, info: AccessibilityNodeInfo? ) { super.onInitializeAccessibilityNodeInfo(host, info) - info?.let { - info.collectionItemInfo = info.collectionItemInfo?.let { initialInfo -> - AccessibilityNodeInfo.CollectionItemInfo.obtain( - newIndex, - initialInfo.rowSpan, - initialInfo.columnIndex, - initialInfo.columnSpan, - false, - initialInfo.isSelected - ) - } - } + info?.collectionItemInfo = + AccessibilityNodeInfo.CollectionItemInfo.obtain( + newIndex, + 1, + 1, + 1, + false, + isSelected + ) } } } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 684a2d55c..78ee5c67c 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -103,7 +103,7 @@ class Settings(private val appContext: Context) : PreferencesHolder { var showTopFrecentSites by featureFlagPreference( appContext.getPreferenceKey(R.string.pref_key_enable_top_frecent_sites), - default = false, + default = true, featureFlag = FeatureFlags.topFrecentSite ) @@ -937,4 +937,19 @@ class Settings(private val appContext: Context) : PreferencesHolder { SavedLoginsSortingStrategyMenu.Item.LastUsedSort.strategyString } } + + var isPullToRefreshEnabledInBrowser by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_website_pull_to_refresh), + default = true + ) + + var isDynamicToolbarEnabled by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_dynamic_toolbar), + default = true + ) + + var isSwipeToolbarToSwitchTabsEnabled by booleanPreference( + appContext.getPreferenceKey(R.string.pref_key_swipe_toolbar_switch_tabs), + default = true + ) } diff --git a/app/src/main/res/drawable/ic_private_browsing.xml b/app/src/main/res/drawable/ic_private_browsing.xml index 9bef32e8a..baf1a8062 100644 --- a/app/src/main/res/drawable/ic_private_browsing.xml +++ b/app/src/main/res/drawable/ic_private_browsing.xml @@ -8,6 +8,6 @@ android:viewportWidth="24" android:viewportHeight="24"> diff --git a/app/src/main/res/layout/checkbox_item.xml b/app/src/main/res/layout/checkbox_item.xml new file mode 100644 index 000000000..21c125fe8 --- /dev/null +++ b/app/src/main/res/layout/checkbox_item.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/layout/download_dialog_layout.xml b/app/src/main/res/layout/download_dialog_layout.xml index a24206c2e..d4c1f7649 100644 --- a/app/src/main/res/layout/download_dialog_layout.xml +++ b/app/src/main/res/layout/download_dialog_layout.xml @@ -78,6 +78,7 @@ android:backgroundTint="?accent" android:text="@string/mozac_feature_downloads_button_open" android:textAllCaps="false" + android:padding="16dp" android:textColor="?contrastText" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/tab_tray_item.xml b/app/src/main/res/layout/tab_tray_item.xml index e1f57d360..1e94b5164 100644 --- a/app/src/main/res/layout/tab_tray_item.xml +++ b/app/src/main/res/layout/tab_tray_item.xml @@ -52,25 +52,7 @@ android:layout_height="match_parent" android:contentDescription="@string/mozac_browser_tabstray_open_tab" /> - - - + @@ -104,7 +86,7 @@ android:paddingTop="22dp" android:textColor="@color/tab_tray_item_text_normal_theme" android:textSize="16sp" - tools:text="This is the title of the tab and it is long" + tools:text="Example Domain" app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card" @@ -122,7 +104,7 @@ android:paddingStart="8dp" android:textColor="@color/tab_tray_item_url_normal_theme" android:textSize="14sp" - tools:text="https://www.google.com" + tools:text="example.com" app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_card" diff --git a/app/src/main/res/layout/tab_tray_item_compact.xml b/app/src/main/res/layout/tab_tray_item_compact.xml index 5b65659dc..817eeb7c7 100644 --- a/app/src/main/res/layout/tab_tray_item_compact.xml +++ b/app/src/main/res/layout/tab_tray_item_compact.xml @@ -51,25 +51,7 @@ android:layout_height="match_parent" android:contentDescription="@string/mozac_browser_tabstray_open_tab" /> - - - + @@ -105,7 +87,7 @@ android:textColor="@color/tab_tray_item_text_normal_theme" android:textSize="14sp" android:visibility="visible" - tools:text="Webpage tile that is long and will overflow textview widget" + tools:text="Example Domain" app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close" app:layout_constraintStart_toEndOf="@id/mozac_browser_tabstray_icon_card" app:layout_constraintTop_toTopOf="parent" /> @@ -119,7 +101,7 @@ android:paddingStart="8dp" android:textColor="@color/tab_tray_item_url_normal_theme" android:textSize="14sp" - tools:text="https://www.google.com" + tools:text="example.com" android:visibility="gone" app:layout_constraintEnd_toStartOf="@id/mozac_browser_tabstray_close" app:layout_constraintHorizontal_bias="0.0" diff --git a/app/src/main/res/layout/view_synced_tabs_title.xml b/app/src/main/res/layout/view_synced_tabs_title.xml index 5cb4a47c7..871512830 100644 --- a/app/src/main/res/layout/view_synced_tabs_title.xml +++ b/app/src/main/res/layout/view_synced_tabs_title.xml @@ -19,10 +19,11 @@ diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 452546fd3..78fdde920 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -74,6 +74,9 @@ Agora non + + + Pues afitar que Firefox abra automáticamente enllaces nes aplicaciones. Dir a Axustes diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 6b590610b..d2fde3616 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -44,6 +44,9 @@ Τέλος λειτουργίας πολλαπλής επιλογής + + Σε λειτουργία πολλαπλών επιλογών, επιλέξτε καρτέλες για αποθήκευση σε συλλογή + Το %1$s αναπτύσσεται από τη @fork-maintainers. @@ -58,6 +61,9 @@ Διαγραφή συνεδρίας + + + Προσθέστε συντόμευση για άνοιγμα ιδιωτικών καρτελών από την αρχική οθόνη. Προσθήκη συντόμευσης @@ -68,6 +74,9 @@ Όχι τώρα + + + Μπορείτε να ρυθμίσετε το Firefox να ανοίγει αυτόματα συνδέσμους σε εφαρμογές. Μετάβαση στις ρυθμίσεις @@ -259,6 +268,8 @@ Προσαρμοσμένος διακομιστής λογαριασμού Firefox Προσαρμοσμένος διακομιστής Sync + + Ο διακομιστής λογαριασμού Firefox/Sync τροποποιήθηκε. Τερματισμός εφαρμογής για εφαρμογή αλλαγών… Λογαριασμός @@ -769,8 +780,10 @@ Συλλογές Μενού συλλογής + + Συλλέξτε αυτά που σας ενδιαφέρουν.\nΟμαδοποιήστε παρόμοιες αναζητήσεις, σελίδες και καρτέλες για γρήγορη πρόσβαση αργότερα. - Επιλέξτε καρτέλες + Επιλογή καρτελών Επιλογή συλλογής @@ -860,6 +873,8 @@ Διαγραφή και άνοιγμα + + Με την υποστήριξη του Η συλλογή διαγράφηκε @@ -897,6 +912,8 @@ ΑΡΝΗΣΗ Θέλετε σίγουρα να διαγράψετε το %1$s; + + Η αφαίρεση της καρτέλας θα διαγράψει ολόκληρη τη συλλογή. Μπορείτε να δημιουργήσετε νέες συλλογές ανά πάσα στιγμή. Διαγραφή του %1$s; @@ -1018,14 +1035,22 @@ Έχετε ερωτήσεις σχετικά με το επανασχεδιασμένο %s; Θέλετε να μάθετε τι έχει αλλάξει; Λάβετε απαντήσεις εδώ + + Ξεκινήστε το συγχρονισμό σελιδοδεικτών, κωδικών πρόσβασης και άλλων δεδομένων με το λογαριασμό Firefox σας. Μάθετε περισσότερα + + Έχετε συνδεθεί ως %s σε άλλο πρόγραμμα περιήγησης Firefox σε αυτό το τηλέφωνο. Θέλετε να συνδεθείτε με αυτό το λογαριασμό; Ναι, σύνδεση Σύνδεση… Σύνδεση στο Firefox + + Παραμονή εκτός σύνδεσης Το Sync είναι ενεργό @@ -1042,12 +1067,28 @@ Αυστηρή (προτείνεται) Αυστηρή + + Πάρτε θέση + + Δοκιμάστε την περιήγηση με ένα χέρι με την κάτω γραμμή εργαλείων ή μετακινήστε την στο πάνω μέρος. Ιδιωτική περιήγηση + + Άνοιγμα ιδιωτικής καρτέλας μία φορά: Αγγίξτε το εικονίδιο %s. + + Άνοιγμα ιδιωτικών καρτελών κάθε φορά: Ενημερώστε τις ρυθμίσεις ιδιωτικής περιήγησης. Άνοιγμα ρυθμίσεων Το απόρρητό σας + + Έχουμε σχεδιάσει το %s ώστε να έχετε τον έλεγχο του τι θα κοινοποιείτε + στο διαδίκτυο και σε εμάς. + Διαβάστε τη σημείωση απορρήτου μας @@ -1166,6 +1207,8 @@ Fingerprinters Περιεχόμενο καταγραφής + + Κάθε φορά που η ασπίδα είναι μωβ, το %s έχει αποκλείσει ιχνηλάτες σε μια σελίδα. Πατήστε για περισσότερες πληροφορίες. Η προστασία είναι ΕΝΕΡΓΗ για αυτή τη σελίδα @@ -1233,6 +1276,9 @@ Όνομα συντόμευσης + + Μπορείτε εύκολα να προσθέσετε αυτή την ιστοσελίδα στην αρχική οθόνη για άμεση πρόσβαση και ταχύτερη περιήγηση, σαν να ήταν εφαρμογή. + Συνδέσεις και κωδικοί πρόσβασης @@ -1476,6 +1522,8 @@ Έχετε φτάσει το ανώτατο όριο κορυφαίων ιστοσελίδων + + Για να προσθέσετε μια νέα κορυφαία σελίδα, αφαιρέστε μια υπάρχουσα. Πατήστε παρατεταμένα στη σελίδα και επιλέξτε "Αφαίρεση". OK, το κατάλαβα diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 9ce4d776b..b504650f5 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -24,6 +24,29 @@ %1$s malfermitaj langetoj. Tuŝetu por ŝanĝi langetojn. + + %1$d elektitaj + + Aldoni novan kolekton + + Nomo + + Elekti kolekton + + Eliri el la reĝimo de plurelekto + + Konservi elektitajn langetojn en kolekto + + %1$s elektita + + %1$s ne elektita + + Fino de reĝimo de plurelekto + + Komenco de reĝimo de plurelekto. Elektu langetojn por konservi en kolekto. + + Elektita + %1$s estas kreita de @fork-maintainers. @@ -53,6 +76,28 @@ Ne nun + + + Vi povas igi Firefox aŭtomate malfermi ligilojn en programoj. + + Iri al agordoj + + Ignori + + + Aliro al la fimilo postulata. Iru al agordoj de Android, tuŝetu Permesojn kaj poste Permesi. + + Iri al agordoj + + Ignori + + + Aŭtomate fermi langetojn, kiuj ne estis viditaj en la lasta tago, semajno aŭ monato. + + Vidi eblojn + + Ignori + Nova langeto @@ -146,14 +191,12 @@ Skani - - Ŝparvojoj + + Serĉilo Agordoj de serĉilo - - Serĉi per - Ĉi foje serĉi per: + Ĉi foje serĉi per: Alglui ligilon el la tondujo @@ -241,6 +284,8 @@ Ilaro Etoso + + Eka paĝo Personecigi @@ -261,8 +306,8 @@ Iloj por programistoj Fora sencimigo per USB - - Montri serĉajn ŝparvojojn + + Montri serĉilojn Montri serĉajn sugestojn @@ -280,9 +325,14 @@ Malfermi ligilojn per programoj + + Ekstera administranto de elŝutoj Aldonaĵoj + + Sciigoj + Speguli nun @@ -443,6 +493,32 @@ Fermi + + Ĵuse fermitaj langetoj + + Montri tutan historion + + + %d langetoj + + %d langeto + + Estas neniu ĵuse fermita langeto ĉi tie + + + + Fermi langetojn + + Permane + + Post tago + + Post semajno + + Post monato + Malfermitaj langetoj @@ -462,6 +538,10 @@ Konservi en kolekto Dividi ĉiujn langetojn + + Ĵuse fermitaj langetoj + + Agordoj de langetoj Fermi ĉiujn langetojn @@ -510,9 +590,14 @@ Forigi + + Forigi el historio %1$s (Privata reĝimo) + + Konservi + Forigi historion @@ -551,6 +636,13 @@ Neniu historio estas ĉi tie + + + Neniu elŝuto ĉi tie + + %1$d elektitaj + Bedaŭrinde %1$s ne povas ŝargi tiun paĝon. @@ -579,6 +671,8 @@ Elekti dosierujon Ĉu vi certe volas forigi tiun ĉi dosierujon? + + %s forigos la elektitajn elementojn. %1$s forigita @@ -634,8 +728,10 @@ %1$s forigita - + Legosignoj forigitaj + + Elektitaj dosierujoj forigataj MALFARI @@ -703,10 +799,8 @@ Kolektoj Menuo de kolektoj - - Kolekti la aferojn kiu gravas por vi - Grupigi similajn serĉojn, retejojn kaj langetojn por pli rapida posta aliro. + Kolektu la aferojn kiuj gravas por vi.\nGrupigu similajn serĉojn, retejojn kaj langetojn por posta rapida aliro. Elektitaj langetoj @@ -729,6 +823,8 @@ %d elektita langeto Langetoj konservitaj! + + Kolekto konservita! Langeto konservita! @@ -836,6 +932,10 @@ RIFUZI Ĉu vi certe volas forigi %1$s? + + Forigo de tiu ĉi langeto forigos la tutan kolekton. Vi povas krei novajn kolektojn iam ajn. + + Ĉu forigi %1$s? Forigi @@ -952,9 +1052,10 @@ Ĉu vi havas demandojn pri la refasonita %s? Ĉu vi volas scii kio ŝanĝiĝis? Trovu respondojn ĉi tie - - Eltiru la maksimumon el %s. + + Komenci speguli legosignojn, pasvortojn kaj aliajn aferojn per via konto de Firefox. + + Pli da informo @@ -1238,6 +1339,8 @@ en la reto kaj kion vi dividas kun ni. Nekonservitaj nomoj de uzantoj kaj pasvortoj estos montritaj ĉi tie. Nomoj de uzanto kaj pasvortoj por tiuj ĉi retejoj ne estos konservitaj. + + Forigi ĉiujn esceptojn Serĉi legitimilojn @@ -1276,6 +1379,8 @@ en la reto kaj kion vi dividas kun ni. Kopii nomon de uzanto Kopii retejon + + Malfermi retejon en retumilo Montri pasvorton @@ -1428,9 +1533,7 @@ en la reto kaj kion vi dividas kun ni. Jam ekzistas legitimilo kun tiu nomo de uzanto - - Konekti per konto de Firefox. - + Konekti alian aparaton @@ -1444,6 +1547,9 @@ en la reto kaj kion vi dividas kun ni. Komenci seancon por speguli + + Neniu malfermita langeto + Maksimuma nombro de plej vizititaj retejoj atingita @@ -1452,4 +1558,18 @@ en la reto kaj kion vi dividas kun ni. En ordo, mi komprenis - + + Montri pli vizititajn retejojn + + + Forigi + + + Eltiru la maksimumon el %s. + + + Kolekti la aferojn kiu gravas por vi + + Grupigi similajn serĉojn, retejojn kaj langetojn por pli rapida posta aliro. + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 566346f80..a55a1bcdd 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -315,11 +315,11 @@ USB 経由でリモートデバッグする - 検索エンジンを表示 + 検索エンジンを表示する 検索語句の候補を表示する - 音声検索を表示 + 音声検索を表示する プライベートセッションで表示する @@ -969,7 +969,7 @@ フォントサイズを自動調整 - フォントサイズは Android の設定に従います。このフォントサイズの管理は無効化されます。 + フォントサイズは Android の設定に従います。ここでフォントサイズを管理するには無効化してください。 ブラウジングデータを削除 diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 89ee360ec..6328e556c 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -1391,9 +1391,9 @@ Shkyçni pajisjen tuaj - <em>Zoom</em> në krejt sajtet + Zoom në krejt sajtet - Aktivizojeni që të lejoni pickim dhe <em>zoom</em>, edhe në sajte që e pengojnë këtë gjest. + Aktivizojeni që të lejoni pickim dhe zoom, edhe në sajte që e pengojnë këtë gjest. Emrash (A-Z) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 79614b112..4cbb6eeb9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -518,7 +518,7 @@ - 多久後自動關閉分頁 + 自動關閉分頁 手動 diff --git a/app/src/main/res/values/preference_keys.xml b/app/src/main/res/values/preference_keys.xml index 49eba8213..285471310 100644 --- a/app/src/main/res/values/preference_keys.xml +++ b/app/src/main/res/values/preference_keys.xml @@ -126,6 +126,12 @@ pref_home_category + + pref_key_website_pull_to_refresh + pref_key_dynamic_toolbar + pref_key_swipe_toolbar_switch_tabs + pref_key_swipe_toolbar_show_tabs + pref_tabs_tray_settings_category pref_key_tabs_tray_compact_tab diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 530674eb1..d0509f4ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -286,6 +286,8 @@ Theme Home + + Gestures Tabs tray @@ -463,6 +465,16 @@ Follow device theme + + + Pull to refresh + + Scroll to hide toolbar + + Swipe toolbar sideways to switch tabs + + Swipe toolbar up to open tabs + Sessions @@ -1548,7 +1560,7 @@ Top site limit reached - To add a new top site, remove one. Long press the site and select remove. + To add a new top site, remove one. Touch and hold the site and select remove. OK, Got It diff --git a/app/src/main/res/xml/customization_preferences.xml b/app/src/main/res/xml/customization_preferences.xml index d03f5881c..b350c6484 100644 --- a/app/src/main/res/xml/customization_preferences.xml +++ b/app/src/main/res/xml/customization_preferences.xml @@ -104,6 +104,26 @@ android:title="@string/top_sites_toggle_top_frecent_sites" /> + + + + + + + + diff --git a/app/src/test/java/org/mozilla/fenix/collections/DefaultCollectionCreationControllerTest.kt b/app/src/test/java/org/mozilla/fenix/collections/DefaultCollectionCreationControllerTest.kt index ef4e2c5ed..f9e7df531 100644 --- a/app/src/test/java/org/mozilla/fenix/collections/DefaultCollectionCreationControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/collections/DefaultCollectionCreationControllerTest.kt @@ -1,6 +1,7 @@ package org.mozilla.fenix.collections import io.mockk.MockKAnnotations +import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.mockk @@ -69,7 +70,7 @@ class DefaultCollectionCreationControllerTest { controller.saveCollectionName(tabs, "name") verify { dismiss() } - verify { tabCollectionStorage.createCollection("name", listOf(session)) } + coVerify { tabCollectionStorage.createCollection("name", listOf(session)) } verify { metrics.track(Event.CollectionSaved(2, 1)) } } @@ -117,9 +118,9 @@ class DefaultCollectionCreationControllerTest { verifyAll { dismiss() - tabCollectionStorage.renameCollection(collection, "name") metrics.track(Event.CollectionRenamed) } + coVerify { tabCollectionStorage.renameCollection(collection, "name") } } @Test @@ -162,7 +163,7 @@ class DefaultCollectionCreationControllerTest { controller.selectCollection(collection, tabs) verify { dismiss() } - verify { tabCollectionStorage.addTabsToCollection(collection, listOf(session)) } + coVerify { tabCollectionStorage.addTabsToCollection(collection, listOf(session)) } verify { metrics.track(Event.CollectionTabsAdded(2, 1)) } } diff --git a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt index 5a67c7edc..1d31eaf56 100644 --- a/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt +++ b/app/src/test/java/org/mozilla/fenix/components/TestComponents.kt @@ -24,7 +24,7 @@ class TestComponents(private val context: Context) : Components(context) { core.store, search.searchEngineManager, core.webAppShortcutManager, - core.topSiteStorage + core.topSitesStorage ) } override val intentProcessors by lazy { mockk(relaxed = true) } diff --git a/app/src/test/java/org/mozilla/fenix/components/TestCore.kt b/app/src/test/java/org/mozilla/fenix/components/TestCore.kt index 23c61d2db..31bc4234c 100644 --- a/app/src/test/java/org/mozilla/fenix/components/TestCore.kt +++ b/app/src/test/java/org/mozilla/fenix/components/TestCore.kt @@ -27,5 +27,5 @@ class TestCore(context: Context, crashReporter: CrashReporting) : Core(context, override val client = mockk() override val webAppShortcutManager = mockk() override val thumbnailStorage = mockk() - override val topSiteStorage = mockk() + override val topSitesStorage = mockk() } 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 bd82ee71b..11d1fa383 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 @@ -32,6 +32,7 @@ import mozilla.components.feature.search.SearchUseCases import mozilla.components.feature.session.SessionFeature import mozilla.components.feature.session.SessionUseCases import mozilla.components.feature.tab.collections.TabCollection +import mozilla.components.feature.top.sites.DefaultTopSitesStorage import mozilla.components.feature.top.sites.TopSitesUseCases import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.test.rule.MainCoroutineRule @@ -83,6 +84,7 @@ class DefaultBrowserToolbarMenuControllerTest { @RelaxedMockK private lateinit var readerModeController: ReaderModeController @MockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper @RelaxedMockK private lateinit var sessionFeature: SessionFeature + @RelaxedMockK private lateinit var topSitesStorage: DefaultTopSitesStorage @Before fun setUp() { @@ -105,6 +107,7 @@ class DefaultBrowserToolbarMenuControllerTest { every { id } returns R.id.browserFragment } every { currentSession.id } returns "1" + every { settings.topSitesMaxLimit } returns 16 val onComplete = slot<() -> Unit>() every { browserAnimator.captureEngineViewAndDrawStatically(capture(onComplete)) } answers { onComplete.captured.invoke() } @@ -487,7 +490,8 @@ class DefaultBrowserToolbarMenuControllerTest { bookmarkTapped = bookmarkTapped, readerModeController = readerModeController, sessionManager = sessionManager, - sessionFeature = sessionFeatureWrapper + sessionFeature = sessionFeatureWrapper, + topSitesStorage = topSitesStorage ).apply { ioScope = scope } diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/TabCounterMenuTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/TabCounterMenuTest.kt index ae6926090..a85c533b2 100644 --- a/app/src/test/java/org/mozilla/fenix/components/toolbar/TabCounterMenuTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/TabCounterMenuTest.kt @@ -40,7 +40,7 @@ class TabCounterMenuTest { @Test fun `all items use primary text color styling`() { - val items = menu.menuItems(showOnly = null) + val items = menu.menuItems(ToolbarPosition.BOTTOM) assertEquals(4, items.size) val textItems = items.mapNotNull { it as? TextMenuCandidate } @@ -85,7 +85,7 @@ class TabCounterMenuTest { @Test fun `return two new tab items and a close button`() { - val (newTab, newPrivateTab, divider, closeTab) = menu.menuItems(showOnly = null) + val (newTab, newPrivateTab, divider, closeTab) = menu.menuItems(ToolbarPosition.TOP) assertEquals("New tab", (newTab as TextMenuCandidate).text) assertEquals("New private tab", (newPrivateTab as TextMenuCandidate).text) diff --git a/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DefaultDeleteBrowsingDataControllerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DefaultDeleteBrowsingDataControllerTest.kt index 038d4be4f..0d300350b 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DefaultDeleteBrowsingDataControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DefaultDeleteBrowsingDataControllerTest.kt @@ -97,7 +97,7 @@ class DefaultDeleteBrowsingDataControllerTest { fun deleteSitePermissions() = runBlockingTest { controller.deleteSitePermissions() - verify { + coVerify { engine.clearData(Engine.BrowsingData.select(Engine.BrowsingData.ALL_SITE_SETTINGS)) permissionStorage.deleteAllSitePermissions() } diff --git a/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt b/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt index 5ae94620c..314015612 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/deletebrowsingdata/DeleteAndQuitTest.kt @@ -9,7 +9,6 @@ package org.mozilla.fenix.settings.deletebrowsingdata import io.mockk.coVerify import io.mockk.every import io.mockk.mockk -import io.mockk.verify import io.mockk.verifyOrder import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher @@ -72,7 +71,7 @@ class DeleteAndQuitTest { activity.finish() } - verify(exactly = 0) { + coVerify(exactly = 0) { engine.clearData( Engine.BrowsingData.select( Engine.BrowsingData.COOKIES @@ -100,7 +99,7 @@ class DeleteAndQuitTest { deleteAndQuit(activity, this, snackbar) - verify(exactly = 1) { + coVerify(exactly = 1) { snackbar.show() engine.clearData(Engine.BrowsingData.allCaches()) diff --git a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt index 4b0924c15..f0df1e6b8 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/quicksettings/DefaultQuickSettingsControllerTest.kt @@ -7,12 +7,12 @@ package org.mozilla.fenix.settings.quicksettings import androidx.navigation.NavController import androidx.navigation.NavDirections import io.mockk.Runs +import io.mockk.coVerifyOrder import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.spyk import io.mockk.verify -import io.mockk.verifyOrder import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.runBlockingTest @@ -179,7 +179,7 @@ class DefaultQuickSettingsControllerTest { controller.handlePermissionsChange(testPermissions) advanceUntilIdle() - verifyOrder { + coVerifyOrder { permissionStorage.updateSitePermissions(testPermissions) reload(browserSession) } diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 851bb5d31..6cf6bff2e 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 = "58.0.20200906130403" + const val VERSION = "58.0.20200908130811" }