1
0
Fork 0

Copione merged onto master
continuous-integration/drone/push Build is passing Details

master
blallo 2020-06-28 00:01:14 +02:00
commit 2abc9d72d3
74 changed files with 1354 additions and 660 deletions

File diff suppressed because it is too large Load Diff

View File

@ -340,7 +340,7 @@ class BookmarksTest {
} }
multipleSelectionToolbar { multipleSelectionToolbar {
}.clickOpenNewTab { }.openTabDrawer { }.clickOpenNewTab {
verifyNormalModeSelected() verifyNormalModeSelected()
verifyExistingTabList() verifyExistingTabList()
} }
@ -363,7 +363,7 @@ class BookmarksTest {
} }
multipleSelectionToolbar { multipleSelectionToolbar {
}.clickOpenPrivateTab { }.openTabDrawer { }.clickOpenPrivateTab {
verifyPrivateModeSelected() verifyPrivateModeSelected()
verifyExistingTabList() verifyExistingTabList()
} }

View File

@ -216,7 +216,7 @@ class HistoryTest {
} }
multipleSelectionToolbar { multipleSelectionToolbar {
}.clickOpenNewTab { }.openTabDrawer { }.clickOpenNewTab {
verifyExistingTabList() verifyExistingTabList()
verifyNormalModeSelected() verifyNormalModeSelected()
} }
@ -236,7 +236,7 @@ class HistoryTest {
} }
multipleSelectionToolbar { multipleSelectionToolbar {
}.clickOpenPrivateTab { }.openTabDrawer { }.clickOpenPrivateTab {
verifyPrivateModeSelected() verifyPrivateModeSelected()
verifyExistingTabList() verifyExistingTabList()
} }

View File

@ -50,6 +50,12 @@ class SearchTest {
@Test @Test
fun shortcutButtonTest() { fun shortcutButtonTest() {
homeScreen { homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openSearchSubMenu {
enableShowSearchShortcuts()
}.goBack {
}.goBack {
}.openSearch { }.openSearch {
verifySearchWithText() verifySearchWithText()
clickSearchEngineButton("DuckDuckGo") clickSearchEngineButton("DuckDuckGo")
@ -63,6 +69,12 @@ class SearchTest {
@Test @Test
fun shortcutSearchEngineSettingsTest() { fun shortcutSearchEngineSettingsTest() {
homeScreen { homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openSearchSubMenu {
enableShowSearchShortcuts()
}.goBack {
}.goBack {
}.openSearch { }.openSearch {
scrollToSearchEngineSettings() scrollToSearchEngineSettings()
clickSearchEngineSettings() clickSearchEngineSettings()

View File

@ -85,23 +85,26 @@ class LibrarySubMenusMultipleSelectionToolbarRobot {
return BookmarksRobot.Transition() return BookmarksRobot.Transition()
} }
fun clickOpenNewTab(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { fun clickOpenNewTab(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
openInNewTabButton().click() openInNewTabButton().click()
mDevice.waitNotNull(Until.findObject(By.text("Collections")), waitingTime)
HomeScreenRobot().interact()
return HomeScreenRobot.Transition()
}
fun clickOpenPrivateTab(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
openInPrivateTabButton().click()
mDevice.waitNotNull( mDevice.waitNotNull(
Until.findObject(By.text(PRIVATE_SESSION_MESSAGE)), Until.findObject(By.res("org.mozilla.fenix.debug:id/tab_layout")),
waitingTime waitingTime
) )
HomeScreenRobot().interact() TabDrawerRobot().interact()
return HomeScreenRobot.Transition() return TabDrawerRobot.Transition()
}
fun clickOpenPrivateTab(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
openInPrivateTabButton().click()
mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/tab_layout")),
waitingTime
)
TabDrawerRobot().interact()
return TabDrawerRobot.Transition()
} }
} }
} }

View File

@ -36,6 +36,7 @@ class SettingsSubMenuSearchRobot {
selectDefaultSearchEngine(searchEngineName) selectDefaultSearchEngine(searchEngineName)
fun disableShowSearchSuggestions() = toggleShowSearchSuggestions() fun disableShowSearchSuggestions() = toggleShowSearchSuggestions()
fun enableShowSearchShortcuts() = toggleShowSearchShortcuts()
class Transition { class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@ -142,5 +143,16 @@ private fun toggleShowSearchSuggestions() {
.perform(click()) .perform(click())
} }
private fun toggleShowSearchShortcuts() {
onView(withId(androidx.preference.R.id.recycler_view)).perform(
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
hasDescendant(withText("Show search shortcuts"))
)
)
onView(withText("Show search shortcuts"))
.perform(click())
}
private fun goBackButton() = private fun goBackButton() =
onView(CoreMatchers.allOf(withContentDescription("Navigate up"))) onView(CoreMatchers.allOf(withContentDescription("Navigate up")))

View File

@ -26,6 +26,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import androidx.test.uiautomator.By.text import androidx.test.uiautomator.By.text
import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import androidx.test.uiautomator.Until.findObject import androidx.test.uiautomator.Until.findObject
import org.hamcrest.CoreMatchers.allOf import org.hamcrest.CoreMatchers.allOf
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -104,6 +105,20 @@ class TabDrawerRobot {
return ThreeDotMenuMainRobot.Transition() return ThreeDotMenuMainRobot.Transition()
} }
fun openTabDrawer(interact: TabDrawerRobot.() -> Unit): TabDrawerRobot.Transition {
org.mozilla.fenix.ui.robots.mDevice.waitForIdle()
tabsCounter().click()
org.mozilla.fenix.ui.robots.mDevice.waitNotNull(
Until.findObject(By.res("org.mozilla.fenix.debug:id/tab_layout")),
waitingTime
)
TabDrawerRobot().interact()
return TabDrawerRobot.Transition()
}
fun openHomeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition { fun openHomeScreen(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
mDevice.waitForIdle() mDevice.waitForIdle()
@ -196,3 +211,5 @@ private fun tab(title: String) =
withText(title) withText(title)
) )
) )
private fun tabsCounter() = onView(withId(R.id.tab_button))

View File

@ -23,7 +23,7 @@ object FeatureFlags {
/** /**
* Enable tab sync feature * Enable tab sync feature
*/ */
val syncedTabs = Config.channel.isNightlyOrDebug const val syncedTabs = true
/** /**
* Enables new tab tray pref * Enables new tab tray pref

View File

@ -173,6 +173,9 @@ open class FenixApplication : LocaleAwareApplication() {
taskQueue.runIfReadyOrQueue { taskQueue.runIfReadyOrQueue {
Experiments.initialize( Experiments.initialize(
applicationContext = applicationContext, applicationContext = applicationContext,
onExperimentsUpdated = {
ExperimentsManager.initSearchWidgetExperiment(this)
},
configuration = mozilla.components.service.experiments.Configuration( configuration = mozilla.components.service.experiments.Configuration(
httpClient = components.core.client, httpClient = components.core.client,
kintoEndpoint = KINTO_ENDPOINT_PROD kintoEndpoint = KINTO_ENDPOINT_PROD

View File

@ -174,10 +174,14 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
lifecycle.addObserver(BreadcrumbsRecorder(components.analytics.crashReporter, lifecycle.addObserver(BreadcrumbsRecorder(components.analytics.crashReporter,
navHost.navController, ::getBreadcrumbMessage)) navHost.navController, ::getBreadcrumbMessage))
intent val safeIntent = intent?.toSafeIntent()
?.toSafeIntent() safeIntent
?.let(::getIntentSource) ?.let(::getIntentSource)
?.also { components.analytics.metrics.track(Event.OpenedApp(it)) } ?.also { components.analytics.metrics.track(Event.OpenedApp(it)) }
// record on cold startup
safeIntent
?.let(::getIntentAllSource)
?.also { components.analytics.metrics.track(Event.AppRecievedIntent(it)) }
} }
supportActionBar?.hide() supportActionBar?.hide()
@ -250,6 +254,15 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
?.let { it as? TabTrayDialogFragment } ?.let { it as? TabTrayDialogFragment }
?.also { it.dismissAllowingStateLoss() } ?.also { it.dismissAllowingStateLoss() }
} }
// If there is a warm or hot startup, onNewIntent method is always called first.
// Note: This does not work in case of an user sending an intent with ACTION_VIEW
// for example, launch the application, and than use adb to send an intent with
// ACTION_VIEW to open a link. In this case, we will get multiple telemetry events.
intent
.toSafeIntent()
.let(::getIntentAllSource)
?.also { components.analytics.metrics.track(Event.AppRecievedIntent(it)) }
} }
/** /**
@ -320,6 +333,14 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
} }
} }
protected open fun getIntentAllSource(intent: SafeIntent): Event.AppRecievedIntent.Source? {
return when {
intent.isLauncherIntent -> Event.AppRecievedIntent.Source.APP_ICON
intent.action == Intent.ACTION_VIEW -> Event.AppRecievedIntent.Source.LINK
else -> Event.AppRecievedIntent.Source.UNKNOWN
}
}
/** /**
* External sources such as 3rd party links and shortcuts use this function to enter * External sources such as 3rd party links and shortcuts use this function to enter
* private mode directly before the content view is created. Returns the mode set by the intent * private mode directly before the content view is created. Returns the mode set by the intent
@ -490,11 +511,6 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
} }
} }
fun updateThemeForSession(session: Session) {
val sessionMode = BrowsingMode.fromBoolean(session.private)
browsingModeManager.mode = sessionMode
}
override fun attachBaseContext(base: Context) { override fun attachBaseContext(base: Context) {
StrictMode.allowThreadDiskReads().resetPoliciesAfter { StrictMode.allowThreadDiskReads().resetPoliciesAfter {
super.attachBaseContext(base) super.attachBaseContext(base)

View File

@ -9,8 +9,6 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.StrictMode import android.os.StrictMode
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import mozilla.components.feature.intent.processing.IntentProcessor import mozilla.components.feature.intent.processing.IntentProcessor
import org.mozilla.fenix.components.IntentProcessorType import org.mozilla.fenix.components.IntentProcessorType
import org.mozilla.fenix.components.getType import org.mozilla.fenix.components.getType
@ -33,19 +31,17 @@ class IntentReceiverActivity : Activity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
MainScope().launch { // The intent property is nullable, but the rest of the code below
// The intent property is nullable, but the rest of the code below // assumes it is not. If it's null, then we make a new one and open
// assumes it is not. If it's null, then we make a new one and open // the HomeActivity.
// the HomeActivity. val intent = intent?.let { Intent(it) } ?: Intent()
val intent = intent?.let { Intent(it) } ?: Intent() intent.stripUnwantedFlags()
intent.stripUnwantedFlags() processIntent(intent)
processIntent(intent)
}
StartupTimeline.onActivityCreateEndIntentReceiver() StartupTimeline.onActivityCreateEndIntentReceiver()
} }
suspend fun processIntent(intent: Intent) { fun processIntent(intent: Intent) {
// Call process for side effects, short on the first that returns true // Call process for side effects, short on the first that returns true
val processor = getIntentProcessors().firstOrNull { it.process(intent) } val processor = getIntentProcessors().firstOrNull { it.process(intent) }
val intentProcessorType = components.intentProcessors.getType(processor) val intentProcessorType = components.intentProcessors.getType(processor)

View File

@ -78,6 +78,7 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.IntentReceiverActivity import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.FindInPageIntegration import org.mozilla.fenix.components.FindInPageIntegration
@ -690,7 +691,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
@CallSuper @CallSuper
override fun onSessionSelected(session: Session) { override fun onSessionSelected(session: Session) {
(activity as HomeActivity).updateThemeForSession(session) updateThemeForSession(session)
if (!browserInitialized) { if (!browserInitialized) {
// Initializing a new coroutineScope to avoid ConcurrentModificationException in ObserverRegistry // Initializing a new coroutineScope to avoid ConcurrentModificationException in ObserverRegistry
// This will be removed when ObserverRegistry is deprecated by browser-state. // This will be removed when ObserverRegistry is deprecated by browser-state.
@ -720,6 +721,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
components.useCases.sessionUseCases.reload() components.useCases.sessionUseCases.reload()
} }
hideToolbar() hideToolbar()
getSessionById()?.let { updateThemeForSession(it) }
} }
@CallSuper @CallSuper
@ -872,6 +875,14 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
} }
} }
/**
* Set the activity normal/private theme to match the current session.
*/
private fun updateThemeForSession(session: Session) {
val sessionMode = BrowsingMode.fromBoolean(session.private)
(activity as HomeActivity).browsingModeManager.mode = sessionMode
}
/** /**
* Returns the current session. * Returns the current session.
*/ */
@ -997,6 +1008,5 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1 private const val REQUEST_CODE_DOWNLOAD_PERMISSIONS = 1
private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2 private const val REQUEST_CODE_PROMPT_PERMISSIONS = 2
private const val REQUEST_CODE_APP_PERMISSIONS = 3 private const val REQUEST_CODE_APP_PERMISSIONS = 3
private const val SNACKBAR_ELEVATION = 80f
} }
} }

View File

@ -30,7 +30,6 @@ import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tabs.WindowFeature import mozilla.components.feature.tabs.WindowFeature
import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.base.feature.UserInteractionHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.addons.runIfFragmentIsAttached import org.mozilla.fenix.addons.runIfFragmentIsAttached
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
@ -40,8 +39,8 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.navigateSafe import org.mozilla.fenix.ext.navigateSafe
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.resetPoliciesAfter import org.mozilla.fenix.ext.resetPoliciesAfter
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.shortcut.FirstTimePwaObserver import org.mozilla.fenix.shortcut.FirstTimePwaObserver
import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
@ -178,9 +177,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
getSessionById()?.let {
(activity as HomeActivity).updateThemeForSession(it)
}
requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this) requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this)
} }

View File

@ -12,9 +12,12 @@ import mozilla.components.browser.session.SessionManager
import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry
import org.mozilla.fenix.utils.Settings
class UriOpenedObserver( class UriOpenedObserver(
private val settings: Settings,
private val owner: LifecycleOwner, private val owner: LifecycleOwner,
private val sessionManager: SessionManager, private val sessionManager: SessionManager,
metrics: MetricController, metrics: MetricController,
@ -22,6 +25,7 @@ class UriOpenedObserver(
) : SessionManager.Observer { ) : SessionManager.Observer {
constructor(activity: FragmentActivity) : this( constructor(activity: FragmentActivity) : this(
activity.applicationContext.settings(),
activity, activity,
activity.components.core.sessionManager, activity.components.core.sessionManager,
activity.metrics, activity.metrics,
@ -41,20 +45,24 @@ class UriOpenedObserver(
} }
override fun onAllSessionsRemoved() { override fun onAllSessionsRemoved() {
settings.setOpenTabsCount(sessionManager.sessions.filter { !it.private }.size)
sessionManager.sessions.forEach { sessionManager.sessions.forEach {
it.unregister(singleSessionObserver) it.unregister(singleSessionObserver)
} }
} }
override fun onSessionAdded(session: Session) { override fun onSessionAdded(session: Session) {
settings.setOpenTabsCount(sessionManager.sessions.filter { !it.private }.size)
session.register(singleSessionObserver, owner) session.register(singleSessionObserver, owner)
} }
override fun onSessionRemoved(session: Session) { override fun onSessionRemoved(session: Session) {
settings.setOpenTabsCount(sessionManager.sessions.filter { !it.private }.size)
session.unregister(singleSessionObserver) session.unregister(singleSessionObserver)
} }
override fun onSessionsRestored() { override fun onSessionsRestored() {
settings.setOpenTabsCount(sessionManager.sessions.filter { !it.private }.size)
sessionManager.sessions.forEach { sessionManager.sessions.forEach {
it.register(singleSessionObserver, owner) it.register(singleSessionObserver, owner)
} }

View File

@ -8,7 +8,6 @@ package org.mozilla.fenix.collections
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
@ -62,13 +61,21 @@ fun List<Tab>.toSessionBundle(sessionManager: SessionManager): List<Session> {
return this.mapNotNull { sessionManager.findSessionById(it.sessionId) } return this.mapNotNull { sessionManager.findSessionById(it.sessionId) }
} }
/**
* @param store Store used to hold in-memory collection state.
* @param dismiss Callback to dismiss the collection creation dialog.
* @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.
*/
class DefaultCollectionCreationController( class DefaultCollectionCreationController(
private val store: CollectionCreationStore, private val store: CollectionCreationStore,
private val dismiss: () -> Unit, private val dismiss: () -> Unit,
private val metrics: MetricController, private val metrics: MetricController,
private val tabCollectionStorage: TabCollectionStorage, private val tabCollectionStorage: TabCollectionStorage,
private val sessionManager: SessionManager, private val sessionManager: SessionManager,
private val scope: CoroutineScope private val ioScope: CoroutineScope
) : CollectionCreationController { ) : CollectionCreationController {
companion object { companion object {
@ -80,7 +87,7 @@ class DefaultCollectionCreationController(
dismiss() dismiss()
val sessionBundle = tabs.toSessionBundle(sessionManager) val sessionBundle = tabs.toSessionBundle(sessionManager)
scope.launch(IO) { ioScope.launch {
tabCollectionStorage.createCollection(name, sessionBundle) tabCollectionStorage.createCollection(name, sessionBundle)
} }
@ -91,7 +98,7 @@ class DefaultCollectionCreationController(
override fun renameCollection(collection: TabCollection, name: String) { override fun renameCollection(collection: TabCollection, name: String) {
dismiss() dismiss()
scope.launch(IO) { ioScope.launch {
tabCollectionStorage.renameCollection(collection, name) tabCollectionStorage.renameCollection(collection, name)
} }
metrics.track(Event.CollectionRenamed) metrics.track(Event.CollectionRenamed)
@ -121,7 +128,7 @@ class DefaultCollectionCreationController(
override fun selectCollection(collection: TabCollection, tabs: List<Tab>) { override fun selectCollection(collection: TabCollection, tabs: List<Tab>) {
dismiss() dismiss()
val sessionBundle = tabs.toList().toSessionBundle(sessionManager) val sessionBundle = tabs.toList().toSessionBundle(sessionManager)
scope.launch(IO) { ioScope.launch {
tabCollectionStorage tabCollectionStorage
.addTabsToCollection(collection, sessionBundle) .addTabsToCollection(collection, sessionBundle)
} }

View File

@ -14,7 +14,9 @@ import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import kotlinx.android.synthetic.main.fragment_create_collection.view.* import kotlinx.android.synthetic.main.fragment_create_collection.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.plus
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import mozilla.components.browser.state.store.BrowserStore import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.lib.publicsuffixlist.PublicSuffixList import mozilla.components.lib.publicsuffixlist.PublicSuffixList
@ -77,7 +79,7 @@ class CollectionCreationFragment : DialogFragment() {
requireComponents.analytics.metrics, requireComponents.analytics.metrics,
requireComponents.core.tabCollectionStorage, requireComponents.core.tabCollectionStorage,
requireComponents.core.sessionManager, requireComponents.core.sessionManager,
scope = lifecycleScope ioScope = lifecycleScope + Dispatchers.IO
) )
) )
collectionCreationView = CollectionCreationView( collectionCreationView = CollectionCreationView(

View File

@ -188,7 +188,7 @@ class Core(private val context: Context) {
WebNotificationFeature( WebNotificationFeature(
context, engine, icons, R.drawable.ic_status_logo, context, engine, icons, R.drawable.ic_status_logo,
HomeActivity::class.java permissionStorage.permissionsStorage, HomeActivity::class.java
) )
} }
} }

View File

@ -22,16 +22,15 @@ import org.mozilla.fenix.GleanMetrics.CustomTab
import org.mozilla.fenix.GleanMetrics.DownloadNotification import org.mozilla.fenix.GleanMetrics.DownloadNotification
import org.mozilla.fenix.GleanMetrics.ErrorPage import org.mozilla.fenix.GleanMetrics.ErrorPage
import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.Events.preferenceToggled
import org.mozilla.fenix.GleanMetrics.FindInPage import org.mozilla.fenix.GleanMetrics.FindInPage
import org.mozilla.fenix.GleanMetrics.History import org.mozilla.fenix.GleanMetrics.History
import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.GleanMetrics.Logins
import org.mozilla.fenix.GleanMetrics.MediaNotification import org.mozilla.fenix.GleanMetrics.MediaNotification
import org.mozilla.fenix.GleanMetrics.MediaState import org.mozilla.fenix.GleanMetrics.MediaState
import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.GleanMetrics.Metrics
import org.mozilla.fenix.GleanMetrics.Onboarding
import org.mozilla.fenix.GleanMetrics.Pings import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.GleanMetrics.Pocket import org.mozilla.fenix.GleanMetrics.Pocket
import org.mozilla.fenix.GleanMetrics.Onboarding
import org.mozilla.fenix.GleanMetrics.Preferences import org.mozilla.fenix.GleanMetrics.Preferences
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingMode import org.mozilla.fenix.GleanMetrics.PrivateBrowsingMode
import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcut import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcut
@ -98,6 +97,10 @@ private val Event.wrapper: EventWrapper<*>?
{ Events.appOpened.record(it) }, { Events.appOpened.record(it) },
{ Events.appOpenedKeys.valueOf(it) } { Events.appOpenedKeys.valueOf(it) }
) )
is Event.AppRecievedIntent -> EventWrapper(
{ Events.appReceivedIntent.record(it) },
{ Events.appReceivedIntentKeys.valueOf(it) }
)
is Event.SearchBarTapped -> EventWrapper( is Event.SearchBarTapped -> EventWrapper(
{ Events.searchBarTapped.record(it) }, { Events.searchBarTapped.record(it) },
{ Events.searchBarTappedKeys.valueOf(it) } { Events.searchBarTappedKeys.valueOf(it) }
@ -602,6 +605,43 @@ private val Event.wrapper: EventWrapper<*>?
{ ContextualHintTrackingProtection.outsideTap.record(it) } { ContextualHintTrackingProtection.outsideTap.record(it) }
) )
is Event.TabsTrayOpened -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.opened.record(it) }
)
is Event.TabsTrayClosed -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.closed.record(it) }
)
is Event.OpenedExistingTab -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.openedExistingTab.record(it) }
)
is Event.ClosedExistingTab -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.closedExistingTab.record(it) }
)
is Event.TabsTrayPrivateModeTapped -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.privateModeTapped.record(it) }
)
is Event.TabsTrayNormalModeTapped -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.normalModeTapped.record(it) }
)
is Event.NewTabTapped -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.newTabTapped.record(it) }
)
is Event.NewPrivateTabTapped -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.newPrivateTabTapped.record(it) }
)
is Event.TabsTrayMenuOpened -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.menuOpened.record(it) }
)
is Event.TabsTraySaveToCollectionPressed -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.saveToCollection.record(it) }
)
is Event.TabsTrayShareAllTabsPressed -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.shareAllTabs.record(it) }
)
is Event.TabsTrayCloseAllTabsPressed -> EventWrapper<NoExtraKeys>(
{ org.mozilla.fenix.GleanMetrics.TabsTray.closeAllTabs.record(it) }
)
// Don't record other events in Glean: // Don't record other events in Glean:
is Event.AddBookmark -> null is Event.AddBookmark -> null
is Event.OpenedBookmark -> null is Event.OpenedBookmark -> null
@ -661,6 +701,12 @@ class GleanMetricsService(private val context: Context) : MetricsService {
searchWidgetInstalled.set(context.settings().searchWidgetInstalled) searchWidgetInstalled.set(context.settings().searchWidgetInstalled)
val openTabsCount = context.settings().openTabsCount
hasOpenTabs.set(openTabsCount > 0)
if (openTabsCount > 0) {
tabsOpenCount.add(openTabsCount)
}
val topSitesSize = context.settings().topSitesSize val topSitesSize = context.settings().topSitesSize
hasTopSites.set(topSitesSize > 0) hasTopSites.set(topSitesSize > 0)
if (topSitesSize > 0) { if (topSitesSize > 0) {

View File

@ -185,6 +185,19 @@ sealed class Event {
object ContextualHintETPOutsideTap : Event() object ContextualHintETPOutsideTap : Event()
object ContextualHintETPInsideTap : Event() object ContextualHintETPInsideTap : Event()
object TabsTrayOpened : Event()
object TabsTrayClosed : Event()
object OpenedExistingTab : Event()
object ClosedExistingTab : Event()
object TabsTrayPrivateModeTapped : Event()
object TabsTrayNormalModeTapped : Event()
object NewTabTapped : Event()
object NewPrivateTabTapped : Event()
object TabsTrayMenuOpened : Event()
object TabsTraySaveToCollectionPressed : Event()
object TabsTrayShareAllTabsPressed : Event()
object TabsTrayCloseAllTabsPressed : Event()
// Interaction events with extras // Interaction events with extras
data class OnboardingToolbarPosition(val position: Position) : Event() { data class OnboardingToolbarPosition(val position: Position) : Event() {
enum class Position { TOP, BOTTOM } enum class Position { TOP, BOTTOM }
@ -296,6 +309,13 @@ sealed class Event {
get() = hashMapOf(Events.appOpenedKeys.source to source.name) get() = hashMapOf(Events.appOpenedKeys.source to source.name)
} }
data class AppRecievedIntent(val source: Source) : Event() {
enum class Source { APP_ICON, LINK, CUSTOM_TAB, UNKNOWN }
override val extras: Map<Events.appReceivedIntentKeys, String>?
get() = hashMapOf(Events.appReceivedIntentKeys.source to source.name)
}
data class CollectionSaveButtonPressed(val fromScreen: String) : Event() { data class CollectionSaveButtonPressed(val fromScreen: String) : Event() {
override val extras: Map<Collections.saveButtonKeys, String>? override val extras: Map<Collections.saveButtonKeys, String>?
get() = mapOf(Collections.saveButtonKeys.fromScreen to fromScreen) get() = mapOf(Collections.saveButtonKeys.fromScreen to fromScreen)

View File

@ -19,6 +19,7 @@ import mozilla.components.browser.search.provider.SearchEngineList
import mozilla.components.browser.search.provider.SearchEngineProvider import mozilla.components.browser.search.provider.SearchEngineProvider
import mozilla.components.browser.search.provider.filter.SearchEngineFilter import mozilla.components.browser.search.provider.filter.SearchEngineFilter
import mozilla.components.browser.search.provider.localization.LocaleSearchLocalizationProvider import mozilla.components.browser.search.provider.localization.LocaleSearchLocalizationProvider
import mozilla.components.browser.search.provider.localization.SearchLocalization
import mozilla.components.browser.search.provider.localization.SearchLocalizationProvider import mozilla.components.browser.search.provider.localization.SearchLocalizationProvider
import mozilla.components.service.location.LocationService import mozilla.components.service.location.LocationService
import mozilla.components.service.location.MozillaLocationService import mozilla.components.service.location.MozillaLocationService
@ -51,12 +52,18 @@ open class FenixSearchEngineProvider(
AssetsSearchEngineProvider(localizationProvider).loadSearchEngines(context) AssetsSearchEngineProvider(localizationProvider).loadSearchEngines(context)
} }
private val loadedRegion = async { localizationProvider.determineRegion() }
// https://github.com/mozilla-mobile/fenix/issues/9935 // https://github.com/mozilla-mobile/fenix/issues/9935
// Adds a Locale search engine provider as a fallback in case the MLS lookup takes longer // Adds a Locale search engine provider as a fallback in case the MLS lookup takes longer
// than the time it takes for a user to try to search. // than the time it takes for a user to try to search.
private val fallBackEngines = async { private val fallbackLocationService: SearchLocalizationProvider = LocaleSearchLocalizationProvider()
AssetsSearchEngineProvider(LocaleSearchLocalizationProvider()).loadSearchEngines(context) private val fallBackProvider =
} AssetsSearchEngineProvider(fallbackLocationService)
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
open val fallbackEngines = async { fallBackProvider.loadSearchEngines(context) }
private val fallbackRegion = async { fallbackLocationService.determineRegion() }
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
open val bundledSearchEngines = async { open val bundledSearchEngines = async {
@ -88,7 +95,15 @@ open class FenixSearchEngineProvider(
if (loadedSearchEngines.isCompleted) { if (loadedSearchEngines.isCompleted) {
loadedSearchEngines loadedSearchEngines
} else { } else {
fallBackEngines fallbackEngines
}
private val region: Deferred<SearchLocalization>
get() =
if (loadedRegion.isCompleted) {
loadedRegion
} else {
fallbackRegion
} }
fun getDefaultEngine(context: Context): SearchEngine { fun getDefaultEngine(context: Context): SearchEngine {
@ -199,7 +214,11 @@ open class FenixSearchEngineProvider(
} }
if (!prefs.contains(installedEnginesKey)) { if (!prefs.contains(installedEnginesKey)) {
val defaultSet = baseSearchEngines.await() val searchEngines =
if (baseSearchEngines.isCompleted) baseSearchEngines
else fallbackEngines
val defaultSet = searchEngines.await()
.list .list
.map { it.identifier } .map { it.identifier }
.toSet() .toSet()
@ -215,7 +234,7 @@ open class FenixSearchEngineProvider(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
suspend fun localeAwareInstalledEnginesKey(): String { suspend fun localeAwareInstalledEnginesKey(): String {
val tag = localizationProvider.determineRegion().let { val tag = region.await().let {
val region = it.region?.let { region -> val region = it.region?.let { region ->
if (region.isEmpty()) "" else "-$region" if (region.isEmpty()) "" else "-$region"
} }

View File

@ -67,7 +67,7 @@ class CustomTabsIntegration(
) )
} }
toolbar.background = getDrawable(activity, R.drawable.toolbar_background_private) toolbar.background = getDrawable(activity, R.drawable.toolbar_background)
} }
} }

View File

@ -41,6 +41,8 @@ open class ExternalAppBrowserActivity : HomeActivity() {
final override fun getIntentSource(intent: SafeIntent) = Event.OpenedApp.Source.CUSTOM_TAB final override fun getIntentSource(intent: SafeIntent) = Event.OpenedApp.Source.CUSTOM_TAB
final override fun getIntentAllSource(intent: SafeIntent) = Event.AppRecievedIntent.Source.CUSTOM_TAB
final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId() final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId()
override fun getNavDirections( override fun getNavDirections(

View File

@ -9,6 +9,7 @@ import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT import android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import kotlinx.coroutines.runBlocking
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.Session.Source import mozilla.components.browser.session.Session.Source
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
@ -52,16 +53,17 @@ class FennecWebAppIntentProcessor(
* A custom tab config is also set so a custom tab toolbar can be shown when the user leaves * A custom tab config is also set so a custom tab toolbar can be shown when the user leaves
* the scope defined in the manifest. * the scope defined in the manifest.
*/ */
override suspend fun process(intent: Intent): Boolean { override fun process(intent: Intent): Boolean {
val safeIntent = intent.toSafeIntent() val safeIntent = intent.toSafeIntent()
val url = safeIntent.dataString val url = safeIntent.dataString
return if (!url.isNullOrEmpty() && matches(intent)) { return if (!url.isNullOrEmpty() && matches(intent)) {
val webAppManifest = loadManifest(safeIntent, url) val webAppManifest = runBlocking { loadManifest(safeIntent, url) }
val session = Session(url, private = false, source = Source.HOME_SCREEN) val session = Session(url, private = false, source = Source.HOME_SCREEN)
session.webAppManifest = webAppManifest session.webAppManifest = webAppManifest
session.customTabConfig = webAppManifest?.toCustomTabConfig() ?: createFallbackCustomTabConfig() session.customTabConfig =
webAppManifest?.toCustomTabConfig() ?: createFallbackCustomTabConfig()
sessionManager.add(session) sessionManager.add(session)
loadUrlUseCase(url, session, EngineSession.LoadUrlFlags.external()) loadUrlUseCase(url, session, EngineSession.LoadUrlFlags.external())

View File

@ -35,7 +35,7 @@ class FennecBookmarkShortcutsIntentProcessor(
* If this is an Intent for a Fennec pinned website shortcut * If this is an Intent for a Fennec pinned website shortcut
* prepare it for opening website's URL in a new tab. * prepare it for opening website's URL in a new tab.
*/ */
override suspend fun process(intent: Intent): Boolean { override fun process(intent: Intent): Boolean {
val safeIntent = intent.toSafeIntent() val safeIntent = intent.toSafeIntent()
val url = safeIntent.dataString val url = safeIntent.dataString

View File

@ -204,14 +204,14 @@ class BookmarkFragment : LibraryPageFragment<BookmarkNode>(), UserInteractionHan
R.id.open_bookmarks_in_new_tabs_multi_select -> { R.id.open_bookmarks_in_new_tabs_multi_select -> {
openItemsInNewTab { node -> node.url } openItemsInNewTab { node -> node.url }
navigate(BookmarkFragmentDirections.actionGlobalHome()) navigate(BookmarkFragmentDirections.actionGlobalTabTrayDialogFragment())
metrics?.track(Event.OpenedBookmarksInNewTabs) metrics?.track(Event.OpenedBookmarksInNewTabs)
true true
} }
R.id.open_bookmarks_in_private_tabs_multi_select -> { R.id.open_bookmarks_in_private_tabs_multi_select -> {
openItemsInNewTab(private = true) { node -> node.url } openItemsInNewTab(private = true) { node -> node.url }
navigate(BookmarkFragmentDirections.actionGlobalHome()) navigate(BookmarkFragmentDirections.actionGlobalTabTrayDialogFragment())
metrics?.track(Event.OpenedBookmarksInPrivateTabs) metrics?.track(Event.OpenedBookmarksInPrivateTabs)
true true
} }

View File

@ -183,7 +183,7 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
nav( nav(
R.id.historyFragment, R.id.historyFragment,
HistoryFragmentDirections.actionGlobalHome() HistoryFragmentDirections.actionGlobalTabTrayDialogFragment()
) )
true true
} }
@ -199,7 +199,7 @@ class HistoryFragment : LibraryPageFragment<HistoryItem>(), UserInteractionHandl
} }
nav( nav(
R.id.historyFragment, R.id.historyFragment,
HistoryFragmentDirections.actionGlobalHome() HistoryFragmentDirections.actionGlobalTabTrayDialogFragment()
) )
true true
} }

View File

@ -294,8 +294,6 @@ class SearchFragment : Fragment(), UserInteractionHandler {
view.search_suggestions_onboarding.setOnInflateListener((stubListener)) view.search_suggestions_onboarding.setOnInflateListener((stubListener))
view.toolbar_wrapper.clipToOutline = false
fill_link_from_clipboard.setOnClickListener { fill_link_from_clipboard.setOnClickListener {
(activity as HomeActivity) (activity as HomeActivity)
.openToBrowserAndLoad( .openToBrowserAndLoad(

View File

@ -8,7 +8,6 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.BrowserToolbar
@ -16,7 +15,6 @@ import mozilla.components.concept.engine.Engine
import mozilla.components.concept.storage.HistoryStorage import mozilla.components.concept.storage.HistoryStorage
import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature
import mozilla.components.support.ktx.android.content.getColorFromAttr import mozilla.components.support.ktx.android.content.getColorFromAttr
import mozilla.components.support.ktx.android.util.dpToPx
import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.search.SearchFragmentState import org.mozilla.fenix.search.SearchFragmentState
@ -64,8 +62,6 @@ class ToolbarView(
view.apply { view.apply {
editMode() editMode()
elevation = TOOLBAR_ELEVATION_IN_DP.dpToPx(resources.displayMetrics).toFloat()
setOnUrlCommitListener { setOnUrlCommitListener {
// We're hiding the keyboard as early as possible to prevent the engine view // We're hiding the keyboard as early as possible to prevent the engine view
// from resizing in case the BrowserFragment is being displayed before the // from resizing in case the BrowserFragment is being displayed before the
@ -80,8 +76,6 @@ class ToolbarView(
context, ThemeManager.resolveAttribute(R.attr.foundation, context) context, ThemeManager.resolveAttribute(R.attr.foundation, context)
) )
layoutParams.height = CoordinatorLayout.LayoutParams.MATCH_PARENT
edit.hint = context.getString(R.string.search_hint) edit.hint = context.getString(R.string.search_hint)
edit.colors = edit.colors.copy( edit.colors = edit.colors.copy(
@ -156,8 +150,4 @@ class ToolbarView(
view.edit.setIcon(icon, searchState.searchEngineSource.searchEngine.name) view.edit.setIcon(icon, searchState.searchEngineSource.searchEngine.name)
} }
companion object {
private const val TOOLBAR_ELEVATION_IN_DP = 16
}
} }

View File

@ -12,7 +12,8 @@ import org.mozilla.fenix.ext.showToolbar
/** /**
* Lets the user customize Private browsing options. * Lets the user customize Private browsing options.
*/ */
class SecretSettingsPreference : PreferenceFragmentCompat() { class SecretSettingsFragment : PreferenceFragmentCompat() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
showToolbar(getString(R.string.preferences_debug_settings)) showToolbar(getString(R.string.preferences_debug_settings))

View File

@ -25,13 +25,13 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.crashes.CrashListActivity import org.mozilla.fenix.crashes.CrashListActivity
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.Do
import org.mozilla.fenix.settings.SupportUtils import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.settings.about.AboutItemType.LICENSING_INFO import org.mozilla.fenix.settings.about.AboutItemType.LICENSING_INFO
import org.mozilla.fenix.settings.about.AboutItemType.PRIVACY_NOTICE import org.mozilla.fenix.settings.about.AboutItemType.PRIVACY_NOTICE
import org.mozilla.fenix.settings.about.AboutItemType.RIGHTS import org.mozilla.fenix.settings.about.AboutItemType.RIGHTS
import org.mozilla.fenix.settings.about.AboutItemType.SUPPORT import org.mozilla.fenix.settings.about.AboutItemType.SUPPORT
import org.mozilla.fenix.settings.about.AboutItemType.WHATS_NEW import org.mozilla.fenix.settings.about.AboutItemType.WHATS_NEW
import org.mozilla.fenix.utils.Do
import org.mozilla.fenix.whatsnew.WhatsNew import org.mozilla.fenix.whatsnew.WhatsNew
import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig
@ -145,39 +145,39 @@ class AboutFragment : Fragment(), AboutPageListener {
val context = requireContext() val context = requireContext()
return listOf( return listOf(
AboutPageItem.Item( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
WHATS_NEW, WHATS_NEW,
SupportUtils.getWhatsNewUrl(context) SupportUtils.getWhatsNewUrl(context)
), getString(R.string.about_whats_new, getString(R.string.app_name)) ), getString(R.string.about_whats_new, getString(R.string.app_name))
), ),
AboutPageItem.Item( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
SUPPORT, SUPPORT,
SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.HELP) SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.HELP)
), getString(R.string.about_support) ), getString(R.string.about_support)
), ),
AboutPageItem.Item( AboutPageItem(
AboutItem.Crashes, AboutItem.Crashes,
getString(R.string.about_crashes) getString(R.string.about_crashes)
), ),
AboutPageItem.Item( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
PRIVACY_NOTICE, PRIVACY_NOTICE,
SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE) SupportUtils.getMozillaPageUrl(SupportUtils.MozillaPage.PRIVATE_NOTICE)
), getString(R.string.about_privacy_notice) ), getString(R.string.about_privacy_notice)
), ),
AboutPageItem.Item( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
RIGHTS, RIGHTS,
SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS) SupportUtils.getSumoURLForTopic(context, SupportUtils.SumoTopic.YOUR_RIGHTS)
), getString(R.string.about_know_your_rights) ), getString(R.string.about_know_your_rights)
), ),
AboutPageItem.Item( AboutPageItem(
AboutItem.ExternalLink(LICENSING_INFO, ABOUT_LICENSE_URL), AboutItem.ExternalLink(LICENSING_INFO, ABOUT_LICENSE_URL),
getString(R.string.about_licensing_information) getString(R.string.about_licensing_information)
), ),
AboutPageItem.Item( AboutPageItem(
AboutItem.Libraries, AboutItem.Libraries,
getString(R.string.about_other_open_source_libraries) getString(R.string.about_other_open_source_libraries)
) )

View File

@ -14,6 +14,4 @@ enum class AboutItemType {
WHATS_NEW, SUPPORT, PRIVACY_NOTICE, RIGHTS, LICENSING_INFO WHATS_NEW, SUPPORT, PRIVACY_NOTICE, RIGHTS, LICENSING_INFO
} }
sealed class AboutPageItem { data class AboutPageItem(val type: AboutItem, val title: String)
data class Item(val type: AboutItem, val title: String) : AboutPageItem()
}

View File

@ -20,19 +20,16 @@ class AboutPageAdapter(private val listener: AboutPageListener) :
} }
override fun onBindViewHolder(holder: AboutItemViewHolder, position: Int) { override fun onBindViewHolder(holder: AboutItemViewHolder, position: Int) {
holder.bind(getItem(position) as AboutPageItem.Item) holder.bind(getItem(position))
} }
private object DiffCallback : DiffUtil.ItemCallback<AboutPageItem>() { private object DiffCallback : DiffUtil.ItemCallback<AboutPageItem>() {
override fun areItemsTheSame(oldItem: AboutPageItem, newItem: AboutPageItem) = override fun areItemsTheSame(oldItem: AboutPageItem, newItem: AboutPageItem) =
oldItem === newItem oldItem.title == newItem.title
override fun areContentsTheSame(oldItem: AboutPageItem, newItem: AboutPageItem) = override fun areContentsTheSame(oldItem: AboutPageItem, newItem: AboutPageItem) =
when (oldItem) { oldItem == newItem
is AboutPageItem.Item ->
newItem is AboutPageItem.Item && oldItem.title == newItem.title
}
} }
} }

View File

@ -17,7 +17,7 @@ class AboutItemViewHolder(
) : RecyclerView.ViewHolder(view) { ) : RecyclerView.ViewHolder(view) {
private val title = view.about_item_title private val title = view.about_item_title
private lateinit var item: AboutPageItem.Item private lateinit var item: AboutPageItem
init { init {
itemView.setOnClickListener { itemView.setOnClickListener {
@ -25,7 +25,7 @@ class AboutItemViewHolder(
} }
} }
fun bind(item: AboutPageItem.Item) { fun bind(item: AboutPageItem) {
this.item = item this.item = item
title.text = item.title title.text = item.title
} }

View File

@ -23,9 +23,8 @@ class LocaleAdapter(private val interactor: LocaleSettingsViewInteractor) :
private var selectedLocale: Locale = Locale.getDefault() private var selectedLocale: Locale = Locale.getDefault()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseLocaleViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseLocaleViewHolder {
val view = val view = LayoutInflater.from(parent.context)
LayoutInflater.from(parent.context) .inflate(R.layout.locale_settings_item, parent, false)
.inflate(R.layout.locale_settings_item, parent, false)
return when (viewType) { return when (viewType) {
ItemType.DEFAULT.ordinal -> SystemLocaleViewHolder( ItemType.DEFAULT.ordinal -> SystemLocaleViewHolder(

View File

@ -10,7 +10,7 @@ import android.view.ViewGroup
import androidx.core.content.ContextCompat.getColor import androidx.core.content.ContextCompat.getColor
import androidx.core.view.isVisible import androidx.core.view.isVisible
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.quicksettings_website_info.view.* import kotlinx.android.synthetic.main.quicksettings_website_info.*
import mozilla.components.support.ktx.android.content.getDrawableWithTint import mozilla.components.support.ktx.android.content.getDrawableWithTint
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -19,13 +19,14 @@ import org.mozilla.fenix.R
* *
* Currently it does not support any user interaction. * Currently it does not support any user interaction.
* *
* @param containerView [ViewGroup] in which this View will inflate itself. * @param container [ViewGroup] in which this View will inflate itself.
*/ */
class WebsiteInfoView( class WebsiteInfoView(
override val containerView: ViewGroup container: ViewGroup
) : LayoutContainer { ) : LayoutContainer {
val view: View = LayoutInflater.from(containerView.context)
.inflate(R.layout.quicksettings_website_info, containerView, true) override val containerView: View = LayoutInflater.from(container.context)
.inflate(R.layout.quicksettings_website_info, container, true)
/** /**
* Allows changing what this View displays. * Allows changing what this View displays.
@ -39,25 +40,25 @@ class WebsiteInfoView(
bindCertificateName(state.certificateName) bindCertificateName(state.certificateName)
} }
private fun bindUrl(url: String) { private fun bindUrl(websiteUrl: String) {
view.url.text = url url.text = websiteUrl
} }
private fun bindTitle(title: String) { private fun bindTitle(websiteTitle: String) {
view.title.text = title title.text = websiteTitle
} }
private fun bindCertificateName(cert: String) { private fun bindCertificateName(cert: String) {
val certificateLabel = view.context.getString(R.string.certificate_info_verified_by, cert) val certificateLabel = containerView.context.getString(R.string.certificate_info_verified_by, cert)
view.certificateInfo.text = certificateLabel certificateInfo.text = certificateLabel
view.certificateInfo.isVisible = cert.isNotEmpty() certificateInfo.isVisible = cert.isNotEmpty()
} }
private fun bindSecurityInfo(uiValues: WebsiteSecurityUiValues) { private fun bindSecurityInfo(uiValues: WebsiteSecurityUiValues) {
val tint = getColor(view.context, uiValues.iconTintRes) val tint = getColor(containerView.context, uiValues.iconTintRes)
view.securityInfo.setText(uiValues.securityInfoRes) securityInfo.setText(uiValues.securityInfoRes)
view.securityInfoIcon.setImageDrawable( securityInfoIcon.setImageDrawable(
view.context.getDrawableWithTint(uiValues.iconRes, tint) containerView.context.getDrawableWithTint(uiValues.iconRes, tint)
) )
} }
} }

View File

@ -28,7 +28,7 @@ class NewTabShortcutIntentProcessor : IntentProcessor {
* @param intent The intent to process. * @param intent The intent to process.
* @return True if the intent was processed, otherwise false. * @return True if the intent was processed, otherwise false.
*/ */
override suspend fun process(intent: Intent): Boolean { override fun process(intent: Intent): Boolean {
val safeIntent = SafeIntent(intent) val safeIntent = SafeIntent(intent)
val (searchExtra, startPrivateMode) = when (safeIntent.action) { val (searchExtra, startPrivateMode) = when (safeIntent.action) {
ACTION_OPEN_TAB -> StartSearchIntentProcessor.STATIC_SHORTCUT_NEW_TAB to false ACTION_OPEN_TAB -> StartSearchIntentProcessor.STATIC_SHORTCUT_NEW_TAB to false

View File

@ -10,11 +10,13 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.app.AppCompatDialogFragment
import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.component_tabstray.view.* import kotlinx.android.synthetic.main.component_tabstray.view.*
import kotlinx.android.synthetic.main.component_tabstray_fab.view.*
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.* import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.*
import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.view.* import kotlinx.android.synthetic.main.fragment_tab_tray_dialog.view.*
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
@ -31,6 +33,7 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.TabCollectionStorage import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.utils.allowUndo import org.mozilla.fenix.utils.allowUndo
@ -42,6 +45,10 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
private val tabTrayView: TabTrayView private val tabTrayView: TabTrayView
get() = _tabTrayView!! get() = _tabTrayView!!
private val snackbarAnchor: View?
get() = if (tabTrayView.fabView.new_tab_button.isVisible) tabTrayView.fabView.new_tab_button
else null
private val collectionStorageObserver = object : TabCollectionStorage.Observer { private val collectionStorageObserver = object : TabCollectionStorage.Observer {
override fun onCollectionCreated(title: String, sessions: List<Session>) { override fun onCollectionCreated(title: String, sessions: List<Session>) {
showCollectionSnackbar() showCollectionSnackbar()
@ -54,11 +61,13 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
private val selectTabUseCase = object : TabsUseCases.SelectTabUseCase { private val selectTabUseCase = object : TabsUseCases.SelectTabUseCase {
override fun invoke(tabId: String) { override fun invoke(tabId: String) {
requireContext().components.analytics.metrics.track(Event.OpenedExistingTab)
requireComponents.useCases.tabsUseCases.selectTab(tabId) requireComponents.useCases.tabsUseCases.selectTab(tabId)
navigateToBrowser() navigateToBrowser()
} }
override fun invoke(session: Session) { override fun invoke(session: Session) {
requireContext().components.analytics.metrics.track(Event.OpenedExistingTab)
requireComponents.useCases.tabsUseCases.selectTab(session) requireComponents.useCases.tabsUseCases.selectTab(session)
navigateToBrowser() navigateToBrowser()
} }
@ -66,11 +75,13 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
private val removeTabUseCase = object : TabsUseCases.RemoveTabUseCase { private val removeTabUseCase = object : TabsUseCases.RemoveTabUseCase {
override fun invoke(sessionId: String) { override fun invoke(sessionId: String) {
requireContext().components.analytics.metrics.track(Event.ClosedExistingTab)
showUndoSnackbarForTab(sessionId) showUndoSnackbarForTab(sessionId)
requireComponents.useCases.tabsUseCases.removeTab(sessionId) requireComponents.useCases.tabsUseCases.removeTab(sessionId)
} }
override fun invoke(session: Session) { override fun invoke(session: Session) {
requireContext().components.analytics.metrics.track(Event.ClosedExistingTab)
showUndoSnackbarForTab(session.id) showUndoSnackbarForTab(session.id)
requireComponents.useCases.tabsUseCases.removeTab(session) requireComponents.useCases.tabsUseCases.removeTab(session)
} }
@ -134,6 +145,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
) )
tabLayout.setOnClickListener { tabLayout.setOnClickListener {
requireContext().components.analytics.metrics.track(Event.TabsTrayClosed)
dismissAllowingStateLoss() dismissAllowingStateLoss()
} }
@ -182,7 +194,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
sessionManager.add(snapshot.session, isSelected, engineSessionState = state) sessionManager.add(snapshot.session, isSelected, engineSessionState = state)
}, },
operation = { }, operation = { },
elevation = ELEVATION elevation = ELEVATION,
anchorView = snackbarAnchor
) )
} }
} }
@ -226,7 +239,8 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
context?.components?.core?.sessionManager?.restore(snapshot) context?.components?.core?.sessionManager?.restore(snapshot)
}, },
operation = { }, operation = { },
elevation = ELEVATION elevation = ELEVATION,
anchorView = snackbarAnchor
) )
} }
} }
@ -239,6 +253,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
isDisplayedWithBrowserToolbar = true, isDisplayedWithBrowserToolbar = true,
view = (view as View) view = (view as View)
) )
.setAnchorView(snackbarAnchor)
.setText(requireContext().getString(R.string.create_collection_tabs_saved)) .setText(requireContext().getString(R.string.create_collection_tabs_saved))
.setAction(requireContext().getString(R.string.create_collection_view)) { .setAction(requireContext().getString(R.string.create_collection_view)) {
dismissAllowingStateLoss() dismissAllowingStateLoss()

View File

@ -30,6 +30,7 @@ import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.tabstray.BrowserTabsTray import mozilla.components.browser.tabstray.BrowserTabsTray
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.settings
@ -64,6 +65,8 @@ class TabTrayView(
get() = container get() = container
init { init {
container.context.components.analytics.metrics.track(Event.TabsTrayOpened)
val hasAccessibilityEnabled = view.context.settings().accessibilityServicesEnabled val hasAccessibilityEnabled = view.context.settings().accessibilityServicesEnabled
toggleFabText(isPrivate) toggleFabText(isPrivate)
@ -81,6 +84,7 @@ class TabTrayView(
override fun onStateChanged(bottomSheet: View, newState: Int) { override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) { if (newState == BottomSheetBehavior.STATE_HIDDEN) {
container.context.components.analytics.metrics.track(Event.TabsTrayClosed)
interactor.onTabTrayDismissed() interactor.onTabTrayDismissed()
} }
} }
@ -154,6 +158,7 @@ class TabTrayView(
} }
view.tab_tray_overflow.setOnClickListener { view.tab_tray_overflow.setOnClickListener {
container.context.components.analytics.metrics.track(Event.TabsTrayMenuOpened)
menu = tabTrayItemMenu.menuBuilder.build(container.context) menu = tabTrayItemMenu.menuBuilder.build(container.context)
menu?.show(it) menu?.show(it)
?.also { pu -> ?.also { pu ->
@ -167,6 +172,7 @@ class TabTrayView(
view.tab_tray_new_tab.apply { view.tab_tray_new_tab.apply {
isVisible = hasAccessibilityEnabled isVisible = hasAccessibilityEnabled
setOnClickListener { setOnClickListener {
sendNewTabEvent(isPrivateModeSelected)
interactor.onNewTabTapped(isPrivateModeSelected) interactor.onNewTabTapped(isPrivateModeSelected)
} }
} }
@ -174,11 +180,22 @@ class TabTrayView(
fabView.new_tab_button.apply { fabView.new_tab_button.apply {
isVisible = !hasAccessibilityEnabled isVisible = !hasAccessibilityEnabled
setOnClickListener { setOnClickListener {
sendNewTabEvent(isPrivateModeSelected)
interactor.onNewTabTapped(isPrivateModeSelected) interactor.onNewTabTapped(isPrivateModeSelected)
} }
} }
} }
private fun sendNewTabEvent(isPrivateModeSelected: Boolean) {
val eventToSend = if (isPrivateModeSelected) {
Event.NewPrivateTabTapped
} else {
Event.NewTabTapped
}
container.context.components.analytics.metrics.track(eventToSend)
}
fun expand() { fun expand() {
behavior.state = BottomSheetBehavior.STATE_EXPANDED behavior.state = BottomSheetBehavior.STATE_EXPANDED
} }
@ -195,6 +212,12 @@ class TabTrayView(
updateState(view.context.components.core.store.state) updateState(view.context.components.core.store.state)
scrollToSelectedTab() scrollToSelectedTab()
if (isPrivateModeSelected) {
container.context.components.analytics.metrics.track(Event.TabsTrayPrivateModeTapped)
} else {
container.context.components.analytics.metrics.track(Event.TabsTrayNormalModeTapped)
}
} }
override fun onTabReselected(tab: TabLayout.Tab?) { /*noop*/ } override fun onTabReselected(tab: TabLayout.Tab?) { /*noop*/ }
@ -294,6 +317,7 @@ class TabTrayItemMenu(
context.getString(R.string.tab_tray_menu_item_save), context.getString(R.string.tab_tray_menu_item_save),
textColorResource = R.color.primary_text_normal_theme textColorResource = R.color.primary_text_normal_theme
) { ) {
context.components.analytics.metrics.track(Event.TabsTraySaveToCollectionPressed)
onItemTapped.invoke(Item.SaveToCollection) onItemTapped.invoke(Item.SaveToCollection)
}.apply { visible = shouldShowSaveToCollection }, }.apply { visible = shouldShowSaveToCollection },
@ -301,6 +325,7 @@ class TabTrayItemMenu(
context.getString(R.string.tab_tray_menu_item_share), context.getString(R.string.tab_tray_menu_item_share),
textColorResource = R.color.primary_text_normal_theme textColorResource = R.color.primary_text_normal_theme
) { ) {
context.components.analytics.metrics.track(Event.TabsTrayShareAllTabsPressed)
onItemTapped.invoke(Item.ShareAllTabs) onItemTapped.invoke(Item.ShareAllTabs)
}, },
@ -308,6 +333,7 @@ class TabTrayItemMenu(
context.getString(R.string.tab_tray_menu_item_close), context.getString(R.string.tab_tray_menu_item_close),
textColorResource = R.color.primary_text_normal_theme textColorResource = R.color.primary_text_normal_theme
) { ) {
context.components.analytics.metrics.track(Event.TabsTrayCloseAllTabsPressed)
onItemTapped.invoke(Item.CloseAllTabs) onItemTapped.invoke(Item.CloseAllTabs)
} }
) )

View File

@ -309,7 +309,7 @@ class Settings private constructor(
val shouldShowSearchShortcuts by booleanPreference( val shouldShowSearchShortcuts by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_show_search_shortcuts), appContext.getPreferenceKey(R.string.pref_key_show_search_shortcuts),
default = true default = false
) )
val shouldUseDarkTheme by booleanPreference( val shouldUseDarkTheme by booleanPreference(
@ -775,6 +775,19 @@ class Settings private constructor(
default = 0 default = 0
) )
fun setOpenTabsCount(count: Int) {
preferences.edit().putInt(
appContext.getPreferenceKey(R.string.pref_key_open_tabs_count),
count
).apply()
}
val openTabsCount: Int
get() = preferences.getInt(
appContext.getPreferenceKey(R.string.pref_key_open_tabs_count),
0
)
private var savedLoginsSortingStrategyString by stringPreference( private var savedLoginsSortingStrategyString by stringPreference(
appContext.getPreferenceKey(R.string.pref_key_saved_logins_sorting_strategy), appContext.getPreferenceKey(R.string.pref_key_saved_logins_sorting_strategy),
default = SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY default = SavedLoginsFragment.SORTING_STRATEGY_ALPHABETICALLY

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<translate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0"
android:toXDelta="0"
android:duration="900" />

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<gradient
android:angle="45"
android:startColor="@color/toolbar_start_gradient_private_theme"
android:centerColor="@color/toolbar_center_gradient_private_theme"
android:endColor="@color/toolbar_end_gradient_private_theme"
android:type="linear" />
</shape>
</item>
<item android:gravity="top">
<shape android:shape="rectangle">
<size android:height="1dp" />
<solid android:color="@color/neutral_faded_private_theme" />
</shape>
</item>
</layer-list>

View File

@ -46,10 +46,9 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@id/displayName" android:layout_below="@id/displayName"
android:layout_alignStart="@id/displayName" android:layout_alignStart="@id/displayName"
android:textColor="?secondaryText"
android:maxLines="4" android:maxLines="4"
android:text="@string/preferences_account_default_name" android:text="@string/preferences_account_default_name" />
android:textColor="?primaryText"
app:fontFamily="@font/metropolis_bold" />
</RelativeLayout> </RelativeLayout>

View File

@ -56,7 +56,7 @@
android:ellipsize="end" android:ellipsize="end"
android:maxLines="2" android:maxLines="2"
android:minLines="2" android:minLines="2"
android:textAppearance="@style/SubtitleTextStyle" android:textColor="?secondaryText"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/collection_item" app:layout_constraintEnd_toEndOf="@+id/collection_item"
app:layout_constraintStart_toStartOf="@+id/collection_item" app:layout_constraintStart_toStartOf="@+id/collection_item"

View File

@ -47,6 +47,7 @@
android:textAllCaps="false" android:textAllCaps="false"
android:textColor="@color/neutral_text" android:textColor="@color/neutral_text"
android:textSize="16sp" android:textSize="16sp"
android:fontFamily="@font/metropolis_bold"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1" app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toEndOf="@+id/guideline" app:layout_constraintStart_toEndOf="@+id/guideline"
@ -163,7 +164,7 @@
android:text="@string/create_collection_save_to_collection_empty" android:text="@string/create_collection_save_to_collection_empty"
android:textColor="?neutral" android:textColor="?neutral"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:fontFamily="@font/metropolis_semibold"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/save_button" app:layout_constraintEnd_toStartOf="@id/save_button"
app:layout_constraintStart_toEndOf="@id/bottom_bar_icon_button" app:layout_constraintStart_toEndOf="@id/bottom_bar_icon_button"
@ -176,7 +177,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:text="@string/create_collection_save" android:text="@string/create_collection_save"
android:textColor="?neutral" android:textColor="@color/photonWhite"
app:fontFamily="@font/metropolis_medium"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View File

@ -140,6 +140,7 @@
android:gravity="center_vertical" android:gravity="center_vertical"
android:singleLine="true" android:singleLine="true"
android:text="@string/create_collection_add_new_collection" android:text="@string/create_collection_add_new_collection"
android:fontFamily="@font/metropolis"
android:textColor="?neutral" android:textColor="?neutral"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"

View File

@ -6,7 +6,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/history_wrapper" android:id="@+id/synced_tabs_wrapper"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ProgressBar <ProgressBar
@ -43,7 +43,7 @@
android:id="@+id/synced_tabs_list" android:id="@+id/synced_tabs_list"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/history_list_item"/> tools:listitem="@layout/sync_tabs_list_item"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -11,38 +11,24 @@
android:background="?foundation" android:background="?foundation"
tools:context=".search.SearchFragment"> tools:context=".search.SearchFragment">
<androidx.constraintlayout.widget.ConstraintLayout <mozilla.components.browser.toolbar.BrowserToolbar
android:id="@+id/toolbar_wrapper" android:id="@+id/toolbar"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="@dimen/browser_toolbar_height" android:layout_height="@dimen/browser_toolbar_height"
android:layout_margin="0dp" android:background="@drawable/toolbar_background_top"
android:outlineProvider="paddedBounds" android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
app:browserToolbarClearColor="?primaryText"
app:browserToolbarInsecureColor="?primaryText"
app:browserToolbarMenuColor="?primaryText"
app:browserToolbarProgressBarGravity="bottom"
app:browserToolbarSecureColor="?primaryText"
app:browserToolbarTrackingProtectionAndSecurityIndicatorSeparatorColor="?toolbarDivider"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent"/>
<mozilla.components.browser.toolbar.BrowserToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/browser_toolbar_height"
android:layout_gravity="top"
android:background="@drawable/toolbar_background_top"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_scrollFlags="scroll|enterAlways|snap|exitUntilCollapsed"
app:browserToolbarClearColor="?primaryText"
app:browserToolbarInsecureColor="?primaryText"
app:browserToolbarMenuColor="?primaryText"
app:browserToolbarProgressBarGravity="bottom"
app:browserToolbarSecureColor="?primaryText"
app:browserToolbarTrackingProtectionAndSecurityIndicatorSeparatorColor="?toolbarDivider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:layout_width="0dp" android:layout_width="0dp"
@ -51,7 +37,7 @@
app:layout_constraintBottom_toBottomOf="@id/search_divider" app:layout_constraintBottom_toBottomOf="@id/search_divider"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar_wrapper"> app:layout_constraintTop_toBottomOf="@id/toolbar">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/scrollable_area" android:id="@+id/scrollable_area"

View File

@ -14,14 +14,14 @@
<TextView <TextView
android:id="@+id/details_blocking_header" android:id="@+id/details_blocking_header"
style="@style/QuickSettingsText" style="@style/QuickSettingsText"
android:fontFamily="@font/metropolis_bold"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="@dimen/tracking_protection_item_height" android:layout_height="@dimen/tracking_protection_item_height"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:paddingStart="0dp" android:paddingStart="0dp"
android:paddingEnd="0dp" android:paddingEnd="0dp"
android:text="@string/enhanced_tracking_protection_blocked" android:text="@string/enhanced_tracking_protection_blocked" />
android:textStyle="bold" />
<org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem <org.mozilla.fenix.trackingprotection.TrackingProtectionCategoryItem
android:id="@+id/category_social_media" android:id="@+id/category_social_media"

View File

@ -20,8 +20,9 @@
android:id="@+id/header_title" android:id="@+id/header_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="17sp" android:textSize="14sp"
android:textColor="?primaryText" android:textColor="?primaryText"
android:fontFamily="@font/metropolis_semibold"
android:paddingStart="20dp" android:paddingStart="20dp"
android:paddingEnd="0dp" android:paddingEnd="0dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"

View File

@ -30,10 +30,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:text="@string/no_collections_description1" android:text="@string/no_collections_description1"
android:textColor="?primaryText"
android:textSize="14sp" android:textSize="14sp"
android:textAlignment="viewStart" android:textAlignment="viewStart" />
app:fontFamily="@font/metropolis_medium" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/add_tabs_to_collections_button" android:id="@+id/add_tabs_to_collections_button"

View File

@ -8,34 +8,31 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingStart="60dp"
android:paddingEnd="20dp"
android:minHeight="@dimen/library_item_height"
android:background="?android:attr/selectableItemBackground"> android:background="?android:attr/selectableItemBackground">
<TextView <TextView
android:id="@+id/synced_tab_item_title" android:id="@+id/synced_tab_item_title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="72dp"
android:ellipsize="end" android:layout_marginEnd="48dp"
android:layout_marginTop="6dp"
android:singleLine="true" android:singleLine="true"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="?primaryText" android:textColor="?primaryText"
android:textSize="18sp" android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@+id/synced_tab_item_url"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Tab Title" /> tools:text="Tab Title" />
<TextView <TextView
android:id="@+id/synced_tab_item_url" android:id="@+id/synced_tab_item_url"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="72dp"
android:ellipsize="end" android:layout_marginEnd="48dp"
android:layout_marginTop="2dp"
android:singleLine="true" android:singleLine="true"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textColor="?secondaryText" android:textColor="?secondaryText"
@ -43,21 +40,18 @@
tools:text="https://example.com/" tools:text="https://example.com/"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/synced_tab_item_title" app:layout_constraintTop_toBottomOf="@+id/synced_tab_item_title" />
app:layout_constraintBottom_toBottomOf="parent" />
<View <View
android:id="@+id/synced_tab_item_separator" android:id="@+id/synced_tab_item_separator"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="2dp" android:layout_height="1dp"
android:layout_marginStart="16dp" android:layout_marginTop="7dp"
android:layout_marginEnd="16dp" android:background="?syncedTabsSeparator"
android:importantForAccessibility="no" android:importantForAccessibility="no"
app:layout_constraintTop_toTopOf="parent" android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:background="?neutralFaded" app:layout_constraintStart_toStartOf="parent"
android:visibility="gone"/> app:layout_constraintTop_toBottomOf="@+id/synced_tab_item_url" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -29,10 +29,20 @@
android:layout_height="69dp" android:layout_height="69dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:backgroundTint="?tabTrayThumbnailItemBackground"
app:cardBackgroundColor="@color/photonWhite" app:cardBackgroundColor="@color/photonWhite"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/default_tab_thumbnail"
android:src="@drawable/mozac_ic_globe"
android:tint="?tabTrayThumbnailIcon"
android:padding="22dp"
android:importantForAccessibility="no"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<mozilla.components.browser.tabstray.thumbnail.TabThumbnailView <mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
android:id="@+id/mozac_browser_tabstray_thumbnail" android:id="@+id/mozac_browser_tabstray_thumbnail"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -2,30 +2,39 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public <!-- 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 - 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/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/synced_tabs_group" android:id="@+id/synced_tabs_group"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="16dp" android:layout_marginTop="8dp">
android:paddingBottom="8dp"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView <TextView
android:id="@+id/synced_tabs_group_name" android:id="@+id/synced_tabs_group_name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:drawablePadding="15dp" android:layout_marginStart="16dp"
app:drawableStartCompat="@drawable/mozac_ic_device_desktop" android:layout_marginTop="8dp"
app:drawableTint="?primaryText" android:drawablePadding="32dp"
android:textSize="17sp"
android:textAppearance="@style/Header14TextStyle" android:textAppearance="@style/Header14TextStyle"
android:textColor="?primaryText" android:textColor="?primaryText"
android:paddingStart="20dp" android:textSize="12sp"
android:paddingEnd="0dp" android:gravity="center"
android:layout_marginBottom="8dp" app:drawableStartCompat="@drawable/mozac_ic_device_desktop"
app:drawableTint="?primaryText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Header" /> tools:text="Header" />
</RelativeLayout> <View
android:id="@+id/synced_tabs_group_separator"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="6dp"
android:background="?syncedTabsSeparator"
android:importantForAccessibility="no"
android:visibility="visible"
app:layout_constraintTop_toBottomOf="@+id/synced_tabs_group_name" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -544,7 +544,7 @@
android:name="org.mozilla.fenix.settings.about.AboutFragment"/> android:name="org.mozilla.fenix.settings.about.AboutFragment"/>
<fragment <fragment
android:id="@+id/secretSettingsPreference" android:id="@+id/secretSettingsPreference"
android:name="org.mozilla.fenix.settings.SecretSettingsPreference" android:name="org.mozilla.fenix.settings.SecretSettingsFragment"
android:label="@string/preferences_debug_settings" /> android:label="@string/preferences_debug_settings" />
<fragment <fragment
android:id="@+id/crashReporterFragment" android:id="@+id/crashReporterFragment"

View File

@ -58,12 +58,17 @@
<color name="tab_tray_item_media_background_normal_theme">@color/tab_tray_item_media_background_dark_theme</color> <color name="tab_tray_item_media_background_normal_theme">@color/tab_tray_item_media_background_dark_theme</color>
<color name="tab_tray_heading_icon_normal_theme">@color/tab_tray_heading_icon_dark_theme</color> <color name="tab_tray_heading_icon_normal_theme">@color/tab_tray_heading_icon_dark_theme</color>
<color name="tab_tray_heading_icon_inactive_normal_theme">@color/tab_tray_heading_icon_inactive_dark_theme</color> <color name="tab_tray_heading_icon_inactive_normal_theme">@color/tab_tray_heading_icon_inactive_dark_theme</color>
<color name="tab_tray_item_thumbnail_background_normal_theme">@color/tab_tray_item_thumbnail_background_dark_theme</color>
<color name="tab_tray_item_thumbnail_icon_normal_theme">@color/tab_tray_item_thumbnail_icon_dark_theme</color>
<!--Top site colors --> <!--Top site colors -->
<color name="top_site_background">@color/top_site_background_dark_theme</color> <color name="top_site_background">@color/top_site_background_dark_theme</color>
<color name="top_site_border">@color/top_site_border_dark_theme</color> <color name="top_site_border">@color/top_site_border_dark_theme</color>
<color name="top_site_title_text">@color/top_site_title_text_dark_theme</color> <color name="top_site_title_text">@color/top_site_title_text_dark_theme</color>
<!-- Synced tabs colors-->
<color name="synced_tabs_separator">@color/synced_tabs_separator_dark_theme</color>
<!-- Collection icons--> <!-- Collection icons-->
<color name="collection_icon_color_violet">@color/collection_icon_color_violet_dark_theme</color> <color name="collection_icon_color_violet">@color/collection_icon_color_violet_dark_theme</color>
<color name="collection_icon_color_blue">@color/collection_icon_color_blue_dark_theme</color> <color name="collection_icon_color_blue">@color/collection_icon_color_blue_dark_theme</color>

View File

@ -1062,24 +1062,139 @@
<string name="sign_in_with_camera">Gāyì\'ì sēsiûn ngà si kamarât</string> <string name="sign_in_with_camera">Gāyì\'ì sēsiûn ngà si kamarât</string>
<!-- Text shown for settings option for sign with email --> <!-- Text shown for settings option for sign with email -->
<string name="sign_in_with_email">Gārāsun si kōrreôt si lūgaj</string> <string name="sign_in_with_email">Gārāsun si kōrreôt si lūgaj</string>
<!-- Text shown in confirmation dialog to sign out of account -->
<string name="sign_out_confirmation_message">Dūnâj Firefox si nāgi\'iaj nūguàn\'anj si kuentât, sanī si dure\'ej nej datô râj sunt ngà aché nunt riña aga\' nan.</string>
<!-- Text shown in confirmation dialog to sign out of account. The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="sign_out_confirmation_message_2">Dūnâj %s si nāgi\'iaj nūguàn\'anj si kuentât, sanī si dure\'ej nej datô râj sunt ngà aché nunt riña aga\' nan.</string>
<!-- Option to continue signing out of account shown in confirmation dialog to sign out of account --> <!-- Option to continue signing out of account shown in confirmation dialog to sign out of account -->
<string name="sign_out_disconnect">Gāhuī riña internet</string> <string name="sign_out_disconnect">Gāhuī riña internet</string>
<!-- Option to cancel signing out shown in confirmation dialog to sign out of account --> <!-- Option to cancel signing out shown in confirmation dialog to sign out of account -->
<string name="sign_out_cancel">Dūyichin\'</string> <string name="sign_out_cancel">Dūyichin\'</string>
<!-- Error message snackbar shown after the user tried to select a default folder which cannot be altered -->
<string name="bookmark_cannot_edit_root">Sī ga\'ue nādūnāt sa mà riña nej karpetâ ngà nīka \'naj</string>
<!-- Enhanced Tracking Protection -->
<!-- Link displayed in enhanced tracking protection panel to access tracking protection settings -->
<string name="etp_settings">Riña gā\'ue nāgi\'iát riña sa dugumî sò\'</string>
<!-- Preference title for enhanced tracking protection settings -->
<string name="preference_enhanced_tracking_protection">Sa huā hue\'ê doj guendâ nārán riña sa naga\'nāj a</string>
<!-- Title for the description of enhanced tracking protection -->
<string name="preference_enhanced_tracking_protection_explanation_title">Nitāj gà\' \'ngō ganikò\' sò\' si gāchē nunt</string>
<!-- Description of enhanced tracking protection. The first parameter is the name of the application (For example: Fenix) -->
<string name="preference_enhanced_tracking_protection_explanation">Duguminj nej si datôt. %s naran rayi\'ît riña nej sa naga\'nāj sa \'iát nga aché nunt.</string>
<!-- Text displayed that links to website about enhanced tracking protection --> <!-- Text displayed that links to website about enhanced tracking protection -->
<string name="preference_enhanced_tracking_protection_explanation_learn_more">Gāhuin chrūn doj</string> <string name="preference_enhanced_tracking_protection_explanation_learn_more">Gāhuin chrūn doj</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_default_1">Sa huā chre\' (dàj huaj \'naj)</string>
<!-- Preference description for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_description_3">Riña dòj sa naga\'nāj a naran. Dàj rû\' nayi\'nïnj riña pâjina nāyi\'nïn nīñanj.</string>
<!-- Accessibility text for the Standard protection information icon -->
<string name="preference_enhanced_tracking_protection_standard_info_button">Ahuin si riñā narán sa dugumî sò\' \'na\' niñā guendâ nej sa naga\'nāj a</string>
<!-- Preference for enhanced tracking protection for the strict protection settings --> <!-- Preference for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict">Sa nùkuaj doj</string> <string name="preference_enhanced_tracking_protection_strict">Sa nùkuaj doj</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict_description_2">Narán man riña doj sa naga\'nāj a, nej nuguan\' nata\'a nī nej bēntanâ narugui\' man\'an. Hìo doj nayî\'nïnj riña nej pâjina, sanī huā sa sī ga\'ue gi\'iaj sun hue\'e.</string>
<!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_strict_info_button">Ahuin si riñā narán sa nùkuaj doj dugumî sò\' \'na\' niñā guendâ nej sa naga\'nāj a</string>
<!-- Preference for enhanced tracking protection for the custom protection settings -->
<string name="preference_enhanced_tracking_protection_custom">Nāgi\'iaj mun\'ûn\'</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_custom_description_2">Nāguī nej sa naga\'nāj a asi a\'ngô sa riñā ruhuât naránt.</string>
<!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_custom_info_button">Nan huin sa riñā narán sa dugumî sò\' \'na\' niñā guendâ nej sa naga\'nāj a</string>
<!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings --> <!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings -->
<!-- Preference for enhanced tracking protection for the custom protection settings for cookies--> <!-- Preference for enhanced tracking protection for the custom protection settings for cookies-->
<string name="preference_enhanced_tracking_protection_custom_cookies">Nej kôki</string> <string name="preference_enhanced_tracking_protection_custom_cookies">Nej kôki</string>
<!-- Option for enhanced tracking protection for the custom protection settings for cookies-->
<string name="preference_enhanced_tracking_protection_custom_cookies_1">Sa naga\'nāj riña nej sîtio nī nej rêd sociâl</string>
<!-- Option for enhanced tracking protection for the custom protection settings for cookies-->
<string name="preference_enhanced_tracking_protection_custom_cookies_2">Si kokî nej sitiô nu atûjt</string>
<!-- Option for enhanced tracking protection for the custom protection settings for cookies-->
<string name="preference_enhanced_tracking_protection_custom_cookies_3">Daran\' nej kokî ânej e (ga\'ue gī\'iaj sun a\'nan\' web gī\'ia)</string>
<!-- Option for enhanced tracking protection for the custom protection settings for cookies-->
<string name="preference_enhanced_tracking_protection_custom_cookies_4">Daran\' nej kokî (ga\'ue dūre\'ej dā\'āj sîtio)</string>
<!-- Preference for enhanced tracking protection for the custom protection settings for tracking content -->
<string name="preference_enhanced_tracking_protection_custom_tracking_content">Sa nīkāj sa naga\'nāj a</string>
<!-- Option for enhanced tracking protection for the custom protection settings for tracking content-->
<string name="preference_enhanced_tracking_protection_custom_tracking_content_1">Riña daran\' nej rakïj ñanj</string>
<!-- Option for enhanced tracking protection for the custom protection settings for tracking content-->
<string name="preference_enhanced_tracking_protection_custom_tracking_content_2">Màn riña nej rakïj ñanj huìi</string>
<!-- Option for enhanced tracking protection for the custom protection settings for tracking content-->
<string name="preference_enhanced_tracking_protection_custom_tracking_content_3">Màn riña nej rakïj ñanj nagi\'iaj mān\'ânt</string>
<!-- Preference for enhanced tracking protection for the custom protection settings -->
<string name="preference_enhanced_tracking_protection_custom_cryptominers">Nej kriptominêro</string>
<!-- Preference for enhanced tracking protection for the custom protection settings -->
<string name="preference_enhanced_tracking_protection_custom_fingerprinters">Nej da\'nga\' rā\'a</string>
<string name="enhanced_tracking_protection_blocked">Nitāj si hūaj nāyi\'nïn</string> <string name="enhanced_tracking_protection_blocked">Nitāj si hūaj nāyi\'nïn</string>
<!-- Header for categories that are being not being blocked by current Enhanced Tracking Protection settings --> <!-- Header for categories that are being not being blocked by current Enhanced Tracking Protection settings -->
<string name="enhanced_tracking_protection_allowed">Gā\'ue</string> <string name="enhanced_tracking_protection_allowed">Gā\'ue</string>
<!-- Category of trackers (social media trackers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_social_media_trackers_title">Nej sa naga\'nāj nej rêd sosiâl</string>
<!-- Description of social media trackers that can be blocked by Enhanced Tracking Protection -->
<string name="etp_social_media_trackers_description">Sī ga\'nïnjt riña nej rêd sōsiâl da\' nāga\'nāj nìko sa \'iát ngà aché nunt.</string>
<!-- Category of trackers (cross-site tracking cookies) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cookies_title">Nej kokî naga\'nāj riña nej dūguì\' nej sîtio</string>
<!-- Description of cross-site tracking cookies that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cookies_description">Nārán riña nej kokî arâj sun nej rêd duyinga\' sa ane\'e nī nej dukuâ sun nadigi\'ñûn sa anïn ruhuâ guìi da\' si naga\'nāj nìko man nej sa \'iát nga aché nunt.</string>
<!-- Category of trackers (cryptominers) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cryptominers_title">Nej kriptominêro</string>
<!-- Description of cryptominers that can be blocked by Enhanced Tracking Protection -->
<string name="etp_cryptominers_description">Nu â\'nïn riña nej skripts yi\'ìi da\' gātū nej man riña si āgâ\'t nī gīrī nej man san\'ānj nānèe.</string>
<!-- Category of trackers (fingerprinters) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_fingerprinters_title">Nej sa nani\'in nej da\'nga\' rā\'a</string>
<!-- Description of fingerprinters that can be blocked by Enhanced Tracking Protection -->
<string name="etp_fingerprinters_description">Nu â\'nïn da\' sī nari a\'ngô sa yi\'ìi nej nuguan\' nīkāj si āgâ\'t nī ga\'ue nāga\'nāj nej man sò\'.</string>
<!-- Category of trackers (tracking content) that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_title">Sa nīkāj sa naga\'nāj a</string>
<!-- Description of tracking content that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_description">Naran riña nej nuguan\' duyinga\' nej sa ane\'e, sa ni\'io\' nī riña nej nuguan\' nīkāj sa naga\'nāj a. Ga\'ue gā\'uì\' yī\'ij dàj \'iaj sun nej sîtio.</string>
<!-- Enhanced Tracking Protection Onboarding Message shown in a dialog above the toolbar. The first parameter is the name of the application (For example: Fenix) -->
<string name="etp_onboarding_cfr_message">Da\' nanûn kolô dînda riña eskûdo, ngà narán %s sa naga\'nāj riña \'ngō sitio. Gūru\'man ra\'a da\' gīni\'înt doj sa huā rayi\'î man.</string>
<!-- Enhanced Tracking Protection message that protection is currently on for this site -->
<string name="etp_panel_on">Ngà \'IAJ SUN nej sa dugumî sò\' riña sitiô nan</string>
<!-- Enhanced Tracking Protection message that protection is currently off for this site -->
<string name="etp_panel_off">NITĀJ SI \'IAJ SUN nej sa dugumî sò\' riña sitiô nan</string>
<!-- Header for exceptions list for which sites enhanced tracking protection is always off -->
<string name="enhanced_tracking_protection_exceptions">Nitāj si \'iaj sun sa nùkuaj narán riña sa naga\'nāj a riña nej sitiô nan</string>
<!-- Content description (not visible, for screen readers etc.): Navigate
back from ETP details (Ex: Tracking content) -->
<string name="etp_back_button_content_description">Gūij ne\' rūkùu</string>
<!-- About page Your rights link text -->
<string name="about_your_rights">Nej nuguan\' tna\'uēj rayi\'ît</string>
<!-- About page link text to open open source licenses screen -->
<string name="about_open_source_licenses">Dukuâ ñanj nīkāj kodigô huā nî\'nïnj arân sun ñûnj</string>
<!-- About page link text to open what's new link -->
<string name="about_whats_new">Sa nākà doj huā riña %s</string>
<!-- Open source licenses page title
The first parameter is the app name -->
<string name="open_source_licenses_title">%s Dukuâ ñanj OSS</string>
<!-- About page link text to open support link -->
<string name="about_support">Sa rugûñu\'ūnj un</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
<string name="about_crashes">Nej sa gahui a\'nan\'</string>
<!-- About page link text to open privacy notice link -->
<string name="about_privacy_notice">Nuguan\' huì huin si dūkuan</string>
<!-- About page link text to open know your rights link -->
<string name="about_know_your_rights">Gīni\'in nej nuguan\' tna\'uēj rayi\'ît</string>
<!-- About page link text to open licensing information link -->
<string name="about_licensing_information">Si nùguàn\' līsênsia</string>
<!-- About page link text to open a screen with libraries that are used -->
<string name="about_other_open_source_libraries">Nej dukuâ ñanj arâj sun ñûnj</string>
<!-- Toast shown to the user when they are activating the secret dev menu
The first parameter is number of long clicks left to enable the menu -->
<string name="about_debug_menu_toast_progress">Menû nagi\'aj hìo: gūru\'man ra\'a %1$d da\' nāchrūnt</string>
<string name="about_debug_menu_toast_done">Menû nagi\'aj hìo ngà \'iaj sun</string>
<!-- Content description of the tab counter toolbar button when one tab is open --> <!-- Content description of the tab counter toolbar button when one tab is open -->
<string name="tab_counter_content_description_one_tab">1 rakïj ñanj</string> <string name="tab_counter_content_description_one_tab">1 rakïj ñanj</string>
<!-- Content description of the tab counter toolbar button when multiple tabs are open. First parameter will be replaced with the number of tabs (always more than one) --> <!-- Content description of the tab counter toolbar button when multiple tabs are open. First parameter will be replaced with the number of tabs (always more than one) -->
@ -1088,22 +1203,58 @@
<!-- Browser long press popup menu --> <!-- Browser long press popup menu -->
<!-- Copy the current url --> <!-- Copy the current url -->
<string name="browser_toolbar_long_press_popup_copy">Gūxūn nī nāchrūnt a\'ngô hiūj u</string> <string name="browser_toolbar_long_press_popup_copy">Gūxūn nī nāchrūnt a\'ngô hiūj u</string>
<!-- Paste & go the text in the clipboard. '&amp;' is replaced with the ampersand symbol: & -->
<string name="browser_toolbar_long_press_popup_paste_and_go">Gāchrūn &amp; Gūij</string>
<!-- Paste the text in the clipboard --> <!-- Paste the text in the clipboard -->
<string name="browser_toolbar_long_press_popup_paste">Gāchrūn</string> <string name="browser_toolbar_long_press_popup_paste">Gāchrūn</string>
<!-- Snackbar message shown after an URL has been copied to clipboard. -->
<string name="browser_toolbar_url_copied_to_clipboard_snackbar">URL ngà nachîn riña portapapeles</string>
<!-- Title text for the Add To Homescreen dialog -->
<string name="add_to_homescreen_title">Gāchrūn man riña pantâya</string>
<!-- Cancel button text for the Add to Homescreen dialog --> <!-- Cancel button text for the Add to Homescreen dialog -->
<string name="add_to_homescreen_cancel">Dūyichin\'</string> <string name="add_to_homescreen_cancel">Dūyichin\'</string>
<!-- Add button text for the Add to Homescreen dialog --> <!-- Add button text for the Add to Homescreen dialog -->
<string name="add_to_homescreen_add">Nūtà\'</string> <string name="add_to_homescreen_add">Nūtà\'</string>
<!-- Continue to website button text for the first-time Add to Homescreen dialog -->
<string name="add_to_homescreen_continue">Ginu ngè riña sitiô nan</string>
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Si yūgui aksêso dīrêkto</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Gā\'ue nāchrūnt sitiô nan riña pajinâ ayi\'ìt riña aga\' a\'min nīkājt da\' ga\'ue hìo gātūt riñanj dàj rû\' \'iaj \'ngō aplikāsiûn</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Nej riña gayi\'ìt sēsiûn nī nej da\'nga\' huìi</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
<string name="preferences_passwords_save_logins">Nā\'nïnj sà\' nej riña gayi\'ìt sēsiûn nī nej da\'nga\' huìi</string>
<!-- Preference option for asking to save passwords in Fenix -->
<string name="preferences_passwords_save_logins_ask_to_save">Gāchìnj nì\'iaj nā\'nïnj sà\'t</string>
<!-- Preference option for never saving passwords in Fenix -->
<string name="preferences_passwords_save_logins_never_save">Nitāj āmān nā\'nïnj sà\'t</string>
<!-- Preference for autofilling saved logins in Fenix -->
<string name="preferences_passwords_autofill">Nāchra man\'an</string>
<!-- Preference for syncing saved logins in Fenix -->
<string name="preferences_passwords_sync_logins">Nāgi\'iaj nūguàn\'àn nej riña ayì\'ìt sēsiûn</string>
<!-- Syncing saved logins in Fenix is on --> <!-- Syncing saved logins in Fenix is on -->
<string name="preferences_passwords_sync_logins_on">Nāchrūn</string> <string name="preferences_passwords_sync_logins_on">Nāchrūn</string>
<!-- Syncing saved logins in Fenix is off --> <!-- Syncing saved logins in Fenix is off -->
<string name="preferences_passwords_sync_logins_off">Dūnâ\àj</string> <string name="preferences_passwords_sync_logins_off">Dūnâ\àj</string>
<!-- Syncing saved logins in Fenix needs reconnect to sync -->
<string name="preferences_passwords_sync_logins_reconnect">Gānātū ñû</string>
<!-- Syncing saved logins in Fenix needs login -->
<string name="preferences_passwords_sync_logins_sign_in">Gāyi\'ì sēsiûn riña Sync</string>
<!-- Preference to access list of saved logins -->
<string name="preferences_passwords_saved_logins">Sa gayi\'ìt sesiûn ngà naginu sà\'</string>
<!-- Learn more link that will link to a page with more information displayed when a connection is insecure and we detect the user is entering a password --> <!-- Learn more link that will link to a page with more information displayed when a connection is insecure and we detect the user is entering a password -->
<string name="logins_insecure_connection_warning_learn_more">Gāhuin chrūn doj</string> <string name="logins_insecure_connection_warning_learn_more">Gāhuin chrūn doj</string>
<!-- Positive confirmation that Fenix should save the new or updated login -->
<string name="logins_doorhanger_save_confirmation">Nā\'nïnj sà\'</string>
<!-- Content description (not visible, for screen readers etc.): Title for the button to add a search engine in the action bar --> <!-- Content description (not visible, for screen readers etc.): Title for the button to add a search engine in the action bar -->
<string name="search_engine_add_button_content_description">Nūtà\'</string> <string name="search_engine_add_button_content_description">Nūtà\'</string>

View File

@ -51,6 +51,7 @@
<attr name="addOnPrivateBrowsingInteriorIconBackground" format="reference"/> <attr name="addOnPrivateBrowsingInteriorIconBackground" format="reference"/>
<attr name="readerModeStartGradient" format="reference"/> <attr name="readerModeStartGradient" format="reference"/>
<attr name="readerModeEndGradient" format="reference"/> <attr name="readerModeEndGradient" format="reference"/>
<attr name="syncedTabsSeparator" format="reference"/>
<!-- Tab tray --> <!-- Tab tray -->
<attr name="tabTrayItemBackground" format="reference" /> <attr name="tabTrayItemBackground" format="reference" />
@ -59,6 +60,8 @@
<attr name="tabTrayItemMediaBackground" format="reference" /> <attr name="tabTrayItemMediaBackground" format="reference" />
<attr name="tabTrayHeadingIcon" format="reference" /> <attr name="tabTrayHeadingIcon" format="reference" />
<attr name="tabTrayHeadingIconInactive" format="reference" /> <attr name="tabTrayHeadingIconInactive" format="reference" />
<attr name="tabTrayThumbnailItemBackground" format="reference" />
<attr name="tabTrayThumbnailIcon" format="reference" />
<declare-styleable name="TrackingProtectionCategory"> <declare-styleable name="TrackingProtectionCategory">
<attr name="categoryItemTitle" format="reference" /> <attr name="categoryItemTitle" format="reference" />

View File

@ -47,6 +47,7 @@
<color name="dark_grey_90_gradient_end">#0015141A</color> <color name="dark_grey_90_gradient_end">#0015141A</color>
<color name="top_site_background_light_theme">@android:color/white</color> <color name="top_site_background_light_theme">@android:color/white</color>
<color name="top_site_border_light_theme">@color/light_grey_30</color> <color name="top_site_border_light_theme">@color/light_grey_30</color>
<color name="synced_tabs_separator_light_theme">@color/light_grey_30</color>
<color name="top_site_title_text_light_theme">@color/light_grey_80</color> <color name="top_site_title_text_light_theme">@color/light_grey_80</color>
<color name="collection_icon_color_violet_light_theme">#7542E5</color> <color name="collection_icon_color_violet_light_theme">#7542E5</color>
<color name="collection_icon_color_blue_light_theme">#0250BB</color> <color name="collection_icon_color_blue_light_theme">#0250BB</color>
@ -82,6 +83,8 @@
<color name="tab_tray_item_media_background_light_theme">#312A65</color> <color name="tab_tray_item_media_background_light_theme">#312A65</color>
<color name="tab_tray_heading_icon_light_theme">@color/ink_20</color> <color name="tab_tray_heading_icon_light_theme">@color/ink_20</color>
<color name="tab_tray_heading_icon_inactive_light_theme">@color/ink_20_48a</color> <color name="tab_tray_heading_icon_inactive_light_theme">@color/ink_20_48a</color>
<color name="tab_tray_item_thumbnail_background_light_theme">@color/light_grey_10</color>
<color name="tab_tray_item_thumbnail_icon_light_theme">@color/light_grey_60</color>
<!-- Dark theme color palette --> <!-- Dark theme color palette -->
<color name="primary_text_dark_theme">#FBFBFE</color> <color name="primary_text_dark_theme">#FBFBFE</color>
@ -104,6 +107,7 @@
<color name="scrimEnd_dark_theme">#F515141A</color> <color name="scrimEnd_dark_theme">#F515141A</color>
<color name="top_site_background_dark_theme">@color/dark_grey_50</color> <color name="top_site_background_dark_theme">@color/dark_grey_50</color>
<color name="top_site_border_dark_theme">@color/dark_grey_10</color> <color name="top_site_border_dark_theme">@color/dark_grey_10</color>
<color name="synced_tabs_separator_dark_theme">@color/dark_grey_10</color>
<color name="top_site_title_text_dark_theme">@color/light_grey_90</color> <color name="top_site_title_text_dark_theme">@color/light_grey_90</color>
<color name="collection_icon_color_violet_dark_theme">#AB71FF</color> <color name="collection_icon_color_violet_dark_theme">#AB71FF</color>
<color name="collection_icon_color_blue_dark_theme">#00B3F4</color> <color name="collection_icon_color_blue_dark_theme">#00B3F4</color>
@ -138,6 +142,8 @@
<color name="tab_tray_item_media_background_dark_theme">#9059FF</color> <color name="tab_tray_item_media_background_dark_theme">#9059FF</color>
<color name="tab_tray_heading_icon_dark_theme">@color/violet_50</color> <color name="tab_tray_heading_icon_dark_theme">@color/violet_50</color>
<color name="tab_tray_heading_icon_inactive_dark_theme">@color/violet_50_48a</color> <color name="tab_tray_heading_icon_inactive_dark_theme">@color/violet_50_48a</color>
<color name="tab_tray_item_thumbnail_background_dark_theme">@color/dark_grey_50</color>
<color name="tab_tray_item_thumbnail_icon_dark_theme">@color/dark_grey_05</color>
<!-- Private theme color palette --> <!-- Private theme color palette -->
<color name="primary_text_private_theme">#FBFBFE</color> <color name="primary_text_private_theme">#FBFBFE</color>
@ -241,6 +247,8 @@
<color name="tab_tray_item_media_background_normal_theme">@color/tab_tray_item_media_background_light_theme</color> <color name="tab_tray_item_media_background_normal_theme">@color/tab_tray_item_media_background_light_theme</color>
<color name="tab_tray_heading_icon_normal_theme">@color/tab_tray_heading_icon_light_theme</color> <color name="tab_tray_heading_icon_normal_theme">@color/tab_tray_heading_icon_light_theme</color>
<color name="tab_tray_heading_icon_inactive_normal_theme">@color/tab_tray_heading_icon_inactive_light_theme</color> <color name="tab_tray_heading_icon_inactive_normal_theme">@color/tab_tray_heading_icon_inactive_light_theme</color>
<color name="tab_tray_item_thumbnail_background_normal_theme">@color/tab_tray_item_thumbnail_background_light_theme</color>
<color name="tab_tray_item_thumbnail_icon_normal_theme">@color/tab_tray_item_thumbnail_icon_light_theme</color>
<!-- Bookmark buttons --> <!-- Bookmark buttons -->
<color name="bookmark_favicon_background">#DFDFE3</color> <color name="bookmark_favicon_background">#DFDFE3</color>
@ -250,6 +258,9 @@
<color name="top_site_border">@color/top_site_border_light_theme</color> <color name="top_site_border">@color/top_site_border_light_theme</color>
<color name="top_site_title_text">@color/top_site_title_text_light_theme</color> <color name="top_site_title_text">@color/top_site_title_text_light_theme</color>
<!-- Synced tabs colors-->
<color name="synced_tabs_separator">@color/synced_tabs_separator_light_theme</color>
<!-- Collection icons--> <!-- Collection icons-->
<color name="collection_icon_color_violet">@color/collection_icon_color_violet_light_theme</color> <color name="collection_icon_color_violet">@color/collection_icon_color_violet_light_theme</color>
<color name="collection_icon_color_blue">@color/collection_icon_color_blue_light_theme</color> <color name="collection_icon_color_blue">@color/collection_icon_color_blue_light_theme</color>

View File

@ -79,7 +79,7 @@
<dimen name="search_fragment_clipboard_item_horizontal_margin">12dp</dimen> <dimen name="search_fragment_clipboard_item_horizontal_margin">12dp</dimen>
<dimen name="search_fragment_clipboard_item_vertical_margin">8dp</dimen> <dimen name="search_fragment_clipboard_item_vertical_margin">8dp</dimen>
<dimen name="search_fragment_clipboard_item_title_margin_start">8dp</dimen> <dimen name="search_fragment_clipboard_item_title_margin_start">8dp</dimen>
<dimen name="search_fragment_shortcuts_label_margin_horizontal">8dp</dimen> <dimen name="search_fragment_shortcuts_label_margin_horizontal">18dp</dimen>
<dimen name="search_fragment_shortcuts_label_margin_vertical">18dp</dimen> <dimen name="search_fragment_shortcuts_label_margin_vertical">18dp</dimen>
<dimen name="search_fragment_pill_height">40dp</dimen> <dimen name="search_fragment_pill_height">40dp</dimen>
<dimen name="search_fragment_pill_padding_start">20dp</dimen> <dimen name="search_fragment_pill_padding_start">20dp</dimen>

View File

@ -177,6 +177,8 @@
<string name="pref_key_debug_settings" translatable="false">pref_key_debug_settings</string> <string name="pref_key_debug_settings" translatable="false">pref_key_debug_settings</string>
<string name="pref_key_open_tabs_count" translatable="false">pref_key_open_tabs_count</string>
<string name="pref_key_search_count" translatable="false">pref_key_search_count</string> <string name="pref_key_search_count" translatable="false">pref_key_search_count</string>
<string name="pref_key_search_widget_cfr_display_count" translatable="false">pref_key_search_widget_cfr_display_count</string> <string name="pref_key_search_widget_cfr_display_count" translatable="false">pref_key_search_widget_cfr_display_count</string>
<string name="pref_key_search_widget_cfr_dismiss_count" translatable="false">pref_key_search_widget_cfr_dismiss_count</string> <string name="pref_key_search_widget_cfr_dismiss_count" translatable="false">pref_key_search_widget_cfr_dismiss_count</string>

View File

@ -20,6 +20,7 @@
<item name="alertDialogStyle">@style/DialogStyleNormal</item> <item name="alertDialogStyle">@style/DialogStyleNormal</item>
<item name="alertDialogTheme">@style/DialogStyleNormal</item> <item name="alertDialogTheme">@style/DialogStyleNormal</item>
<item name="android:windowEnableSplitTouch">false</item> <item name="android:windowEnableSplitTouch">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:splitMotionEvents">false</item> <item name="android:splitMotionEvents">false</item>
<item name="mozacInputLayoutErrorTextColor" <item name="mozacInputLayoutErrorTextColor"
@ -75,7 +76,7 @@
<item name="mozacPromptLoginEditTextCursorColor">@color/prompt_login_edit_text_cursor_color_normal_theme</item> <item name="mozacPromptLoginEditTextCursorColor">@color/prompt_login_edit_text_cursor_color_normal_theme</item>
<item name="readerModeStartGradient">@color/readermode_start_gradient_normal_theme</item> <item name="readerModeStartGradient">@color/readermode_start_gradient_normal_theme</item>
<item name="readerModeEndGradient">@color/readermode_end_gradient_normal_theme</item> <item name="readerModeEndGradient">@color/readermode_end_gradient_normal_theme</item>
<item name="syncedTabsSeparator">@color/synced_tabs_separator</item>
<item name="tabTrayItemBackground">@color/tab_tray_item_background_normal_theme</item> <item name="tabTrayItemBackground">@color/tab_tray_item_background_normal_theme</item>
<item name="tabTrayItemSelectedBackground">@color/tab_tray_item_selected_background_normal_theme</item> <item name="tabTrayItemSelectedBackground">@color/tab_tray_item_selected_background_normal_theme</item>
@ -83,6 +84,8 @@
<item name="tabTrayItemMediaBackground">@color/tab_tray_item_media_background_normal_theme</item> <item name="tabTrayItemMediaBackground">@color/tab_tray_item_media_background_normal_theme</item>
<item name="tabTrayHeadingIcon">@color/tab_tray_heading_icon_normal_theme</item> <item name="tabTrayHeadingIcon">@color/tab_tray_heading_icon_normal_theme</item>
<item name="tabTrayHeadingIconInactive">@color/tab_tray_heading_icon_inactive_normal_theme</item> <item name="tabTrayHeadingIconInactive">@color/tab_tray_heading_icon_inactive_normal_theme</item>
<item name="tabTrayThumbnailItemBackground">@color/tab_tray_item_thumbnail_background_normal_theme</item>
<item name="tabTrayThumbnailIcon">@color/tab_tray_item_thumbnail_icon_normal_theme</item>
<!-- Drawables --> <!-- Drawables -->
<item name="fenixLogo">@drawable/ic_logo_wordmark_normal</item> <item name="fenixLogo">@drawable/ic_logo_wordmark_normal</item>
@ -208,6 +211,8 @@
<item name="tabTrayItemMediaBackground">@color/tab_tray_item_media_background_private_theme</item> <item name="tabTrayItemMediaBackground">@color/tab_tray_item_media_background_private_theme</item>
<item name="tabTrayHeadingIcon">@color/tab_tray_heading_icon_dark_theme</item> <item name="tabTrayHeadingIcon">@color/tab_tray_heading_icon_dark_theme</item>
<item name="tabTrayHeadingIconInactive">@color/tab_tray_heading_icon_inactive_dark_theme</item> <item name="tabTrayHeadingIconInactive">@color/tab_tray_heading_icon_inactive_dark_theme</item>
<item name="tabTrayThumbnailItemBackground">@color/tab_tray_item_thumbnail_background_normal_theme</item>
<item name="tabTrayThumbnailIcon">@color/tab_tray_item_thumbnail_icon_normal_theme</item>
<!-- Drawables --> <!-- Drawables -->
<item name="fenixLogo">@drawable/ic_logo_wordmark_private</item> <item name="fenixLogo">@drawable/ic_logo_wordmark_private</item>
@ -445,7 +450,7 @@
</style> </style>
<style name="SearchShortcutsLabelStyle"> <style name="SearchShortcutsLabelStyle">
<item name="android:fontFamily">Inter UI</item> <item name="android:fontFamily">@font/metropolis_semibold</item>
<item name="android:letterSpacing">0.15</item> <item name="android:letterSpacing">0.15</item>
<item name="android:textAllCaps">true</item> <item name="android:textAllCaps">true</item>
<item name="android:textColor">?secondaryText</item> <item name="android:textColor">?secondaryText</item>
@ -480,6 +485,7 @@
<item name="android:minHeight">40dp</item> <item name="android:minHeight">40dp</item>
<item name="android:layout_marginTop">32dp</item> <item name="android:layout_marginTop">32dp</item>
<item name="android:textColor">?accentUsedOnDarkBackground</item> <item name="android:textColor">?accentUsedOnDarkBackground</item>
<item name="android:fontFamily">@font/metropolis_semibold</item>
</style> </style>
<style name="ShareHeaderTextStyle"> <style name="ShareHeaderTextStyle">

View File

@ -39,6 +39,6 @@ class MigrationDecisionActivity : Activity() {
// and then we switch to the actual activity without an animation. This visually looks like // and then we switch to the actual activity without an animation. This visually looks like
// a faster start than launching this activity invisibly and switching to the actual // a faster start than launching this activity invisibly and switching to the actual
// activity after that. // activity after that.
overridePendingTransition(0, 0) overridePendingTransition(0, R.anim.placeholder_animation)
} }
} }

View File

@ -16,9 +16,11 @@ import org.junit.Test
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry
import org.mozilla.fenix.utils.Settings
class TelemetrySessionObserverTest { class TelemetrySessionObserverTest {
private val settings: Settings = mockk(relaxed = true)
private val owner: LifecycleOwner = mockk(relaxed = true) private val owner: LifecycleOwner = mockk(relaxed = true)
private val sessionManager: SessionManager = mockk(relaxed = true) private val sessionManager: SessionManager = mockk(relaxed = true)
private val metrics: MetricController = mockk(relaxed = true) private val metrics: MetricController = mockk(relaxed = true)
@ -29,7 +31,7 @@ class TelemetrySessionObserverTest {
@Before @Before
fun setup() { fun setup() {
singleSessionObserver = singleSessionObserver =
UriOpenedObserver(owner, sessionManager, metrics, ads).singleSessionObserver UriOpenedObserver(settings, owner, sessionManager, metrics, ads).singleSessionObserver
} }
@Test @Test

View File

@ -11,11 +11,13 @@ import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry
import org.mozilla.fenix.components.metrics.MetricController import org.mozilla.fenix.components.metrics.MetricController
import org.mozilla.fenix.search.telemetry.ads.AdsTelemetry
import org.mozilla.fenix.utils.Settings
class UriOpenedObserverTest { class UriOpenedObserverTest {
private val settings: Settings = mockk(relaxed = true)
private val owner: LifecycleOwner = mockk(relaxed = true) private val owner: LifecycleOwner = mockk(relaxed = true)
private val sessionManager: SessionManager = mockk(relaxed = true) private val sessionManager: SessionManager = mockk(relaxed = true)
private val metrics: MetricController = mockk() private val metrics: MetricController = mockk()
@ -24,7 +26,7 @@ class UriOpenedObserverTest {
@Before @Before
fun setup() { fun setup() {
observer = UriOpenedObserver(owner, sessionManager, metrics, ads) observer = UriOpenedObserver(settings, owner, sessionManager, metrics, ads)
} }
@Test @Test

View File

@ -100,7 +100,7 @@ class IntentProcessorTypeTest {
@Test @Test
fun `get type for generic intent processor`() { fun `get type for generic intent processor`() {
val processor = object : IntentProcessor { val processor = object : IntentProcessor {
override suspend fun process(intent: Intent) = true override fun process(intent: Intent) = true
} }
val type = testContext.components.intentProcessors.getType(processor) val type = testContext.components.intentProcessors.getType(processor)

View File

@ -119,6 +119,21 @@ class FakeFenixSearchEngineProvider(context: Context) : FenixSearchEngineProvide
) )
} }
override val fallbackEngines: Deferred<SearchEngineList>
get() {
val google = mockSearchEngine(id = "google-b-1-m", n = "Google")
return CompletableDeferred(
SearchEngineList(
listOf(
google,
mockSearchEngine("bing", "Bing"),
mockSearchEngine("amazondotcom", "Amazon.com")
), default = google
)
)
}
override val bundledSearchEngines = CompletableDeferred( override val bundledSearchEngines = CompletableDeferred(
SearchEngineList( SearchEngineList(
listOf( listOf(

View File

@ -150,6 +150,10 @@ class DefaultSearchControllerTest {
@Test @Test
fun `show search shortcuts when setting enabled AND query empty`() { fun `show search shortcuts when setting enabled AND query empty`() {
val text = "" val text = ""
testContext.settings().preferences
.edit()
.putBoolean(testContext.getString(R.string.pref_key_show_search_shortcuts), true)
.apply()
controller.handleTextChanged(text) controller.handleTextChanged(text)
@ -160,6 +164,10 @@ class DefaultSearchControllerTest {
fun `show search shortcuts when setting enabled AND query equals url`() { fun `show search shortcuts when setting enabled AND query equals url`() {
val text = "mozilla.org" val text = "mozilla.org"
every { session?.url } returns "mozilla.org" every { session?.url } returns "mozilla.org"
testContext.settings().preferences
.edit()
.putBoolean(testContext.getString(R.string.pref_key_show_search_shortcuts), true)
.apply()
assertEquals(text, session?.url) assertEquals(text, session?.url)

View File

@ -17,20 +17,21 @@ import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.settings.about.viewholders.AboutItemViewHolder
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.settings.about.viewholders.AboutItemViewHolder
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class AboutPageAdapterTest { class AboutPageAdapterTest {
private var aboutList: List<AboutPageItem> = private val aboutList: List<AboutPageItem> =
mutableListOf( listOf(
AboutPageItem.Item( AboutPageItem(
AboutItem.ExternalLink( AboutItem.ExternalLink(
AboutItemType.WHATS_NEW, AboutItemType.WHATS_NEW,
"https://mozilla.org" "https://mozilla.org"
), "Libraries" ), "Libraries"
), ),
AboutPageItem.Item(AboutItem.Libraries, "Libraries") AboutPageItem(AboutItem.Libraries, "Libraries"),
AboutPageItem(AboutItem.Crashes, "Crashes")
) )
private val listener: AboutPageListener = mockk(relaxed = true) private val listener: AboutPageListener = mockk(relaxed = true)
@ -47,7 +48,7 @@ class AboutPageAdapterTest {
adapter.submitList(aboutList) adapter.submitList(aboutList)
assertEquals(2, adapter.itemCount) assertEquals(3, adapter.itemCount)
} }
@Test @Test
@ -78,6 +79,6 @@ class AboutPageAdapterTest {
adapter.submitList(aboutList) adapter.submitList(aboutList)
adapter.bindViewHolder(viewHolder, 1) adapter.bindViewHolder(viewHolder, 1)
verify { viewHolder.bind(aboutList[1] as AboutPageItem.Item) } verify { viewHolder.bind(aboutList[1]) }
} }
} }

View File

@ -22,7 +22,7 @@ import org.mozilla.fenix.settings.about.AboutPageListener
@RunWith(FenixRobolectricTestRunner::class) @RunWith(FenixRobolectricTestRunner::class)
class AboutItemViewHolderTest { class AboutItemViewHolderTest {
private val item = AboutPageItem.Item(AboutItem.Libraries, "Libraries") private val item = AboutPageItem(AboutItem.Libraries, "Libraries")
private lateinit var view: View private lateinit var view: View
private lateinit var listener: AboutPageListener private lateinit var listener: AboutPageListener

View File

@ -6,10 +6,13 @@ package org.mozilla.fenix.settings.account
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavDestination import androidx.navigation.NavDestination
import io.mockk.Called
import io.mockk.every import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -34,6 +37,7 @@ class AccountSettingsInteractorTest {
@Test @Test
fun onChangeDeviceName() { fun onChangeDeviceName() {
val store: AccountSettingsFragmentStore = mockk(relaxed = true) val store: AccountSettingsFragmentStore = mockk(relaxed = true)
val invalidNameResponse = mockk<() -> Unit>(relaxed = true)
val interactor = AccountSettingsInteractor( val interactor = AccountSettingsInteractor(
mockk(), mockk(),
@ -42,9 +46,28 @@ class AccountSettingsInteractorTest {
store store
) )
interactor.onChangeDeviceName("New Name") {} assertTrue(interactor.onChangeDeviceName("New Name", invalidNameResponse))
verify { store.dispatch(AccountSettingsFragmentAction.UpdateDeviceName("New Name")) } verify { store.dispatch(AccountSettingsFragmentAction.UpdateDeviceName("New Name")) }
verify { invalidNameResponse wasNot Called }
}
@Test
fun onChangeDeviceNameSyncFalse() {
val store: AccountSettingsFragmentStore = mockk(relaxed = true)
val invalidNameResponse = mockk<() -> Unit>(relaxed = true)
val interactor = AccountSettingsInteractor(
mockk(),
mockk(),
{ false },
store
)
assertFalse(interactor.onChangeDeviceName("New Name", invalidNameResponse))
verify { store wasNot Called }
verify { invalidNameResponse() }
} }
@Test @Test

View File

@ -0,0 +1,59 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.settings.quicksettings
import android.widget.FrameLayout
import androidx.core.view.isVisible
import kotlinx.android.synthetic.main.library_site_item.title
import kotlinx.android.synthetic.main.library_site_item.url
import kotlinx.android.synthetic.main.quicksettings_website_info.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class WebsiteInfoViewTest {
private lateinit var view: WebsiteInfoView
@Before
fun setup() {
view = WebsiteInfoView(FrameLayout(testContext))
}
@Test
fun bindUrlAndTitle() {
view.update(WebsiteInfoState(
websiteUrl = "https://mozilla.org",
websiteTitle = "Mozilla",
websiteSecurityUiValues = WebsiteSecurityUiValues.SECURE,
certificateName = ""
))
assertEquals("https://mozilla.org", view.url.text)
assertEquals("Mozilla", view.title.text)
assertEquals("Secure Connection", view.securityInfo.text)
assertFalse(view.certificateInfo.isVisible)
}
@Test
fun bindCert() {
view.update(WebsiteInfoState(
websiteUrl = "https://mozilla.org",
websiteTitle = "Mozilla",
websiteSecurityUiValues = WebsiteSecurityUiValues.INSECURE,
certificateName = "Certificate"
))
assertEquals("Insecure Connection", view.securityInfo.text)
assertEquals("Verified By: Certificate", view.certificateInfo.text)
assertTrue(view.certificateInfo.isVisible)
}
}

View File

@ -254,7 +254,7 @@ class SettingsTest {
fun shouldShowSearchShortcuts() { fun shouldShowSearchShortcuts() {
// When just created // When just created
// Then // Then
assertTrue(settings.shouldShowSearchShortcuts) assertFalse(settings.shouldShowSearchShortcuts)
} }
@Test @Test

View File

@ -3,5 +3,5 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
object AndroidComponents { object AndroidComponents {
const val VERSION = "48.0.20200626130049" const val VERSION = "48.0.20200626213814"
} }

File diff suppressed because one or more lines are too long