GitLab is used only for code review, issue tracking and project management. Canonical locations for source code are still https://gitweb.torproject.org/ https://git.torproject.org/ and git-rw.torproject.org.

SessionControlView.kt 5.97 KB
Newer Older
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2 3
 * 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/. */
4 5 6

package org.mozilla.fenix.home.sessioncontrol

7
import android.view.View
8
import androidx.lifecycle.LifecycleOwner
9
import androidx.recyclerview.widget.ItemTouchHelper
10 11
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
12 13
import kotlinx.android.extensions.LayoutContainer
import mozilla.components.feature.tab.collections.TabCollection
14
import mozilla.components.feature.top.sites.TopSite
15
import org.mozilla.fenix.R
16
import org.mozilla.fenix.components.tips.Tip
17
import org.mozilla.fenix.ext.components
18
import org.mozilla.fenix.home.HomeFragmentState
19
import org.mozilla.fenix.home.HomeScreenViewModel
20 21
import org.mozilla.fenix.home.Mode
import org.mozilla.fenix.home.OnboardingState
22

23 24
// This method got a little complex with the addition of the tab tray feature flag
// When we remove the tabs from the home screen this will get much simpler again.
25
@Suppress("ComplexMethod")
26
private fun normalModeAdapterItems(
27
    topSites: List<TopSite>,
28
    collections: List<TabCollection>,
29
    expandedCollections: Set<Long>,
30 31
    tip: Tip?,
    showCollectionsPlaceholder: Boolean
32
): List<AdapterItem> {
33
    val items = mutableListOf<AdapterItem>()
34

35 36
    tip?.let { items.add(AdapterItem.TipItem(it)) }

37
    if (topSites.isNotEmpty()) {
38
        items.add(AdapterItem.TopSitePager(topSites))
39 40
    }

41
    if (collections.isEmpty()) {
42 43 44
        if (showCollectionsPlaceholder) {
            items.add(AdapterItem.NoCollectionsMessage)
        }
45 46
    } else {
        showCollections(collections, expandedCollections, items)
47 48
    }

49 50 51
    return items
}

52 53 54 55 56 57 58 59
private fun showCollections(
    collections: List<TabCollection>,
    expandedCollections: Set<Long>,
    items: MutableList<AdapterItem>
) {
    // If the collection is expanded, we want to add all of its tabs beneath it in the adapter
    items.add(AdapterItem.CollectionHeader)
    collections.map {
60
        AdapterItem.CollectionItem(it, expandedCollections.contains(it.id))
61 62 63 64 65 66 67 68
    }.forEach {
        items.add(it)
        if (it.expanded) {
            items.addAll(collectionTabItems(it.collection))
        }
    }
}

69
private fun privateModeAdapterItems() = listOf(AdapterItem.PrivateBrowsingDescription)
70

71 72
private fun bootstrapAdapterItems() = listOf(AdapterItem.TorBootstrap)

73 74 75 76
private fun onboardingAdapterItems(onboardingState: OnboardingState): List<AdapterItem> {
    val items: MutableList<AdapterItem> = mutableListOf(AdapterItem.OnboardingHeader)

    // Customize FxA items based on where we are with the account state:
77 78 79 80 81 82 83 84 85 86 87 88 89
    items.addAll(
        when (onboardingState) {
            OnboardingState.SignedOutNoAutoSignIn -> {
                listOf(
                    AdapterItem.OnboardingManualSignIn
                )
            }
            is OnboardingState.SignedOutCanAutoSignIn -> {
                listOf(
                    AdapterItem.OnboardingAutomaticSignIn(onboardingState)
                )
            }
            OnboardingState.SignedIn -> listOf()
90
        }
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    )

    items.addAll(
        listOf(
            AdapterItem.OnboardingSectionHeader {
                val appName = it.getString(R.string.app_name)
                it.getString(R.string.onboarding_feature_section_header, appName)
            },
            AdapterItem.OnboardingTrackingProtection,
            AdapterItem.OnboardingThemePicker,
            AdapterItem.OnboardingPrivateBrowsing,
            AdapterItem.OnboardingToolbarPositionPicker,
            AdapterItem.OnboardingPrivacyNotice,
            AdapterItem.OnboardingFinish
        )
    )
107 108 109

    return items
}
110

111 112 113 114 115 116 117
private fun torOnboardingAdapterItems() =
    listOf(
        AdapterItem.TorOnboardingSecurityLevel,
        AdapterItem.TorOnboardingDonate,
        AdapterItem.OnboardingFinish
    )

118
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
119 120 121 122 123 124 125
    is Mode.Normal -> normalModeAdapterItems(
        topSites,
        collections,
        expandedCollections,
        tip,
        showCollectionPlaceholder
    )
126
    is Mode.Private -> privateModeAdapterItems()
127
    is Mode.Onboarding -> torOnboardingAdapterItems()
128
    is Mode.Bootstrap -> bootstrapAdapterItems()
129 130
}

131 132
private fun collectionTabItems(collection: TabCollection) =
    collection.tabs.mapIndexed { index, tab ->
133
        AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
134
    }
135

136
class SessionControlView(
137 138
    override val containerView: View,
    viewLifecycleOwner: LifecycleOwner,
139
    interactor: SessionControlInteractor,
140
    private var homeScreenViewModel: HomeScreenViewModel
141 142
) : LayoutContainer {

143
    val view: RecyclerView = containerView as RecyclerView
144

145 146 147 148 149
    private val sessionControlAdapter = SessionControlAdapter(
        interactor,
        viewLifecycleOwner,
        containerView.context.components
    )
150 151 152 153

    init {
        view.apply {
            adapter = sessionControlAdapter
154
            layoutManager = LinearLayoutManager(containerView.context)
155 156 157
            val itemTouchHelper =
                ItemTouchHelper(
                    SwipeToDeleteCallback(
158
                        interactor
159 160 161
                    )
                )
            itemTouchHelper.attachToRecyclerView(this)
162 163 164
        }
    }

165
    fun update(state: HomeFragmentState) {
166

167
        val stateAdapterList = state.toAdapterList()
168 169 170 171
        if (homeScreenViewModel.shouldScrollToTopSites) {
            sessionControlAdapter.submitList(stateAdapterList) {

                val loadedTopSites = stateAdapterList.find { adapterItem ->
172
                    adapterItem is AdapterItem.TopSitePager && adapterItem.topSites.isNotEmpty()
173 174 175
                }
                loadedTopSites?.run {
                    homeScreenViewModel.shouldScrollToTopSites = false
176
                    view.scrollToPosition(0)
177 178 179 180 181
                }
            }
        } else {
            sessionControlAdapter.submitList(stateAdapterList)
        }
182 183
    }
}