diff --git a/README.md b/README.md index 6e879b08a..ad6224826 100644 --- a/README.md +++ b/README.md @@ -123,10 +123,7 @@ If you want to run **performance tests/benchmarks** in automation or locally: For additional context on these recommendations, see [the perf build variant analysis](https://docs.google.com/document/d/1aW-m0HYncTDDiRz_2x6EjcYkjBpL9SHhhYix13Vil30/edit#). -Before you can install any release variants including `forPerformanceTest`, **you will need to sign them.** To do this automatically in local development, you can add the following to `/local.properties`: -```sh -autosignReleaseWithDebugKey -``` +Before you can install any release variants including `forPerformanceTest`, **you will need to sign them:** see [Automatically signing release builds](#automatically-sign-release-builds) for details. ## Pre-push hooks To reduce review turn-around time, we'd like all pushes to run tests locally. We'd @@ -164,8 +161,19 @@ Steps to downgrade Java Version on Mac with Brew: 7. Verify java-version by running ```java -version``` ## local.properties helpers -There are multiple helper flags available via `local.properties` that will help speed up local development workflow -when working across multiple layers of the dependency stack - specifically, with android-components, geckoview or application-services. +You can speed up local development by setting a few helper flags available in `local.properties`. Some flags will make it easy to +work across multiple layers of the dependency stack - specifically, with android-components, geckoview or application-services. + +### Automatically sign release builds +To sign your release builds with your debug key automatically, add the following to `/local.properties`: + +```sh +autosignReleaseWithDebugKey +``` + +With this line, release build variants will automatically be signed with your debug key (like debug builds), allowing them to be built and installed directly through Android Studio or the command line. + +This is helpful when you're building release variants frequently, for example to test feature flags and or do performance analyses. ### Auto-publication workflow for android-components and application-services If you're making changes to these projects and want to test them in Fenix, auto-publication workflow is the fastest, most reliable diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index e0ef1c075..2112a3f3b 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -45,6 +45,8 @@ import mozilla.components.feature.search.SearchAdapter import mozilla.components.service.fxa.sync.SyncReason import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.ktx.android.arch.lifecycle.addObservers +import mozilla.components.support.ktx.android.content.call +import mozilla.components.support.ktx.android.content.email import mozilla.components.support.ktx.android.content.share import mozilla.components.support.ktx.kotlin.isUrl import mozilla.components.support.ktx.kotlin.toNormalizedUrl @@ -294,10 +296,12 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { EngineView::class.java.name -> components.core.engine.createView(context, attrs).apply { selectionActionDelegate = DefaultSelectionActionDelegate( getSearchAdapter(components.core.store), - resources = context.resources - ) { - share(it) - } + resources = context.resources, + shareTextClicked = { share(it) }, + emailTextClicked = { email(it) }, + callTextClicked = { call(it) }, + actionSorter = ::actionSorter + ) }.asView() TabsTray::class.java.name -> { val layout = LinearLayoutManager(context).apply { @@ -313,6 +317,22 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { else -> super.onCreateView(parent, name, context, attrs) } + @Suppress("MagicNumber") + // Defining the positions as constants doesn't seem super useful here. + private fun actionSorter(actions: Array): Array { + val order = hashMapOf() + + order["org.mozilla.geckoview.COPY"] = 0 + order["CUSTOM_CONTEXT_MENU_SEARCH"] = 1 + order["org.mozilla.geckoview.SELECT_ALL"] = 2 + order["CUSTOM_CONTEXT_MENU_SHARE"] = 3 + + return actions.sortedBy { actionName -> + // Sort the actions in our preferred order, putting "other" actions unsorted at the end + order[actionName] ?: actions.size + }.toTypedArray() + } + final override fun onBackPressed() { supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach { if (it is UserInteractionHandler && it.onBackPressed()) { @@ -502,6 +522,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { engine: SearchEngine?, forceSearch: Boolean ) { + val startTime = components.core.engine.profiler?.getProfilerTime() val mode = browsingModeManager.mode val loadUrlUseCase = if (newTab) { @@ -529,6 +550,12 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity { } else { searchUseCase.invoke(searchTermOrURL) } + + if (components.core.engine.profiler?.isProfilerActive() == true) { + // Wrapping the `addMarker` method with `isProfilerActive` even though it's no-op when + // profiler is not active. That way, `text` argument will not create a string builder all the time. + components.core.engine.profiler?.addMarker("HomeActivity.load", startTime, "newTab: $newTab") + } } override fun attachBaseContext(base: Context) { diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index c0ae508b4..ed9b83c58 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -275,9 +275,4 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { ) + ContextMenuCandidate.createOpenInExternalAppCandidate(requireContext(), contextMenuCandidateAppLinksUseCases) } - - companion object { - private const val SHARED_TRANSITION_MS = 200L - private const val TAB_ITEM_TRANSITION_NAME = "tab_item" - } } diff --git a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt index 1f2fd9a91..ba3db15fb 100644 --- a/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt +++ b/app/src/main/java/org/mozilla/fenix/sync/SyncedTabsLayout.kt @@ -34,6 +34,9 @@ class SyncedTabsLayout @JvmOverloads constructor( } override fun onError(error: SyncedTabsView.ErrorType) { + // We may still be displaying a "loading" spinner, hide it. + stopLoading() + val stringResId = when (error) { SyncedTabsView.ErrorType.MULTIPLE_DEVICES_UNAVAILABLE -> R.string.synced_tabs_connect_another_device SyncedTabsView.ErrorType.SYNC_ENGINE_UNAVAILABLE -> R.string.synced_tabs_enable_tab_syncing diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt index 72718208c..27ac903cd 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt @@ -38,9 +38,11 @@ class DefaultTabTrayController( private val registerCollectionStorageObserver: () -> Unit ) : TabTrayController { override fun onNewTabTapped(private: Boolean) { + val startTime = activity.components.core.engine.profiler?.getProfilerTime() activity.browsingModeManager.mode = BrowsingMode.fromBoolean(private) navController.navigate(TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = true)) dismissTabTray() + activity.components.core.engine.profiler?.addMarker("DefaultTabTrayController.onNewTabTapped", startTime) } override fun onTabTrayDismissed() { diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt index b1d47985f..903b699c9 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabsTouchHelper.kt @@ -4,14 +4,86 @@ package org.mozilla.fenix.tabtray +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import androidx.appcompat.content.res.AppCompatResources import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.RecyclerView import mozilla.components.browser.tabstray.TabTouchCallback import mozilla.components.concept.tabstray.TabsTray import mozilla.components.support.base.observer.Observable +import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.ktx.android.content.getDrawableWithTint +import mozilla.components.support.ktx.android.util.dpToPx +import org.mozilla.fenix.R +import org.mozilla.fenix.home.sessioncontrol.SwipeToDeleteCallback class TabsTouchHelper(observable: Observable) : ItemTouchHelper(object : TabTouchCallback(observable) { - override fun alphaForItemSwipe(dX: Float, distanceToAlphaMin: Int): Float { - return 1f - 2f * Math.abs(dX) / distanceToAlphaMin + override fun onChildDraw( + c: Canvas, + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + dX: Float, + dY: Float, + actionState: Int, + isCurrentlyActive: Boolean + ) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) + val icon = recyclerView.context.getDrawableWithTint( + R.drawable.ic_delete, + recyclerView.context.getColorFromAttr(R.attr.destructive) + )!! + val background = AppCompatResources.getDrawable( + recyclerView.context, + R.drawable.tab_tray_background + )!! + val itemView = viewHolder.itemView + val iconLeft: Int + val iconRight: Int + val margin = + SwipeToDeleteCallback.MARGIN.dpToPx(recyclerView.context.resources.displayMetrics) + val iconWidth = icon.intrinsicWidth + val iconHeight = icon.intrinsicHeight + val cellHeight = itemView.bottom - itemView.top + val iconTop = itemView.top + (cellHeight - iconHeight) / 2 + val iconBottom = iconTop + iconHeight + + when { + dX > 0 -> { // Swiping to the right + iconLeft = itemView.left + margin + iconRight = itemView.left + margin + iconWidth + background.setBounds( + itemView.left, itemView.top, + (itemView.left + dX).toInt() + SwipeToDeleteCallback.BACKGROUND_CORNER_OFFSET, + itemView.bottom + ) + icon.setBounds(iconLeft, iconTop, iconRight, iconBottom) + draw(background, icon, c) + } + dX < 0 -> { // Swiping to the left + iconLeft = itemView.right - margin - iconWidth + iconRight = itemView.right - margin + background.setBounds( + (itemView.right + dX).toInt() - SwipeToDeleteCallback.BACKGROUND_CORNER_OFFSET, + itemView.top, itemView.right, itemView.bottom + ) + icon.setBounds(iconLeft, iconTop, iconRight, iconBottom) + draw(background, icon, c) + } + else -> { // View not swiped + background.setBounds(0, 0, 0, 0) + icon.setBounds(0, 0, 0, 0) + } + } + } + + private fun draw( + background: Drawable, + icon: Drawable, + c: Canvas + ) { + background.draw(c) + icon.draw(c) } }) diff --git a/app/src/main/res/drawable/tab_tray_background.xml b/app/src/main/res/drawable/tab_tray_background.xml new file mode 100644 index 000000000..33f7b152b --- /dev/null +++ b/app/src/main/res/drawable/tab_tray_background.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 305e19826..881f04879 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -54,14 +54,15 @@ - - - + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 2efbf79d8..21854871f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -76,7 +76,7 @@ @color/ink_80 @color/dark_grey_05 @color/light_grey_10 - @color/violet_70_12a + #E5DFF4 @color/light_grey_10 @color/light_grey_30 #ffffff @@ -136,7 +136,7 @@ @color/light_grey_05 @color/light_grey_60 @color/dark_grey_80 - @color/violet_50_32a + #412E69 @color/dark_grey_50 @color/dark_grey_10 #9059FF @@ -190,7 +190,7 @@ @color/light_grey_05 @color/light_grey_60 @color/ink_90 - @color/violet_50_32a + #422875 @color/ink_50 @color/dark_grey_10 #9059FF diff --git a/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt b/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt index 503a316eb..a352a925b 100644 --- a/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/tabtray/DefaultTabTrayControllerTest.kt @@ -61,6 +61,7 @@ class DefaultTabTrayControllerTest { every { activity.components.core.sessionManager } returns sessionManager every { activity.components.core.tabCollectionStorage } returns tabCollectionStorage + every { activity.components.core.engine.profiler } returns mockk(relaxed = true) every { sessionManager.sessionsOfType(private = true) } returns listOf(session).asSequence() every { sessionManager.sessionsOfType(private = false) } returns listOf(nonPrivateSession).asSequence() every { sessionManager.createSessionSnapshot(any()) } returns SessionManager.Snapshot.Item( diff --git a/app/src/test/resources/robolectric.properties b/app/src/test/resources/robolectric.properties new file mode 100644 index 000000000..89a6c8b4c --- /dev/null +++ b/app/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 \ No newline at end of file diff --git a/buildSrc/src/main/java/AndroidComponents.kt b/buildSrc/src/main/java/AndroidComponents.kt index 670302c5a..6c5312277 100644 --- a/buildSrc/src/main/java/AndroidComponents.kt +++ b/buildSrc/src/main/java/AndroidComponents.kt @@ -3,5 +3,5 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ object AndroidComponents { - const val VERSION = "50.0.20200709130100" + const val VERSION = "50.0.20200710130140" }