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

7
import android.content.Context
8
9
10
11
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
12
import androidx.appcompat.app.AppCompatDialogFragment
Tiger Oakes's avatar
Tiger Oakes committed
13
14
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory
Mihai Branescu's avatar
Mihai Branescu committed
15
import androidx.lifecycle.lifecycleScope
Tiger Oakes's avatar
Tiger Oakes committed
16
import androidx.lifecycle.observe
17
import androidx.navigation.fragment.findNavController
18
import androidx.navigation.fragment.navArgs
Mihai Branescu's avatar
Mihai Branescu committed
19
20
21
22
23
import kotlinx.android.synthetic.main.fragment_share.view.appsShareLayout
import kotlinx.android.synthetic.main.fragment_share.view.closeSharingContent
import kotlinx.android.synthetic.main.fragment_share.view.closeSharingScrim
import kotlinx.android.synthetic.main.fragment_share.view.devicesShareLayout
import kotlinx.android.synthetic.main.fragment_share.view.shareWrapper
24
25
26
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.selector.findTabOrCustomTab
import mozilla.components.concept.engine.prompt.PromptRequest
27
import mozilla.components.feature.accounts.push.SendTabUseCases
Mihai Branescu's avatar
Mihai Branescu committed
28
import mozilla.components.feature.share.RecentAppsStorage
29
import org.mozilla.fenix.R
30
import org.mozilla.fenix.components.FenixSnackbar
31
import org.mozilla.fenix.ext.getRootView
32
import org.mozilla.fenix.ext.requireComponents
33

34
class ShareFragment : AppCompatDialogFragment() {
Tiger Oakes's avatar
Tiger Oakes committed
35

36
    private val args by navArgs<ShareFragmentArgs>()
Tiger Oakes's avatar
Tiger Oakes committed
37
38
39
    private val viewModel: ShareViewModel by viewModels {
        AndroidViewModelFactory(requireActivity().application)
    }
40
41
42
43
    private lateinit var shareInteractor: ShareInteractor
    private lateinit var shareCloseView: ShareCloseView
    private lateinit var shareToAccountDevicesView: ShareToAccountDevicesView
    private lateinit var shareToAppsView: ShareToAppsView
44

45
46
    override fun onAttach(context: Context) {
        super.onAttach(context)
Tiger Oakes's avatar
Tiger Oakes committed
47
        viewModel.loadDevicesAndApps()
48
49
    }

50
51
52
53
54
    override fun dismiss() {
        consumePrompt { onDismiss() }
        super.dismiss()
    }

55
56
57
58
59
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setStyle(STYLE_NO_TITLE, R.style.ShareDialogStyle)
    }

60
61
62
63
64
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
65
        val view = inflater.inflate(R.layout.fragment_share, container, false)
66
        val shareData = args.data.toList()
67

68
        val accountManager = requireComponents.backgroundServices.accountManager
69

70
71
        shareInteractor = ShareInteractor(
            DefaultShareController(
72
                context = requireContext(),
73
                shareData = shareData,
74
                snackbar = FenixSnackbar.makeWithToolbarPadding(requireActivity().getRootView()!!),
75
                navController = findNavController(),
Mihai Branescu's avatar
Mihai Branescu committed
76
77
78
                sendTabUseCases = SendTabUseCases(accountManager),
                recentAppsStorage = RecentAppsStorage(requireContext()),
                lifecycleScope = lifecycleScope
79
80
81
82
83
84
85
86
87
88
            ) { result ->
                consumePrompt {
                    when (result) {
                        ShareController.Result.DISMISSED -> onDismiss()
                        ShareController.Result.SHARE_ERROR -> onFailure()
                        ShareController.Result.SUCCESS -> onSuccess()
                    }
                }
                super.dismiss()
            }
89
        )
90

91
        view.shareWrapper.setOnClickListener { shareInteractor.onShareClosed() }
92
93
        shareToAccountDevicesView =
            ShareToAccountDevicesView(view.devicesShareLayout, shareInteractor)
94

95
96
97
        if (args.showPage) {
            // Show the previous fragment underneath the share background scrim
            // by making it translucent.
98
99
100
101
102
103
            view.closeSharingScrim.alpha = SHOW_PAGE_ALPHA
            view.shareWrapper.setOnClickListener { shareInteractor.onShareClosed() }
        } else {
            // Otherwise, show a list of tabs to share.
            view.closeSharingScrim.alpha = 1.0f
            shareCloseView = ShareCloseView(view.closeSharingContent, shareInteractor)
104
            shareCloseView.setTabs(shareData)
105
        }
106
        shareToAppsView = ShareToAppsView(view.appsShareLayout, shareInteractor)
107

108
        return view
109
    }
110
111
112

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
Tiger Oakes's avatar
Tiger Oakes committed
113
        viewModel.devicesList.observe(viewLifecycleOwner) { devicesShareOptions ->
114
            shareToAccountDevicesView.setShareTargets(devicesShareOptions)
115
        }
Tiger Oakes's avatar
Tiger Oakes committed
116
117
        viewModel.appsList.observe(viewLifecycleOwner) { appsToShareTo ->
            shareToAppsView.setShareTargets(appsToShareTo)
118
        }
Mihai Branescu's avatar
Mihai Branescu committed
119
120
121
        viewModel.recentAppsList.observe(viewLifecycleOwner) { appsToShareTo ->
            shareToAppsView.setRecentShareTargets(appsToShareTo)
        }
122
    }
123

124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
    /**
     * If [ShareFragmentArgs.sessionId] is set and the session has a pending Web Share
     * prompt request, call [consume] then clean up the prompt.
     */
    private fun consumePrompt(
        consume: PromptRequest.Share.() -> Unit
    ) {
        val browserStore = requireComponents.core.store
        args.sessionId
            ?.let { sessionId -> browserStore.state.findTabOrCustomTab(sessionId) }
            ?.let { tab ->
                val promptRequest = tab.content.promptRequest
                if (promptRequest is PromptRequest.Share) {
                    consume(promptRequest)
                    browserStore.dispatch(ContentAction.ConsumePromptRequestAction(tab.id))
                }
            }
    }

143
144
145
    companion object {
        const val SHOW_PAGE_ALPHA = 0.6f
    }
146
}