Copione merged onto master
commit
543bc849ad
|
@ -330,7 +330,6 @@ dependencies {
|
||||||
implementation Deps.androidx_coordinatorlayout
|
implementation Deps.androidx_coordinatorlayout
|
||||||
|
|
||||||
implementation Deps.sentry
|
implementation Deps.sentry
|
||||||
implementation Deps.osslicenses_library
|
|
||||||
|
|
||||||
implementation Deps.leanplum_core
|
implementation Deps.leanplum_core
|
||||||
implementation Deps.leanplum_fcm
|
implementation Deps.leanplum_fcm
|
||||||
|
@ -429,7 +428,6 @@ dependencies {
|
||||||
implementation Deps.androidx_lifecycle_viewmodel
|
implementation Deps.androidx_lifecycle_viewmodel
|
||||||
implementation Deps.androidx_core
|
implementation Deps.androidx_core
|
||||||
implementation Deps.androidx_core_ktx
|
implementation Deps.androidx_core_ktx
|
||||||
implementation Deps.androidx_dynamic_animation
|
|
||||||
implementation Deps.androidx_transition
|
implementation Deps.androidx_transition
|
||||||
implementation Deps.androidx_work_ktx
|
implementation Deps.androidx_work_ktx
|
||||||
implementation Deps.google_material
|
implementation Deps.google_material
|
||||||
|
|
524
app/metrics.yaml
524
app/metrics.yaml
File diff suppressed because it is too large
Load Diff
|
@ -16,6 +16,7 @@ import org.junit.Test
|
||||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||||
|
import org.mozilla.fenix.ui.robots.clickUrlbar
|
||||||
import org.mozilla.fenix.ui.robots.homeScreen
|
import org.mozilla.fenix.ui.robots.homeScreen
|
||||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||||
|
|
||||||
|
@ -254,4 +255,69 @@ class SmokeTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun verifySearchEngineCanBeChangedTemporarilyUsingShortcuts() {
|
||||||
|
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||||
|
|
||||||
|
homeScreen {
|
||||||
|
}.openSearch {
|
||||||
|
verifyKeyboardVisibility()
|
||||||
|
clickSearchEngineButton()
|
||||||
|
verifySearchEngineList()
|
||||||
|
changeDefaultSearchEngine("Amazon.com")
|
||||||
|
verifySearchEngineIcon("Amazon.com")
|
||||||
|
}.goToSearchEngine {
|
||||||
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
|
}.openTabDrawer {
|
||||||
|
// Changing search engine to Bing
|
||||||
|
}.openHomeScreen {
|
||||||
|
}.openSearch {
|
||||||
|
clickSearchEngineButton()
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
changeDefaultSearchEngine("Bing")
|
||||||
|
verifySearchEngineIcon("Bing")
|
||||||
|
}.goToSearchEngine {
|
||||||
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
|
}.openTabDrawer {
|
||||||
|
// Changing search engine to DuckDuckGo
|
||||||
|
}.openHomeScreen {
|
||||||
|
}.openSearch {
|
||||||
|
clickSearchEngineButton()
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
changeDefaultSearchEngine("DuckDuckGo")
|
||||||
|
verifySearchEngineIcon("DuckDuckGo")
|
||||||
|
}.goToSearchEngine {
|
||||||
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
|
}.openTabDrawer {
|
||||||
|
// Changing search engine to Twitter
|
||||||
|
}.openHomeScreen {
|
||||||
|
}.openSearch {
|
||||||
|
clickSearchEngineButton()
|
||||||
|
mDevice.waitForIdle()
|
||||||
|
changeDefaultSearchEngine("Twitter")
|
||||||
|
verifySearchEngineIcon("Twitter")
|
||||||
|
}.goToSearchEngine {
|
||||||
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
|
}.openTabDrawer {
|
||||||
|
// Changing search engine to Wikipedia
|
||||||
|
}.openHomeScreen {
|
||||||
|
}.openSearch {
|
||||||
|
clickSearchEngineButton()
|
||||||
|
changeDefaultSearchEngine("Wikipedia")
|
||||||
|
verifySearchEngineIcon("Wikipedia")
|
||||||
|
}.goToSearchEngine {
|
||||||
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
|
}.openTabDrawer {
|
||||||
|
// Checking whether the next search will be with default or not
|
||||||
|
}.openHomeScreen {
|
||||||
|
}.openSearch {
|
||||||
|
}.goToSearchEngine {
|
||||||
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
|
}.openNavigationToolbar {
|
||||||
|
clickUrlbar {
|
||||||
|
verifyDefaultSearchEngine("Google")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,14 @@ import android.net.Uri
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.Espresso.pressBack
|
import androidx.test.espresso.Espresso.pressBack
|
||||||
import androidx.test.espresso.action.ViewActions
|
import androidx.test.espresso.action.ViewActions
|
||||||
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import androidx.test.espresso.intent.matcher.BundleMatchers
|
import androidx.test.espresso.intent.matcher.BundleMatchers
|
||||||
import androidx.test.espresso.intent.matcher.IntentMatchers
|
import androidx.test.espresso.intent.matcher.IntentMatchers
|
||||||
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
|
|
@ -239,6 +239,12 @@ fun navigationToolbar(interact: NavigationToolbarRobot.() -> Unit): NavigationTo
|
||||||
return NavigationToolbarRobot.Transition()
|
return NavigationToolbarRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clickUrlbar(interact: SearchRobot.() -> Unit): SearchRobot.Transition {
|
||||||
|
urlBar().click()
|
||||||
|
SearchRobot().interact()
|
||||||
|
return SearchRobot.Transition()
|
||||||
|
}
|
||||||
|
|
||||||
private fun assertSuggestionsAreEqualTo(suggestionSize: Int, searchTerm: String) {
|
private fun assertSuggestionsAreEqualTo(suggestionSize: Int, searchTerm: String) {
|
||||||
mDevice.waitForIdle()
|
mDevice.waitForIdle()
|
||||||
awesomeBar().perform(typeText(searchTerm))
|
awesomeBar().perform(typeText(searchTerm))
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
package org.mozilla.fenix.ui.robots
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
|
import android.widget.ToggleButton
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.ViewInteraction
|
import androidx.test.espresso.ViewInteraction
|
||||||
|
@ -16,7 +17,9 @@ import androidx.test.espresso.action.ViewActions.typeText
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
@ -28,8 +31,11 @@ import androidx.test.uiautomator.Until
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.hamcrest.CoreMatchers.startsWith
|
import org.hamcrest.CoreMatchers.startsWith
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||||
|
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||||
|
import org.mozilla.fenix.helpers.click
|
||||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,6 +53,24 @@ class SearchRobot {
|
||||||
fun verifySearchSettings() = assertSearchSettings()
|
fun verifySearchSettings() = assertSearchSettings()
|
||||||
fun verifySearchBarEmpty() = assertSearchBarEmpty()
|
fun verifySearchBarEmpty() = assertSearchBarEmpty()
|
||||||
|
|
||||||
|
fun verifyKeyboardVisibility() = assertKeyboardVisibility(isExpectedToBeVisible = true)
|
||||||
|
fun verifySearchEngineList() = assertSearchEngineList()
|
||||||
|
fun verifySearchEngineIcon(expectedText: String) {
|
||||||
|
onView(withContentDescription(expectedText))
|
||||||
|
}
|
||||||
|
fun verifyDefaultSearchEngine(expectedText: String) = assertDefaultSearchEngine(expectedText)
|
||||||
|
|
||||||
|
fun changeDefaultSearchEngine(searchEngineName: String) =
|
||||||
|
selectDefaultSearchEngine(searchEngineName)
|
||||||
|
|
||||||
|
fun clickSearchEngineButton() {
|
||||||
|
val searchEngineButton = mDevice.findObject(UiSelector()
|
||||||
|
.instance(1)
|
||||||
|
.className(ToggleButton::class.java))
|
||||||
|
searchEngineButton.waitForExists(waitingTime)
|
||||||
|
searchEngineButton.click()
|
||||||
|
}
|
||||||
|
|
||||||
fun clickScanButton() {
|
fun clickScanButton() {
|
||||||
scanButton().perform(click())
|
scanButton().perform(click())
|
||||||
}
|
}
|
||||||
|
@ -106,6 +130,11 @@ class SearchRobot {
|
||||||
BrowserRobot().interact()
|
BrowserRobot().interact()
|
||||||
return BrowserRobot.Transition()
|
return BrowserRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun goToSearchEngine(interact: NavigationToolbarRobot.() -> Unit): NavigationToolbarRobot.Transition {
|
||||||
|
NavigationToolbarRobot().interact()
|
||||||
|
return NavigationToolbarRobot.Transition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,4 +207,46 @@ fun searchScreen(interact: SearchRobot.() -> Unit): SearchRobot.Transition {
|
||||||
return SearchRobot.Transition()
|
return SearchRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assertKeyboardVisibility(isExpectedToBeVisible: Boolean) = {
|
||||||
|
mDevice.waitNotNull(
|
||||||
|
Until.findObject(
|
||||||
|
By.text("Search Engine")
|
||||||
|
), waitingTime
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
isExpectedToBeVisible,
|
||||||
|
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
.executeShellCommand("dumpsys input_method | grep mInputShown")
|
||||||
|
.contains("mInputShown=true")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertSearchEngineList() {
|
||||||
|
onView(withId(R.id.mozac_browser_toolbar_edit_icon)).click()
|
||||||
|
onView(withText("Google"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
onView(withText("Amazon.com"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
onView(withText("Bing"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
onView(withText("DuckDuckGo"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
onView(withText("Twitter"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
onView(withText("Wikipedia"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun selectDefaultSearchEngine(searchEngine: String) {
|
||||||
|
onView(withId(R.id.mozac_browser_toolbar_edit_icon)).click()
|
||||||
|
onView(withText(searchEngine))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
.perform(click())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertDefaultSearchEngine(expectedText: String) {
|
||||||
|
onView(allOf(withId(R.id.mozac_browser_toolbar_edit_icon), withContentDescription(expectedText)))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
}
|
||||||
|
|
||||||
private fun goBackButton() = onView(allOf(withContentDescription("Navigate up")))
|
private fun goBackButton() = onView(allOf(withContentDescription("Navigate up")))
|
||||||
|
|
|
@ -228,13 +228,8 @@
|
||||||
<activity android:name=".settings.account.AuthIntentReceiverActivity"
|
<activity android:name=".settings.account.AuthIntentReceiverActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
|
<activity android:name=".settings.about.AboutLibrariesActivity"
|
||||||
android:exported="false"
|
android:exported="false" />
|
||||||
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"/>
|
|
||||||
|
|
||||||
<activity android:name="com.google.android.gms.oss.licenses.OssLicensesActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:theme="@style/Theme.AppCompat.DayNight.DarkActionBar"/>
|
|
||||||
|
|
||||||
<service android:name=".media.MediaService"
|
<service android:name=".media.MediaService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
|
@ -29,11 +29,6 @@ object FeatureFlags {
|
||||||
*/
|
*/
|
||||||
val tabTray = Config.channel.isNightlyOrDebug
|
val tabTray = Config.channel.isNightlyOrDebug
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables gestures on the browser chrome that depend on a [SwipeGestureLayout]
|
|
||||||
*/
|
|
||||||
val browserChromeGestures = Config.channel.isNightlyOrDebug
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables viewing tab history
|
* Enables viewing tab history
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -168,11 +168,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
||||||
require(arguments != null)
|
require(arguments != null)
|
||||||
customTabSessionId = arguments?.getString(EXTRA_SESSION_ID)
|
customTabSessionId = arguments?.getString(EXTRA_SESSION_ID)
|
||||||
|
|
||||||
val view = if (FeatureFlags.browserChromeGestures) {
|
val view = inflater.inflate(R.layout.fragment_browser, container, false)
|
||||||
inflater.inflate(R.layout.browser_gesture_wrapper, container, false)
|
|
||||||
} else {
|
|
||||||
inflater.inflate(R.layout.fragment_browser, container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val activity = activity as HomeActivity
|
val activity = activity as HomeActivity
|
||||||
activity.themeManager.applyStatusBarTheme(activity)
|
activity.themeManager.applyStatusBarTheme(activity)
|
||||||
|
|
|
@ -15,7 +15,6 @@ import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.android.synthetic.main.browser_gesture_wrapper.*
|
|
||||||
import kotlinx.android.synthetic.main.fragment_browser.*
|
import kotlinx.android.synthetic.main.fragment_browser.*
|
||||||
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
import kotlinx.android.synthetic.main.fragment_browser.view.*
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
@ -32,7 +31,6 @@ import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.feature.tabs.WindowFeature
|
import mozilla.components.feature.tabs.WindowFeature
|
||||||
import mozilla.components.support.base.feature.UserInteractionHandler
|
import mozilla.components.support.base.feature.UserInteractionHandler
|
||||||
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
||||||
import org.mozilla.fenix.FeatureFlags
|
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
||||||
import org.mozilla.fenix.components.FenixSnackbar
|
import org.mozilla.fenix.components.FenixSnackbar
|
||||||
|
@ -77,19 +75,15 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
|
||||||
val components = context.components
|
val components = context.components
|
||||||
|
|
||||||
return super.initializeUI(view)?.also {
|
return super.initializeUI(view)?.also {
|
||||||
// We need to wrap this whole thing in an if here because gestureLayout will not exist
|
gestureLayout.addGestureListener(
|
||||||
// if the feature flag is off
|
ToolbarGestureHandler(
|
||||||
if (FeatureFlags.browserChromeGestures) {
|
activity = requireActivity(),
|
||||||
gestureLayout.addGestureListener(
|
contentLayout = browserLayout,
|
||||||
ToolbarGestureHandler(
|
tabPreview = tabPreview,
|
||||||
activity = requireActivity(),
|
toolbarLayout = browserToolbarView.view,
|
||||||
contentLayout = browserLayout,
|
sessionManager = components.core.sessionManager
|
||||||
tabPreview = tabPreview,
|
|
||||||
toolbarLayout = browserToolbarView.view,
|
|
||||||
sessionManager = components.core.sessionManager
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
val readerModeAction =
|
val readerModeAction =
|
||||||
BrowserToolbar.ToggleButton(
|
BrowserToolbar.ToggleButton(
|
||||||
|
|
|
@ -6,19 +6,19 @@ package org.mozilla.fenix.browser
|
||||||
|
|
||||||
import android.animation.Animator
|
import android.animation.Animator
|
||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
|
import android.animation.ValueAnimator
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.util.TypedValue
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewConfiguration
|
import android.view.ViewConfiguration
|
||||||
import androidx.annotation.Dimension
|
import androidx.annotation.Dimension
|
||||||
import androidx.annotation.Dimension.DP
|
import androidx.annotation.Dimension.DP
|
||||||
|
import androidx.core.animation.doOnEnd
|
||||||
import androidx.core.graphics.contains
|
import androidx.core.graphics.contains
|
||||||
import androidx.core.graphics.toPoint
|
import androidx.core.graphics.toPoint
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.dynamicanimation.animation.DynamicAnimation
|
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
|
||||||
import androidx.dynamicanimation.animation.FlingAnimation
|
|
||||||
import mozilla.components.browser.session.Session
|
import mozilla.components.browser.session.Session
|
||||||
import mozilla.components.browser.session.SessionManager
|
import mozilla.components.browser.session.SessionManager
|
||||||
import mozilla.components.support.ktx.android.util.dpToPx
|
import mozilla.components.support.ktx.android.util.dpToPx
|
||||||
|
@ -61,11 +61,6 @@ class ToolbarGestureHandler(
|
||||||
|
|
||||||
private val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
|
private val touchSlop = ViewConfiguration.get(activity).scaledTouchSlop
|
||||||
private val minimumFlingVelocity = ViewConfiguration.get(activity).scaledMinimumFlingVelocity
|
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
|
private var gestureDirection = GestureDirection.LEFT_TO_RIGHT
|
||||||
|
|
||||||
|
@ -143,25 +138,12 @@ class ToolbarGestureHandler(
|
||||||
) {
|
) {
|
||||||
val destination = getDestination()
|
val destination = getDestination()
|
||||||
if (destination is Destination.Tab && isGestureComplete(velocityX)) {
|
if (destination is Destination.Tab && isGestureComplete(velocityX)) {
|
||||||
animateToNextTab(velocityX, destination.session)
|
animateToNextTab(destination.session)
|
||||||
} else {
|
} else {
|
||||||
animateCanceledGesture(velocityX)
|
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 {
|
private fun getDestination(): Destination {
|
||||||
val isLtr = activity.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
|
val isLtr = activity.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
|
||||||
val currentSession = sessionManager.selectedSession ?: return Destination.None
|
val currentSession = sessionManager.selectedSession ?: return Destination.None
|
||||||
|
@ -234,73 +216,59 @@ class ToolbarGestureHandler(
|
||||||
abs(velocityX) >= minimumFlingVelocity)
|
abs(velocityX) >= minimumFlingVelocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getVelocityFromFling(velocityX: Float): Float {
|
private fun getAnimator(finalContextX: Float, duration: Long): ValueAnimator {
|
||||||
return max(abs(velocityX), defaultVelocity)
|
return ValueAnimator.ofFloat(contentLayout.translationX, finalContextX).apply {
|
||||||
|
this.duration = duration
|
||||||
|
this.interpolator = LinearOutSlowInInterpolator()
|
||||||
|
addUpdateListener { animator ->
|
||||||
|
val value = animator.animatedValue as Float
|
||||||
|
contentLayout.translationX = value
|
||||||
|
tabPreview.translationX = when (gestureDirection) {
|
||||||
|
GestureDirection.RIGHT_TO_LEFT -> value + windowWidth + previewOffset
|
||||||
|
GestureDirection.LEFT_TO_RIGHT -> value - windowWidth - previewOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateToNextTab(velocityX: Float, session: Session) {
|
private fun animateToNextTab(session: Session) {
|
||||||
val browserFinalXCoordinate: Float = when (gestureDirection) {
|
val browserFinalXCoordinate: Float = when (gestureDirection) {
|
||||||
GestureDirection.RIGHT_TO_LEFT -> -windowWidth.toFloat() - previewOffset
|
GestureDirection.RIGHT_TO_LEFT -> -windowWidth.toFloat() - previewOffset
|
||||||
GestureDirection.LEFT_TO_RIGHT -> 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
|
// Finish animating the contentLayout off screen and tabPreview on screen
|
||||||
createFlingAnimation(
|
getAnimator(browserFinalXCoordinate, FINISHED_GESTURE_ANIMATION_DURATION).apply {
|
||||||
view = contentLayout,
|
doOnEnd {
|
||||||
minValue = min(0f, browserFinalXCoordinate),
|
contentLayout.translationX = 0f
|
||||||
maxValue = max(0f, browserFinalXCoordinate),
|
sessionManager.select(session)
|
||||||
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
|
// Fade out the tab preview to prevent flickering
|
||||||
val shortAnimationDuration =
|
val shortAnimationDuration =
|
||||||
activity.resources.getInteger(android.R.integer.config_shortAnimTime)
|
activity.resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||||
tabPreview.animate()
|
tabPreview.animate()
|
||||||
.alpha(0f)
|
.alpha(0f)
|
||||||
.setDuration(shortAnimationDuration.toLong())
|
.setDuration(shortAnimationDuration.toLong())
|
||||||
.setListener(object : AnimatorListenerAdapter() {
|
.setListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator?) {
|
override fun onAnimationEnd(animation: Animator?) {
|
||||||
tabPreview.isVisible = false
|
tabPreview.isVisible = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}.start()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun animateCanceledGesture(gestureVelocity: Float) {
|
private fun animateCanceledGesture(velocityX: Float) {
|
||||||
val velocity = if (getDestination() is Destination.None) {
|
val duration = if (abs(velocityX) >= minimumFlingVelocity) {
|
||||||
defaultVelocity
|
CANCELED_FLING_ANIMATION_DURATION
|
||||||
} else {
|
} else {
|
||||||
getVelocityFromFling(gestureVelocity)
|
CANCELED_GESTURE_ANIMATION_DURATION
|
||||||
}.let { v ->
|
|
||||||
when (gestureDirection) {
|
|
||||||
GestureDirection.RIGHT_TO_LEFT -> v
|
|
||||||
GestureDirection.LEFT_TO_RIGHT -> -v
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createFlingAnimation(
|
getAnimator(0f, duration).apply {
|
||||||
view = contentLayout,
|
doOnEnd {
|
||||||
minValue = min(0f, contentLayout.translationX),
|
tabPreview.isVisible = false
|
||||||
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()
|
}.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,16 +304,25 @@ class ToolbarGestureHandler(
|
||||||
*/
|
*/
|
||||||
private const val OVERSCROLL_HIDE_PERCENT = 0.20
|
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.
|
* The size of the gap between the tab preview and content layout.
|
||||||
*/
|
*/
|
||||||
@Dimension(unit = DP)
|
@Dimension(unit = DP)
|
||||||
private const val PREVIEW_OFFSET = 48
|
private const val PREVIEW_OFFSET = 48
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation duration when switching to another tab
|
||||||
|
*/
|
||||||
|
private const val FINISHED_GESTURE_ANIMATION_DURATION = 250L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation duration gesture is canceled due to the swipe not being far enough
|
||||||
|
*/
|
||||||
|
private const val CANCELED_GESTURE_ANIMATION_DURATION = 200L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animation duration gesture is canceled due to a swipe in the opposite direction
|
||||||
|
*/
|
||||||
|
private const val CANCELED_FLING_ANIMATION_DURATION = 150L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package org.mozilla.fenix.components.metrics
|
package org.mozilla.fenix.components.metrics
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.leanplum.Leanplum
|
||||||
import mozilla.components.browser.awesomebar.facts.BrowserAwesomeBarFacts
|
import mozilla.components.browser.awesomebar.facts.BrowserAwesomeBarFacts
|
||||||
import mozilla.components.browser.menu.facts.BrowserMenuFacts
|
import mozilla.components.browser.menu.facts.BrowserMenuFacts
|
||||||
import mozilla.components.browser.toolbar.facts.ToolbarFacts
|
import mozilla.components.browser.toolbar.facts.ToolbarFacts
|
||||||
|
@ -193,6 +194,7 @@ internal class ReleaseMetricController(
|
||||||
if (installedAddons is List<*>) {
|
if (installedAddons is List<*>) {
|
||||||
Addons.installedAddons.set(installedAddons.map { it.toString() })
|
Addons.installedAddons.set(installedAddons.map { it.toString() })
|
||||||
Addons.hasInstalledAddons.set(installedAddons.size > 0)
|
Addons.hasInstalledAddons.set(installedAddons.size > 0)
|
||||||
|
Leanplum.setUserAttributes(mapOf("installed_addons" to installedAddons.size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,6 +202,7 @@ internal class ReleaseMetricController(
|
||||||
if (enabledAddons is List<*>) {
|
if (enabledAddons is List<*>) {
|
||||||
Addons.enabledAddons.set(enabledAddons.map { it.toString() })
|
Addons.enabledAddons.set(enabledAddons.map { it.toString() })
|
||||||
Addons.hasEnabledAddons.set(enabledAddons.size > 0)
|
Addons.hasEnabledAddons.set(enabledAddons.size > 0)
|
||||||
|
Leanplum.setUserAttributes(mapOf("enabled_addons" to enabledAddons.size))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import android.view.ViewGroup
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
import androidx.core.content.pm.PackageInfoCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
|
|
||||||
import kotlinx.android.synthetic.main.fragment_about.*
|
import kotlinx.android.synthetic.main.fragment_about.*
|
||||||
import org.mozilla.fenix.BrowserDirection
|
import org.mozilla.fenix.BrowserDirection
|
||||||
import org.mozilla.fenix.BuildConfig
|
import org.mozilla.fenix.BuildConfig
|
||||||
|
@ -168,13 +167,8 @@ class AboutFragment : Fragment(), AboutPageListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openLibrariesPage() {
|
private fun openLibrariesPage() {
|
||||||
startActivity(Intent(context, OssLicensesMenuActivity::class.java))
|
val intent = Intent(requireContext(), AboutLibrariesActivity::class.java)
|
||||||
OssLicensesMenuActivity.setActivityTitle(
|
startActivity(intent)
|
||||||
getString(
|
|
||||||
R.string.open_source_licenses_title,
|
|
||||||
appName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAboutItemClicked(item: AboutItem) {
|
override fun onAboutItemClicked(item: AboutItem) {
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* 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.graphics.Typeface
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.util.Linkify
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.ListView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the licenses of all the libraries used by Fenix.
|
||||||
|
*
|
||||||
|
* This is a re-implementation of play-services-oss-licenses library.
|
||||||
|
* We can't use the official implementation in the OSS flavor of Fenix
|
||||||
|
* because it is proprietary and closed-source.
|
||||||
|
*
|
||||||
|
* There are popular FLOSS alternatives to Google's plugin and library
|
||||||
|
* such as AboutLibraries (https://github.com/mikepenz/AboutLibraries)
|
||||||
|
* but we considered the risk of introducing such third-party dependency
|
||||||
|
* to Fenix too high. Therefore, we use Google's gradle plugin to
|
||||||
|
* extract the dependencies and their licenses, and this activity
|
||||||
|
* to show the extracted licenses to the end-user.
|
||||||
|
*/
|
||||||
|
class AboutLibrariesActivity : AppCompatActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
val appName = getString(R.string.app_name)
|
||||||
|
title = getString(R.string.open_source_licenses_title, appName)
|
||||||
|
setContentView(R.layout.about_libraries_activity)
|
||||||
|
|
||||||
|
setSupportActionBar(findViewById(R.id.toolbar))
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
|
setupLibrariesListView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSupportNavigateUp(): Boolean {
|
||||||
|
onBackPressed()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupLibrariesListView() {
|
||||||
|
val libraries = parseLibraries()
|
||||||
|
val listView = findViewById<ListView>(R.id.about_libraries_listview)
|
||||||
|
listView.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, libraries)
|
||||||
|
listView.setOnItemClickListener { _, _, position, _ ->
|
||||||
|
showLicenseDialog(libraries[position])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseLibraries(): List<LibraryItem> {
|
||||||
|
/*
|
||||||
|
The gradle plugin "oss-licenses-plugin" creates two "raw" resources:
|
||||||
|
|
||||||
|
- third_party_licenses which is the binary concatenation of all the licenses text for
|
||||||
|
all the libraries. License texts can either be an URL to a license file or just the
|
||||||
|
raw text of the license.
|
||||||
|
|
||||||
|
- third_party_licenses_metadata which contains one dependency per line formatted in
|
||||||
|
the following way: "[start_offset]:[length] [name]"
|
||||||
|
|
||||||
|
[start_offset] : first byte in third_party_licenses that contains the license
|
||||||
|
text for this library.
|
||||||
|
[length] : length of the license text for this library in
|
||||||
|
third_party_licenses.
|
||||||
|
[name] : either the name of the library, or its artifact name.
|
||||||
|
|
||||||
|
See https://github.com/google/play-services-plugins/tree/master/oss-licenses-plugin
|
||||||
|
*/
|
||||||
|
val licensesData = resources
|
||||||
|
.openRawResource(R.raw.third_party_licenses)
|
||||||
|
.readBytes()
|
||||||
|
val licensesMetadataReader = resources
|
||||||
|
.openRawResource(R.raw.third_party_license_metadata)
|
||||||
|
.bufferedReader()
|
||||||
|
|
||||||
|
return licensesMetadataReader.use { reader -> reader.readLines() }.map { line ->
|
||||||
|
val (section, name) = line.split(" ", limit = 2)
|
||||||
|
val (startOffset, length) = section.split(":", limit = 2).map(String::toInt)
|
||||||
|
val licenseData = licensesData.sliceArray(startOffset until startOffset + length)
|
||||||
|
val licenseText = licenseData.toString(Charset.forName("UTF-8"))
|
||||||
|
LibraryItem(name, licenseText)
|
||||||
|
}.sortedBy { item -> item.name.toLowerCase(Locale.ROOT) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLicenseDialog(libraryItem: LibraryItem) {
|
||||||
|
val dialog = AlertDialog.Builder(this)
|
||||||
|
.setTitle(libraryItem.name)
|
||||||
|
.setMessage(libraryItem.license)
|
||||||
|
.create()
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
val textView = dialog.findViewById<TextView>(android.R.id.message)!!
|
||||||
|
Linkify.addLinks(textView, Linkify.ALL)
|
||||||
|
textView.linksClickable = true
|
||||||
|
textView.textSize = LICENSE_TEXT_SIZE
|
||||||
|
textView.typeface = Typeface.MONOSPACE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val LICENSE_TEXT_SIZE = 10F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LibraryItem(val name: String, val license: String) {
|
||||||
|
override fun toString(): String {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?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/. -->
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/about_libraries"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
tools:context="org.mozilla.fenix.settings.about.AboutLibrariesActivity">
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />
|
||||||
|
<ListView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/about_libraries_listview" />
|
||||||
|
</RelativeLayout>
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
||||||
<org.mozilla.fenix.browser.SwipeGestureLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/gestureLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<include layout="@layout/fragment_browser" />
|
|
||||||
|
|
||||||
<org.mozilla.fenix.browser.TabPreview
|
|
||||||
android:id="@+id/tabPreview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clickable="false"
|
|
||||||
android:focusable="false"
|
|
||||||
android:visibility="gone" />
|
|
||||||
</org.mozilla.fenix.browser.SwipeGestureLayout>
|
|
|
@ -1,53 +1,66 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
<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:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/browserLayout"
|
android:id="@+id/gestureLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
tools:context="browser.BrowserFragment">
|
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/swipeRefresh"
|
android:id="@+id/browserLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:alpha="0"
|
tools:context="browser.BrowserFragment">
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
|
||||||
|
|
||||||
<mozilla.components.concept.engine.EngineView
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/engineView"
|
android:id="@+id/swipeRefresh"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="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" />
|
android:visibility="gone" />
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
|
||||||
|
|
||||||
|
<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" />
|
||||||
|
|
||||||
<ViewStub
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
android:id="@+id/stubFindInPage"
|
|
||||||
|
<org.mozilla.fenix.browser.TabPreview
|
||||||
|
android:id="@+id/tabPreview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="bottom"
|
android:clickable="false"
|
||||||
android:inflatedId="@+id/findInPageView"
|
android:focusable="false"
|
||||||
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" />
|
android:visibility="gone" />
|
||||||
|
</org.mozilla.fenix.browser.SwipeGestureLayout>
|
||||||
<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>
|
|
||||||
|
|
|
@ -306,6 +306,8 @@ Tiktiwin tigejdanin yuzzlen ur nṣeḥḥi ara
|
||||||
<!-- Preference for open links in third party apps -->
|
<!-- Preference for open links in third party apps -->
|
||||||
<string name="preferences_open_links_in_apps">Ldi iseɣwan deg isnasen</string>
|
<string name="preferences_open_links_in_apps">Ldi iseɣwan deg isnasen</string>
|
||||||
|
|
||||||
|
<!-- Preference for open download with an external download manager app -->
|
||||||
|
<string name="preferences_external_download_manager">Amsefrak n usider azɣaray</string>
|
||||||
<!-- Preference for add_ons -->
|
<!-- Preference for add_ons -->
|
||||||
<string name="preferences_addons">Izegrar</string>
|
<string name="preferences_addons">Izegrar</string>
|
||||||
|
|
||||||
|
|
|
@ -30,16 +30,16 @@
|
||||||
<!-- Tab tray multi select title in app bar. The first parameter is the number of tabs selected -->
|
<!-- Tab tray multi select title in app bar. The first parameter is the number of tabs selected -->
|
||||||
<string name="tab_tray_multi_select_title">%1$d개 선택됨</string>
|
<string name="tab_tray_multi_select_title">%1$d개 선택됨</string>
|
||||||
<!-- Label of button in create collection dialog for creating a new collection -->
|
<!-- Label of button in create collection dialog for creating a new collection -->
|
||||||
<string name="tab_tray_add_new_collection">새 컬렉션 추가</string>
|
<string name="tab_tray_add_new_collection">새 모음집 추가</string>
|
||||||
<!-- Label of editable text in create collection dialog for naming a new collection -->
|
<!-- Label of editable text in create collection dialog for naming a new collection -->
|
||||||
<string name="tab_tray_add_new_collection_name">이름</string>
|
<string name="tab_tray_add_new_collection_name">이름</string>
|
||||||
<!-- Label of button in save to collection dialog for selecting a current collection -->
|
<!-- Label of button in save to collection dialog for selecting a current collection -->
|
||||||
<string name="tab_tray_select_collection">컬렉션 선택</string>
|
<string name="tab_tray_select_collection">모음집 선택</string>
|
||||||
|
|
||||||
<!-- Content description for close button while in multiselect mode in tab tray -->
|
<!-- Content description for close button while in multiselect mode in tab tray -->
|
||||||
<string name="tab_tray_close_multiselect_content_description">다중 선택 모드 종료</string>
|
<string name="tab_tray_close_multiselect_content_description">다중 선택 모드 종료</string>
|
||||||
<!-- Content description for save to collection button while in multiselect mode in tab tray -->
|
<!-- Content description for save to collection button while in multiselect mode in tab tray -->
|
||||||
<string name="tab_tray_collection_button_multiselect_content_description">선택한 탭을 컬렉션에 저장</string>
|
<string name="tab_tray_collection_button_multiselect_content_description">선택한 탭을 모음집에 저장</string>
|
||||||
|
|
||||||
<!-- Content description for checkmark while tab is selected while in multiselect mode in tab tray. The first parameter is the title of the tab selected -->
|
<!-- Content description for checkmark while tab is selected while in multiselect mode in tab tray. The first parameter is the title of the tab selected -->
|
||||||
<string name="tab_tray_item_selected_multiselect_content_description">%1$s 선택됨</string>
|
<string name="tab_tray_item_selected_multiselect_content_description">%1$s 선택됨</string>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
<!-- Content description announcement when exiting multiselect mode in tab tray -->
|
<!-- Content description announcement when exiting multiselect mode in tab tray -->
|
||||||
<string name="tab_tray_exit_multiselect_content_description">다중 선택 모드 종료됨</string>
|
<string name="tab_tray_exit_multiselect_content_description">다중 선택 모드 종료됨</string>
|
||||||
<!-- Content description announcement when entering multiselect mode in tab tray -->
|
<!-- Content description announcement when entering multiselect mode in tab tray -->
|
||||||
<string name="tab_tray_enter_multiselect_content_description">다중 선택 모드로 전환됨, 컬렉션에 저장할 탭을 선택하세요</string>
|
<string name="tab_tray_enter_multiselect_content_description">다중 선택 모드로 전환됨, 모음집에 저장할 탭을 선택하세요</string>
|
||||||
<!-- Content description on checkmark while tab is selected in multiselect mode in tab tray -->
|
<!-- Content description on checkmark while tab is selected in multiselect mode in tab tray -->
|
||||||
<string name="tab_tray_multiselect_selected_content_description">선택됨</string>
|
<string name="tab_tray_multiselect_selected_content_description">선택됨</string>
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@
|
||||||
<!-- Browser menu button that creates a new tab -->
|
<!-- Browser menu button that creates a new tab -->
|
||||||
<string name="browser_menu_new_tab">새 탭</string>
|
<string name="browser_menu_new_tab">새 탭</string>
|
||||||
<!-- Browser menu button that saves the current tab to a collection -->
|
<!-- Browser menu button that saves the current tab to a collection -->
|
||||||
<string name="browser_menu_save_to_collection_2">컬렉션에 저장</string>
|
<string name="browser_menu_save_to_collection_2">모음집에 저장</string>
|
||||||
<!-- Browser menu button that open a share menu to share the current site -->
|
<!-- Browser menu button that open a share menu to share the current site -->
|
||||||
<string name="browser_menu_share">공유</string>
|
<string name="browser_menu_share">공유</string>
|
||||||
<!-- Share menu title, displayed when a user is sharing their current site -->
|
<!-- Share menu title, displayed when a user is sharing their current site -->
|
||||||
|
@ -497,7 +497,7 @@
|
||||||
<!-- Text shown as the title of the open tab tray -->
|
<!-- Text shown as the title of the open tab tray -->
|
||||||
<string name="tab_tray_title">탭 열기</string>
|
<string name="tab_tray_title">탭 열기</string>
|
||||||
<!-- Text shown in the menu for saving tabs to a collection -->
|
<!-- Text shown in the menu for saving tabs to a collection -->
|
||||||
<string name="tab_tray_menu_item_save">컬렉션에 저장</string>
|
<string name="tab_tray_menu_item_save">모음집에 저장</string>
|
||||||
<!-- Text shown in the menu for sharing all tabs -->
|
<!-- Text shown in the menu for sharing all tabs -->
|
||||||
<string name="tab_tray_menu_item_share">모든 탭 공유</string>
|
<string name="tab_tray_menu_item_share">모든 탭 공유</string>
|
||||||
<!-- Text shown in the menu for closing all tabs -->
|
<!-- Text shown in the menu for closing all tabs -->
|
||||||
|
@ -509,7 +509,7 @@
|
||||||
<!-- Shortcut action to toggle private mode -->
|
<!-- Shortcut action to toggle private mode -->
|
||||||
<string name="tab_tray_menu_toggle">탭 모드 전환</string>
|
<string name="tab_tray_menu_toggle">탭 모드 전환</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed -->
|
<!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed -->
|
||||||
<string name="remove_tab_from_collection">컬렉션에서 탭 삭제</string>
|
<string name="remove_tab_from_collection">모음집에서 탭 삭제</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
|
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
|
||||||
<string name="close_tab">탭 닫기</string>
|
<string name="close_tab">탭 닫기</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Close tab <title> button. First parameter is tab title -->
|
<!-- Content description (not visible, for screen readers etc.): Close tab <title> button. First parameter is tab title -->
|
||||||
|
@ -521,7 +521,7 @@
|
||||||
<!-- Open tabs menu item to share all tabs -->
|
<!-- Open tabs menu item to share all tabs -->
|
||||||
<string name="tabs_menu_share_tabs">탭 공유</string>
|
<string name="tabs_menu_share_tabs">탭 공유</string>
|
||||||
<!-- Open tabs menu item to save tabs to collection -->
|
<!-- Open tabs menu item to save tabs to collection -->
|
||||||
<string name="tabs_menu_save_to_collection1">탭을 컬렉션에 저장</string>
|
<string name="tabs_menu_save_to_collection1">탭을 모음집에 저장</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
|
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
|
||||||
<string name="tab_menu">탭 메뉴</string>
|
<string name="tab_menu">탭 메뉴</string>
|
||||||
<!-- Tab menu item to share the tab -->
|
<!-- Tab menu item to share the tab -->
|
||||||
|
@ -536,11 +536,11 @@
|
||||||
<string name="current_session_image">현재 세션 이미지</string>
|
<string name="current_session_image">현재 세션 이미지</string>
|
||||||
|
|
||||||
<!-- Button to save the current set of tabs into a collection -->
|
<!-- Button to save the current set of tabs into a collection -->
|
||||||
<string name="save_to_collection">컬렉션에 저장</string>
|
<string name="save_to_collection">모음집에 저장</string>
|
||||||
<!-- Text for the menu button to delete a collection -->
|
<!-- Text for the menu button to delete a collection -->
|
||||||
<string name="collection_delete">컬렉션 삭제</string>
|
<string name="collection_delete">모음집 삭제</string>
|
||||||
<!-- Text for the menu button to rename a collection -->
|
<!-- Text for the menu button to rename a collection -->
|
||||||
<string name="collection_rename">컬렉션 이름 변경</string>
|
<string name="collection_rename">모음집 이름 변경</string>
|
||||||
<!-- Text for the button to open tabs of the selected collection -->
|
<!-- Text for the button to open tabs of the selected collection -->
|
||||||
<string name="collection_open_tabs">열린 탭</string>
|
<string name="collection_open_tabs">열린 탭</string>
|
||||||
|
|
||||||
|
@ -751,25 +751,25 @@
|
||||||
|
|
||||||
<!-- Collections -->
|
<!-- Collections -->
|
||||||
<!-- Collections header on home fragment -->
|
<!-- Collections header on home fragment -->
|
||||||
<string name="collections_header">컬렉션</string>
|
<string name="collections_header">모음집</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
|
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
|
||||||
<string name="collection_menu_button_content_description">컬렉션 메뉴</string>
|
<string name="collection_menu_button_content_description">모음집 메뉴</string>
|
||||||
|
|
||||||
<!-- No Open Tabs Message Header -->
|
<!-- No Open Tabs Message Header -->
|
||||||
<string name="no_collections_header1">중요한 것들을 모으세요</string>
|
<string name="no_collections_header1">중요한 것들 수집하기</string>
|
||||||
<!-- Label to describe what collections are to a new user without any collections -->
|
<!-- Label to describe what collections are to a new user without any collections -->
|
||||||
<string name="no_collections_description1">나중에 빠르게 액세스 할 수 있도록 유사한 검색, 사이트 및 탭을 그룹화하세요.</string>
|
<string name="no_collections_description1">나중에 빠르게 접근할 수 있도록 유사한 검색, 사이트 및 탭을 모아 보세요.</string>
|
||||||
<!-- Title for the "select tabs" step of the collection creator -->
|
<!-- Title for the "select tabs" step of the collection creator -->
|
||||||
<string name="create_collection_select_tabs">탭 선택</string>
|
<string name="create_collection_select_tabs">탭 선택</string>
|
||||||
|
|
||||||
<!-- Title for the "select collection" step of the collection creator -->
|
<!-- Title for the "select collection" step of the collection creator -->
|
||||||
<string name="create_collection_select_collection">컬렉션 선택</string>
|
<string name="create_collection_select_collection">모음집 선택</string>
|
||||||
|
|
||||||
<!-- Title for the "name collection" step of the collection creator -->
|
<!-- Title for the "name collection" step of the collection creator -->
|
||||||
<string name="create_collection_name_collection">컬렉션 이름</string>
|
<string name="create_collection_name_collection">모음집 이름</string>
|
||||||
|
|
||||||
<!-- Button to add new collection for the "select collection" step of the collection creator -->
|
<!-- Button to add new collection for the "select collection" step of the collection creator -->
|
||||||
<string name="create_collection_add_new_collection">새 컬렉션 추가</string>
|
<string name="create_collection_add_new_collection">새 모음집 추가</string>
|
||||||
|
|
||||||
<!-- Button to select all tabs in the "select tabs" step of the collection creator -->
|
<!-- Button to select all tabs in the "select tabs" step of the collection creator -->
|
||||||
<string name="create_collection_select_all">모두 선택</string>
|
<string name="create_collection_select_all">모두 선택</string>
|
||||||
|
@ -790,7 +790,7 @@
|
||||||
<string name="create_collection_tabs_saved">탭이 저장되었습니다!</string>
|
<string name="create_collection_tabs_saved">탭이 저장되었습니다!</string>
|
||||||
|
|
||||||
<!-- Text shown in snackbar when one or multiple tabs have been saved in a new collection -->
|
<!-- 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>
|
<string name="create_collection_tabs_saved_new_collection">모음집 저장됨!</string>
|
||||||
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
<!-- Text shown in snackbar when one tab has been saved in a collection -->
|
||||||
<string name="create_collection_tab_saved">탭이 저장되었습니다!</string>
|
<string name="create_collection_tab_saved">탭이 저장되었습니다!</string>
|
||||||
|
|
||||||
|
@ -804,7 +804,7 @@
|
||||||
<string name="create_collection_view">보기</string>
|
<string name="create_collection_view">보기</string>
|
||||||
|
|
||||||
<!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections-->
|
<!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections-->
|
||||||
<string name="create_collection_default_name">컬렉션 %d개</string>
|
<string name="create_collection_default_name">모음집 %d개</string>
|
||||||
|
|
||||||
<!-- Share -->
|
<!-- Share -->
|
||||||
<!-- Share screen header -->
|
<!-- Share screen header -->
|
||||||
|
@ -864,10 +864,10 @@
|
||||||
<!-- Name of the "Powered by Fenix" notification channel. Displayed in the "App notifications" system settings for the app -->
|
<!-- Name of the "Powered by Fenix" notification channel. Displayed in the "App notifications" system settings for the app -->
|
||||||
<string name="notification_powered_by_channel_name">제공:</string>
|
<string name="notification_powered_by_channel_name">제공:</string>
|
||||||
<!-- Text shown in snackbar when user deletes a collection -->
|
<!-- Text shown in snackbar when user deletes a collection -->
|
||||||
<string name="snackbar_collection_deleted">컬렉션 삭제됨</string>
|
<string name="snackbar_collection_deleted">모음집 삭제됨</string>
|
||||||
|
|
||||||
<!-- Text shown in snackbar when user renames a collection -->
|
<!-- Text shown in snackbar when user renames a collection -->
|
||||||
<string name="snackbar_collection_renamed">컬렉션 이름 변경됨</string>
|
<string name="snackbar_collection_renamed">모음집 이름 변경됨</string>
|
||||||
<!-- Text shown in snackbar when user deletes a tab -->
|
<!-- Text shown in snackbar when user deletes a tab -->
|
||||||
<string name="snackbar_tab_deleted">탭 삭제됨</string>
|
<string name="snackbar_tab_deleted">탭 삭제됨</string>
|
||||||
|
|
||||||
|
@ -904,9 +904,9 @@
|
||||||
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
<!-- Tab collection deletion prompt dialog message. Placeholder will be replaced with the collection name -->
|
||||||
<string name="tab_collection_dialog_message">%1$s 파일을 삭제하시겠습니까?</string>
|
<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 -->
|
<!-- 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>
|
<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 -->
|
<!-- 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>
|
<string name="delete_tab_and_collection_dialog_title">%1$s 모음집을 삭제하시겠습니까?</string>
|
||||||
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
<!-- Tab collection deletion prompt dialog option to delete the collection -->
|
||||||
<string name="tab_collection_dialog_positive">삭제</string>
|
<string name="tab_collection_dialog_positive">삭제</string>
|
||||||
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
<!-- Tab collection deletion prompt dialog option to cancel deleting the collection -->
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
<string name="tab_tray_collection_button_multiselect_content_description">Geselecteerde tabbladen in collectie opslaan</string>
|
<string name="tab_tray_collection_button_multiselect_content_description">Geselecteerde tabbladen in collectie opslaan</string>
|
||||||
<!-- Content description for checkmark while tab is selected while in multiselect mode in tab tray. The first parameter is the title of the tab selected -->
|
<!-- Content description for checkmark while tab is selected while in multiselect mode in tab tray. The first parameter is the title of the tab selected -->
|
||||||
<string name="tab_tray_item_selected_multiselect_content_description">%1$s geselecteerd</string>
|
<string name="tab_tray_item_selected_multiselect_content_description">%1$s geselecteerd</string>
|
||||||
|
<!-- Content description when tab is unselected while in multiselect mode in tab tray. The first parameter is the title of the tab unselected -->
|
||||||
|
<string name="tab_tray_item_unselected_multiselect_content_description">Selectie %1$s ongedaan gemaakt</string>
|
||||||
<!-- Content description on checkmark while tab is selected in multiselect mode in tab tray -->
|
<!-- Content description on checkmark while tab is selected in multiselect mode in tab tray -->
|
||||||
<string name="tab_tray_multiselect_selected_content_description">Geselecteerd</string>
|
<string name="tab_tray_multiselect_selected_content_description">Geselecteerd</string>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* 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.widget.ListView
|
||||||
|
import android.widget.TextView
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||||
|
import org.robolectric.Robolectric
|
||||||
|
import org.robolectric.Shadows.shadowOf
|
||||||
|
import org.robolectric.shadows.ShadowAlertDialog
|
||||||
|
|
||||||
|
@RunWith(FenixRobolectricTestRunner::class)
|
||||||
|
class AboutLibrariesActivityTest {
|
||||||
|
@Test
|
||||||
|
fun `activity should display licenses`() {
|
||||||
|
val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get()
|
||||||
|
val listView = activity.findViewById<ListView>(R.id.about_libraries_listview)
|
||||||
|
|
||||||
|
assertTrue(0 < listView.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `item click should open license dialog`() {
|
||||||
|
val activity = Robolectric.buildActivity(AboutLibrariesActivity::class.java).create().get()
|
||||||
|
|
||||||
|
val listView = activity.findViewById<ListView>(R.id.about_libraries_listview)
|
||||||
|
val listViewShadow = shadowOf(listView)
|
||||||
|
listViewShadow.clickFirstItemContainingText("org.mozilla.geckoview:geckoview")
|
||||||
|
|
||||||
|
val alertDialogShadow = ShadowAlertDialog.getLatestDialog()
|
||||||
|
assertTrue(alertDialogShadow.isShowing)
|
||||||
|
|
||||||
|
val alertDialogText = alertDialogShadow
|
||||||
|
.findViewById<TextView>(android.R.id.message)
|
||||||
|
.text
|
||||||
|
.toString()
|
||||||
|
assertTrue(alertDialogText.contains("MPL"))
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,5 +3,5 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
object AndroidComponents {
|
object AndroidComponents {
|
||||||
const val VERSION = "54.0.20200814130102"
|
const val VERSION = "54.0.20200818130156"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ object Versions {
|
||||||
const val leakcanary = "2.4"
|
const val leakcanary = "2.4"
|
||||||
const val leanplum = "5.4.0"
|
const val leanplum = "5.4.0"
|
||||||
const val osslicenses_plugin = "0.9.5"
|
const val osslicenses_plugin = "0.9.5"
|
||||||
const val osslicenses_library = "17.0.0"
|
|
||||||
const val detekt = "1.9.1"
|
const val detekt = "1.9.1"
|
||||||
|
|
||||||
const val androidx_appcompat = "1.2.0-rc01"
|
const val androidx_appcompat = "1.2.0-rc01"
|
||||||
|
@ -28,7 +27,6 @@ object Versions {
|
||||||
const val androidx_paging = "2.1.0"
|
const val androidx_paging = "2.1.0"
|
||||||
const val androidx_transition = "1.3.0"
|
const val androidx_transition = "1.3.0"
|
||||||
const val androidx_work = "2.2.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_material = "1.1.0"
|
||||||
const val google_flexbox = "2.0.1"
|
const val google_flexbox = "2.0.1"
|
||||||
|
|
||||||
|
@ -59,7 +57,6 @@ object Deps {
|
||||||
|
|
||||||
const val allopen = "org.jetbrains.kotlin:kotlin-allopen:${Versions.kotlin}"
|
const val allopen = "org.jetbrains.kotlin:kotlin-allopen:${Versions.kotlin}"
|
||||||
const val osslicenses_plugin = "com.google.android.gms:oss-licenses-plugin:${Versions.osslicenses_plugin}"
|
const val osslicenses_plugin = "com.google.android.gms:oss-licenses-plugin:${Versions.osslicenses_plugin}"
|
||||||
const val osslicenses_library = "com.google.android.gms:play-services-oss-licenses:${Versions.osslicenses_library}"
|
|
||||||
|
|
||||||
const val mozilla_concept_engine = "org.mozilla.components:concept-engine:${Versions.mozilla_android_components}"
|
const val mozilla_concept_engine = "org.mozilla.components:concept-engine:${Versions.mozilla_android_components}"
|
||||||
const val mozilla_concept_menu = "org.mozilla.components:concept-menu:${Versions.mozilla_android_components}"
|
const val mozilla_concept_menu = "org.mozilla.components:concept-menu:${Versions.mozilla_android_components}"
|
||||||
|
@ -174,7 +171,6 @@ object Deps {
|
||||||
const val androidx_recyclerview = "androidx.recyclerview:recyclerview:${Versions.androidx_recyclerview}"
|
const val androidx_recyclerview = "androidx.recyclerview:recyclerview:${Versions.androidx_recyclerview}"
|
||||||
const val androidx_core = "androidx.core:core:${Versions.androidx_core}"
|
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_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_transition = "androidx.transition:transition:${Versions.androidx_transition}"
|
||||||
const val androidx_work_ktx = "androidx.work:work-runtime-ktx:${Versions.androidx_work}"
|
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}"
|
const val androidx_work_testing = "androidx.work:work-testing:${Versions.androidx_work}"
|
||||||
|
|
10
docs/mma.md
10
docs/mma.md
|
@ -159,6 +159,16 @@ User Attributes
|
||||||
<td>A boolean indicating that this is a Fenix installation</td>
|
<td>A boolean indicating that this is a Fenix installation</td>
|
||||||
<td><a href="https://github.com/mozilla-mobile/fenix/pull/8208">#8208</a></td>
|
<td><a href="https://github.com/mozilla-mobile/fenix/pull/8208">#8208</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>`installed_addons`</td>
|
||||||
|
<td>A boolean indicating that there are addons installed</td>
|
||||||
|
<td><a href="https://github.com/mozilla-mobile/fenix/pull/13233">#13233</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>`enabled_addons`</td>
|
||||||
|
<td>A boolean indicating that there are addons enabled</td>
|
||||||
|
<td><a href="https://github.com/mozilla-mobile/fenix/pull/13233">#13233</a></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
Events
|
Events
|
||||||
|
|
Loading…
Reference in New Issue