Copione merged onto master
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
commit
53660d7a41
|
@ -174,19 +174,12 @@ tasks:
|
|||
- index.mobile.v2.${project}.branch.${short_head_branch}.latest.taskgraph.decision
|
||||
- index.mobile.v2.${project}.branch.${short_head_branch}.revision.${head_sha}.taskgraph.decision
|
||||
- index.mobile.v2.${project}.revision.${head_sha}.taskgraph.decision
|
||||
# TODO Bug 1631839: Remove the following routes once all consumers have migrated
|
||||
- index.project.mobile.${project}.v2.branch.${short_head_branch}.latest.taskgraph.decision
|
||||
- index.project.mobile.${project}.v2.branch.${short_head_branch}.revision.${head_sha}.taskgraph.decision
|
||||
- $if: 'tasks_for == "cron"'
|
||||
then:
|
||||
# cron context provides ${head_branch} as a short one
|
||||
- index.mobile.v2.${project}.branch.${head_branch}.latest.taskgraph.decision-${cron.job_name}
|
||||
- index.mobile.v2.${project}.branch.${head_branch}.revision.${head_sha}.taskgraph.decision-${cron.job_name}
|
||||
- index.mobile.v2.${project}.branch.${head_branch}.revision.${head_sha}.taskgraph.cron.${ownTaskId}
|
||||
# TODO Bug 1631839: Remove the following routes once all consumers have migrated
|
||||
- index.project.mobile.${project}.v2.branch.${head_branch}.latest.taskgraph.decision-${cron.job_name}
|
||||
- index.project.mobile.${project}.v2.branch.${head_branch}.revision.${head_sha}.taskgraph.decision-${cron.job_name}
|
||||
- index.project.mobile.${project}.v2.branch.${head_branch}.revision.${head_sha}.taskgraph.cron.${ownTaskId}
|
||||
scopes:
|
||||
$if: 'tasks_for == "github-push"'
|
||||
then:
|
||||
|
|
|
@ -503,6 +503,7 @@ dependencies {
|
|||
implementation Deps.androidx_lifecycle_viewmodel
|
||||
implementation Deps.androidx_core
|
||||
implementation Deps.androidx_core_ktx
|
||||
implementation Deps.androidx_dynamic_animation
|
||||
implementation Deps.androidx_transition
|
||||
implementation Deps.androidx_work_ktx
|
||||
implementation Deps.google_material
|
||||
|
|
|
@ -85,10 +85,10 @@ private fun assertShowSearchSuggestions() {
|
|||
private fun assertShowSearchShortcuts() {
|
||||
onView(withId(androidx.preference.R.id.recycler_view)).perform(
|
||||
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
|
||||
hasDescendant(withText("Show search shortcuts"))
|
||||
hasDescendant(withText("Show search engines"))
|
||||
)
|
||||
)
|
||||
onView(withText("Show search shortcuts"))
|
||||
onView(withText("Show search engines"))
|
||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||
}
|
||||
|
||||
|
@ -146,11 +146,11 @@ private fun toggleShowSearchSuggestions() {
|
|||
private fun toggleShowSearchShortcuts() {
|
||||
onView(withId(androidx.preference.R.id.recycler_view)).perform(
|
||||
RecyclerViewActions.scrollTo<RecyclerView.ViewHolder>(
|
||||
hasDescendant(withText("Show search shortcuts"))
|
||||
hasDescendant(withText("Show search engines"))
|
||||
)
|
||||
)
|
||||
|
||||
onView(withText("Show search shortcuts"))
|
||||
onView(withText("Show search engines"))
|
||||
.perform(click())
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import mozilla.components.browser.engine.gecko.autofill.GeckoLoginDelegateWrapper
|
||||
import mozilla.components.browser.engine.gecko.ext.toContentBlockingSetting
|
||||
import mozilla.components.browser.engine.gecko.glean.GeckoAdapter
|
||||
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
|
||||
import mozilla.components.concept.storage.LoginsStorage
|
||||
import mozilla.components.lib.crash.handler.CrashHandlerService
|
||||
import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate
|
||||
|
@ -21,10 +23,11 @@ object GeckoProvider {
|
|||
@Synchronized
|
||||
fun getOrCreateRuntime(
|
||||
context: Context,
|
||||
storage: Lazy<LoginsStorage>
|
||||
storage: Lazy<LoginsStorage>,
|
||||
trackingProtectionPolicy: TrackingProtectionPolicy
|
||||
): GeckoRuntime {
|
||||
if (runtime == null) {
|
||||
runtime = createRuntime(context, storage)
|
||||
runtime = createRuntime(context, storage, trackingProtectionPolicy)
|
||||
}
|
||||
|
||||
return runtime!!
|
||||
|
@ -32,7 +35,8 @@ object GeckoProvider {
|
|||
|
||||
private fun createRuntime(
|
||||
context: Context,
|
||||
storage: Lazy<LoginsStorage>
|
||||
storage: Lazy<LoginsStorage>,
|
||||
policy: TrackingProtectionPolicy
|
||||
): GeckoRuntime {
|
||||
val builder = GeckoRuntimeSettings.Builder()
|
||||
|
||||
|
@ -44,6 +48,7 @@ object GeckoProvider {
|
|||
val runtimeSettings = builder
|
||||
.crashHandler(CrashHandlerService::class.java)
|
||||
.telemetryDelegate(GeckoAdapter())
|
||||
.contentBlocking(policy.toContentBlockingSetting())
|
||||
.aboutConfigEnabled(Config.channel.isBeta)
|
||||
.debugLogging(Config.channel.isDebug)
|
||||
.build()
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import mozilla.components.browser.engine.gecko.autofill.GeckoLoginDelegateWrapper
|
||||
import mozilla.components.browser.engine.gecko.ext.toContentBlockingSetting
|
||||
import mozilla.components.browser.engine.gecko.glean.GeckoAdapter
|
||||
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
|
||||
import mozilla.components.concept.storage.LoginsStorage
|
||||
import mozilla.components.lib.crash.handler.CrashHandlerService
|
||||
import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate
|
||||
|
@ -21,10 +23,11 @@ object GeckoProvider {
|
|||
@Synchronized
|
||||
fun getOrCreateRuntime(
|
||||
context: Context,
|
||||
storage: Lazy<LoginsStorage>
|
||||
storage: Lazy<LoginsStorage>,
|
||||
trackingProtectionPolicy: TrackingProtectionPolicy
|
||||
): GeckoRuntime {
|
||||
if (runtime == null) {
|
||||
runtime = createRuntime(context, storage)
|
||||
runtime = createRuntime(context, storage, trackingProtectionPolicy)
|
||||
}
|
||||
|
||||
return runtime!!
|
||||
|
@ -32,7 +35,8 @@ object GeckoProvider {
|
|||
|
||||
private fun createRuntime(
|
||||
context: Context,
|
||||
storage: Lazy<LoginsStorage>
|
||||
storage: Lazy<LoginsStorage>,
|
||||
policy: TrackingProtectionPolicy
|
||||
): GeckoRuntime {
|
||||
val builder = GeckoRuntimeSettings.Builder()
|
||||
|
||||
|
@ -44,6 +48,7 @@ object GeckoProvider {
|
|||
val runtimeSettings = builder
|
||||
.crashHandler(CrashHandlerService::class.java)
|
||||
.telemetryDelegate(GeckoAdapter())
|
||||
.contentBlocking(policy.toContentBlockingSetting())
|
||||
.debugLogging(Config.channel.isDebug)
|
||||
.aboutConfigEnabled(true)
|
||||
.build()
|
||||
|
|
|
@ -24,4 +24,9 @@ object FeatureFlags {
|
|||
* Enables new tab tray pref
|
||||
*/
|
||||
val tabTray = Config.channel.isNightlyOrDebug
|
||||
|
||||
/**
|
||||
* Enables swipe on toolbar to switch tabs
|
||||
*/
|
||||
val swipeToSwitchTabs = Config.channel.isNightlyOrDebug
|
||||
}
|
||||
|
|
|
@ -139,14 +139,7 @@ open class FenixApplication : LocaleAwareApplication() {
|
|||
|
||||
prefetchForHomeFragment()
|
||||
setupLeakCanary()
|
||||
if (settings().isTelemetryEnabled) {
|
||||
components.analytics.metrics.start(MetricServiceType.Data)
|
||||
}
|
||||
|
||||
if (settings().isMarketingTelemetryEnabled) {
|
||||
components.analytics.metrics.start(MetricServiceType.Marketing)
|
||||
}
|
||||
|
||||
startMetricsIfEnabled()
|
||||
setupPush()
|
||||
|
||||
visibilityLifecycleCallback = VisibilityLifecycleCallback(getSystemService())
|
||||
|
@ -158,49 +151,73 @@ open class FenixApplication : LocaleAwareApplication() {
|
|||
// runStorageMaintenance()
|
||||
// }
|
||||
|
||||
val taskQueue = components.performance.visualCompletenessQueue
|
||||
registerActivityLifecycleCallbacks(
|
||||
PerformanceActivityLifecycleCallbacks(taskQueue)
|
||||
)
|
||||
initVisualCompletenessQueueAndQueueTasks()
|
||||
}
|
||||
|
||||
// Enable the service-experiments component to be initialized after visual completeness
|
||||
// for performance wins.
|
||||
if (settings().isExperimentationEnabled) {
|
||||
taskQueue.runIfReadyOrQueue {
|
||||
Experiments.initialize(
|
||||
applicationContext = applicationContext,
|
||||
onExperimentsUpdated = {
|
||||
ExperimentsManager.initSearchWidgetExperiment(this)
|
||||
},
|
||||
configuration = mozilla.components.service.experiments.Configuration(
|
||||
httpClient = components.core.client,
|
||||
kintoEndpoint = KINTO_ENDPOINT_PROD
|
||||
)
|
||||
)
|
||||
ExperimentsManager.initSearchWidgetExperiment(this)
|
||||
}
|
||||
} else {
|
||||
// We should make a better way to opt out for when we have more experiments
|
||||
// See https://github.com/mozilla-mobile/fenix/issues/6278
|
||||
ExperimentsManager.optOutSearchWidgetExperiment(this)
|
||||
private fun initVisualCompletenessQueueAndQueueTasks() {
|
||||
val taskQueue = components.performance.visualCompletenessQueue
|
||||
|
||||
fun initQueue() {
|
||||
registerActivityLifecycleCallbacks(PerformanceActivityLifecycleCallbacks(taskQueue))
|
||||
}
|
||||
|
||||
components.performance.visualCompletenessQueue.runIfReadyOrQueue {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
logger.info("Running post-visual completeness tasks...")
|
||||
logElapsedTime(logger, "Storage initialization") {
|
||||
components.core.historyStorage.warmUp()
|
||||
components.core.bookmarksStorage.warmUp()
|
||||
components.core.passwordsStorage.warmUp()
|
||||
}
|
||||
}
|
||||
// Account manager initialization needs to happen on the main thread.
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
logElapsedTime(logger, "Kicking-off account manager") {
|
||||
components.backgroundServices.accountManager
|
||||
fun queueInitExperiments() {
|
||||
if (settings().isExperimentationEnabled) {
|
||||
taskQueue.runIfReadyOrQueue {
|
||||
Experiments.initialize(
|
||||
applicationContext = applicationContext,
|
||||
onExperimentsUpdated = {
|
||||
ExperimentsManager.initSearchWidgetExperiment(this)
|
||||
},
|
||||
configuration = mozilla.components.service.experiments.Configuration(
|
||||
httpClient = components.core.client,
|
||||
kintoEndpoint = KINTO_ENDPOINT_PROD
|
||||
)
|
||||
)
|
||||
ExperimentsManager.initSearchWidgetExperiment(this)
|
||||
}
|
||||
} else {
|
||||
// We should make a better way to opt out for when we have more experiments
|
||||
// See https://github.com/mozilla-mobile/fenix/issues/6278
|
||||
ExperimentsManager.optOutSearchWidgetExperiment(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun queueInitStorageAndServices() {
|
||||
components.performance.visualCompletenessQueue.runIfReadyOrQueue {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
logger.info("Running post-visual completeness tasks...")
|
||||
logElapsedTime(logger, "Storage initialization") {
|
||||
components.core.historyStorage.warmUp()
|
||||
components.core.bookmarksStorage.warmUp()
|
||||
components.core.passwordsStorage.warmUp()
|
||||
}
|
||||
}
|
||||
// Account manager initialization needs to happen on the main thread.
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
logElapsedTime(logger, "Kicking-off account manager") {
|
||||
components.backgroundServices.accountManager
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initQueue()
|
||||
|
||||
// We init these items in the visual completeness queue to avoid them initing in the critical
|
||||
// startup path, before the UI finishes drawing (i.e. visual completeness).
|
||||
queueInitExperiments()
|
||||
queueInitStorageAndServices()
|
||||
}
|
||||
|
||||
private fun startMetricsIfEnabled() {
|
||||
if (settings().isTelemetryEnabled) {
|
||||
components.analytics.metrics.start(MetricServiceType.Data)
|
||||
}
|
||||
|
||||
if (settings().isMarketingTelemetryEnabled) {
|
||||
components.analytics.metrics.start(MetricServiceType.Marketing)
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/mozilla-mobile/fenix/issues/7227 for context.
|
||||
|
@ -231,6 +248,7 @@ open class FenixApplication : LocaleAwareApplication() {
|
|||
components.core.topSiteStorage.prefetch()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupPush() {
|
||||
// Sets the PushFeature as the singleton instance for push messages to go to.
|
||||
// We need the push feature setup here to deliver messages in the case where the service
|
||||
|
|
|
@ -6,11 +6,14 @@ package org.mozilla.fenix
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
import android.text.format.DateUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.annotation.IdRes
|
||||
|
@ -30,6 +33,8 @@ import kotlinx.android.synthetic.main.activity_home.*
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
|
@ -52,6 +57,7 @@ import mozilla.components.support.ktx.android.content.share
|
|||
import mozilla.components.support.ktx.kotlin.isUrl
|
||||
import mozilla.components.support.ktx.kotlin.toNormalizedUrl
|
||||
import mozilla.components.support.locale.LocaleAwareAppCompatActivity
|
||||
import mozilla.components.support.utils.RunWhenReadyQueue
|
||||
import mozilla.components.support.utils.SafeIntent
|
||||
import mozilla.components.support.utils.toSafeIntent
|
||||
import mozilla.components.support.webextensions.WebExtensionPopupFeature
|
||||
|
@ -97,7 +103,6 @@ import org.mozilla.fenix.theme.DefaultThemeManager
|
|||
import org.mozilla.fenix.theme.ThemeManager
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.TrackingProtectionExceptionsFragmentDirections
|
||||
import org.mozilla.fenix.utils.BrowsersCache
|
||||
import org.mozilla.fenix.utils.RunWhenReadyQueue
|
||||
|
||||
/**
|
||||
* The main activity of the application. The application is primarily a single Activity (this one)
|
||||
|
@ -139,6 +144,9 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
|||
)
|
||||
}
|
||||
|
||||
// See onKeyDown for why this is necessary
|
||||
private var backLongPressJob: Job? = null
|
||||
|
||||
private lateinit var navigationToolbar: Toolbar
|
||||
|
||||
final override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -349,6 +357,50 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
|||
super.onBackPressed()
|
||||
}
|
||||
|
||||
private fun isAndroidN(): Boolean =
|
||||
Build.VERSION.SDK_INT == Build.VERSION_CODES.N || Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1
|
||||
|
||||
private fun handleBackLongPress(): Boolean {
|
||||
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
||||
if (it is OnBackLongPressedListener && it.onBackLongPressed()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
final override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
// Inspired by https://searchfox.org/mozilla-esr68/source/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java#584-613
|
||||
// Android N has broken passing onKeyLongPress events for the back button, so we
|
||||
// instead implement the long press behavior ourselves
|
||||
// - For short presses, we cancel the callback in onKeyUp
|
||||
// - For long presses, the normal keypress is marked as cancelled, hence won't be handled elsewhere
|
||||
// (but Android still provides the haptic feedback), and the long press action is run
|
||||
if (isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
backLongPressJob = lifecycleScope.launch {
|
||||
delay(ViewConfiguration.getLongPressTimeout().toLong())
|
||||
handleBackLongPress()
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
final override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
backLongPressJob?.cancel()
|
||||
}
|
||||
return super.onKeyUp(keyCode, event)
|
||||
}
|
||||
|
||||
final override fun onKeyLongPress(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
// onKeyLongPress is broken in Android N so we don't handle back button long presses here
|
||||
// for N. The version check ensures we don't handle back button long presses twice.
|
||||
if (!isAndroidN() && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return handleBackLongPress()
|
||||
}
|
||||
return super.onKeyLongPress(keyCode, event)
|
||||
}
|
||||
|
||||
final override fun onUserLeaveHint() {
|
||||
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
||||
if (it is UserInteractionHandler && it.onHomePressed()) {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/* 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
|
||||
|
||||
/**
|
||||
* Interface for features and fragments that want to handle long presses of the system back button
|
||||
*/
|
||||
interface OnBackLongPressedListener {
|
||||
|
||||
/**
|
||||
* Called when the system back button is long pressed.
|
||||
*
|
||||
* Note: This cannot be called when gesture navigation is enabled on Android 10+ due to system
|
||||
* limitations.
|
||||
*
|
||||
* @return true if the event was handled
|
||||
*/
|
||||
fun onBackLongPressed(): Boolean
|
||||
}
|
|
@ -77,6 +77,7 @@ import org.mozilla.fenix.FeatureFlags
|
|||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.NavGraphDirections
|
||||
import org.mozilla.fenix.OnBackLongPressedListener
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
||||
import org.mozilla.fenix.browser.readermode.DefaultReaderModeController
|
||||
|
@ -116,7 +117,8 @@ import java.lang.ref.WeakReference
|
|||
*/
|
||||
@ExperimentalCoroutinesApi
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer {
|
||||
abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, SessionManager.Observer,
|
||||
OnBackLongPressedListener {
|
||||
private lateinit var browserFragmentStore: BrowserFragmentStore
|
||||
private lateinit var browserAnimator: BrowserAnimator
|
||||
|
||||
|
@ -383,7 +385,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
tryAgain = downloadFeature::tryAgain,
|
||||
onCannotOpenFile = {
|
||||
FenixSnackbar.make(
|
||||
view = view,
|
||||
view = view.browserLayout,
|
||||
duration = Snackbar.LENGTH_SHORT,
|
||||
isDisplayedWithBrowserToolbar = true
|
||||
)
|
||||
|
@ -647,7 +649,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
|
||||
val onCannotOpenFile = {
|
||||
FenixSnackbar.make(
|
||||
view = view,
|
||||
view = view.browserLayout,
|
||||
duration = Snackbar.LENGTH_SHORT,
|
||||
isDisplayedWithBrowserToolbar = true
|
||||
)
|
||||
|
@ -757,6 +759,11 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
removeSessionIfNeeded()
|
||||
}
|
||||
|
||||
override fun onBackLongPressed(): Boolean {
|
||||
findNavController().navigate(R.id.action_global_tabHistoryDialogFragment)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the external app session ID to be restored later in [onViewStateRestored].
|
||||
*/
|
||||
|
@ -925,7 +932,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
|
||||
view?.let { view ->
|
||||
FenixSnackbar.make(
|
||||
view = view,
|
||||
view = view.browserLayout,
|
||||
duration = FenixSnackbar.LENGTH_LONG,
|
||||
isDisplayedWithBrowserToolbar = true
|
||||
)
|
||||
|
@ -971,7 +978,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
// Close find in page bar if opened
|
||||
findInPageIntegration.onBackPressed()
|
||||
FenixSnackbar.make(
|
||||
view = requireView(),
|
||||
view = requireView().browserLayout,
|
||||
duration = Snackbar.LENGTH_SHORT,
|
||||
isDisplayedWithBrowserToolbar = false
|
||||
)
|
||||
|
|
|
@ -15,6 +15,7 @@ import androidx.core.content.ContextCompat
|
|||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.session.Session
|
||||
|
@ -29,6 +30,7 @@ import mozilla.components.feature.tab.collections.TabCollection
|
|||
import mozilla.components.feature.tabs.WindowFeature
|
||||
import mozilla.components.support.base.feature.UserInteractionHandler
|
||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
|
@ -66,11 +68,24 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
|||
return view
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun initializeUI(view: View): Session? {
|
||||
val context = requireContext()
|
||||
val components = context.components
|
||||
|
||||
return super.initializeUI(view)?.also {
|
||||
if (FeatureFlags.swipeToSwitchTabs) {
|
||||
gestureLayout.addGestureListener(
|
||||
ToolbarGestureHandler(
|
||||
activity = requireActivity(),
|
||||
contentLayout = browserLayout,
|
||||
tabPreview = tabPreview,
|
||||
toolbarLayout = browserToolbarView.view,
|
||||
sessionManager = components.core.sessionManager
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val readerModeAction =
|
||||
BrowserToolbar.ToggleButton(
|
||||
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_readermode)!!,
|
||||
|
@ -243,7 +258,7 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
|||
}
|
||||
}
|
||||
FenixSnackbar.make(
|
||||
view = view,
|
||||
view = view.browserLayout,
|
||||
duration = Snackbar.LENGTH_SHORT,
|
||||
isDisplayedWithBrowserToolbar = true
|
||||
)
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/* 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.browser
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PointF
|
||||
import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
|
||||
/**
|
||||
* Interface that allows intercepting and handling swipe gestures received in a [SwipeGestureLayout].
|
||||
*/
|
||||
interface SwipeGestureListener {
|
||||
|
||||
/**
|
||||
* Called when the [SwipeGestureLayout] detects the start of a swipe gesture. The listener
|
||||
* should return true if it wants to handle the swipe gesture. If the listener returns false
|
||||
* it will not receive any callbacks for future events that the swipe produces.
|
||||
*
|
||||
* @param start the initial point where the gesture started
|
||||
* @param next the next point in the gesture
|
||||
*/
|
||||
fun onSwipeStarted(start: PointF, next: PointF): Boolean
|
||||
|
||||
/**
|
||||
* Called when the swipe gesture receives a new event.
|
||||
*
|
||||
* @param distanceX the change along the x-axis since the last swipe update
|
||||
* @param distanceY the change along the y-axis since the last swipe update
|
||||
*/
|
||||
fun onSwipeUpdate(distanceX: Float, distanceY: Float)
|
||||
|
||||
/**
|
||||
* Called when the user finishes the swipe gesture (ie lifts their finger off the screen)
|
||||
*
|
||||
* @param velocityX the velocity of the swipe along the x-axis
|
||||
* @param velocityY the velocity of the swipe along the y-axis
|
||||
*/
|
||||
fun onSwipeFinished(velocityX: Float, velocityY: Float)
|
||||
}
|
||||
|
||||
/**
|
||||
* A [FrameLayout] that allows listeners to intercept and handle swipe events.
|
||||
*
|
||||
* Listeners are called in the order they are added and the first listener to intercept a swipe event
|
||||
* is the only listener that will receive events for the duration of that swipe.
|
||||
*/
|
||||
class SwipeGestureLayout @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onDown(e: MotionEvent?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onScroll(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent?,
|
||||
distanceX: Float,
|
||||
distanceY: Float
|
||||
): Boolean {
|
||||
val start = e1?.let { event -> PointF(event.rawX, event.rawY) } ?: return false
|
||||
val next = e2?.let { event -> PointF(event.rawX, event.rawY) } ?: return false
|
||||
|
||||
if (activeListener == null && !handledInitialScroll) {
|
||||
activeListener = listeners.firstOrNull { listener ->
|
||||
listener.onSwipeStarted(start, next)
|
||||
}
|
||||
handledInitialScroll = true
|
||||
}
|
||||
activeListener?.onSwipeUpdate(distanceX, distanceY)
|
||||
return activeListener != null
|
||||
}
|
||||
|
||||
override fun onFling(
|
||||
e1: MotionEvent?,
|
||||
e2: MotionEvent?,
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
): Boolean {
|
||||
activeListener?.onSwipeFinished(velocityX, velocityY)
|
||||
return if (activeListener != null) {
|
||||
activeListener = null
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val gestureDetector = GestureDetectorCompat(context, gestureListener)
|
||||
|
||||
private val listeners = mutableListOf<SwipeGestureListener>()
|
||||
private var activeListener: SwipeGestureListener? = null
|
||||
private var handledInitialScroll = false
|
||||
|
||||
fun addGestureListener(listener: SwipeGestureListener) {
|
||||
listeners.add(listener)
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
|
||||
return when (event?.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
handledInitialScroll = false
|
||||
gestureDetector.onTouchEvent(event)
|
||||
false
|
||||
}
|
||||
else -> gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||
return when (event?.actionMasked) {
|
||||
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
|
||||
gestureDetector.onTouchEvent(event)
|
||||
// If the active listener is not null here, then we haven't detected a fling
|
||||
// so notify the listener that the swipe was finished with 0 velocity
|
||||
activeListener?.onSwipeFinished(
|
||||
velocityX = 0f,
|
||||
velocityY = 0f
|
||||
)
|
||||
activeListener = null
|
||||
false
|
||||
}
|
||||
else -> gestureDetector.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/* 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.browser
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import kotlinx.android.synthetic.main.mozac_ui_tabcounter_layout.view.*
|
||||
import kotlinx.android.synthetic.main.tab_preview.view.*
|
||||
import mozilla.components.browser.thumbnails.loader.ThumbnailLoader
|
||||
import mozilla.components.support.images.ImageLoadRequest
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.theme.ThemeManager
|
||||
import kotlin.math.max
|
||||
|
||||
class TabPreview @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : FrameLayout(context, attrs, defStyle) {
|
||||
|
||||
private val thumbnailLoader = ThumbnailLoader(context.components.core.thumbnailStorage)
|
||||
|
||||
init {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
inflater.inflate(R.layout.tab_preview, this, true)
|
||||
|
||||
if (!context.settings().shouldUseBottomToolbar) {
|
||||
fakeToolbar.updateLayoutParams<LayoutParams> {
|
||||
gravity = Gravity.TOP
|
||||
}
|
||||
|
||||
fakeToolbar.background = ResourcesCompat.getDrawable(
|
||||
resources,
|
||||
ThemeManager.resolveAttribute(R.attr.bottomBarBackgroundTop, context),
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
// Change view properties to avoid confusing the UI tests
|
||||
tab_button.counter_box.id = View.NO_ID
|
||||
tab_button.counter_text.id = View.NO_ID
|
||||
}
|
||||
|
||||
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
||||
super.onLayout(changed, left, top, right, bottom)
|
||||
previewThumbnail.translationY = if (!context.settings().shouldUseBottomToolbar) {
|
||||
fakeToolbar.height.toFloat()
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
}
|
||||
|
||||
fun loadPreviewThumbnail(thumbnailId: String) {
|
||||
doOnNextLayout {
|
||||
val thumbnailSize = max(previewThumbnail.height, previewThumbnail.width)
|
||||
thumbnailLoader.loadIntoView(previewThumbnail, ImageLoadRequest(thumbnailId, thumbnailSize))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
/* 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.browser
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.AnimatorListenerAdapter
|
||||
import android.app.Activity
|
||||
import android.graphics.PointF
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import androidx.annotation.Dimension
|
||||
import androidx.annotation.Dimension.DP
|
||||
import androidx.core.graphics.contains
|
||||
import androidx.core.graphics.toPoint
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
||||
import androidx.dynamicanimation.animation.FlingAnimation
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.support.ktx.android.util.dpToPx
|
||||
import mozilla.components.support.ktx.android.view.getRectWithViewLocation
|
||||
import org.mozilla.fenix.ext.sessionsOfType
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Handles intercepting touch events on the toolbar for swipe gestures and executes the
|
||||
* necessary animations.
|
||||
*/
|
||||
@Suppress("LargeClass", "TooManyFunctions")
|
||||
class ToolbarGestureHandler(
|
||||
private val activity: Activity,
|
||||
private val contentLayout: View,
|
||||
private val tabPreview: TabPreview,
|
||||
private val toolbarLayout: View,
|
||||
private val sessionManager: SessionManager
|
||||
) : SwipeGestureListener {
|
||||
|
||||
private enum class GestureDirection {
|
||||
LEFT_TO_RIGHT, RIGHT_TO_LEFT
|
||||
}
|
||||
|
||||
private sealed class Destination {
|
||||
data class Tab(val session: Session) : Destination()
|
||||
object None : Destination()
|
||||
}
|
||||
|
||||
private val windowWidth: Int
|
||||
get() = activity.resources.displayMetrics.widthPixels
|
||||
|
||||
private val windowInsets: WindowInsetsCompat?
|
||||
get() =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// In theory, the rootWindowInsets should exist at this point but if the decorView is
|
||||
// not attached for some reason we'll get a NullPointerException without the check.
|
||||
activity.window.decorView.rootWindowInsets?.let {
|
||||
WindowInsetsCompat.toWindowInsetsCompat(it)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
private val previewOffset = PREVIEW_OFFSET.dpToPx(activity.resources.displayMetrics)
|
||||
|
||||
private val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
|
||||
private val minimumFlingVelocity = ViewConfiguration.get(activity).scaledMinimumFlingVelocity
|
||||
private val defaultVelocity = TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
MINIMUM_ANIMATION_VELOCITY,
|
||||
activity.resources.displayMetrics
|
||||
)
|
||||
|
||||
private var gestureDirection = GestureDirection.LEFT_TO_RIGHT
|
||||
|
||||
override fun onSwipeStarted(start: PointF, next: PointF): Boolean {
|
||||
val dx = next.x - start.x
|
||||
val dy = next.y - start.y
|
||||
gestureDirection = if (dx < 0) {
|
||||
GestureDirection.RIGHT_TO_LEFT
|
||||
} else {
|
||||
GestureDirection.LEFT_TO_RIGHT
|
||||
}
|
||||
|
||||
return if (start.isInToolbar() && abs(dx) > touchSlop && abs(dy) < abs(dx)) {
|
||||
preparePreview(getDestination())
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeUpdate(distanceX: Float, distanceY: Float) {
|
||||
when (getDestination()) {
|
||||
is Destination.Tab -> {
|
||||
// Restrict the range of motion for the views so you can't start a swipe in one direction
|
||||
// then move your finger far enough in the other direction and make the content visually
|
||||
// start sliding off screen the other way.
|
||||
tabPreview.translationX = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> min(
|
||||
windowWidth.toFloat() + previewOffset,
|
||||
tabPreview.translationX - distanceX
|
||||
)
|
||||
GestureDirection.LEFT_TO_RIGHT -> max(
|
||||
-windowWidth.toFloat() - previewOffset,
|
||||
tabPreview.translationX - distanceX
|
||||
)
|
||||
}
|
||||
contentLayout.translationX = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> min(
|
||||
0f,
|
||||
contentLayout.translationX - distanceX
|
||||
)
|
||||
GestureDirection.LEFT_TO_RIGHT -> max(
|
||||
0f,
|
||||
contentLayout.translationX - distanceX
|
||||
)
|
||||
}
|
||||
}
|
||||
is Destination.None -> {
|
||||
// If there is no "next" tab to swipe to in the gesture direction, only do a
|
||||
// partial animation to show that we are at the end of the tab list
|
||||
val maxContentHidden = contentLayout.width * OVERSCROLL_HIDE_PERCENT
|
||||
contentLayout.translationX = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> max(
|
||||
-maxContentHidden.toFloat(),
|
||||
contentLayout.translationX - distanceX
|
||||
).coerceAtMost(0f)
|
||||
GestureDirection.LEFT_TO_RIGHT -> min(
|
||||
maxContentHidden.toFloat(),
|
||||
contentLayout.translationX - distanceX
|
||||
).coerceAtLeast(0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeFinished(
|
||||
velocityX: Float,
|
||||
velocityY: Float
|
||||
) {
|
||||
val destination = getDestination()
|
||||
if (destination is Destination.Tab && isGestureComplete(velocityX)) {
|
||||
animateToNextTab(velocityX, destination.session)
|
||||
} else {
|
||||
animateCanceledGesture(velocityX)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFlingAnimation(
|
||||
view: View,
|
||||
minValue: Float,
|
||||
maxValue: Float,
|
||||
startVelocity: Float
|
||||
): FlingAnimation =
|
||||
FlingAnimation(view, DynamicAnimation.TRANSLATION_X).apply {
|
||||
setMinValue(minValue)
|
||||
setMaxValue(maxValue)
|
||||
setStartVelocity(startVelocity)
|
||||
friction = ViewConfiguration.getScrollFriction()
|
||||
}
|
||||
|
||||
private fun getDestination(): Destination {
|
||||
val isLtr = activity.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
|
||||
val currentSession = sessionManager.selectedSession ?: return Destination.None
|
||||
val currentIndex = sessionManager.sessionsOfType(currentSession.private).indexOfFirst {
|
||||
it.id == currentSession.id
|
||||
}
|
||||
|
||||
return if (currentIndex == -1) {
|
||||
Destination.None
|
||||
} else {
|
||||
val sessions = sessionManager.sessionsOfType(currentSession.private)
|
||||
val index = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> if (isLtr) {
|
||||
currentIndex + 1
|
||||
} else {
|
||||
currentIndex - 1
|
||||
}
|
||||
GestureDirection.LEFT_TO_RIGHT -> if (isLtr) {
|
||||
currentIndex - 1
|
||||
} else {
|
||||
currentIndex + 1
|
||||
}
|
||||
}
|
||||
|
||||
if (index < sessions.count() && index >= 0) {
|
||||
Destination.Tab(sessions.elementAt(index))
|
||||
} else {
|
||||
Destination.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun preparePreview(destination: Destination) {
|
||||
val thumbnailId = when (destination) {
|
||||
is Destination.Tab -> destination.session.id
|
||||
is Destination.None -> return
|
||||
}
|
||||
|
||||
tabPreview.loadPreviewThumbnail(thumbnailId)
|
||||
tabPreview.alpha = 1f
|
||||
tabPreview.translationX = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> windowWidth.toFloat() + previewOffset
|
||||
GestureDirection.LEFT_TO_RIGHT -> -windowWidth.toFloat() - previewOffset
|
||||
}
|
||||
tabPreview.isVisible = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the gesture is complete based on the position of tab preview and the velocity of
|
||||
* the gesture. A completed gesture means the user has indicated they want to swipe to the next
|
||||
* tab. The gesture is considered complete if one of the following is true:
|
||||
*
|
||||
* 1. The user initiated a fling in the same direction as the initial movement
|
||||
* 2. There is no fling initiated, but the percentage of the tab preview shown is at least
|
||||
* [GESTURE_FINISH_PERCENT]
|
||||
*
|
||||
* If the user initiated a fling in the opposite direction of the initial movement, the
|
||||
* gesture is always considered incomplete.
|
||||
*/
|
||||
private fun isGestureComplete(velocityX: Float): Boolean {
|
||||
val previewWidth = tabPreview.getRectWithViewLocation().visibleWidth.toDouble()
|
||||
val velocityMatchesDirection = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> velocityX <= 0
|
||||
GestureDirection.LEFT_TO_RIGHT -> velocityX >= 0
|
||||
}
|
||||
val reverseFling =
|
||||
abs(velocityX) >= minimumFlingVelocity && !velocityMatchesDirection
|
||||
|
||||
return !reverseFling && (previewWidth / windowWidth >= GESTURE_FINISH_PERCENT ||
|
||||
abs(velocityX) >= minimumFlingVelocity)
|
||||
}
|
||||
|
||||
private fun getVelocityFromFling(velocityX: Float): Float {
|
||||
return max(abs(velocityX), defaultVelocity)
|
||||
}
|
||||
|
||||
private fun animateToNextTab(velocityX: Float, session: Session) {
|
||||
val browserFinalXCoordinate: Float = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> -windowWidth.toFloat() - previewOffset
|
||||
GestureDirection.LEFT_TO_RIGHT -> windowWidth.toFloat() + previewOffset
|
||||
}
|
||||
val animationVelocity = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> -getVelocityFromFling(velocityX)
|
||||
GestureDirection.LEFT_TO_RIGHT -> getVelocityFromFling(velocityX)
|
||||
}
|
||||
|
||||
// Finish animating the contentLayout off screen and tabPreview on screen
|
||||
createFlingAnimation(
|
||||
view = contentLayout,
|
||||
minValue = min(0f, browserFinalXCoordinate),
|
||||
maxValue = max(0f, browserFinalXCoordinate),
|
||||
startVelocity = animationVelocity
|
||||
).addUpdateListener { _, value, _ ->
|
||||
tabPreview.translationX = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> value + windowWidth + previewOffset
|
||||
GestureDirection.LEFT_TO_RIGHT -> value - windowWidth - previewOffset
|
||||
}
|
||||
}.addEndListener { _, _, _, _ ->
|
||||
contentLayout.translationX = 0f
|
||||
sessionManager.select(session)
|
||||
|
||||
// Fade out the tab preview to prevent flickering
|
||||
val shortAnimationDuration =
|
||||
activity.resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||
tabPreview.animate()
|
||||
.alpha(0f)
|
||||
.setDuration(shortAnimationDuration.toLong())
|
||||
.setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
tabPreview.isVisible = false
|
||||
}
|
||||
})
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun animateCanceledGesture(gestureVelocity: Float) {
|
||||
val velocity = if (getDestination() is Destination.None) {
|
||||
defaultVelocity
|
||||
} else {
|
||||
getVelocityFromFling(gestureVelocity)
|
||||
}.let { v ->
|
||||
when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> v
|
||||
GestureDirection.LEFT_TO_RIGHT -> -v
|
||||
}
|
||||
}
|
||||
|
||||
createFlingAnimation(
|
||||
view = contentLayout,
|
||||
minValue = min(0f, contentLayout.translationX),
|
||||
maxValue = max(0f, contentLayout.translationX),
|
||||
startVelocity = velocity
|
||||
).addUpdateListener { _, value, _ ->
|
||||
tabPreview.translationX = when (gestureDirection) {
|
||||
GestureDirection.RIGHT_TO_LEFT -> value + windowWidth + previewOffset
|
||||
GestureDirection.LEFT_TO_RIGHT -> value - windowWidth - previewOffset
|
||||
}
|
||||
}.addEndListener { _, _, _, _ ->
|
||||
tabPreview.isVisible = false
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun PointF.isInToolbar(): Boolean {
|
||||
val toolbarLocation = toolbarLayout.getRectWithViewLocation()
|
||||
// In Android 10, the system gesture touch area overlaps the bottom of the toolbar, so
|
||||
// lets make our swipe area taller by that amount
|
||||
windowInsets?.let { insets ->
|
||||
if (activity.settings().shouldUseBottomToolbar) {
|
||||
toolbarLocation.top -= (insets.mandatorySystemGestureInsets.bottom - insets.stableInsetBottom)
|
||||
}
|
||||
}
|
||||
return toolbarLocation.contains(toPoint())
|
||||
}
|
||||
|
||||
private val Rect.visibleWidth: Int
|
||||
get() = if (left < 0) {
|
||||
right
|
||||
} else {
|
||||
windowWidth - left
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* The percentage of the tab preview that needs to be visible to consider the
|
||||
* tab switching gesture complete.
|
||||
*/
|
||||
private const val GESTURE_FINISH_PERCENT = 0.25
|
||||
|
||||
/**
|
||||
* The percentage of the content view that can be hidden by the tab switching gesture if
|
||||
* there is not tab available to switch to
|
||||
*/
|
||||
private const val OVERSCROLL_HIDE_PERCENT = 0.20
|
||||
|
||||
/**
|
||||
* The speed of the fling animation (in dp per second).
|
||||
*/
|
||||
@Dimension(unit = DP)
|
||||
private const val MINIMUM_ANIMATION_VELOCITY = 1500f
|
||||
|
||||
/**
|
||||
* The size of the gap between the tab preview and content layout.
|
||||
*/
|
||||
@Dimension(unit = DP)
|
||||
private const val PREVIEW_OFFSET = 48
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ import mozilla.components.service.fxa.manager.SCOPE_SYNC
|
|||
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
||||
import mozilla.components.service.fxa.sync.GlobalSyncableStoreProvider
|
||||
import mozilla.components.service.sync.logins.SyncableLoginsStorage
|
||||
import mozilla.components.support.utils.RunWhenReadyQueue
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -39,7 +40,6 @@ import org.mozilla.fenix.ext.components
|
|||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.sync.SyncedTabsIntegration
|
||||
import org.mozilla.fenix.utils.Mockable
|
||||
import org.mozilla.fenix.utils.RunWhenReadyQueue
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
|
|
|
@ -86,7 +86,11 @@ class Core(private val context: Context) {
|
|||
GeckoEngine(
|
||||
context,
|
||||
defaultSettings,
|
||||
GeckoProvider.getOrCreateRuntime(context, lazyPasswordsStorage)
|
||||
GeckoProvider.getOrCreateRuntime(
|
||||
context,
|
||||
lazyPasswordsStorage,
|
||||
trackingProtectionPolicyFactory.createTrackingProtectionPolicy()
|
||||
)
|
||||
).also {
|
||||
WebCompatFeature.install(it)
|
||||
|
||||
|
@ -108,7 +112,11 @@ class Core(private val context: Context) {
|
|||
val client: Client by lazy {
|
||||
GeckoViewFetchClient(
|
||||
context,
|
||||
GeckoProvider.getOrCreateRuntime(context, lazyPasswordsStorage)
|
||||
GeckoProvider.getOrCreateRuntime(
|
||||
context,
|
||||
lazyPasswordsStorage,
|
||||
trackingProtectionPolicyFactory.createTrackingProtectionPolicy()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.components
|
||||
|
||||
import org.mozilla.fenix.utils.RunWhenReadyQueue
|
||||
import mozilla.components.support.utils.RunWhenReadyQueue
|
||||
|
||||
/**
|
||||
* Component group for all functionality related to performance.
|
||||
|
|
|
@ -170,7 +170,13 @@ class DefaultBrowserToolbarController(
|
|||
|
||||
Do exhaustive when (item) {
|
||||
ToolbarMenu.Item.Back -> sessionUseCases.goBack.invoke(currentSession)
|
||||
ToolbarMenu.Item.Forward -> sessionUseCases.goForward.invoke(currentSession)
|
||||
is ToolbarMenu.Item.Forward -> {
|
||||
if (item.viewHistory) {
|
||||
navController.navigate(R.id.action_global_tabHistoryDialogFragment)
|
||||
} else {
|
||||
sessionUseCases.goForward.invoke(currentSession)
|
||||
}
|
||||
}
|
||||
ToolbarMenu.Item.Reload -> sessionUseCases.reload.invoke(currentSession)
|
||||
ToolbarMenu.Item.Stop -> sessionUseCases.stopLoading.invoke(currentSession)
|
||||
ToolbarMenu.Item.Settings -> browserAnimator.captureEngineViewAndDrawStatically {
|
||||
|
@ -333,7 +339,7 @@ class DefaultBrowserToolbarController(
|
|||
private fun trackToolbarItemInteraction(item: ToolbarMenu.Item) {
|
||||
val eventItem = when (item) {
|
||||
ToolbarMenu.Item.Back -> Event.BrowserMenuItemTapped.Item.BACK
|
||||
ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD
|
||||
is ToolbarMenu.Item.Forward -> Event.BrowserMenuItemTapped.Item.FORWARD
|
||||
ToolbarMenu.Item.Reload -> Event.BrowserMenuItemTapped.Item.RELOAD
|
||||
ToolbarMenu.Item.Stop -> Event.BrowserMenuItemTapped.Item.STOP
|
||||
ToolbarMenu.Item.Settings -> Event.BrowserMenuItemTapped.Item.SETTINGS
|
||||
|
|
|
@ -82,9 +82,10 @@ class DefaultToolbarMenu(
|
|||
session?.canGoForward ?: true
|
||||
},
|
||||
secondaryImageTintResource = ThemeManager.resolveAttribute(R.attr.disabled, context),
|
||||
disableInSecondaryState = true
|
||||
disableInSecondaryState = true,
|
||||
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward)
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
|
||||
}
|
||||
|
||||
val refresh = BrowserMenuItemToolbar.TwoStateButton(
|
||||
|
|
|
@ -14,7 +14,7 @@ interface ToolbarMenu {
|
|||
object FindInPage : Item()
|
||||
object Share : Item()
|
||||
object Back : Item()
|
||||
object Forward : Item()
|
||||
data class Forward(val viewHistory: Boolean) : Item()
|
||||
object Reload : Item()
|
||||
object Stop : Item()
|
||||
object OpenInFenix : Item()
|
||||
|
|
|
@ -75,9 +75,10 @@ class CustomTabToolbarMenu(
|
|||
R.attr.disabled,
|
||||
context
|
||||
),
|
||||
disableInSecondaryState = true
|
||||
disableInSecondaryState = true,
|
||||
longClickListener = { onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = true)) }
|
||||
) {
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward)
|
||||
onItemTapped.invoke(ToolbarMenu.Item.Forward(viewHistory = false))
|
||||
}
|
||||
|
||||
val refresh = BrowserMenuItemToolbar.TwoStateButton(
|
||||
|
|
|
@ -119,10 +119,10 @@ class AwesomeBarView(
|
|||
|
||||
bookmarksStorageSuggestionProvider =
|
||||
BookmarksStorageSuggestionProvider(
|
||||
components.core.bookmarksStorage,
|
||||
loadUrlUseCase,
|
||||
components.core.icons,
|
||||
engineForSpeculativeConnects
|
||||
bookmarksStorage = components.core.bookmarksStorage,
|
||||
loadUrlUseCase = loadUrlUseCase,
|
||||
icons = components.core.icons,
|
||||
engine = engineForSpeculativeConnects
|
||||
)
|
||||
|
||||
val searchBitmap = getDrawable(context, R.drawable.ic_search)!!.apply {
|
||||
|
|
|
@ -7,11 +7,11 @@ package org.mozilla.fenix.session
|
|||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import mozilla.components.support.utils.RunWhenReadyQueue
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.browser.BrowserPerformanceTestActivity
|
||||
import org.mozilla.fenix.settings.account.AuthIntentReceiverActivity
|
||||
import org.mozilla.fenix.utils.RunWhenReadyQueue
|
||||
import org.mozilla.fenix.widget.VoiceSearchActivity
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,13 +10,11 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.pm.PackageInfoCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
||||
import kotlinx.android.synthetic.main.fragment_about.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.BuildConfig
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
|
@ -39,10 +37,9 @@ import org.mozilla.geckoview.BuildConfig as GeckoViewBuildConfig
|
|||
* Displays the logo and information about the app, including library versions.
|
||||
*/
|
||||
class AboutFragment : Fragment(), AboutPageListener {
|
||||
|
||||
private lateinit var appName: String
|
||||
private val aboutPageAdapter: AboutPageAdapter = AboutPageAdapter(this)
|
||||
private var secretDebugMenuClicks = 0
|
||||
private var lastDebugMenuToast: Toast? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
|
@ -56,15 +53,7 @@ class AboutFragment : Fragment(), AboutPageListener {
|
|||
return rootView
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
secretDebugMenuClicks = 0
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
about_list.run {
|
||||
adapter = aboutPageAdapter
|
||||
addItemDecoration(
|
||||
|
@ -75,33 +64,10 @@ class AboutFragment : Fragment(), AboutPageListener {
|
|||
)
|
||||
}
|
||||
|
||||
// 5 taps on the logo activate the "secret" debug menu.
|
||||
wordmark.setOnClickListener {
|
||||
// Because the user will mostly likely tap the logo in rapid succession,
|
||||
// we ensure only 1 toast is shown at any given time.
|
||||
lastDebugMenuToast?.let { toast -> toast.cancel() }
|
||||
secretDebugMenuClicks += 1
|
||||
when (secretDebugMenuClicks) {
|
||||
in 2 until SECRET_DEBUG_MENU_CLICKS -> {
|
||||
val clicksLeft = SECRET_DEBUG_MENU_CLICKS - secretDebugMenuClicks
|
||||
val toast = Toast.makeText(
|
||||
context,
|
||||
getString(R.string.about_debug_menu_toast_progress, clicksLeft),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
toast.show()
|
||||
lastDebugMenuToast = toast
|
||||
}
|
||||
SECRET_DEBUG_MENU_CLICKS -> {
|
||||
Toast.makeText(
|
||||
context,
|
||||
getString(R.string.about_debug_menu_toast_done),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
requireContext().settings().showSecretDebugMenuThisSession = true
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle.addObserver(SecretDebugMenuTrigger(
|
||||
logoView = wordmark,
|
||||
settings = view.context.settings()
|
||||
))
|
||||
|
||||
populateAboutHeader()
|
||||
aboutPageAdapter.submitList(populateAboutList())
|
||||
|
@ -233,7 +199,5 @@ class AboutFragment : Fragment(), AboutPageListener {
|
|||
|
||||
companion object {
|
||||
private const val ABOUT_LICENSE_URL = "about:license"
|
||||
// Number of clicks on the app logo to enable the "secret" debug menu.
|
||||
private const val SECRET_DEBUG_MENU_CLICKS = 5
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* 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.about
|
||||
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
/**
|
||||
* Triggers the "secret" debug menu when logoView is tapped 5 times.
|
||||
*/
|
||||
class SecretDebugMenuTrigger(
|
||||
logoView: View,
|
||||
private val settings: Settings
|
||||
) : View.OnClickListener, LifecycleObserver {
|
||||
|
||||
private var secretDebugMenuClicks = 0
|
||||
private var lastDebugMenuToast: Toast? = null
|
||||
|
||||
init {
|
||||
if (!settings.showSecretDebugMenuThisSession) {
|
||||
logoView.setOnClickListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the [secretDebugMenuClicks] counter.
|
||||
*/
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
||||
fun clearClickCounter() {
|
||||
secretDebugMenuClicks = 0
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
// Because the user will mostly likely tap the logo in rapid succession,
|
||||
// we ensure only 1 toast is shown at any given time.
|
||||
lastDebugMenuToast?.cancel()
|
||||
secretDebugMenuClicks += 1
|
||||
when (secretDebugMenuClicks) {
|
||||
in 2 until SECRET_DEBUG_MENU_CLICKS -> {
|
||||
val clicksLeft = SECRET_DEBUG_MENU_CLICKS - secretDebugMenuClicks
|
||||
val toast = Toast.makeText(
|
||||
v.context,
|
||||
v.context.getString(R.string.about_debug_menu_toast_progress, clicksLeft),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
toast.show()
|
||||
lastDebugMenuToast = toast
|
||||
}
|
||||
SECRET_DEBUG_MENU_CLICKS -> {
|
||||
Toast.makeText(
|
||||
v.context,
|
||||
R.string.about_debug_menu_toast_done,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
settings.showSecretDebugMenuThisSession = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Number of clicks on the app logo to enable the "secret" debug menu.
|
||||
private const val SECRET_DEBUG_MENU_CLICKS = 5
|
||||
}
|
||||
}
|
|
@ -37,6 +37,7 @@ class SyncedTabsFragment : LibraryPageFragment<Tab>() {
|
|||
|
||||
syncedTabsFeature.set(
|
||||
feature = SyncedTabsFeature(
|
||||
context = requireContext(),
|
||||
storage = backgroundServices.syncedTabsStorage,
|
||||
accountManager = backgroundServices.accountManager,
|
||||
view = synced_tabs_layout,
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
data class TabHistoryItem(
|
||||
val title: String,
|
||||
val url: String,
|
||||
val index: Int,
|
||||
val isSelected: Boolean
|
||||
)
|
||||
|
||||
class TabHistoryAdapter(
|
||||
private val interactor: TabHistoryViewInteractor
|
||||
) : RecyclerView.Adapter<TabHistoryViewHolder>() {
|
||||
|
||||
var historyList: List<TabHistoryItem> = emptyList()
|
||||
set(value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabHistoryViewHolder {
|
||||
val view =
|
||||
LayoutInflater.from(parent.context).inflate(R.layout.history_list_item, parent, false)
|
||||
return TabHistoryViewHolder(view, interactor)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TabHistoryViewHolder, position: Int) {
|
||||
holder.bind(historyList[position])
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = historyList.size
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
interface TabHistoryController {
|
||||
fun handleGoToHistoryItem(item: TabHistoryItem)
|
||||
}
|
||||
|
||||
class DefaultTabHistoryController(
|
||||
private val navController: NavController,
|
||||
private val goToHistoryIndexUseCase: SessionUseCases.GoToHistoryIndexUseCase
|
||||
) : TabHistoryController {
|
||||
|
||||
override fun handleGoToHistoryItem(item: TabHistoryItem) {
|
||||
navController.popBackStack(R.id.browserFragment, false)
|
||||
goToHistoryIndexUseCase.invoke(item.index)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.android.synthetic.main.fragment_tab_history_dialog.*
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
||||
class TabHistoryDialogFragment : BottomSheetDialogFragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setStyle(STYLE_NO_TITLE, R.style.BottomSheet)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? = inflater.inflate(R.layout.fragment_tab_history_dialog, container, false)
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val controller = DefaultTabHistoryController(
|
||||
navController = findNavController(),
|
||||
goToHistoryIndexUseCase = requireComponents.useCases.sessionUseCases.goToHistoryIndex
|
||||
)
|
||||
val tabHistoryView = TabHistoryView(
|
||||
container = tabHistoryLayout,
|
||||
expandDialog = ::expand,
|
||||
interactor = TabHistoryInteractor(controller)
|
||||
)
|
||||
|
||||
consumeFrom(requireComponents.core.store) {
|
||||
tabHistoryView.updateState(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun expand() {
|
||||
(dialog as BottomSheetDialog).behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
class TabHistoryInteractor(
|
||||
private val controller: TabHistoryController
|
||||
) : TabHistoryViewInteractor {
|
||||
|
||||
override fun goToHistoryItem(item: TabHistoryItem) {
|
||||
controller.handleGoToHistoryItem(item)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_tabhistory.*
|
||||
import mozilla.components.browser.state.selector.selectedTab
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
interface TabHistoryViewInteractor {
|
||||
|
||||
/**
|
||||
* Jump to a specific index in the tab's history.
|
||||
*/
|
||||
fun goToHistoryItem(item: TabHistoryItem)
|
||||
}
|
||||
|
||||
class TabHistoryView(
|
||||
private val container: ViewGroup,
|
||||
private val expandDialog: () -> Unit,
|
||||
interactor: TabHistoryViewInteractor
|
||||
) : LayoutContainer {
|
||||
|
||||
override val containerView: View?
|
||||
get() = container
|
||||
|
||||
val view: View = LayoutInflater.from(container.context)
|
||||
.inflate(R.layout.component_tabhistory, container, true)
|
||||
|
||||
private val adapter = TabHistoryAdapter(interactor)
|
||||
private val layoutManager = object : LinearLayoutManager(view.context) {
|
||||
override fun onLayoutCompleted(state: RecyclerView.State?) {
|
||||
super.onLayoutCompleted(state)
|
||||
currentIndex?.let { index ->
|
||||
// Force expansion of the dialog, otherwise scrolling to the current history item
|
||||
// won't work when its position is near the bottom of the recyclerview.
|
||||
expandDialog.invoke()
|
||||
// Also, attempt to center the current history item.
|
||||
val itemView = tabHistoryRecyclerView.findViewHolderForLayoutPosition(
|
||||
findFirstCompletelyVisibleItemPosition()
|
||||
)?.itemView
|
||||
val offset = tabHistoryRecyclerView.height / 2 - (itemView?.height ?: 0) / 2
|
||||
scrollToPositionWithOffset(index, offset)
|
||||
}
|
||||
}
|
||||
}.apply {
|
||||
reverseLayout = true
|
||||
}
|
||||
|
||||
private var currentIndex: Int? = null
|
||||
|
||||
init {
|
||||
tabHistoryRecyclerView.adapter = adapter
|
||||
tabHistoryRecyclerView.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
fun updateState(state: BrowserState) {
|
||||
state.selectedTab?.content?.history?.let { historyState ->
|
||||
currentIndex = historyState.currentIndex
|
||||
val items = historyState.items.mapIndexed { index, historyItem ->
|
||||
TabHistoryItem(
|
||||
title = historyItem.title,
|
||||
url = historyItem.uri,
|
||||
index = index,
|
||||
isSelected = index == historyState.currentIndex
|
||||
)
|
||||
}
|
||||
adapter.historyList = items
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.tabhistory
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.text.bold
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.history_list_item.view.*
|
||||
|
||||
class TabHistoryViewHolder(
|
||||
private val view: View,
|
||||
private val interactor: TabHistoryViewInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
fun bind(item: TabHistoryItem) {
|
||||
view.history_layout.overflowView.isVisible = false
|
||||
view.history_layout.urlView.text = item.url
|
||||
view.history_layout.loadFavicon(item.url)
|
||||
|
||||
view.history_layout.titleView.text = if (item.isSelected) {
|
||||
buildSpannedString {
|
||||
bold { append(item.title) }
|
||||
}
|
||||
} else {
|
||||
item.title
|
||||
}
|
||||
|
||||
view.setOnClickListener { interactor.goToHistoryItem(item) }
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/* 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.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
* A queue that acts as a gate, either executing tasks right away if the queue is marked as "ready",
|
||||
* i.e. gate is open, or queues them to be executed whenever the queue is marked as ready in the
|
||||
* future, i.e. gate becomes open.
|
||||
*/
|
||||
class RunWhenReadyQueue {
|
||||
private val tasks = CopyOnWriteArrayList<() -> Unit>()
|
||||
private val isReady = AtomicBoolean(false)
|
||||
|
||||
/**
|
||||
* Was this queue ever marked as 'ready' via a call to [ready]?
|
||||
*
|
||||
* @return Boolean value indicating if this queue is 'ready'.
|
||||
*/
|
||||
fun isReady(): Boolean = isReady.get()
|
||||
|
||||
/**
|
||||
* Runs the [task] if this queue is marked as ready, or queues it for later execution.
|
||||
* Task will be executed on the main thread.
|
||||
*
|
||||
* @param task: The task to run now if queue is ready or queue for later execution.
|
||||
*/
|
||||
fun runIfReadyOrQueue(task: () -> Unit) {
|
||||
if (isReady.get()) {
|
||||
CoroutineScope(Dispatchers.Main).launch { task.invoke() }
|
||||
} else {
|
||||
tasks.add(task)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark queue as ready. Pending tasks will execute, and all tasks passed to [runIfReadyOrQueue]
|
||||
* after this point will be executed immediately.
|
||||
*/
|
||||
fun ready() {
|
||||
// Make sure that calls to `ready` are idempotent.
|
||||
if (!isReady.compareAndSet(false, true)) {
|
||||
return
|
||||
}
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
tasks.forEach { it.invoke() }.also { tasks.clear() }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?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/. -->
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tabHistoryWrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<View
|
||||
android:id="@+id/handle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="3dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@color/secondary_text_normal_theme"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.1" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/tabHistoryRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="19dp"
|
||||
tools:listitem="@layout/history_list_item" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -2,51 +2,66 @@
|
|||
<!-- 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/. -->
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.mozilla.fenix.browser.SwipeGestureLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/browserLayout"
|
||||
android:id="@+id/gestureLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="browser.BrowserFragment">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/browserLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<mozilla.components.concept.engine.EngineView
|
||||
android:id="@+id/engineView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/stubFindInPage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:inflatedId="@+id/findInPageView"
|
||||
android:layout="@layout/stub_find_in_page" />
|
||||
|
||||
<include
|
||||
android:id="@+id/viewDynamicDownloadDialog"
|
||||
layout="@layout/download_dialog_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="gone" />
|
||||
|
||||
<mozilla.components.feature.readerview.view.ReaderViewControlsBar
|
||||
android:id="@+id/readerViewControlsBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?foundation"
|
||||
android:elevation="24dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<org.mozilla.fenix.browser.TabPreview
|
||||
android:id="@+id/tabPreview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:alpha="0"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
<mozilla.components.concept.engine.EngineView
|
||||
android:id="@+id/engineView"
|
||||
android:visibility="gone"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/stubFindInPage"
|
||||
android:inflatedId="@+id/findInPageView"
|
||||
android:layout="@layout/stub_find_in_page"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp" />
|
||||
|
||||
<include
|
||||
android:id="@+id/viewDynamicDownloadDialog"
|
||||
layout="@layout/download_dialog_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<mozilla.components.feature.readerview.view.ReaderViewControlsBar
|
||||
android:id="@+id/readerViewControlsBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?foundation"
|
||||
android:elevation="24dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</org.mozilla.fenix.browser.SwipeGestureLayout>
|
||||
|
|
|
@ -147,7 +147,7 @@
|
|||
android:layout_marginTop="@dimen/search_fragment_shortcuts_label_margin_vertical"
|
||||
android:layout_marginEnd="@dimen/search_fragment_shortcuts_label_margin_horizontal"
|
||||
android:visibility="gone"
|
||||
android:text="@string/search_shortcuts_search_with_2"
|
||||
android:text="@string/search_engines_search_with"
|
||||
app:layout_constraintStart_toStartOf="@id/scrollable_area"
|
||||
app:layout_constraintTop_toBottomOf="@id/awesomeBar_barrier"
|
||||
tools:text="This time, search with:" />
|
||||
|
@ -197,8 +197,8 @@
|
|||
<ToggleButton
|
||||
android:id="@+id/search_shortcuts_button"
|
||||
style="@style/search_pill"
|
||||
android:textOff="@string/search_shortcuts_button"
|
||||
android:textOn="@string/search_shortcuts_button"
|
||||
android:textOff="@string/search_engines_shortcut_button"
|
||||
android:textOn="@string/search_engines_shortcut_button"
|
||||
app:drawableStartCompat="@drawable/ic_search" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,10 @@
|
|||
<?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/. -->
|
||||
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/tabHistoryLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
|
@ -0,0 +1,49 @@
|
|||
<?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/. -->
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<mozilla.components.browser.tabstray.thumbnail.TabThumbnailView
|
||||
android:id="@+id/previewThumbnail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?tabTrayThumbnailItemBackground" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/fakeToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/browser_toolbar_height"
|
||||
android:layout_gravity="bottom"
|
||||
android:background="?bottomBarBackground"
|
||||
android:elevation="5dp"
|
||||
android:foregroundGravity="bottom"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<View
|
||||
android:id="@+id/toolbar_wrapper"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/home_search_background" />
|
||||
|
||||
<org.mozilla.fenix.components.toolbar.TabCounter
|
||||
android:id="@+id/tab_button"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/menuButton"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="center"
|
||||
app:srcCompat="@drawable/ic_menu"
|
||||
app:tint="?primaryText" />
|
||||
</LinearLayout>
|
||||
</merge>
|
|
@ -102,6 +102,9 @@
|
|||
<action
|
||||
android:id="@+id/action_global_savedLoginsAuthFragment"
|
||||
app:destination="@id/savedLoginsAuthFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_tabHistoryDialogFragment"
|
||||
app:destination="@id/tabHistoryDialogFragment" />
|
||||
|
||||
<dialog
|
||||
android:id="@+id/tabTrayDialogFragment"
|
||||
|
@ -859,4 +862,7 @@
|
|||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
<dialog
|
||||
android:id="@+id/tabHistoryDialogFragment"
|
||||
android:name="org.mozilla.fenix.tabhistory.TabHistoryDialogFragment" />
|
||||
</navigation>
|
||||
|
|
|
@ -573,6 +573,8 @@
|
|||
<string name="bookmark_select_folder">Select folder</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Are you sure you want to delete this folder?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s will delete the selected items.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">Deleted %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -627,8 +629,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">Deleted %1$s</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Bookmarks deleted</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Deleting selected folders</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">UNDO</string>
|
||||
|
||||
|
@ -721,6 +725,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d tab selected</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Tabs saved!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Collection saved!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Tab saved!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -825,6 +831,10 @@
|
|||
<string name="qr_scanner_dialog_negative">DENY</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Are you sure you want to delete %1$s?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Deleting this tab will delete the entire collection. You can create new collections at any time.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Delete %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Delete</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -879,8 +889,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Automatically deletes browsing data when you select "Quit" from the main menu</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Automatically deletes browsing data when you select \"Quit\" from the main menu</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Browsing history</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Quit</string>
|
||||
|
||||
|
@ -1223,6 +1231,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Logins and passwords that are not saved will be shown here.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Logins and passwords will not be saved for these sites.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Delete all exceptions</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Search logins</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1261,6 +1271,8 @@
|
|||
<string name="saved_login_copy_username">Copy username</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Copy site</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Open site in browser</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Show password</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -593,6 +593,8 @@
|
|||
<string name="bookmark_select_folder">Seleccionar carpeta</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">¿Estás seguro de que querés eliminar eliminar esta carpeta?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s va a eliminar los elementos seleccionados.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">Se eliminó %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -650,8 +652,10 @@
|
|||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">Se eliminó %1$s</string>
|
||||
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Marcadores eliminados</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Eliminar carpetas seleccionadas</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">UNDO</string>
|
||||
|
||||
|
@ -746,6 +750,8 @@
|
|||
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">¡Pestañas guardadas!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">¡Colección guardada!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">¡Pestaña guardada!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -852,6 +858,10 @@
|
|||
<string name="qr_scanner_dialog_negative">DENY</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">¿Estás seguro de que querés eliminar %1$s?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Eliminar esta pestaña va a eliminar toda la colección. Podés crear nuevas colecciones en cualquier momento.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">¿Eliminar %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Eliminar</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -1249,6 +1259,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Los inicios de sesión y las contraseñas que no se guardan se mostrarán aquí.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Los inicios de sesión y las contraseñas no se van a guardar para estos sitios.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Eliminar todas las excepciones</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Buscar inicios de sesión</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1287,6 +1299,8 @@
|
|||
<string name="saved_login_copy_username">Copiar nombre de usuario</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Copiar sitio</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Abrir sitio en el navegador</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Mostrar contraseña</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -581,6 +581,8 @@
|
|||
<string name="bookmark_select_folder">Válasszon mappát</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Biztos, hogy törölni szeretné ezt a mappát?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">A(z) %s törölni fogja a kiválasztott elemeket.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">%1$s törölve</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -636,8 +638,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">%1$s törölve</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Könyvjelzők törölve</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Kiválasztott mappák törlése</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">VISSZAVONÁS</string>
|
||||
|
||||
|
@ -731,6 +735,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d lap kiválasztva</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Lapok mentve.</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Gyűjtemény mentve.</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Lap mentve.</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -838,6 +844,10 @@
|
|||
<string name="qr_scanner_dialog_negative">ELUTASÍTÁS</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Biztos, hogy törli ezt: %1$s?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">A lap törlésével törli az egész gyűjteményt. Bármikor létrehozhat új gyűjteményeket.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Törli ezt: %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Törlés</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -893,8 +903,6 @@
|
|||
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Automatikusan törli a böngészési adatokat, ha a főmenüben a „Kilépés” lehetőséget választja</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Böngészési előzmények</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Kilépés</string>
|
||||
|
||||
|
@ -1244,6 +1252,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Itt jelennek meg a nem mentett bejelentkezések és jelszavak.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">A bejelentkezéseket és a jelszavak nem lesznek elmentve ezeknél a webhelyeknél.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Összes kivétel törlése</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Bejelentkezések keresése</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1282,6 +1292,8 @@
|
|||
<string name="saved_login_copy_username">Felhasználónév másolása</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Oldal másolása</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Oldal megnyitása böngészőben</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Jelszó megjelenítése</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -569,6 +569,8 @@
|
|||
<string name="bookmark_select_folder">Буманы таңдау</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Бұл буманы өшіруді шынымен қалайсыз ба?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s таңдалған элементтерді өшіреді.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">%1$s өшірілді</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -623,8 +625,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">%1$s өшірілді</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Бетбелгілер өшірілді</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Таңдалған бумаларды өшіру</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">БОЛДЫРМАУ</string>
|
||||
|
||||
|
@ -718,6 +722,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d бет таңдалды</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Беттер сақталды!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Жинақ сақталды!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Бет сақталды!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -822,6 +828,10 @@
|
|||
<string name="qr_scanner_dialog_negative">ТЫЙЫМ САЛУ</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">%1$s өшіруді шынымен қалайсыз ба?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Бұл бетті өшіру жинақты толығымен өшіреді. Жаңа жинақтарды кез келген уақытта жасауға болады.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">%1$s өшіру керек пе?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Өшіру</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -878,8 +888,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Негізгі мәзірден "Шығу" таңдау кезінде, шолу деректерін автоматты түрде өшіреді</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Негізгі мәзірден \"Шығу\" таңдау кезінде, шолу деректерін автоматты түрде өшіреді</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Шолу тарихы</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Шығу</string>
|
||||
|
||||
|
@ -1227,6 +1235,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Сақталмаған логиндер мен парольдер осында көрсетіледі.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Бұл сайттар үшін логиндер мен парольдер сақталмайды.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Барлық ережеден тыс жағдайларды өшіру</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Логиндерден іздеу</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1265,6 +1275,8 @@
|
|||
<string name="saved_login_copy_username">Пайдаланушы атын көшіріп алу</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Сайтты көшіріп алу</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Сайтты браузерде ашу</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Парольді көрсету</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -593,6 +593,8 @@
|
|||
<string name="bookmark_select_folder">폴더 선택</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">이 폴더를 삭제하시겠습니까?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s가 선택된 항목들을 삭제합니다.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">%1$s 삭제됨</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -652,8 +654,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">%1$s 삭제됨</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">북마크 삭제됨</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">선택한 폴더 삭제 중</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">실행 취소</string>
|
||||
|
||||
|
@ -757,6 +761,8 @@
|
|||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">탭이 저장되었습니다!</string>
|
||||
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">모음집 저장됨!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">탭이 저장되었습니다!</string>
|
||||
|
||||
|
@ -869,6 +875,10 @@
|
|||
<string name="qr_scanner_dialog_negative">거부</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">%1$s 파일을 삭제하시겠습니까?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">이 탭을 삭제하면 전체 모음집이 삭제됩니다. 언제든지 새 모음집을 만들 수 있습니다.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">%1$s 모음집을 삭제하시겠습니까?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">삭제</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -1276,6 +1286,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">저장되지 않은 로그인과 비밀번호가 여기에 표시됩니다.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">이 사이트에 대한 로그인과 비밀번호는 저장되지 않습니다.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">모든 예외 삭제</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">로그인 검색</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1314,6 +1326,8 @@
|
|||
<string name="saved_login_copy_username">사용자 이름 복사</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">사이트 복사</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">브라우저에서 사이트 열기</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">비밀번호 보이기</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -582,6 +582,8 @@
|
|||
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Ar tikrai norite pašalinti šį aplanką?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">„%s“ pašalins pasirinktus elementus.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">Pašalintas „%1$s“</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -637,8 +639,10 @@
|
|||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">Pašalintas %1$s</string>
|
||||
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Adresyno įrašai pašalinti</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Šalinami pasirinkti aplankai</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">Atšaukti</string>
|
||||
|
||||
|
@ -732,6 +736,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">Pažymėta %d kortelė</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Kortelės įrašytos!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Rinkinys įrašytas!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Kortelė įrašyta!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -837,6 +843,10 @@
|
|||
<string name="qr_scanner_dialog_negative">Drausti</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Ar tikrai norite pašalinti „%1$s“?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Pašalindami šią kortelę, pašalinsite visą rinkinį. Naujus rinkinius galite sukurti bet kada.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Pašalinti „%1$s“?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Pašalinti</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -894,8 +904,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Naršymo duomenys bus pašalinami automatiškai, pagrindiniame meniu pasirinkus „Išeiti“</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Naršymo duomenys bus pašalinami automatiškai, pagrindiniame meniu pasirinkus „Išeiti“</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Naršymo žurnalas</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Išeiti</string>
|
||||
|
||||
|
@ -1281,6 +1289,8 @@
|
|||
<string name="saved_login_copy_username">Kopijuoti naudotojo vardą</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Kopijuoti svetainę</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Atverti svetainę naršyklėje</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Rodyti slaptažodį</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -582,6 +582,8 @@
|
|||
<string name="bookmark_select_folder">Velg mappe</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Er du sikker på at du vil slette denne mappen?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s vil slette de valgte elementene.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">Slettet %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -637,8 +639,10 @@
|
|||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">Slettet %1$s</string>
|
||||
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Bokmerker slettet</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Sletter valgte mapper</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">ANGRE</string>
|
||||
|
||||
|
@ -734,6 +738,8 @@
|
|||
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Faner lagret!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Samling lagret!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Fane lagret!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -838,6 +844,10 @@
|
|||
<string name="qr_scanner_dialog_negative">AVSLÅ</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Er du sikker på at du vil slette %1$s?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Hvis du sletter denne fanen, blir hele samlingen slettet. Du kan når som helst lage nye samlinger.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Vil du slette %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Slett</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -894,8 +904,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Sletter nettleserdata automatisk når du velger «Avslutt» fra hovedmenyen</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Sletter nettleserdata automatisk når du velger «Avslutt» fra hovedmenyen</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Nettleserhistorikk</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Avslutt</string>
|
||||
|
||||
|
@ -1250,6 +1258,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Innlogginger og passord som ikke er lagret vil vises her.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Innlogginger og passord vil ikke bli lagret for disse nettstedene.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Slett alle unntak</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Søk innlogginger</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1289,6 +1299,8 @@
|
|||
<string name="saved_login_copy_username">Kopier brukernavn</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Kopier nettsted</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Åpne nettsted i nettleseren</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Vis passord</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -584,6 +584,8 @@
|
|||
<string name="bookmark_select_folder">Map selecteren</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Weet u zeker dat u deze map wilt verwijderen?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s zal de geselecteerde items verwijderen.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">%1$s verwijderd</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -638,8 +640,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">%1$s verwijderd</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Bladwijzers verwijderd</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Geselecteerde mappen verwijderen</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">ONGEDAAN MAKEN</string>
|
||||
|
||||
|
@ -733,6 +737,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d tabblad geselecteerd</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Tabbladen opgeslagen!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Collectie opgeslagen!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Tabblad opgeslagen!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -840,6 +846,10 @@
|
|||
<string name="qr_scanner_dialog_negative">WEIGEREN</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Weet u zeker dat u %1$s wilt verwijderen?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Als u dit tabblad verwijdert, wordt de hele collectie verwijderd. U kunt op elk moment nieuwe collecties maken.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">%1$s verwijderen?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Verwijderen</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -895,8 +905,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Verwijdert automatisch navigatiegegevens wanneer u in het hoofdmenu ‘Afsluiten’ selecteert</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Verwijdert automatisch navigatiegegevens wanneer u in het hoofdmenu ‘Afsluiten’ selecteert</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Navigatiegeschiedenis</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Afsluiten</string>
|
||||
|
||||
|
@ -1243,6 +1251,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Niet-opgeslagen aanmeldingen en wachtwoorden worden hier weergegeven.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Aanmeldingen en wachtwoorden worden voor deze websites niet opgeslagen.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Alle uitzonderingen verwijderen</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Aanmeldingen zoeken</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1281,6 +1291,8 @@
|
|||
<string name="saved_login_copy_username">Gebruikersnaam kopiëren</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Website kopiëren</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Website openen in browser</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Wachtwoord tonen</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -576,6 +576,8 @@
|
|||
<string name="bookmark_select_folder">Selecionar pasta</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Tem certeza que deseja excluir esta pasta?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">O %s excluirá os itens selecionados.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">%1$s excluída</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -630,8 +632,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">Excluiu %1$s</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Favoritos excluídos</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Excluindo pastas selecionadas</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">DESFAZER</string>
|
||||
|
||||
|
@ -726,6 +730,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d aba selecionada</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Abas salvas!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Coleção salva!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Aba salva!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -830,6 +836,10 @@
|
|||
<string name="qr_scanner_dialog_negative">NEGAR</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Tem certeza que deseja excluir %1$s?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Excluir esta aba também excluirá toda a coleção. Você pode criar novas coleções quando quiser.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Excluir %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Excluir</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -1234,6 +1244,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Contas e senhas que não são salvas são mostradas aqui.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Contas e senhas desses sites não serão salvas.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Excluir todas as exceções</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Pesquisar contas</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1272,6 +1284,8 @@
|
|||
<string name="saved_login_copy_username">Copiar nome de usuário</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Copiar site</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Abrir site no navegador</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Exibir senha</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -586,6 +586,8 @@
|
|||
<string name="bookmark_select_folder">Välj mapp</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Är du säker på att du vill ta bort den här mappen?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s kommer att radera de markerade objekten.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">Tog bort %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -640,8 +642,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">%1$s har tagits bort</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Bokmärken borttagna</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Tar bort valda mappar</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">ÅNGRA</string>
|
||||
|
||||
|
@ -735,6 +739,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d flik vald</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Flikar sparade!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Samling sparad!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Flik sparad!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -843,6 +849,10 @@
|
|||
<string name="qr_scanner_dialog_negative">NEKA</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Är du säker att du vill ta bort %1$s?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Om du tar bort den här fliken raderas hela samlingen. Du kan skapa nya samlingar när som helst.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Tog bort %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Ta bort</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -899,8 +909,6 @@
|
|||
<string name="preference_summary_delete_browsing_data_on_quit">Tar automatiskt bort surfdata när du väljer "Avsluta" från huvudmenyn</string>
|
||||
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
|
||||
<string name="preference_summary_delete_browsing_data_on_quit_2">Tar automatiskt bort surfdata när du väljer \"Avsluta\" från huvudmenyn</string>
|
||||
<!-- Category for history items to delete on quit in delete browsing data on quit -->
|
||||
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Webbläsarhistorik</string>
|
||||
<!-- Action item in menu for the Delete browsing data on quit feature -->
|
||||
<string name="delete_browsing_data_on_quit_action">Avsluta</string>
|
||||
|
||||
|
@ -1249,6 +1257,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Inloggningar och lösenord som inte sparas visas här.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Inloggningar och lösenord sparas inte för dessa webbplatser.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Ta bort alla undantag</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Sök inloggningar</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1287,6 +1297,8 @@
|
|||
<string name="saved_login_copy_username">Kopiera användarnamn</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Kopiera webbplats</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Öppna webbplatsen i webbläsaren</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Visa lösenord</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
<string name="browser_menu_open_in_fenix">%1$s ile aç</string>
|
||||
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
|
||||
The first parameter is the name of the app defined in app_name (for example: Fenix) -->
|
||||
<string name="browser_menu_powered_by">%1$s TARAFINDAN GELİŞTİRİLDİ</string>
|
||||
<string name="browser_menu_powered_by">%1$s SEKMESİ</string>
|
||||
|
||||
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
|
||||
The first parameter is the name of the app defined in app_name (for example: Fenix) -->
|
||||
|
|
|
@ -575,6 +575,8 @@
|
|||
<string name="bookmark_select_folder">Chọn thư mục</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">Bạn có chắc chắn muốn xóa thư mục này?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s sẽ xóa các mục đã chọn.</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">Đã xóa %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -629,8 +631,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">Đã xóa %1$s</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">Đã xóa dấu trang</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">Đang xóa các thư mục đã chọn</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">HOÀN TÁC</string>
|
||||
|
||||
|
@ -724,6 +728,8 @@
|
|||
<string name="create_collection_save_to_collection_tab_selected">%d thẻ được chọn</string>
|
||||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">Đã lưu các thẻ!</string>
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">Đã lưu bộ sưu tập!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">Đã lưu thẻ!</string>
|
||||
<!-- Content description (not visible, for screen readers etc.): button to close the collection creator -->
|
||||
|
@ -828,6 +834,10 @@
|
|||
<string name="qr_scanner_dialog_negative">TỪ CHỐI</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">Bạn có chắc chắn muốn xóa %1$s không?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">Xóa thẻ này sẽ xóa bộ sưu tập này. Bạn có thể tạo bộ sưu tập mới bất cứ lúc nào.</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">Xóa %1$s?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">Xóa</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -1224,6 +1234,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Đăng nhập và mật khẩu không được lưu sẽ được hiển thị ở đây.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Đăng nhập và mật khẩu sẽ không được lưu cho các trang web này.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Xóa tất cả các ngoại lệ</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Tìm thông tin đăng nhập</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1262,6 +1274,8 @@
|
|||
<string name="saved_login_copy_username">Sao chép tên người dùng</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">Sao chép URL trang web</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">Mở trang web trong trình duyệt</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">Hiện mật khẩu</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -593,6 +593,8 @@
|
|||
<string name="bookmark_select_folder">选择文件夹</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">您确定要删除这个文件夹吗?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s 将删除所选项目。</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">已删除 %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -650,8 +652,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">已删除 %1$s 条书签</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">书签已删除</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">正在删除所选文件夹</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">撤销</string>
|
||||
|
||||
|
@ -756,6 +760,8 @@
|
|||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">标签页已保存!</string>
|
||||
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">收藏集已保存!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">标签页已保存!</string>
|
||||
|
||||
|
@ -867,6 +873,10 @@
|
|||
<string name="qr_scanner_dialog_negative">拒绝</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">您确定要删除“%1$s”吗?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">删除此标签页将删除整个收藏集。您可以随时新建收藏集。</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">要删除 %1$s 吗?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">删除</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -1268,6 +1278,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">不保存登录名和密码的网站将显示于此处。</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">将不保存这些网站的登录名和密码。</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">删除所有例外</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">搜索登录信息</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1306,6 +1318,8 @@
|
|||
<string name="saved_login_copy_username">复制用户名</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">复制网站</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">在浏览器中打开网站</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">显示密码</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -588,6 +588,8 @@
|
|||
<string name="bookmark_select_folder">選擇資料夾</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete the selected folder -->
|
||||
<string name="bookmark_delete_folder_confirmation_dialog">您確定要刪除這個資料夾嗎?</string>
|
||||
<!-- Confirmation message for a dialog confirming if the user wants to delete multiple items including folders. Parameter will be replaced by app name. -->
|
||||
<string name="bookmark_delete_multiple_folders_confirmation_dialog">%s 將刪除選擇的項目。</string>
|
||||
<!-- Snackbar title shown after a folder has been deleted. This first parameter is the name of the deleted folder -->
|
||||
<string name="bookmark_delete_folder_snackbar">已刪除 %1$s</string>
|
||||
<!-- Screen title for adding a bookmarks folder -->
|
||||
|
@ -643,8 +645,10 @@
|
|||
<!-- Bookmark snackbar message on deletion
|
||||
The first parameter is the host part of the URL of the bookmark deleted, if any -->
|
||||
<string name="bookmark_deletion_snackbar_message">已刪除書籤 %1$s</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks -->
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks not including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_2">已刪除書籤</string>
|
||||
<!-- Bookmark snackbar message on deleting multiple bookmarks including folders-->
|
||||
<string name="bookmark_deletion_multiple_snackbar_message_3">刪除選擇的資料夾</string>
|
||||
<!-- Bookmark undo button for deletion snackbar action -->
|
||||
<string name="bookmark_undo_deletion">還原</string>
|
||||
|
||||
|
@ -748,6 +752,8 @@
|
|||
<!-- Text shown in snackbar when multiple tabs have been saved in a collection -->
|
||||
<string name="create_collection_tabs_saved">已儲存分頁!</string>
|
||||
|
||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
||||
<string name="create_collection_tabs_saved_new_collection">已儲存收藏集!</string>
|
||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||
<string name="create_collection_tab_saved">已儲存分頁!</string>
|
||||
|
||||
|
@ -859,6 +865,10 @@
|
|||
<string name="qr_scanner_dialog_negative">拒絕</string>
|
||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||
<string name="tab_collection_dialog_message">您確定要刪除 %1$s 嗎?</string>
|
||||
<!-- Collection and tab deletion prompt dialog message. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_message">刪除此分頁也會同時刪除整個收藏集,您可以之後再建立新的收藏集。</string>
|
||||
<!-- Collection and tab deletion prompt dialog title. Placeholder will be replaced with the collection name. This will show when the last tab from a collection is deleted -->
|
||||
<string name="delete_tab_and_collection_dialog_title">要刪除 %1$s 嗎?</string>
|
||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||
<string name="tab_collection_dialog_positive">刪除</string>
|
||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||
|
@ -1258,6 +1268,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">不儲存登入資訊與密碼的網站將顯示於此處。</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">將不儲存這些網站的登入資訊與密碼。</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">刪除所有例外</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">搜尋登入資訊</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
@ -1296,6 +1308,8 @@
|
|||
<string name="saved_login_copy_username">複製使用者名稱</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
|
||||
<string name="saved_login_copy_site">複製網站</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to open a site in logins -->
|
||||
<string name="saved_login_open_site">用瀏覽器開啟</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
|
||||
<string name="saved_login_reveal_password">顯示密碼</string>
|
||||
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
|
||||
|
|
|
@ -146,13 +146,11 @@
|
|||
<!-- Button in the search view that lets a user search by scanning a QR code -->
|
||||
<string name="search_scan_button">Scan</string>
|
||||
<!-- Button in the search view that lets a user search by using a shortcut -->
|
||||
<string name="search_shortcuts_button">Shortcuts</string>
|
||||
<string name="search_engines_shortcut_button">Search Engine</string>
|
||||
<!-- Button in the search view when shortcuts are displayed that takes a user to the search engine settings -->
|
||||
<string name="search_shortcuts_engine_settings">Search engine settings</string>
|
||||
<!-- DEPRECATED: Header displayed when selecting a shortcut search engine -->
|
||||
<string name="search_shortcuts_search_with">Search with</string>
|
||||
<!-- Header displayed when selecting a shortcut search engine -->
|
||||
<string name="search_shortcuts_search_with_2">This time, search with:</string>
|
||||
<string name="search_engines_search_with">This time, search with:</string>
|
||||
<!-- Button in the search view that lets a user navigate to the site in their clipboard -->
|
||||
<string name="awesomebar_clipboard_title">Fill link from clipboard</string>
|
||||
<!-- Button in the search suggestions onboarding that allows search suggestions in private sessions -->
|
||||
|
@ -258,8 +256,8 @@
|
|||
<string name="developer_tools_category">Developer tools</string>
|
||||
<!-- Preference for developers -->
|
||||
<string name="preferences_remote_debugging">Remote debugging via USB</string>
|
||||
<!-- Preference title for switch preference to show search shortcuts -->
|
||||
<string name="preferences_show_search_shortcuts">Show search shortcuts</string>
|
||||
<!-- Preference title for switch preference to show search engines -->
|
||||
<string name="preferences_show_search_engines">Show search engines</string>
|
||||
<!-- Preference title for switch preference to show search suggestions -->
|
||||
<string name="preferences_show_search_suggestions">Show search suggestions</string>
|
||||
<!-- Preference title for switch preference to show voice search button -->
|
||||
|
@ -1432,4 +1430,14 @@
|
|||
<string name="top_sites_max_limit_content">To add a new top site, remove one. Long press the site and select remove.</string>
|
||||
<!-- Confirmation dialog button text when top sites limit is reached. -->
|
||||
<string name="top_sites_max_limit_confirmation_button">OK, Got It</string>
|
||||
|
||||
<!-- DEPRECATED STRINGS -->
|
||||
<!-- Button in the search view that lets a user search by using a shortcut -->
|
||||
<string name="search_shortcuts_button">Shortcuts</string>
|
||||
<!-- DEPRECATED: Header displayed when selecting a shortcut search engine -->
|
||||
<string name="search_shortcuts_search_with">Search with</string>
|
||||
<!-- Header displayed when selecting a shortcut search engine -->
|
||||
<string name="search_shortcuts_search_with_2">This time, search with:</string>
|
||||
<!-- Preference title for switch preference to show search shortcuts -->
|
||||
<string name="preferences_show_search_shortcuts">Show search shortcuts</string>
|
||||
</resources>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="@string/pref_key_show_search_shortcuts"
|
||||
android:title="@string/preferences_show_search_shortcuts" />
|
||||
android:title="@string/preferences_show_search_engines" />
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="@string/pref_key_show_clipboard_suggestions"
|
||||
|
|
|
@ -220,7 +220,7 @@ class DefaultBrowserToolbarControllerTest {
|
|||
|
||||
@Test
|
||||
fun handleToolbarForwardPress() = runBlockingTest {
|
||||
val item = ToolbarMenu.Item.Forward
|
||||
val item = ToolbarMenu.Item.Forward(false)
|
||||
|
||||
val controller = createController(scope = this)
|
||||
controller.handleToolbarItemInteraction(item)
|
||||
|
@ -229,6 +229,17 @@ class DefaultBrowserToolbarControllerTest {
|
|||
verify { sessionUseCases.goForward(currentSession) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleToolbarForwardLongPress() = runBlockingTest {
|
||||
val item = ToolbarMenu.Item.Forward(true)
|
||||
|
||||
val controller = createController(scope = this)
|
||||
controller.handleToolbarItemInteraction(item)
|
||||
|
||||
verify { metrics.track(Event.BrowserMenuItemTapped(Event.BrowserMenuItemTapped.Item.FORWARD)) }
|
||||
verify { navController.navigate(R.id.action_global_tabHistoryDialogFragment) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleToolbarReloadPress() = runBlockingTest {
|
||||
val item = ToolbarMenu.Item.Reload
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/* 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.about
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import io.mockk.CapturingSlot
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
import io.mockk.just
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.slot
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class SecretDebugMenuTriggerTest {
|
||||
|
||||
@MockK private lateinit var logoView: View
|
||||
@MockK private lateinit var context: Context
|
||||
@MockK private lateinit var settings: Settings
|
||||
@MockK(relaxUnitFun = true) private lateinit var toast: Toast
|
||||
private lateinit var clickListener: CapturingSlot<View.OnClickListener>
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockKAnnotations.init(this)
|
||||
mockkStatic(Toast::class)
|
||||
clickListener = slot()
|
||||
|
||||
every { logoView.setOnClickListener(capture(clickListener)) } just Runs
|
||||
every { logoView.context } returns context
|
||||
every {
|
||||
context.getString(R.string.about_debug_menu_toast_progress, any())
|
||||
} returns "Debug menu: x click(s) left to enable"
|
||||
every { settings.showSecretDebugMenuThisSession } returns false
|
||||
every { settings.showSecretDebugMenuThisSession = any() } just Runs
|
||||
every { Toast.makeText(context, any<Int>(), any()) } returns toast
|
||||
every { Toast.makeText(context, any<String>(), any()) } returns toast
|
||||
}
|
||||
|
||||
@After
|
||||
fun teardown() {
|
||||
unmockkStatic(Toast::class)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toast is not displayed on first click`() {
|
||||
SecretDebugMenuTrigger(logoView, settings)
|
||||
clickListener.captured.onClick(logoView)
|
||||
|
||||
verify(inverse = true) { Toast.makeText(context, any<String>(), any()) }
|
||||
verify(inverse = true) { toast.show() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toast is displayed on second click`() {
|
||||
SecretDebugMenuTrigger(logoView, settings)
|
||||
clickListener.captured.onClick(logoView)
|
||||
clickListener.captured.onClick(logoView)
|
||||
|
||||
verify { context.getString(R.string.about_debug_menu_toast_progress, 3) }
|
||||
verify { Toast.makeText(context, any<String>(), Toast.LENGTH_SHORT) }
|
||||
verify { toast.show() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearClickCounter resets counter`() {
|
||||
val trigger = SecretDebugMenuTrigger(logoView, settings)
|
||||
|
||||
clickListener.captured.onClick(logoView)
|
||||
trigger.clearClickCounter()
|
||||
|
||||
clickListener.captured.onClick(logoView)
|
||||
|
||||
verify(inverse = true) { Toast.makeText(context, any<String>(), any()) }
|
||||
verify(inverse = true) { toast.show() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `toast is displayed on fifth click`() {
|
||||
SecretDebugMenuTrigger(logoView, settings)
|
||||
clickListener.captured.onClick(logoView)
|
||||
clickListener.captured.onClick(logoView)
|
||||
clickListener.captured.onClick(logoView)
|
||||
clickListener.captured.onClick(logoView)
|
||||
clickListener.captured.onClick(logoView)
|
||||
|
||||
verify { Toast.makeText(
|
||||
context,
|
||||
R.string.about_debug_menu_toast_done,
|
||||
Toast.LENGTH_LONG
|
||||
) }
|
||||
verify { toast.show() }
|
||||
verify { settings.showSecretDebugMenuThisSession = true }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `don't register click listener if menu is already shown`() {
|
||||
every { settings.showSecretDebugMenuThisSession } returns true
|
||||
SecretDebugMenuTrigger(logoView, settings)
|
||||
|
||||
verify(inverse = true) { logoView.setOnClickListener(any()) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.session.SessionManager
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import org.junit.Test
|
||||
|
||||
class TabHistoryControllerTest {
|
||||
|
||||
val sessionManager: SessionManager = mockk(relaxed = true)
|
||||
val navController: NavController = mockk(relaxed = true)
|
||||
val sessionUseCases = SessionUseCases(sessionManager)
|
||||
val goToHistoryIndexUseCase = sessionUseCases.goToHistoryIndex
|
||||
val controller = DefaultTabHistoryController(
|
||||
navController = navController,
|
||||
goToHistoryIndexUseCase = goToHistoryIndexUseCase
|
||||
)
|
||||
|
||||
val currentItem = TabHistoryItem(
|
||||
index = 0,
|
||||
title = "",
|
||||
url = "",
|
||||
isSelected = true
|
||||
)
|
||||
|
||||
@Test
|
||||
fun handleGoToHistoryIndex() {
|
||||
controller.handleGoToHistoryItem(currentItem)
|
||||
|
||||
verify { goToHistoryIndexUseCase.invoke(currentItem.index) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* 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.tabhistory
|
||||
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.Test
|
||||
|
||||
class TabHistoryInteractorTest {
|
||||
|
||||
val controller: TabHistoryController = mockk(relaxed = true)
|
||||
val interactor = TabHistoryInteractor(controller)
|
||||
|
||||
@Test
|
||||
fun onGoToHistoryItem() {
|
||||
val item: TabHistoryItem = mockk()
|
||||
|
||||
interactor.goToHistoryItem(item)
|
||||
|
||||
verify { controller.handleGoToHistoryItem(item) }
|
||||
}
|
||||
}
|
|
@ -3,5 +3,5 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
object AndroidComponents {
|
||||
const val VERSION = "51.0.20200721130108"
|
||||
const val VERSION = "52.0.20200722023149"
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ object Versions {
|
|||
const val androidx_paging = "2.1.0"
|
||||
const val androidx_transition = "1.3.0"
|
||||
const val androidx_work = "2.2.0"
|
||||
const val androidx_dynamic_animation = "1.0.0"
|
||||
const val google_material = "1.1.0"
|
||||
const val google_flexbox = "2.0.1"
|
||||
|
||||
|
@ -170,6 +171,7 @@ object Deps {
|
|||
const val androidx_recyclerview = "androidx.recyclerview:recyclerview:${Versions.androidx_recyclerview}"
|
||||
const val androidx_core = "androidx.core:core:${Versions.androidx_core}"
|
||||
const val androidx_core_ktx = "androidx.core:core-ktx:${Versions.androidx_core}"
|
||||
const val androidx_dynamic_animation = "androidx.dynamicanimation:dynamicanimation:${Versions.androidx_dynamic_animation}"
|
||||
const val androidx_transition = "androidx.transition:transition:${Versions.androidx_transition}"
|
||||
const val androidx_work_ktx = "androidx.work:work-runtime-ktx:${Versions.androidx_work}"
|
||||
const val androidx_work_testing = "androidx.work:work-testing:${Versions.androidx_work}"
|
||||
|
|
|
@ -16,11 +16,6 @@ SIGNING_ROUTE_TEMPLATES = [
|
|||
"index.{trust-domain}.v2.{project}.{variant}.{build_date}.revision.{head_rev}.{abi}",
|
||||
"index.{trust-domain}.v2.{project}.{variant}.{build_date}.latest.{abi}",
|
||||
"index.{trust-domain}.v2.{project}.{variant}.revision.{head_rev}.{abi}",
|
||||
|
||||
# TODO Bug 1631839: Remove the following scopes once all consumers have migrated
|
||||
"index.project.{trust-domain}.{project}.v2.{variant}.{build_date}.revision.{head_rev}",
|
||||
"index.project.{trust-domain}.{project}.v2.{variant}.{build_date}.latest",
|
||||
"index.project.{trust-domain}.{project}.v2.{variant}.latest",
|
||||
]
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue