2019-02-13 01:04:01 +01:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
2019-07-12 20:38:15 +02:00
|
|
|
* 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/. */
|
2019-01-09 23:22:58 +01:00
|
|
|
|
|
|
|
package org.mozilla.fenix.home
|
|
|
|
|
2019-06-11 16:01:00 +02:00
|
|
|
import android.animation.Animator
|
2020-01-21 22:02:14 +01:00
|
|
|
import android.content.Context
|
2019-05-23 21:16:40 +02:00
|
|
|
import android.content.DialogInterface
|
2019-01-29 00:26:37 +01:00
|
|
|
import android.graphics.drawable.BitmapDrawable
|
2020-03-18 15:41:18 +01:00
|
|
|
import android.graphics.drawable.ColorDrawable
|
2019-01-09 23:22:58 +01:00
|
|
|
import android.os.Bundle
|
2019-09-11 19:52:33 +02:00
|
|
|
import android.view.Gravity
|
2019-01-09 23:22:58 +01:00
|
|
|
import android.view.LayoutInflater
|
|
|
|
import android.view.View
|
|
|
|
import android.view.ViewGroup
|
2019-09-11 19:52:33 +02:00
|
|
|
import android.widget.Button
|
|
|
|
import android.widget.LinearLayout
|
|
|
|
import android.widget.PopupWindow
|
2019-06-13 02:14:46 +02:00
|
|
|
import androidx.annotation.StringRes
|
2019-05-23 21:16:40 +02:00
|
|
|
import androidx.appcompat.app.AlertDialog
|
2020-02-04 05:40:51 +01:00
|
|
|
import androidx.constraintlayout.widget.ConstraintLayout
|
2020-02-10 19:32:26 +01:00
|
|
|
import androidx.constraintlayout.widget.ConstraintSet
|
|
|
|
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
|
2020-03-11 22:08:58 +01:00
|
|
|
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
|
2020-02-10 19:32:26 +01:00
|
|
|
import androidx.constraintlayout.widget.ConstraintSet.TOP
|
2020-02-04 05:40:51 +01:00
|
|
|
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
2020-01-21 22:02:14 +01:00
|
|
|
import androidx.core.content.ContextCompat
|
2020-04-30 22:53:10 +02:00
|
|
|
import androidx.core.view.isVisible
|
2020-02-04 05:40:51 +01:00
|
|
|
import androidx.core.view.updateLayoutParams
|
2019-01-15 02:42:58 +01:00
|
|
|
import androidx.fragment.app.Fragment
|
2019-08-08 18:05:01 +02:00
|
|
|
import androidx.fragment.app.activityViewModels
|
2020-03-10 14:07:31 +01:00
|
|
|
import androidx.fragment.app.viewModels
|
2019-07-23 23:15:46 +02:00
|
|
|
import androidx.lifecycle.Lifecycle
|
|
|
|
import androidx.lifecycle.LifecycleObserver
|
2019-05-16 23:02:24 +02:00
|
|
|
import androidx.lifecycle.Observer
|
2019-07-23 23:15:46 +02:00
|
|
|
import androidx.lifecycle.OnLifecycleEvent
|
2019-08-12 20:02:35 +02:00
|
|
|
import androidx.lifecycle.ViewModelProvider
|
2019-06-13 02:14:46 +02:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2019-08-20 19:45:41 +02:00
|
|
|
import androidx.navigation.fragment.findNavController
|
2019-06-12 22:08:00 +02:00
|
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
|
|
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE
|
2020-02-04 05:40:51 +01:00
|
|
|
import com.google.android.material.appbar.AppBarLayout
|
2019-06-07 16:45:38 +02:00
|
|
|
import com.google.android.material.snackbar.Snackbar
|
2020-03-31 20:27:10 +02:00
|
|
|
import kotlinx.android.synthetic.main.fragment_home.*
|
|
|
|
import kotlinx.android.synthetic.main.fragment_home.view.*
|
2020-03-26 19:20:59 +01:00
|
|
|
import kotlinx.coroutines.CoroutineScope
|
2020-01-02 23:31:52 +01:00
|
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
|
|
import kotlinx.coroutines.Dispatchers.Main
|
2020-03-26 19:20:59 +01:00
|
|
|
import kotlinx.coroutines.cancel
|
2020-01-02 23:31:52 +01:00
|
|
|
import kotlinx.coroutines.delay
|
2020-03-26 19:20:59 +01:00
|
|
|
import kotlinx.coroutines.flow.collect
|
2019-05-01 02:45:56 +02:00
|
|
|
import kotlinx.coroutines.launch
|
2019-07-23 23:15:46 +02:00
|
|
|
import kotlinx.coroutines.withContext
|
2019-10-22 10:37:25 +02:00
|
|
|
import mozilla.appservices.places.BookmarkRoot
|
2020-03-30 18:48:40 +02:00
|
|
|
import mozilla.components.browser.menu.view.MenuButton
|
2019-02-08 19:35:48 +01:00
|
|
|
import mozilla.components.browser.session.Session
|
|
|
|
import mozilla.components.browser.session.SessionManager
|
2020-04-14 03:57:16 +02:00
|
|
|
import mozilla.components.browser.state.state.MediaState.State.PLAYING
|
2020-03-26 19:20:59 +01:00
|
|
|
import mozilla.components.browser.state.store.BrowserStore
|
2019-05-23 06:40:10 +02:00
|
|
|
import mozilla.components.concept.sync.AccountObserver
|
2019-09-04 22:56:22 +02:00
|
|
|
import mozilla.components.concept.sync.AuthType
|
2019-05-23 06:40:10 +02:00
|
|
|
import mozilla.components.concept.sync.OAuthAccount
|
2020-04-14 03:57:16 +02:00
|
|
|
import mozilla.components.feature.media.ext.pauseIfPlaying
|
2019-07-23 23:15:46 +02:00
|
|
|
import mozilla.components.feature.tab.collections.TabCollection
|
2019-12-30 19:12:16 +01:00
|
|
|
import mozilla.components.feature.top.sites.TopSite
|
2020-04-30 22:53:10 +02:00
|
|
|
import mozilla.components.lib.state.ext.consumeFrom
|
2020-03-26 19:20:59 +01:00
|
|
|
import mozilla.components.lib.state.ext.flowScoped
|
2020-02-27 22:29:47 +01:00
|
|
|
import mozilla.components.support.ktx.android.util.dpToPx
|
2020-03-26 19:20:59 +01:00
|
|
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
2019-03-15 17:26:07 +01:00
|
|
|
import org.mozilla.fenix.BrowserDirection
|
2019-02-08 18:43:17 +01:00
|
|
|
import org.mozilla.fenix.HomeActivity
|
2019-01-09 23:22:58 +01:00
|
|
|
import org.mozilla.fenix.R
|
2020-05-21 18:29:04 +02:00
|
|
|
import org.mozilla.fenix.addons.runIfFragmentIsAttached
|
2020-03-30 21:07:00 +02:00
|
|
|
import org.mozilla.fenix.browser.BrowserAnimator.Companion.getToolbarNavOptions
|
2019-09-11 19:52:33 +02:00
|
|
|
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
|
2019-06-07 16:45:38 +02:00
|
|
|
import org.mozilla.fenix.components.FenixSnackbar
|
2019-09-11 19:52:33 +02:00
|
|
|
import org.mozilla.fenix.components.PrivateShortcutCreateManager
|
2019-12-05 04:06:05 +01:00
|
|
|
import org.mozilla.fenix.components.StoreProvider
|
2019-06-07 16:45:38 +02:00
|
|
|
import org.mozilla.fenix.components.TabCollectionStorage
|
2019-03-18 22:54:36 +01:00
|
|
|
import org.mozilla.fenix.components.metrics.Event
|
2020-04-22 23:29:43 +02:00
|
|
|
import org.mozilla.fenix.components.tips.FenixTipManager
|
|
|
|
import org.mozilla.fenix.components.tips.providers.MigrationTipProvider
|
2019-08-30 19:42:46 +02:00
|
|
|
import org.mozilla.fenix.ext.components
|
2019-11-25 21:36:47 +01:00
|
|
|
import org.mozilla.fenix.ext.hideToolbar
|
2019-09-23 18:33:55 +02:00
|
|
|
import org.mozilla.fenix.ext.metrics
|
2019-06-06 21:40:10 +02:00
|
|
|
import org.mozilla.fenix.ext.nav
|
2019-01-30 03:59:19 +01:00
|
|
|
import org.mozilla.fenix.ext.requireComponents
|
2019-08-27 22:04:03 +02:00
|
|
|
import org.mozilla.fenix.ext.sessionsOfType
|
2019-09-11 19:52:33 +02:00
|
|
|
import org.mozilla.fenix.ext.settings
|
2019-06-10 03:50:09 +02:00
|
|
|
import org.mozilla.fenix.ext.toTab
|
2020-03-06 09:26:40 +01:00
|
|
|
import org.mozilla.fenix.home.sessioncontrol.AdapterItem
|
2019-12-05 04:06:05 +01:00
|
|
|
import org.mozilla.fenix.home.sessioncontrol.DefaultSessionControlController
|
2020-03-06 09:26:40 +01:00
|
|
|
import org.mozilla.fenix.home.sessioncontrol.SessionControlAdapter
|
2019-12-05 04:06:05 +01:00
|
|
|
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
|
|
|
|
import org.mozilla.fenix.home.sessioncontrol.SessionControlView
|
2019-06-11 16:01:00 +02:00
|
|
|
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
|
2019-05-16 05:46:52 +02:00
|
|
|
import org.mozilla.fenix.onboarding.FenixOnboarding
|
2019-02-28 17:36:45 +01:00
|
|
|
import org.mozilla.fenix.settings.SupportUtils
|
2019-09-26 20:46:05 +02:00
|
|
|
import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
|
2020-05-21 04:57:47 +02:00
|
|
|
import org.mozilla.fenix.tabtray.TabTrayDialogFragment
|
2020-01-21 22:02:14 +01:00
|
|
|
import org.mozilla.fenix.theme.ThemeManager
|
2019-08-20 11:52:25 +02:00
|
|
|
import org.mozilla.fenix.utils.FragmentPreDrawManager
|
2020-04-30 22:53:10 +02:00
|
|
|
import org.mozilla.fenix.utils.Settings
|
2019-05-30 02:14:51 +02:00
|
|
|
import org.mozilla.fenix.utils.allowUndo
|
2019-09-03 22:16:29 +02:00
|
|
|
import org.mozilla.fenix.whatsnew.WhatsNew
|
2020-03-30 18:48:40 +02:00
|
|
|
import java.lang.ref.WeakReference
|
2020-02-04 05:40:51 +01:00
|
|
|
import kotlin.math.abs
|
2019-11-26 19:07:31 +01:00
|
|
|
import kotlin.math.min
|
2019-01-29 00:26:37 +01:00
|
|
|
|
2019-03-21 01:26:13 +01:00
|
|
|
@SuppressWarnings("TooManyFunctions", "LargeClass")
|
2019-09-23 18:33:55 +02:00
|
|
|
class HomeFragment : Fragment() {
|
2020-03-10 14:07:31 +01:00
|
|
|
private val homeViewModel: HomeScreenViewModel by viewModels {
|
|
|
|
ViewModelProvider.AndroidViewModelFactory(requireActivity().application)
|
|
|
|
}
|
|
|
|
|
2020-03-30 12:43:01 +02:00
|
|
|
private val sharedViewModel: SharedViewModel by activityViewModels()
|
|
|
|
|
2020-01-31 16:55:02 +01:00
|
|
|
private val snackbarAnchorView: View?
|
|
|
|
get() {
|
|
|
|
return if (requireContext().settings().shouldUseBottomToolbar) {
|
|
|
|
toolbarLayout
|
|
|
|
} else {
|
|
|
|
null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-23 06:46:50 +01:00
|
|
|
private val browsingModeManager get() = (activity as HomeActivity).browsingModeManager
|
2020-02-04 05:40:51 +01:00
|
|
|
private var homeAppBarOffset = 0
|
2019-05-20 20:24:21 +02:00
|
|
|
private val singleSessionObserver = object : Session.Observer {
|
|
|
|
override fun onTitleChanged(session: Session, title: String) {
|
2019-07-23 23:15:46 +02:00
|
|
|
if (deleteAllSessionsJob == null) emitSessionChanges()
|
2019-05-20 20:24:21 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-12 19:49:20 +02:00
|
|
|
|
2019-06-27 20:29:00 +02:00
|
|
|
private val collectionStorageObserver = object : TabCollectionStorage.Observer {
|
|
|
|
override fun onCollectionCreated(title: String, sessions: List<Session>) {
|
|
|
|
scrollAndAnimateCollection(sessions.size)
|
|
|
|
}
|
|
|
|
|
2019-07-23 23:15:46 +02:00
|
|
|
override fun onTabsAdded(tabCollection: TabCollection, sessions: List<Session>) {
|
2019-06-27 20:29:00 +02:00
|
|
|
scrollAndAnimateCollection(sessions.size, tabCollection)
|
|
|
|
}
|
|
|
|
|
2019-07-23 23:15:46 +02:00
|
|
|
override fun onCollectionRenamed(tabCollection: TabCollection, title: String) {
|
2019-06-27 20:29:00 +02:00
|
|
|
showRenamedSnackbar()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 17:59:57 +02:00
|
|
|
private val sessionManager: SessionManager
|
|
|
|
get() = requireComponents.core.sessionManager
|
|
|
|
|
2019-05-22 19:26:06 +02:00
|
|
|
var deleteAllSessionsJob: (suspend () -> Unit)? = null
|
2019-06-12 19:49:20 +02:00
|
|
|
private var pendingSessionDeletion: PendingSessionDeletion? = null
|
|
|
|
|
|
|
|
data class PendingSessionDeletion(val deletionJob: (suspend () -> Unit), val sessionId: String)
|
2019-05-05 16:54:45 +02:00
|
|
|
|
2020-02-04 07:15:18 +01:00
|
|
|
private lateinit var homeAppBarOffSetListener: AppBarLayout.OnOffsetChangedListener
|
2019-05-16 05:46:52 +02:00
|
|
|
private val onboarding by lazy { FenixOnboarding(requireContext()) }
|
2019-12-05 04:06:05 +01:00
|
|
|
private lateinit var homeFragmentStore: HomeFragmentStore
|
2020-04-18 04:13:43 +02:00
|
|
|
private var _sessionControlInteractor: SessionControlInteractor? = null
|
|
|
|
protected val sessionControlInteractor: SessionControlInteractor
|
|
|
|
get() = _sessionControlInteractor!!
|
|
|
|
|
2020-02-04 05:40:51 +01:00
|
|
|
private var sessionControlView: SessionControlView? = null
|
2019-09-23 18:33:55 +02:00
|
|
|
private lateinit var currentMode: CurrentMode
|
2019-02-08 19:35:48 +01:00
|
|
|
|
2019-05-22 20:46:31 +02:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
2019-08-08 00:41:52 +02:00
|
|
|
postponeEnterTransition()
|
2019-06-14 00:40:12 +02:00
|
|
|
if (!onboarding.userHasBeenOnboarded()) {
|
|
|
|
requireComponents.analytics.metrics.track(Event.OpenedAppFirstRun)
|
|
|
|
}
|
2019-05-22 20:46:31 +02:00
|
|
|
}
|
|
|
|
|
2019-01-09 23:22:58 +01:00
|
|
|
override fun onCreateView(
|
2019-01-30 17:36:14 +01:00
|
|
|
inflater: LayoutInflater,
|
|
|
|
container: ViewGroup?,
|
2019-01-09 23:22:58 +01:00
|
|
|
savedInstanceState: Bundle?
|
|
|
|
): View? {
|
2019-01-30 03:59:19 +01:00
|
|
|
val view = inflater.inflate(R.layout.fragment_home, container, false)
|
2019-12-05 04:06:05 +01:00
|
|
|
val activity = activity as HomeActivity
|
2019-05-16 05:46:52 +02:00
|
|
|
|
2020-04-17 04:28:08 +02:00
|
|
|
val sessionObserver = BrowserSessionsObserver(
|
|
|
|
sessionManager,
|
|
|
|
requireComponents.core.store,
|
|
|
|
singleSessionObserver
|
|
|
|
) {
|
|
|
|
emitSessionChanges()
|
|
|
|
}
|
|
|
|
|
|
|
|
viewLifecycleOwner.lifecycle.addObserver(sessionObserver)
|
|
|
|
|
2019-09-23 18:33:55 +02:00
|
|
|
currentMode = CurrentMode(
|
|
|
|
view.context,
|
|
|
|
onboarding,
|
|
|
|
browsingModeManager,
|
2019-12-05 04:06:05 +01:00
|
|
|
::dispatchModeChanges
|
2019-09-23 18:33:55 +02:00
|
|
|
)
|
2019-05-06 20:20:19 +02:00
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
homeFragmentStore = StoreProvider.get(this) {
|
|
|
|
HomeFragmentStore(
|
|
|
|
HomeFragmentState(
|
|
|
|
collections = requireComponents.core.tabCollectionStorage.cachedTabCollections,
|
|
|
|
expandedCollections = emptySet(),
|
|
|
|
mode = currentMode.getCurrentMode(),
|
2019-12-30 19:12:16 +01:00
|
|
|
tabs = emptyList(),
|
2020-04-22 23:29:43 +02:00
|
|
|
topSites = requireComponents.core.topSiteStorage.cachedTopSites,
|
|
|
|
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
|
2019-06-06 23:54:49 +02:00
|
|
|
)
|
2019-12-05 04:06:05 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-18 04:13:43 +02:00
|
|
|
_sessionControlInteractor = SessionControlInteractor(
|
2019-12-05 04:06:05 +01:00
|
|
|
DefaultSessionControlController(
|
2020-03-26 19:20:59 +01:00
|
|
|
store = requireComponents.core.store,
|
2019-12-05 04:06:05 +01:00
|
|
|
activity = activity,
|
2020-03-26 19:20:59 +01:00
|
|
|
fragmentStore = homeFragmentStore,
|
2019-12-05 04:06:05 +01:00
|
|
|
navController = findNavController(),
|
|
|
|
browsingModeManager = browsingModeManager,
|
2020-04-22 05:41:20 +02:00
|
|
|
viewLifecycleScope = viewLifecycleOwner.lifecycleScope,
|
2019-12-05 04:06:05 +01:00
|
|
|
closeTab = ::closeTab,
|
|
|
|
closeAllTabs = ::closeAllTabs,
|
|
|
|
getListOfTabs = ::getListOfTabs,
|
2020-01-21 14:23:14 +01:00
|
|
|
hideOnboarding = ::hideOnboardingAndOpenSearch,
|
2019-12-05 04:06:05 +01:00
|
|
|
invokePendingDeleteJobs = ::invokePendingDeleteJobs,
|
|
|
|
registerCollectionStorageObserver = ::registerCollectionStorageObserver,
|
|
|
|
scrollToTheTop = ::scrollToTheTop,
|
2020-01-22 14:29:41 +01:00
|
|
|
showDeleteCollectionPrompt = ::showDeleteCollectionPrompt,
|
2020-02-28 15:51:29 +01:00
|
|
|
openSettingsScreen = ::openSettingsScreen,
|
2020-04-06 20:00:47 +02:00
|
|
|
openSearchScreen = ::navigateToSearch,
|
2020-04-18 22:03:57 +02:00
|
|
|
openWhatsNewLink = { openCustomTab(SupportUtils.getWhatsNewUrl(activity)) },
|
2020-04-06 20:00:47 +02:00
|
|
|
openPrivacyNotice = { openCustomTab(SupportUtils.getPrivacyNoticeUrl()) }
|
2019-12-05 04:06:05 +01:00
|
|
|
)
|
2019-02-17 03:55:49 +01:00
|
|
|
)
|
2020-02-04 07:13:20 +01:00
|
|
|
updateLayout(view)
|
2020-02-04 05:40:51 +01:00
|
|
|
setOffset(view)
|
2020-02-04 07:15:18 +01:00
|
|
|
sessionControlView = SessionControlView(
|
2020-04-18 22:03:57 +02:00
|
|
|
view.sessionControlRecyclerView,
|
|
|
|
sessionControlInteractor,
|
2020-03-10 14:07:31 +01:00
|
|
|
homeViewModel
|
2020-02-04 07:15:18 +01:00
|
|
|
)
|
2020-02-04 05:40:51 +01:00
|
|
|
activity.themeManager.applyStatusBarTheme(activity)
|
2020-02-06 00:46:56 +01:00
|
|
|
|
2020-04-30 22:53:10 +02:00
|
|
|
view.consumeFrom(homeFragmentStore, viewLifecycleOwner) {
|
|
|
|
sessionControlView?.update(it)
|
|
|
|
|
|
|
|
if (context?.settings()?.useNewTabTray == true) {
|
|
|
|
view.tab_button.setCountWithAnimation(it.tabs.size)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-22 18:06:26 +01:00
|
|
|
return view
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun updateLayout(view: View) {
|
2020-01-31 16:55:02 +01:00
|
|
|
val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar
|
2020-01-22 18:06:26 +01:00
|
|
|
|
2020-02-04 07:15:18 +01:00
|
|
|
if (!shouldUseBottomToolbar) {
|
|
|
|
view.toolbarLayout.layoutParams = CoordinatorLayout.LayoutParams(
|
2020-03-30 21:07:00 +02:00
|
|
|
ConstraintLayout.LayoutParams.MATCH_PARENT,
|
|
|
|
ConstraintLayout.LayoutParams.WRAP_CONTENT
|
|
|
|
)
|
2020-02-04 05:40:51 +01:00
|
|
|
.apply {
|
|
|
|
gravity = Gravity.TOP
|
|
|
|
}
|
2019-02-26 23:21:29 +01:00
|
|
|
|
2020-02-10 19:32:26 +01:00
|
|
|
ConstraintSet().apply {
|
|
|
|
clone(view.toolbarLayout)
|
2020-03-12 05:42:27 +01:00
|
|
|
clear(view.bottom_bar.id, BOTTOM)
|
2020-02-10 19:32:26 +01:00
|
|
|
clear(view.bottomBarShadow.id, BOTTOM)
|
2020-03-12 05:42:27 +01:00
|
|
|
connect(view.bottom_bar.id, TOP, PARENT_ID, TOP)
|
2020-02-10 19:32:26 +01:00
|
|
|
connect(view.bottomBarShadow.id, TOP, view.bottom_bar.id, BOTTOM)
|
2020-03-11 22:08:58 +01:00
|
|
|
connect(view.bottomBarShadow.id, BOTTOM, PARENT_ID, BOTTOM)
|
2020-02-10 19:32:26 +01:00
|
|
|
applyTo(view.toolbarLayout)
|
|
|
|
}
|
|
|
|
|
|
|
|
view.bottom_bar.background = resources.getDrawable(
|
|
|
|
ThemeManager.resolveAttribute(R.attr.bottomBarBackgroundTop, requireContext()),
|
|
|
|
null
|
|
|
|
)
|
|
|
|
|
2020-02-04 07:15:18 +01:00
|
|
|
view.homeAppBar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
2020-02-04 07:13:20 +01:00
|
|
|
topMargin = HEADER_MARGIN.dpToPx(resources.displayMetrics)
|
2020-01-22 18:06:26 +01:00
|
|
|
}
|
2020-02-04 07:13:20 +01:00
|
|
|
|
|
|
|
createNewAppBarListener(HEADER_MARGIN.dpToPx(resources.displayMetrics).toFloat())
|
|
|
|
view.homeAppBar.addOnOffsetChangedListener(
|
|
|
|
homeAppBarOffSetListener
|
|
|
|
)
|
2020-02-04 07:15:18 +01:00
|
|
|
} else {
|
2020-02-04 07:13:20 +01:00
|
|
|
createNewAppBarListener(0F)
|
|
|
|
view.homeAppBar.addOnOffsetChangedListener(
|
|
|
|
homeAppBarOffSetListener
|
|
|
|
)
|
2020-01-22 18:06:26 +01:00
|
|
|
}
|
2019-01-09 23:22:58 +01:00
|
|
|
}
|
2020-02-04 07:15:18 +01:00
|
|
|
|
2019-03-08 17:37:35 +01:00
|
|
|
@SuppressWarnings("LongMethod")
|
2019-01-10 01:07:33 +01:00
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
|
|
super.onViewCreated(view, savedInstanceState)
|
2019-01-11 18:44:22 +01:00
|
|
|
|
2019-08-20 11:52:25 +02:00
|
|
|
FragmentPreDrawManager(this).execute {
|
2019-08-22 20:35:51 +02:00
|
|
|
val homeViewModel: HomeScreenViewModel by activityViewModels {
|
|
|
|
ViewModelProvider.NewInstanceFactory() // this is a workaround for #4652
|
|
|
|
}
|
2019-08-20 11:52:25 +02:00
|
|
|
homeViewModel.layoutManagerState?.also { parcelable ->
|
2020-02-04 05:40:51 +01:00
|
|
|
sessionControlView!!.view.layoutManager?.onRestoreInstanceState(parcelable)
|
2019-08-20 11:52:25 +02:00
|
|
|
}
|
|
|
|
homeViewModel.layoutManagerState = null
|
2020-02-27 22:29:47 +01:00
|
|
|
|
|
|
|
// We have to delay so that the keyboard collapses and the view is resized before the
|
|
|
|
// animation from SearchFragment happens
|
|
|
|
delay(ANIMATION_DELAY)
|
2019-08-20 11:52:25 +02:00
|
|
|
}
|
|
|
|
|
2019-11-25 20:07:21 +01:00
|
|
|
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
2020-01-07 19:47:33 +01:00
|
|
|
// This is necessary due to a bug in viewLifecycleOwner. See:
|
|
|
|
// https://github.com/mozilla-mobile/android-components/blob/master/components/lib/state/src/main/java/mozilla/components/lib/state/ext/Fragment.kt#L32-L56
|
|
|
|
// TODO remove when viewLifecycleOwner is fixed
|
2020-01-06 23:22:32 +01:00
|
|
|
val context = context ?: return@launch
|
|
|
|
|
2020-02-22 06:04:43 +01:00
|
|
|
val iconSize =
|
|
|
|
context.resources.getDimensionPixelSize(R.dimen.preference_icon_drawable_size)
|
2019-05-01 02:45:56 +02:00
|
|
|
|
2020-02-14 00:29:00 +01:00
|
|
|
val searchEngine = context.components.search.provider.getDefaultEngine(context)
|
|
|
|
val searchIcon = BitmapDrawable(context.resources, searchEngine.icon)
|
2019-05-01 02:45:56 +02:00
|
|
|
searchIcon.setBounds(0, 0, iconSize, iconSize)
|
|
|
|
|
2019-11-25 20:07:21 +01:00
|
|
|
withContext(Main) {
|
2019-09-20 21:11:28 +02:00
|
|
|
search_engine_icon?.setImageDrawable(searchIcon)
|
2019-05-01 02:45:56 +02:00
|
|
|
}
|
2019-01-29 00:26:37 +01:00
|
|
|
}
|
2019-01-25 17:11:43 +01:00
|
|
|
|
2020-05-06 19:17:17 +02:00
|
|
|
createHomeMenu(requireContext(), WeakReference(view.menuButton))
|
2020-01-21 22:02:14 +01:00
|
|
|
|
2020-03-30 18:48:40 +02:00
|
|
|
view.menuButton.setColorFilter(ContextCompat.getColor(
|
2020-05-06 19:17:17 +02:00
|
|
|
requireContext(),
|
|
|
|
ThemeManager.resolveAttribute(R.attr.primaryText, requireContext())
|
2020-03-30 18:48:40 +02:00
|
|
|
))
|
2020-01-21 22:02:14 +01:00
|
|
|
|
2019-09-20 21:11:28 +02:00
|
|
|
view.toolbar.compoundDrawablePadding =
|
2019-08-20 01:22:51 +02:00
|
|
|
view.resources.getDimensionPixelSize(R.dimen.search_bar_search_engine_icon_padding)
|
2019-09-20 21:11:28 +02:00
|
|
|
view.toolbar_wrapper.setOnClickListener {
|
|
|
|
invokePendingDeleteJobs()
|
2019-11-22 02:51:55 +01:00
|
|
|
hideOnboardingIfNeeded()
|
2020-02-27 22:29:47 +01:00
|
|
|
navigateToSearch()
|
2019-09-20 21:11:28 +02:00
|
|
|
requireComponents.analytics.metrics.track(Event.SearchBarTapped(Event.SearchBarTapped.Source.HOME))
|
|
|
|
}
|
2019-02-02 01:04:49 +01:00
|
|
|
|
2019-09-20 21:13:54 +02:00
|
|
|
view.add_tab_button.setOnClickListener {
|
|
|
|
invokePendingDeleteJobs()
|
2019-11-22 02:51:55 +01:00
|
|
|
hideOnboardingIfNeeded()
|
2020-01-21 14:23:14 +01:00
|
|
|
navigateToSearch()
|
2019-09-20 21:13:54 +02:00
|
|
|
}
|
|
|
|
|
2020-04-30 22:53:10 +02:00
|
|
|
view.tab_button.setOnClickListener {
|
|
|
|
invokePendingDeleteJobs()
|
|
|
|
hideOnboardingIfNeeded()
|
2020-05-21 04:57:47 +02:00
|
|
|
val tabTrayDialog = TabTrayDialogFragment()
|
|
|
|
tabTrayDialog.show(parentFragmentManager, null)
|
2020-04-30 22:53:10 +02:00
|
|
|
}
|
|
|
|
|
2019-08-20 19:41:18 +02:00
|
|
|
PrivateBrowsingButtonView(
|
|
|
|
privateBrowsingButton,
|
|
|
|
browsingModeManager
|
|
|
|
) { newMode ->
|
2019-05-28 20:06:07 +02:00
|
|
|
invokePendingDeleteJobs()
|
2019-05-14 22:49:02 +02:00
|
|
|
|
2019-09-11 19:52:33 +02:00
|
|
|
if (newMode == BrowsingMode.Private) {
|
2019-09-24 19:33:46 +02:00
|
|
|
requireContext().settings().incrementNumTimesPrivateModeOpened()
|
2019-09-11 19:52:33 +02:00
|
|
|
}
|
|
|
|
|
2019-05-21 20:03:32 +02:00
|
|
|
if (onboarding.userHasBeenOnboarded()) {
|
2019-12-05 04:06:05 +01:00
|
|
|
homeFragmentStore.dispatch(
|
2020-02-22 06:04:43 +01:00
|
|
|
HomeFragmentAction.ModeChange(Mode.fromBrowsingMode(newMode))
|
|
|
|
)
|
2019-05-21 20:03:32 +02:00
|
|
|
}
|
2019-02-08 18:43:17 +01:00
|
|
|
}
|
2019-02-02 01:04:49 +01:00
|
|
|
}
|
|
|
|
|
2020-02-04 05:40:51 +01:00
|
|
|
override fun onDestroyView() {
|
2020-02-06 00:46:56 +01:00
|
|
|
super.onDestroyView()
|
2020-04-18 04:13:43 +02:00
|
|
|
_sessionControlInteractor = null
|
2020-02-04 05:40:51 +01:00
|
|
|
sessionControlView = null
|
2020-05-06 19:17:17 +02:00
|
|
|
requireView().homeAppBar.removeOnOffsetChangedListener(homeAppBarOffSetListener)
|
2020-02-04 05:40:51 +01:00
|
|
|
}
|
|
|
|
|
2019-11-26 23:53:03 +01:00
|
|
|
override fun onStart() {
|
|
|
|
super.onStart()
|
|
|
|
subscribeToTabCollections()
|
2019-12-30 19:12:16 +01:00
|
|
|
subscribeToTopSites()
|
2019-05-23 06:40:10 +02:00
|
|
|
|
2019-08-30 19:42:46 +02:00
|
|
|
val context = requireContext()
|
|
|
|
val components = context.components
|
|
|
|
|
2020-02-22 06:04:43 +01:00
|
|
|
homeFragmentStore.dispatch(
|
|
|
|
HomeFragmentAction.Change(
|
|
|
|
collections = components.core.tabCollectionStorage.cachedTabCollections,
|
|
|
|
mode = currentMode.getCurrentMode(),
|
|
|
|
tabs = getListOfSessions().toTabs(),
|
2020-04-22 23:29:43 +02:00
|
|
|
topSites = components.core.topSiteStorage.cachedTopSites,
|
|
|
|
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
|
2020-02-22 06:04:43 +01:00
|
|
|
)
|
|
|
|
)
|
2019-05-14 22:49:02 +02:00
|
|
|
|
2020-03-30 18:48:12 +02:00
|
|
|
requireComponents.backgroundServices.accountManagerAvailableQueue.runIfReadyOrQueue {
|
2020-04-17 03:06:57 +02:00
|
|
|
// By the time this code runs, we may not be attached to a context or have a view lifecycle owner.
|
|
|
|
if ((this@HomeFragment).view?.context == null) {
|
2020-03-30 18:48:12 +02:00
|
|
|
return@runIfReadyOrQueue
|
|
|
|
}
|
2020-04-17 03:06:57 +02:00
|
|
|
|
|
|
|
requireComponents.backgroundServices.accountManager.register(
|
|
|
|
currentMode,
|
|
|
|
owner = this@HomeFragment.viewLifecycleOwner
|
|
|
|
)
|
2020-03-30 18:48:12 +02:00
|
|
|
requireComponents.backgroundServices.accountManager.register(object : AccountObserver {
|
|
|
|
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) {
|
|
|
|
if (authType != AuthType.Existing) {
|
|
|
|
view?.let {
|
2020-04-02 21:30:13 +02:00
|
|
|
FenixSnackbar.make(view = it,
|
|
|
|
duration = Snackbar.LENGTH_SHORT,
|
2020-04-13 21:16:01 +02:00
|
|
|
isDisplayedWithBrowserToolbar = false
|
2020-04-02 21:30:13 +02:00
|
|
|
)
|
2020-03-30 18:48:12 +02:00
|
|
|
.setText(it.context.getString(R.string.onboarding_firefox_account_sync_is_on))
|
|
|
|
.setAnchorView(toolbarLayout)
|
|
|
|
.show()
|
|
|
|
}
|
2019-09-23 18:33:55 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-17 03:06:57 +02:00
|
|
|
}, owner = this@HomeFragment.viewLifecycleOwner)
|
2020-03-30 18:48:12 +02:00
|
|
|
}
|
2019-10-03 21:04:27 +02:00
|
|
|
|
2019-09-23 18:33:55 +02:00
|
|
|
if (context.settings().showPrivateModeContextualFeatureRecommender &&
|
2020-02-22 06:04:43 +01:00
|
|
|
browsingModeManager.mode.isPrivate
|
|
|
|
) {
|
2019-09-11 19:52:33 +02:00
|
|
|
recommendPrivateBrowsingShortcut()
|
|
|
|
}
|
2019-06-27 20:29:00 +02:00
|
|
|
|
|
|
|
// We only want this observer live just before we navigate away to the collection creation screen
|
|
|
|
requireComponents.core.tabCollectionStorage.unregister(collectionStorageObserver)
|
2019-02-08 19:35:48 +01:00
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun closeTab(sessionId: String) {
|
|
|
|
val deletionJob = pendingSessionDeletion?.deletionJob
|
2020-04-14 03:57:16 +02:00
|
|
|
context?.let {
|
|
|
|
if (sessionManager.findSessionById(sessionId)?.toTab(it)?.mediaState == PLAYING) {
|
|
|
|
it.components.core.store.state.media.pauseIfPlaying()
|
|
|
|
}
|
|
|
|
}
|
2019-12-05 04:06:05 +01:00
|
|
|
|
|
|
|
if (deletionJob == null) {
|
|
|
|
removeTabWithUndo(sessionId, browsingModeManager.mode.isPrivate)
|
|
|
|
} else {
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
|
|
|
deletionJob.invoke()
|
|
|
|
}.invokeOnCompletion {
|
|
|
|
pendingSessionDeletion = null
|
|
|
|
removeTabWithUndo(sessionId, browsingModeManager.mode.isPrivate)
|
2019-05-16 07:56:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun closeAllTabs(isPrivateMode: Boolean) {
|
|
|
|
val deletionJob = pendingSessionDeletion?.deletionJob
|
2019-09-20 21:13:54 +02:00
|
|
|
|
2020-04-14 03:57:16 +02:00
|
|
|
context?.let {
|
|
|
|
sessionManager.sessionsOfType(private = isPrivateMode).forEach { session ->
|
|
|
|
if (session.toTab(it).mediaState == PLAYING) {
|
|
|
|
it.components.core.store.state.media.pauseIfPlaying()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
if (deletionJob == null) {
|
|
|
|
removeAllTabsWithUndo(
|
|
|
|
sessionManager.sessionsOfType(private = isPrivateMode),
|
|
|
|
isPrivateMode
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
|
|
|
deletionJob.invoke()
|
|
|
|
}.invokeOnCompletion {
|
|
|
|
pendingSessionDeletion = null
|
|
|
|
removeAllTabsWithUndo(
|
|
|
|
sessionManager.sessionsOfType(private = isPrivateMode),
|
|
|
|
isPrivateMode
|
|
|
|
)
|
2019-04-30 00:03:40 +02:00
|
|
|
}
|
2019-04-06 06:24:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun dispatchModeChanges(mode: Mode) {
|
2019-12-05 09:51:14 +01:00
|
|
|
if (mode != Mode.fromBrowsingMode(browsingModeManager.mode)) {
|
|
|
|
homeFragmentStore.dispatch(HomeFragmentAction.ModeChange(mode))
|
|
|
|
}
|
2019-12-05 04:06:05 +01:00
|
|
|
}
|
|
|
|
|
2019-05-20 19:22:05 +02:00
|
|
|
private fun invokePendingDeleteJobs() {
|
2019-06-12 19:49:20 +02:00
|
|
|
pendingSessionDeletion?.deletionJob?.let {
|
2019-06-13 02:14:46 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
2019-05-08 19:27:38 +02:00
|
|
|
it.invoke()
|
|
|
|
}.invokeOnCompletion {
|
2019-06-12 19:49:20 +02:00
|
|
|
pendingSessionDeletion = null
|
2019-05-08 19:27:38 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-20 19:22:05 +02:00
|
|
|
|
2019-05-22 19:26:06 +02:00
|
|
|
deleteAllSessionsJob?.let {
|
2019-06-13 02:14:46 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
2019-05-22 19:26:06 +02:00
|
|
|
it.invoke()
|
|
|
|
}.invokeOnCompletion {
|
|
|
|
deleteAllSessionsJob = null
|
|
|
|
}
|
|
|
|
}
|
2019-05-23 21:16:40 +02:00
|
|
|
}
|
2019-05-22 19:26:06 +02:00
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun showDeleteCollectionPrompt(tabCollection: TabCollection) {
|
2019-08-30 19:42:46 +02:00
|
|
|
val context = context ?: return
|
|
|
|
AlertDialog.Builder(context).apply {
|
|
|
|
val message =
|
|
|
|
context.getString(R.string.tab_collection_dialog_message, tabCollection.title)
|
|
|
|
setMessage(message)
|
|
|
|
setNegativeButton(R.string.tab_collection_dialog_negative) { dialog: DialogInterface, _ ->
|
|
|
|
dialog.cancel()
|
|
|
|
}
|
|
|
|
setPositiveButton(R.string.tab_collection_dialog_positive) { dialog: DialogInterface, _ ->
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
|
|
|
context.components.core.tabCollectionStorage.removeCollection(tabCollection)
|
|
|
|
context.components.analytics.metrics.track(Event.CollectionRemoved)
|
|
|
|
}.invokeOnCompletion {
|
|
|
|
dialog.dismiss()
|
2019-05-23 21:16:40 +02:00
|
|
|
}
|
2019-08-30 19:42:46 +02:00
|
|
|
}
|
|
|
|
create()
|
|
|
|
}.show()
|
2019-05-08 19:27:38 +02:00
|
|
|
}
|
|
|
|
|
2019-11-26 23:53:03 +01:00
|
|
|
override fun onStop() {
|
2019-05-28 21:45:34 +02:00
|
|
|
invokePendingDeleteJobs()
|
2019-11-26 23:53:03 +01:00
|
|
|
super.onStop()
|
2019-08-22 20:35:51 +02:00
|
|
|
val homeViewModel: HomeScreenViewModel by activityViewModels {
|
|
|
|
ViewModelProvider.NewInstanceFactory() // this is a workaround for #4652
|
|
|
|
}
|
2019-08-08 18:05:01 +02:00
|
|
|
homeViewModel.layoutManagerState =
|
2020-02-04 05:40:51 +01:00
|
|
|
sessionControlView!!.view.layoutManager?.onSaveInstanceState()
|
2019-02-08 19:35:48 +01:00
|
|
|
}
|
|
|
|
|
2019-12-05 18:36:39 +01:00
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
2020-03-18 15:41:18 +01:00
|
|
|
if (browsingModeManager.mode == BrowsingMode.Private) {
|
|
|
|
activity?.window?.setBackgroundDrawableResource(R.drawable.private_home_background_gradient)
|
|
|
|
}
|
2019-12-05 18:36:39 +01:00
|
|
|
hideToolbar()
|
2020-03-30 12:43:01 +02:00
|
|
|
if (sharedViewModel.shouldScrollToSelectedTab) {
|
2020-03-06 09:26:40 +01:00
|
|
|
scrollToSelectedTab()
|
2020-03-30 12:43:01 +02:00
|
|
|
sharedViewModel.shouldScrollToSelectedTab = false
|
2020-03-06 09:26:40 +01:00
|
|
|
}
|
2020-04-30 22:53:10 +02:00
|
|
|
|
|
|
|
requireContext().settings().useNewTabTray.also {
|
|
|
|
view?.add_tab_button?.isVisible = !it
|
|
|
|
view?.tab_button?.isVisible = it
|
|
|
|
}
|
2019-12-05 18:36:39 +01:00
|
|
|
}
|
|
|
|
|
2020-02-04 05:40:51 +01:00
|
|
|
override fun onPause() {
|
|
|
|
super.onPause()
|
2020-03-18 15:41:18 +01:00
|
|
|
if (browsingModeManager.mode == BrowsingMode.Private) {
|
|
|
|
activity?.window?.setBackgroundDrawable(
|
|
|
|
ColorDrawable(
|
|
|
|
ContextCompat.getColor(
|
|
|
|
requireContext(),
|
|
|
|
R.color.foundation_private_theme
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}
|
2020-02-04 05:40:51 +01:00
|
|
|
calculateNewOffset()
|
|
|
|
}
|
|
|
|
|
2019-09-11 19:52:33 +02:00
|
|
|
private fun recommendPrivateBrowsingShortcut() {
|
|
|
|
context?.let {
|
|
|
|
val layout = LayoutInflater.from(it)
|
|
|
|
.inflate(R.layout.pbm_shortcut_popup, null)
|
2019-11-26 19:07:31 +01:00
|
|
|
val privateBrowsingRecommend =
|
2019-09-11 19:52:33 +02:00
|
|
|
PopupWindow(
|
|
|
|
layout,
|
2019-11-26 19:07:31 +01:00
|
|
|
min(
|
|
|
|
(resources.displayMetrics.widthPixels / CFR_WIDTH_DIVIDER).toInt(),
|
|
|
|
(resources.displayMetrics.heightPixels / CFR_WIDTH_DIVIDER).toInt()
|
|
|
|
),
|
2019-09-11 19:52:33 +02:00
|
|
|
LinearLayout.LayoutParams.WRAP_CONTENT,
|
|
|
|
true
|
|
|
|
)
|
|
|
|
layout.findViewById<Button>(R.id.cfr_pos_button).apply {
|
|
|
|
setOnClickListener {
|
2019-09-10 18:42:46 +02:00
|
|
|
context.metrics.track(Event.PrivateBrowsingAddShortcutCFR)
|
2019-09-11 19:52:33 +02:00
|
|
|
PrivateShortcutCreateManager.createPrivateShortcut(context)
|
2019-11-26 19:07:31 +01:00
|
|
|
privateBrowsingRecommend.dismiss()
|
2019-09-11 19:52:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
layout.findViewById<Button>(R.id.cfr_neg_button).apply {
|
2019-09-10 18:42:46 +02:00
|
|
|
setOnClickListener {
|
|
|
|
context.metrics.track(Event.PrivateBrowsingCancelCFR)
|
2019-11-26 19:07:31 +01:00
|
|
|
privateBrowsingRecommend.dismiss()
|
2019-09-10 18:42:46 +02:00
|
|
|
}
|
2019-09-11 19:52:33 +02:00
|
|
|
}
|
|
|
|
// We want to show the popup only after privateBrowsingButton is available.
|
|
|
|
// Otherwise, we will encounter an activity token error.
|
|
|
|
privateBrowsingButton.post {
|
2019-11-26 19:07:31 +01:00
|
|
|
privateBrowsingRecommend.showAsDropDown(
|
2020-02-22 06:04:43 +01:00
|
|
|
privateBrowsingButton, 0, CFR_Y_OFFSET, Gravity.TOP or Gravity.END
|
|
|
|
)
|
2019-09-11 19:52:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-20 19:56:28 +02:00
|
|
|
private fun hideOnboardingIfNeeded() {
|
2020-01-21 14:23:14 +01:00
|
|
|
if (!onboarding.userHasBeenOnboarded()) {
|
|
|
|
onboarding.finish()
|
|
|
|
homeFragmentStore.dispatch(
|
|
|
|
HomeFragmentAction.ModeChange(
|
|
|
|
mode = currentMode.getCurrentMode(),
|
2020-02-22 06:04:43 +01:00
|
|
|
tabs = getListOfSessions().toTabs()
|
|
|
|
)
|
|
|
|
)
|
2020-01-21 14:23:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun hideOnboardingAndOpenSearch() {
|
|
|
|
hideOnboardingIfNeeded()
|
|
|
|
navigateToSearch()
|
2019-09-23 18:33:55 +02:00
|
|
|
}
|
|
|
|
|
2020-01-21 14:23:14 +01:00
|
|
|
private fun navigateToSearch() {
|
2020-04-14 06:43:45 +02:00
|
|
|
val directions = HomeFragmentDirections.actionGlobalSearch(
|
2020-01-21 14:23:14 +01:00
|
|
|
sessionId = null
|
|
|
|
)
|
2020-02-27 22:29:47 +01:00
|
|
|
|
2020-03-30 21:07:00 +02:00
|
|
|
nav(R.id.homeFragment, directions, getToolbarNavOptions(requireContext()))
|
2019-08-20 19:56:28 +02:00
|
|
|
}
|
|
|
|
|
2020-01-22 14:29:41 +01:00
|
|
|
private fun openSettingsScreen() {
|
2020-04-14 06:43:45 +02:00
|
|
|
val directions = HomeFragmentDirections.actionGlobalPrivateBrowsingFragment()
|
2020-01-22 14:29:41 +01:00
|
|
|
nav(R.id.homeFragment, directions)
|
|
|
|
}
|
|
|
|
|
2020-04-06 20:00:47 +02:00
|
|
|
private fun openCustomTab(url: String) {
|
|
|
|
context?.let { context ->
|
|
|
|
val intent = SupportUtils.createCustomTabIntent(context, url)
|
|
|
|
startActivity(intent)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-02 21:30:13 +02:00
|
|
|
@SuppressWarnings("ComplexMethod", "LongMethod")
|
2020-03-30 18:48:40 +02:00
|
|
|
private fun createHomeMenu(context: Context, menuButtonView: WeakReference<MenuButton>) = HomeMenu(
|
2020-04-17 03:06:57 +02:00
|
|
|
this.viewLifecycleOwner,
|
2020-03-30 18:48:40 +02:00
|
|
|
context,
|
|
|
|
onItemTapped = {
|
2019-04-22 20:32:28 +02:00
|
|
|
when (it) {
|
2019-05-08 19:27:38 +02:00
|
|
|
HomeMenu.Item.Settings -> {
|
2019-05-20 19:22:05 +02:00
|
|
|
invokePendingDeleteJobs()
|
2019-08-20 19:56:28 +02:00
|
|
|
hideOnboardingIfNeeded()
|
2019-06-06 21:40:10 +02:00
|
|
|
nav(
|
|
|
|
R.id.homeFragment,
|
2020-04-14 06:43:45 +02:00
|
|
|
HomeFragmentDirections.actionGlobalSettingsFragment()
|
2019-05-08 19:27:38 +02:00
|
|
|
)
|
|
|
|
}
|
2019-10-22 10:37:25 +02:00
|
|
|
HomeMenu.Item.Bookmarks -> {
|
2019-05-20 19:22:05 +02:00
|
|
|
invokePendingDeleteJobs()
|
2019-08-20 19:56:28 +02:00
|
|
|
hideOnboardingIfNeeded()
|
2019-06-06 21:40:10 +02:00
|
|
|
nav(
|
|
|
|
R.id.homeFragment,
|
2020-04-14 06:43:45 +02:00
|
|
|
HomeFragmentDirections.actionGlobalBookmarkFragment(BookmarkRoot.Mobile.id)
|
2019-10-22 10:37:25 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
HomeMenu.Item.History -> {
|
|
|
|
invokePendingDeleteJobs()
|
|
|
|
hideOnboardingIfNeeded()
|
|
|
|
nav(
|
|
|
|
R.id.homeFragment,
|
2020-04-14 06:43:45 +02:00
|
|
|
HomeFragmentDirections.actionGlobalHistoryFragment()
|
2019-05-08 19:27:38 +02:00
|
|
|
)
|
|
|
|
}
|
2019-04-22 20:32:28 +02:00
|
|
|
HomeMenu.Item.Help -> {
|
2019-05-20 19:22:05 +02:00
|
|
|
invokePendingDeleteJobs()
|
2019-08-20 19:56:28 +02:00
|
|
|
hideOnboardingIfNeeded()
|
2019-04-22 20:32:28 +02:00
|
|
|
(activity as HomeActivity).openToBrowserAndLoad(
|
2019-06-15 00:00:09 +02:00
|
|
|
searchTermOrURL = SupportUtils.getSumoURLForTopic(
|
2019-08-30 19:42:46 +02:00
|
|
|
context,
|
2019-06-15 00:00:09 +02:00
|
|
|
SupportUtils.SumoTopic.HELP
|
|
|
|
),
|
2019-04-29 21:32:30 +02:00
|
|
|
newTab = true,
|
|
|
|
from = BrowserDirection.FromHome
|
2019-04-22 20:32:28 +02:00
|
|
|
)
|
|
|
|
}
|
2019-09-03 22:16:29 +02:00
|
|
|
HomeMenu.Item.WhatsNew -> {
|
|
|
|
invokePendingDeleteJobs()
|
|
|
|
hideOnboardingIfNeeded()
|
2019-08-30 19:42:46 +02:00
|
|
|
WhatsNew.userViewedWhatsNew(context)
|
2020-01-18 00:13:20 +01:00
|
|
|
context.metrics.track(Event.WhatsNewTapped)
|
2019-09-03 22:16:29 +02:00
|
|
|
(activity as HomeActivity).openToBrowserAndLoad(
|
2020-01-10 15:21:30 +01:00
|
|
|
searchTermOrURL = SupportUtils.getWhatsNewUrl(context),
|
2019-09-03 22:16:29 +02:00
|
|
|
newTab = true,
|
|
|
|
from = BrowserDirection.FromHome
|
|
|
|
)
|
|
|
|
}
|
2019-10-21 18:57:06 +02:00
|
|
|
// We need to show the snackbar while the browsing data is deleting(if "Delete
|
|
|
|
// browsing data on quit" is activated). After the deletion is over, the snackbar
|
|
|
|
// is dismissed.
|
2019-09-26 20:46:05 +02:00
|
|
|
HomeMenu.Item.Quit -> activity?.let { activity ->
|
|
|
|
deleteAndQuit(
|
|
|
|
activity,
|
2020-04-22 05:41:20 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope,
|
2020-04-02 21:30:13 +02:00
|
|
|
view?.let { view -> FenixSnackbar.make(
|
|
|
|
view = view,
|
2020-04-13 21:16:01 +02:00
|
|
|
isDisplayedWithBrowserToolbar = false
|
2020-04-02 21:30:13 +02:00
|
|
|
)
|
|
|
|
}
|
2019-09-26 20:46:05 +02:00
|
|
|
)
|
|
|
|
}
|
2020-02-27 21:54:14 +01:00
|
|
|
HomeMenu.Item.Sync -> {
|
|
|
|
invokePendingDeleteJobs()
|
|
|
|
hideOnboardingIfNeeded()
|
|
|
|
nav(
|
|
|
|
R.id.homeFragment,
|
2020-04-14 06:43:45 +02:00
|
|
|
HomeFragmentDirections.actionGlobalAccountProblemFragment()
|
2020-02-27 21:54:14 +01:00
|
|
|
)
|
|
|
|
}
|
2019-02-13 01:04:01 +01:00
|
|
|
}
|
2020-03-30 18:48:40 +02:00
|
|
|
},
|
|
|
|
onHighlightPresent = { menuButtonView.get()?.setHighlight(it) },
|
|
|
|
onMenuBuilderChanged = { menuButtonView.get()?.menuBuilder = it }
|
|
|
|
)
|
2019-02-13 01:04:01 +01:00
|
|
|
|
2019-05-16 23:02:24 +02:00
|
|
|
private fun subscribeToTabCollections(): Observer<List<TabCollection>> {
|
2019-07-23 23:15:46 +02:00
|
|
|
return Observer<List<TabCollection>> {
|
2019-05-16 23:35:15 +02:00
|
|
|
requireComponents.core.tabCollectionStorage.cachedTabCollections = it
|
2019-12-05 04:06:05 +01:00
|
|
|
homeFragmentStore.dispatch(HomeFragmentAction.CollectionsChange(it))
|
2019-07-23 23:15:46 +02:00
|
|
|
}.also { observer ->
|
|
|
|
requireComponents.core.tabCollectionStorage.getCollections().observe(this, observer)
|
2019-05-16 23:02:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-30 19:12:16 +01:00
|
|
|
private fun subscribeToTopSites(): Observer<List<TopSite>> {
|
|
|
|
return Observer<List<TopSite>> { topSites ->
|
|
|
|
requireComponents.core.topSiteStorage.cachedTopSites = topSites
|
2020-03-31 20:27:10 +02:00
|
|
|
context?.settings()?.preferences?.edit()
|
|
|
|
?.putInt(getString(R.string.pref_key_top_sites_size), topSites.size)?.apply()
|
2019-12-30 19:12:16 +01:00
|
|
|
homeFragmentStore.dispatch(HomeFragmentAction.TopSitesChange(topSites))
|
|
|
|
}.also { observer ->
|
|
|
|
requireComponents.core.topSiteStorage.getTopSites().observe(this, observer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 22:04:03 +02:00
|
|
|
private fun removeAllTabsWithUndo(listOfSessionsToDelete: Sequence<Session>, private: Boolean) {
|
2019-12-05 04:06:05 +01:00
|
|
|
homeFragmentStore.dispatch(HomeFragmentAction.TabsChange(emptyList()))
|
2020-02-22 06:04:43 +01:00
|
|
|
listOfSessionsToDelete.forEach {
|
|
|
|
requireComponents.core.pendingSessionDeletionManager.addSession(
|
|
|
|
it.id
|
|
|
|
)
|
|
|
|
}
|
2019-06-07 18:33:07 +02:00
|
|
|
|
|
|
|
val deleteOperation: (suspend () -> Unit) = {
|
2019-06-25 00:41:38 +02:00
|
|
|
listOfSessionsToDelete.forEach {
|
|
|
|
sessionManager.remove(it)
|
2020-02-22 06:04:43 +01:00
|
|
|
requireComponents.core.pendingSessionDeletionManager.removeSession(it.id)
|
2019-06-01 01:44:56 +02:00
|
|
|
}
|
2019-05-22 19:26:06 +02:00
|
|
|
}
|
2019-06-07 18:33:07 +02:00
|
|
|
deleteAllSessionsJob = deleteOperation
|
|
|
|
|
2019-08-20 22:10:08 +02:00
|
|
|
val snackbarMessage = if (private) {
|
2019-10-08 16:06:02 +02:00
|
|
|
getString(R.string.snackbar_private_tabs_closed)
|
2019-08-20 22:10:08 +02:00
|
|
|
} else {
|
2019-10-08 16:06:02 +02:00
|
|
|
getString(R.string.snackbar_tabs_closed)
|
2019-08-20 22:10:08 +02:00
|
|
|
}
|
|
|
|
|
2019-06-13 02:14:46 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.allowUndo(
|
2020-05-06 19:17:17 +02:00
|
|
|
requireView(),
|
2019-08-20 22:10:08 +02:00
|
|
|
snackbarMessage,
|
2019-05-22 19:26:06 +02:00
|
|
|
getString(R.string.snackbar_deleted_undo), {
|
2020-02-22 06:04:43 +01:00
|
|
|
listOfSessionsToDelete.forEach {
|
|
|
|
requireComponents.core.pendingSessionDeletionManager.removeSession(
|
|
|
|
it.id
|
|
|
|
)
|
|
|
|
}
|
2019-08-28 23:41:37 +02:00
|
|
|
if (private) {
|
|
|
|
requireComponents.analytics.metrics.track(Event.PrivateBrowsingSnackbarUndoTapped)
|
|
|
|
}
|
2019-05-22 19:26:06 +02:00
|
|
|
deleteAllSessionsJob = null
|
|
|
|
emitSessionChanges()
|
2019-06-07 18:33:07 +02:00
|
|
|
},
|
2019-09-25 01:24:19 +02:00
|
|
|
operation = deleteOperation,
|
2020-01-31 16:55:02 +01:00
|
|
|
anchorView = snackbarAnchorView
|
2019-06-07 18:33:07 +02:00
|
|
|
)
|
2019-05-22 19:26:06 +02:00
|
|
|
}
|
|
|
|
|
2019-10-08 16:06:02 +02:00
|
|
|
private fun removeTabWithUndo(sessionId: String, private: Boolean) {
|
2019-06-15 02:16:36 +02:00
|
|
|
val sessionManager = requireComponents.core.sessionManager
|
2020-02-22 06:04:43 +01:00
|
|
|
requireComponents.core.pendingSessionDeletionManager.addSession(sessionId)
|
2019-06-10 22:46:55 +02:00
|
|
|
val deleteOperation: (suspend () -> Unit) = {
|
|
|
|
sessionManager.findSessionById(sessionId)
|
|
|
|
?.let { session ->
|
2019-06-12 19:49:20 +02:00
|
|
|
pendingSessionDeletion = null
|
2019-06-10 22:46:55 +02:00
|
|
|
sessionManager.remove(session)
|
2020-02-22 06:04:43 +01:00
|
|
|
requireComponents.core.pendingSessionDeletionManager.removeSession(sessionId)
|
2019-06-10 22:46:55 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-07 18:33:07 +02:00
|
|
|
|
2019-06-12 19:49:20 +02:00
|
|
|
pendingSessionDeletion = PendingSessionDeletion(deleteOperation, sessionId)
|
2019-06-07 18:33:07 +02:00
|
|
|
|
2019-10-08 16:06:02 +02:00
|
|
|
val snackbarMessage = if (private) {
|
|
|
|
getString(R.string.snackbar_private_tab_closed)
|
|
|
|
} else {
|
|
|
|
getString(R.string.snackbar_tab_closed)
|
|
|
|
}
|
|
|
|
|
2019-06-13 02:14:46 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.allowUndo(
|
2020-05-06 19:17:17 +02:00
|
|
|
requireView(),
|
2019-10-08 16:06:02 +02:00
|
|
|
snackbarMessage,
|
2019-06-10 22:46:55 +02:00
|
|
|
getString(R.string.snackbar_deleted_undo), {
|
2020-02-22 06:04:43 +01:00
|
|
|
requireComponents.core.pendingSessionDeletionManager.removeSession(sessionId)
|
2019-06-12 19:49:20 +02:00
|
|
|
pendingSessionDeletion = null
|
2019-06-10 22:46:55 +02:00
|
|
|
emitSessionChanges()
|
|
|
|
},
|
2019-09-25 01:24:19 +02:00
|
|
|
operation = deleteOperation,
|
2020-01-31 16:55:02 +01:00
|
|
|
anchorView = snackbarAnchorView
|
2019-06-10 22:46:55 +02:00
|
|
|
)
|
2019-05-03 16:41:44 +02:00
|
|
|
|
2019-06-14 17:59:57 +02:00
|
|
|
// Update the UI with the tab removed, but don't remove it from storage yet
|
|
|
|
emitSessionChanges()
|
|
|
|
}
|
2019-06-07 18:33:07 +02:00
|
|
|
|
2019-06-14 17:59:57 +02:00
|
|
|
private fun emitSessionChanges() {
|
2020-05-21 18:29:04 +02:00
|
|
|
runIfFragmentIsAttached {
|
|
|
|
homeFragmentStore.dispatch(HomeFragmentAction.TabsChange(getListOfTabs()))
|
|
|
|
}
|
2019-02-18 22:18:55 +01:00
|
|
|
}
|
|
|
|
|
2019-06-14 17:59:57 +02:00
|
|
|
private fun getListOfSessions(): List<Session> {
|
2019-08-27 22:04:03 +02:00
|
|
|
return sessionManager.sessionsOfType(private = browsingModeManager.mode.isPrivate)
|
|
|
|
.filter { session: Session -> session.id != pendingSessionDeletion?.sessionId }
|
|
|
|
.toList()
|
2019-06-06 23:54:49 +02:00
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun getListOfTabs(): List<Tab> {
|
|
|
|
return getListOfSessions().toTabs()
|
2019-03-01 00:03:28 +01:00
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun registerCollectionStorageObserver() {
|
|
|
|
requireComponents.core.tabCollectionStorage.register(collectionStorageObserver, this)
|
2019-08-20 19:45:41 +02:00
|
|
|
}
|
|
|
|
|
2019-12-05 04:06:05 +01:00
|
|
|
private fun scrollToTheTop() {
|
2020-04-22 05:41:20 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.launch(Main) {
|
2019-12-05 04:06:05 +01:00
|
|
|
delay(ANIM_SCROLL_DELAY)
|
2020-02-04 05:40:51 +01:00
|
|
|
sessionControlView!!.view.smoothScrollToPosition(0)
|
2019-12-05 04:06:05 +01:00
|
|
|
}
|
2019-05-23 19:48:22 +02:00
|
|
|
}
|
|
|
|
|
2019-08-08 00:41:52 +02:00
|
|
|
private fun scrollAndAnimateCollection(
|
|
|
|
tabsAddedToCollectionSize: Int,
|
|
|
|
changedCollection: TabCollection? = null
|
|
|
|
) {
|
2019-07-09 01:42:17 +02:00
|
|
|
if (view != null) {
|
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
2020-02-04 05:40:51 +01:00
|
|
|
val recyclerView = sessionControlView!!.view
|
2019-07-09 01:42:17 +02:00
|
|
|
delay(ANIM_SCROLL_DELAY)
|
|
|
|
val tabsSize = getListOfSessions().size
|
|
|
|
|
|
|
|
var indexOfCollection = tabsSize + NON_TAB_ITEM_NUM
|
|
|
|
changedCollection?.let { changedCollection ->
|
|
|
|
requireComponents.core.tabCollectionStorage.cachedTabCollections
|
|
|
|
.filterIndexed { index, tabCollection ->
|
|
|
|
if (tabCollection.id == changedCollection.id) {
|
|
|
|
indexOfCollection = tabsSize + NON_TAB_ITEM_NUM + index
|
|
|
|
return@filterIndexed true
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2019-06-12 22:08:00 +02:00
|
|
|
}
|
2019-07-09 01:42:17 +02:00
|
|
|
val lastVisiblePosition =
|
2019-08-08 00:41:52 +02:00
|
|
|
(recyclerView.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition()
|
|
|
|
?: 0
|
2019-07-09 01:42:17 +02:00
|
|
|
if (lastVisiblePosition < indexOfCollection) {
|
|
|
|
val onScrollListener = object : RecyclerView.OnScrollListener() {
|
2019-08-08 00:41:52 +02:00
|
|
|
override fun onScrollStateChanged(
|
|
|
|
recyclerView: RecyclerView,
|
|
|
|
newState: Int
|
|
|
|
) {
|
2019-07-09 01:42:17 +02:00
|
|
|
super.onScrollStateChanged(recyclerView, newState)
|
|
|
|
if (newState == SCROLL_STATE_IDLE) {
|
|
|
|
animateCollection(tabsAddedToCollectionSize, indexOfCollection)
|
|
|
|
recyclerView.removeOnScrollListener(this)
|
|
|
|
}
|
2019-06-12 22:08:00 +02:00
|
|
|
}
|
|
|
|
}
|
2019-07-09 01:42:17 +02:00
|
|
|
recyclerView.addOnScrollListener(onScrollListener)
|
|
|
|
recyclerView.smoothScrollToPosition(indexOfCollection)
|
|
|
|
} else {
|
|
|
|
animateCollection(tabsAddedToCollectionSize, indexOfCollection)
|
2019-06-12 22:08:00 +02:00
|
|
|
}
|
|
|
|
}
|
2019-06-11 16:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-12 22:08:00 +02:00
|
|
|
private fun animateCollection(addedTabsSize: Int, indexOfCollection: Int) {
|
2019-06-13 02:14:46 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
2019-08-08 00:41:52 +02:00
|
|
|
val viewHolder =
|
2020-02-04 05:40:51 +01:00
|
|
|
sessionControlView!!.view.findViewHolderForAdapterPosition(indexOfCollection)
|
2019-08-08 00:41:52 +02:00
|
|
|
val border =
|
2020-05-12 23:05:08 +02:00
|
|
|
(viewHolder as? CollectionViewHolder)?.itemView?.findViewById<View>(R.id.selected_border)
|
2019-06-11 16:01:00 +02:00
|
|
|
val listener = object : Animator.AnimatorListener {
|
2019-06-12 22:08:00 +02:00
|
|
|
override fun onAnimationCancel(animation: Animator?) {
|
|
|
|
border?.visibility = View.GONE
|
|
|
|
}
|
|
|
|
|
2020-02-22 06:04:43 +01:00
|
|
|
override fun onAnimationStart(animation: Animator?) { /* noop */
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onAnimationRepeat(animation: Animator?) { /* noop */
|
|
|
|
}
|
|
|
|
|
2019-06-11 16:01:00 +02:00
|
|
|
override fun onAnimationEnd(animation: Animator?) {
|
2019-06-12 22:08:00 +02:00
|
|
|
border?.animate()?.alpha(0.0F)?.setStartDelay(ANIM_ON_SCREEN_DELAY)
|
|
|
|
?.setDuration(FADE_ANIM_DURATION)
|
2019-06-11 16:01:00 +02:00
|
|
|
?.start()
|
|
|
|
}
|
|
|
|
}
|
2019-08-08 00:41:52 +02:00
|
|
|
border?.animate()?.alpha(1.0F)?.setStartDelay(ANIM_ON_SCREEN_DELAY)
|
|
|
|
?.setDuration(FADE_ANIM_DURATION)
|
2019-06-12 22:08:00 +02:00
|
|
|
?.setListener(listener)?.start()
|
|
|
|
}.invokeOnCompletion {
|
|
|
|
showSavedSnackbar(addedTabsSize)
|
2019-06-11 16:01:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-07 16:45:38 +02:00
|
|
|
private fun showSavedSnackbar(tabSize: Int) {
|
2019-06-13 02:14:46 +02:00
|
|
|
viewLifecycleOwner.lifecycleScope.launch {
|
2019-06-12 22:08:00 +02:00
|
|
|
delay(ANIM_SNACKBAR_DELAY)
|
2019-06-13 02:14:46 +02:00
|
|
|
view?.let { view ->
|
|
|
|
@StringRes
|
|
|
|
val stringRes = if (tabSize > 1) {
|
|
|
|
R.string.create_collection_tabs_saved
|
|
|
|
} else {
|
|
|
|
R.string.create_collection_tab_saved
|
2019-06-12 22:08:00 +02:00
|
|
|
}
|
2020-04-02 21:30:13 +02:00
|
|
|
FenixSnackbar.make(view = view,
|
|
|
|
duration = Snackbar.LENGTH_LONG,
|
2020-04-13 21:16:01 +02:00
|
|
|
isDisplayedWithBrowserToolbar = false
|
2020-04-02 21:30:13 +02:00
|
|
|
)
|
2019-09-25 01:24:19 +02:00
|
|
|
.setText(view.context.getString(stringRes))
|
2020-02-10 13:45:41 +01:00
|
|
|
.setAnchorView(snackbarAnchorView)
|
2019-09-25 01:24:19 +02:00
|
|
|
.show()
|
2019-06-07 16:45:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun showRenamedSnackbar() {
|
2019-06-13 02:14:46 +02:00
|
|
|
view?.let { view ->
|
|
|
|
val string = view.context.getString(R.string.snackbar_collection_renamed)
|
2020-04-02 21:30:13 +02:00
|
|
|
FenixSnackbar.make(
|
|
|
|
view = view,
|
|
|
|
duration = Snackbar.LENGTH_LONG,
|
2020-04-13 21:16:01 +02:00
|
|
|
isDisplayedWithBrowserToolbar = false
|
2020-04-02 21:30:13 +02:00
|
|
|
)
|
2019-09-25 01:24:19 +02:00
|
|
|
.setText(string)
|
2020-02-10 13:45:41 +01:00
|
|
|
.setAnchorView(snackbarAnchorView)
|
2019-09-25 01:24:19 +02:00
|
|
|
.show()
|
2019-06-07 16:45:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 17:59:57 +02:00
|
|
|
private fun List<Session>.toTabs(): List<Tab> {
|
|
|
|
val selected = sessionManager.selectedSession
|
2019-09-07 01:11:40 +02:00
|
|
|
|
2020-03-26 19:20:59 +01:00
|
|
|
return map {
|
|
|
|
it.toTab(requireContext(), it == selected)
|
2019-09-07 01:11:40 +02:00
|
|
|
}
|
2019-06-14 17:59:57 +02:00
|
|
|
}
|
|
|
|
|
2020-02-04 05:40:51 +01:00
|
|
|
private fun calculateNewOffset() {
|
2020-02-05 15:28:22 +01:00
|
|
|
homeAppBarOffset = ((homeAppBar.layoutParams as CoordinatorLayout.LayoutParams)
|
2020-02-04 07:15:18 +01:00
|
|
|
.behavior as AppBarLayout.Behavior).topAndBottomOffset
|
2020-02-04 05:40:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun setOffset(currentView: View) {
|
|
|
|
if (homeAppBarOffset <= 0) {
|
|
|
|
(currentView.homeAppBar.layoutParams as CoordinatorLayout.LayoutParams)
|
|
|
|
.behavior = AppBarLayout.Behavior().apply {
|
|
|
|
topAndBottomOffset = this@HomeFragment.homeAppBarOffset
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
currentView.homeAppBar.setExpanded(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 07:15:18 +01:00
|
|
|
private fun createNewAppBarListener(margin: Float) {
|
|
|
|
homeAppBarOffSetListener =
|
|
|
|
AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
|
|
|
|
val reduceScrollRanged = appBarLayout.totalScrollRange.toFloat() - margin
|
|
|
|
appBarLayout.alpha = 1.0f - abs(verticalOffset / reduceScrollRanged)
|
|
|
|
}
|
2020-02-04 07:13:20 +01:00
|
|
|
}
|
|
|
|
|
2020-03-06 09:26:40 +01:00
|
|
|
private fun scrollToSelectedTab() {
|
|
|
|
val position = (sessionControlView!!.view.adapter as SessionControlAdapter)
|
|
|
|
.currentList.indexOfFirst {
|
|
|
|
it is AdapterItem.TabItem && it.tab.selected == true
|
|
|
|
}
|
|
|
|
if (position > 0) {
|
|
|
|
(sessionControlView!!.view.layoutManager as LinearLayoutManager)
|
|
|
|
.scrollToPositionWithOffset(position, SELECTED_TAB_OFFSET)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-30 17:36:14 +01:00
|
|
|
companion object {
|
2020-02-27 22:29:47 +01:00
|
|
|
private const val ANIMATION_DELAY = 100L
|
|
|
|
|
2019-06-11 16:01:00 +02:00
|
|
|
private const val NON_TAB_ITEM_NUM = 3
|
|
|
|
private const val ANIM_SCROLL_DELAY = 100L
|
|
|
|
private const val ANIM_ON_SCREEN_DELAY = 200L
|
|
|
|
private const val FADE_ANIM_DURATION = 150L
|
2019-06-12 22:08:00 +02:00
|
|
|
private const val ANIM_SNACKBAR_DELAY = 100L
|
2019-09-11 19:52:33 +02:00
|
|
|
private const val CFR_WIDTH_DIVIDER = 1.7
|
|
|
|
private const val CFR_Y_OFFSET = -20
|
2020-03-06 09:26:40 +01:00
|
|
|
private const val SELECTED_TAB_OFFSET = 20
|
2020-01-22 18:06:26 +01:00
|
|
|
|
|
|
|
// Layout
|
|
|
|
private const val HEADER_MARGIN = 60
|
2019-01-30 17:36:14 +01:00
|
|
|
}
|
2019-01-09 23:22:58 +01:00
|
|
|
}
|
2019-05-25 01:12:50 +02:00
|
|
|
|
|
|
|
/**
|
2019-06-14 17:59:57 +02:00
|
|
|
* Wrapper around sessions manager to observe changes in sessions.
|
2019-05-25 01:12:50 +02:00
|
|
|
* Similar to [mozilla.components.browser.session.utils.AllSessionsObserver] but ignores CustomTab sessions.
|
|
|
|
*
|
|
|
|
* Call [onStart] to start receiving updates into [onChanged] callback.
|
|
|
|
* Call [onStop] to stop receiving updates.
|
|
|
|
*
|
|
|
|
* @param manager [SessionManager] instance to subscribe to.
|
|
|
|
* @param observer [Session.Observer] instance that will recieve updates.
|
|
|
|
* @param onChanged callback that will be called when any of [SessionManager.Observer]'s events are fired.
|
|
|
|
*/
|
|
|
|
private class BrowserSessionsObserver(
|
|
|
|
private val manager: SessionManager,
|
2020-03-26 19:20:59 +01:00
|
|
|
private val store: BrowserStore,
|
2019-05-25 01:12:50 +02:00
|
|
|
private val observer: Session.Observer,
|
|
|
|
private val onChanged: () -> Unit
|
2019-07-23 23:15:46 +02:00
|
|
|
) : LifecycleObserver {
|
2020-03-26 19:20:59 +01:00
|
|
|
private var scope: CoroutineScope? = null
|
2019-05-25 01:12:50 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Start observing
|
|
|
|
*/
|
2019-07-23 23:15:46 +02:00
|
|
|
@OnLifecycleEvent(Lifecycle.Event.ON_START)
|
2019-05-25 01:12:50 +02:00
|
|
|
fun onStart() {
|
|
|
|
manager.register(managerObserver)
|
|
|
|
subscribeToAll()
|
2020-03-26 19:20:59 +01:00
|
|
|
|
|
|
|
scope = store.flowScoped { flow ->
|
|
|
|
flow.ifChanged { it.media.aggregate }
|
|
|
|
.collect { onChanged() }
|
|
|
|
}
|
2019-05-25 01:12:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Stop observing (will not receive updates till next [onStop] call)
|
|
|
|
*/
|
2019-07-23 23:15:46 +02:00
|
|
|
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
|
2019-05-25 01:12:50 +02:00
|
|
|
fun onStop() {
|
2020-03-26 19:20:59 +01:00
|
|
|
scope?.cancel()
|
2019-05-25 01:12:50 +02:00
|
|
|
manager.unregister(managerObserver)
|
|
|
|
unsubscribeFromAll()
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun subscribeToAll() {
|
|
|
|
manager.sessions.forEach(::subscribeTo)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun unsubscribeFromAll() {
|
|
|
|
manager.sessions.forEach(::unsubscribeFrom)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun subscribeTo(session: Session) {
|
2019-07-23 23:15:46 +02:00
|
|
|
session.register(observer)
|
2019-05-25 01:12:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun unsubscribeFrom(session: Session) {
|
2019-07-23 23:15:46 +02:00
|
|
|
session.unregister(observer)
|
2019-05-25 01:12:50 +02:00
|
|
|
}
|
|
|
|
|
2020-03-26 19:20:59 +01:00
|
|
|
private val managerObserver = object : SessionManager.Observer {
|
2019-05-25 01:12:50 +02:00
|
|
|
override fun onSessionAdded(session: Session) {
|
|
|
|
subscribeTo(session)
|
|
|
|
onChanged()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSessionsRestored() {
|
|
|
|
subscribeToAll()
|
|
|
|
onChanged()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onAllSessionsRemoved() {
|
|
|
|
unsubscribeFromAll()
|
|
|
|
onChanged()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSessionRemoved(session: Session) {
|
|
|
|
unsubscribeFrom(session)
|
|
|
|
onChanged()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onSessionSelected(session: Session) {
|
|
|
|
onChanged()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|