InstalledAddonDetailsFragment.kt 13.2 KB
Newer Older
Gabriel Luong's avatar
Gabriel Luong committed
1
2
3
4
5
6
7
8
9
10
/* 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.addons

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
11
import androidx.core.view.isVisible
Gabriel Luong's avatar
Gabriel Luong committed
12
import androidx.fragment.app.Fragment
13
import androidx.lifecycle.lifecycleScope
Gabriel Luong's avatar
Gabriel Luong committed
14
15
import androidx.navigation.Navigation
import androidx.navigation.findNavController
16
import androidx.navigation.fragment.findNavController
17
import com.google.android.material.switchmaterial.SwitchMaterial
Gabriel Luong's avatar
Gabriel Luong committed
18
import kotlinx.android.synthetic.main.fragment_installed_add_on_details.view.*
19
20
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Gabriel Luong's avatar
Gabriel Luong committed
21
import mozilla.components.feature.addons.Addon
22
import mozilla.components.feature.addons.AddonManagerException
23
import mozilla.components.feature.addons.ui.translateName
Alex Catarineu's avatar
Alex Catarineu committed
24
import mozilla.components.support.webextensions.WebExtensionSupport.installedExtensions
25
import org.mozilla.fenix.GleanMetrics.Addons
26
import org.mozilla.fenix.HomeActivity
Gabriel Luong's avatar
Gabriel Luong committed
27
28
29
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.showToolbar
30
import org.mozilla.fenix.ext.runIfFragmentIsAttached
Gabriel Luong's avatar
Gabriel Luong committed
31
32
33
34

/**
 * An activity to show the details of a installed add-on.
 */
35
@Suppress("LargeClass", "TooManyFunctions")
Gabriel Luong's avatar
Gabriel Luong committed
36
37
38
39
40
41
42
43
44
45
46
47
class InstalledAddonDetailsFragment : Fragment() {
    private lateinit var addon: Addon

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        if (!::addon.isInitialized) {
            addon = AddonDetailsFragmentArgs.fromBundle(requireNotNull(arguments)).addon
        }

48
49
50
        return inflater.inflate(R.layout.fragment_installed_add_on_details, container, false).also {
            bindUI(it)
        }
51
52
53
54
55
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        bindAddon(view)
Gabriel Luong's avatar
Gabriel Luong committed
56
57
    }

58
59
60
61
62
63
64
65
66
67
68
69
70
71
    private fun bindAddon(view: View) {
        lifecycleScope.launch(Dispatchers.IO) {
            try {
                val addons = requireContext().components.addonManager.getAddons()
                lifecycleScope.launch(Dispatchers.Main) {
                    runIfFragmentIsAttached {
                        addons.find { addon.id == it.id }.let {
                            if (it == null) {
                                throw AddonManagerException(Exception("Addon ${addon.id} not found"))
                            } else {
                                addon = it
                                bindUI(view)
                            }
                            view.add_on_progress_bar.isVisible = false
72
                            view.addon_container.isVisible = true
73
74
75
                        }
                    }
                }
76
77
78
79
80
81
82
            } catch (e: AddonManagerException) {
                lifecycleScope.launch(Dispatchers.Main) {
                    runIfFragmentIsAttached {
                        showSnackBar(
                            view,
                            getString(R.string.mozac_feature_addons_failed_to_query_add_ons)
                        )
83
                        findNavController().popBackStack()
84
85
86
                    }
                }
            }
87
88
89
        }
    }

90
    private fun bindUI(view: View) {
91
        val title = addon.translateName(view.context)
Gabriel Luong's avatar
Gabriel Luong committed
92
93
94
95
96
97
        showToolbar(title)

        bindEnableSwitch(view)
        bindSettings(view)
        bindDetails(view)
        bindPermissions(view)
98
        bindAllowInPrivateBrowsingSwitch(view)
Gabriel Luong's avatar
Gabriel Luong committed
99
100
101
        bindRemoveButton(view)
    }

102
    @SuppressWarnings("LongMethod")
Gabriel Luong's avatar
Gabriel Luong committed
103
104
    private fun bindEnableSwitch(view: View) {
        val switch = view.enable_switch
105
        val privateBrowsingSwitch = view.allow_in_private_browsing_switch
Gabriel Luong's avatar
Gabriel Luong committed
106
        switch.setState(addon.isEnabled())
Tiger Oakes's avatar
Tiger Oakes committed
107
108
        switch.setOnCheckedChangeListener { v, isChecked ->
            val addonManager = v.context.components.addonManager
109
            switch.isClickable = false
110
            view.remove_add_on.isEnabled = false
Gabriel Luong's avatar
Gabriel Luong committed
111
            if (isChecked) {
Tiger Oakes's avatar
Tiger Oakes committed
112
                addonManager.enableAddon(
Gabriel Luong's avatar
Gabriel Luong committed
113
114
                    addon,
                    onSuccess = {
115
                        runIfFragmentIsAttached {
116
                            this.addon = it
117
                            switch.isClickable = true
Alex Catarineu's avatar
Alex Catarineu committed
118
                            privateBrowsingSwitch.isVisible = false
119
                            privateBrowsingSwitch.isChecked = it.isAllowedInPrivateBrowsing()
120
                            switch.setText(R.string.mozac_feature_addons_enabled)
121
                            view.settings.isVisible = shouldSettingsBeVisible()
122
                            view.remove_add_on.isEnabled = true
123
124
125
126
127
128
129
                            context?.let {
                                showSnackBar(
                                    view,
                                    getString(
                                        R.string.mozac_feature_addons_successfully_enabled,
                                        addon.translateName(it)
                                    )
130
                                )
131
                            }
132
                            Addons.hasEnabledAddons.set(true)
133
                        }
Gabriel Luong's avatar
Gabriel Luong committed
134
135
                    },
                    onError = {
136
                        runIfFragmentIsAttached {
137
                            switch.isClickable = true
138
                            view.remove_add_on.isEnabled = true
139
                            switch.setState(addon.isEnabled())
140
141
142
143
144
145
146
                            context?.let {
                                showSnackBar(
                                    view,
                                    getString(
                                        R.string.mozac_feature_addons_failed_to_enable,
                                        addon.translateName(it)
                                    )
147
                                )
148
                            }
149
                        }
Gabriel Luong's avatar
Gabriel Luong committed
150
151
152
                    }
                )
            } else {
153
                view.settings.isVisible = false
Tiger Oakes's avatar
Tiger Oakes committed
154
                addonManager.disableAddon(
Gabriel Luong's avatar
Gabriel Luong committed
155
156
                    addon,
                    onSuccess = {
157
                        runIfFragmentIsAttached {
158
                            this.addon = it
159
                            switch.isClickable = true
160
                            privateBrowsingSwitch.isVisible = it.isEnabled()
161
                            switch.setText(R.string.mozac_feature_addons_disabled)
162
                            view.remove_add_on.isEnabled = true
163
164
165
166
167
168
169
                            context?.let {
                                showSnackBar(
                                    view,
                                    getString(
                                        R.string.mozac_feature_addons_successfully_disabled,
                                        addon.translateName(it)
                                    )
170
                                )
171
                            }
172
                        }
Gabriel Luong's avatar
Gabriel Luong committed
173
174
                    },
                    onError = {
175
                        runIfFragmentIsAttached {
176
                            switch.isClickable = true
177
                            privateBrowsingSwitch.isClickable = true
178
                            view.remove_add_on.isEnabled = true
179
                            switch.setState(addon.isEnabled())
180
181
182
183
184
185
186
                            context?.let {
                                showSnackBar(
                                    view,
                                    getString(
                                        R.string.mozac_feature_addons_failed_to_disable,
                                        addon.translateName(it)
                                    )
187
                                )
188
                            }
189
                        }
Gabriel Luong's avatar
Gabriel Luong committed
190
191
192
193
194
195
196
197
                    }
                )
            }
        }
    }

    private fun bindSettings(view: View) {
        view.settings.apply {
198
            isVisible = shouldSettingsBeVisible()
Gabriel Luong's avatar
Gabriel Luong committed
199
            setOnClickListener {
200
                val settingUrl = addon.installedState?.optionsPageUrl ?: return@setOnClickListener
201
202
203
                val directions = if (addon.installedState?.openOptionsPageInTab == true) {
                    val components = it.context.components
                    val shouldCreatePrivateSession =
204
                        (activity as HomeActivity).browsingModeManager.mode.isPrivate
205
206

                    if (shouldCreatePrivateSession) {
207
                        components.useCases.tabsUseCases.addPrivateTab(settingUrl)
208
                    } else {
209
                        components.useCases.tabsUseCases.addTab(settingUrl)
210
211
                    }

212
                    InstalledAddonDetailsFragmentDirections.actionGlobalBrowser(null)
213
214
215
216
                } else {
                    InstalledAddonDetailsFragmentDirections
                        .actionInstalledAddonFragmentToAddonInternalSettingsFragment(addon)
                }
Gabriel Luong's avatar
Gabriel Luong committed
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
                Navigation.findNavController(this).navigate(directions)
            }
        }
    }

    private fun bindDetails(view: View) {
        view.details.setOnClickListener {
            val directions =
                InstalledAddonDetailsFragmentDirections.actionInstalledAddonFragmentToAddonDetailsFragment(
                    addon
                )
            Navigation.findNavController(view).navigate(directions)
        }
    }

    private fun bindPermissions(view: View) {
        view.permissions.setOnClickListener {
            val directions =
                InstalledAddonDetailsFragmentDirections.actionInstalledAddonFragmentToAddonPermissionsDetailsFragment(
                    addon
                )
            Navigation.findNavController(view).navigate(directions)
        }
    }

242
243
244
    private fun bindAllowInPrivateBrowsingSwitch(view: View) {
        val switch = view.allow_in_private_browsing_switch
        switch.isChecked = addon.isAllowedInPrivateBrowsing()
Alex Catarineu's avatar
Alex Catarineu committed
245
        switch.isVisible = false
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
        switch.setOnCheckedChangeListener { v, isChecked ->
            val addonManager = v.context.components.addonManager
            switch.isClickable = false
            view.remove_add_on.isEnabled = false
            addonManager.setAddonAllowedInPrivateBrowsing(
                addon,
                isChecked,
                onSuccess = {
                    runIfFragmentIsAttached {
                        this.addon = it
                        switch.isClickable = true
                        view.remove_add_on.isEnabled = true
                    }
                },
                onError = {
                    runIfFragmentIsAttached {
262
                        switch.isChecked = addon.isAllowedInPrivateBrowsing()
263
264
265
266
267
268
269
                        switch.isClickable = true
                        view.remove_add_on.isEnabled = true
                    }
                }
            )
        }
    }
Gabriel Luong's avatar
Gabriel Luong committed
270
    private fun bindRemoveButton(view: View) {
Alex Catarineu's avatar
Alex Catarineu committed
271
272
        val isBuiltin = installedExtensions[addon.id]?.isBuiltIn() ?: false
        view.remove_add_on.isVisible = !isBuiltin
Gabriel Luong's avatar
Gabriel Luong committed
273
        view.remove_add_on.setOnClickListener {
274
            setAllInteractiveViewsClickable(view, false)
Gabriel Luong's avatar
Gabriel Luong committed
275
276
277
            requireContext().components.addonManager.uninstallAddon(
                addon,
                onSuccess = {
278
                    runIfFragmentIsAttached {
279
                        setAllInteractiveViewsClickable(view, true)
280
281
282
283
284
285
286
                        context?.let {
                            showSnackBar(
                                view,
                                getString(
                                    R.string.mozac_feature_addons_successfully_uninstalled,
                                    addon.translateName(it)
                                )
287
                            )
288
                        }
289
290
                        view.findNavController().popBackStack()
                    }
Gabriel Luong's avatar
Gabriel Luong committed
291
292
                },
                onError = { _, _ ->
293
                    runIfFragmentIsAttached {
294
                        setAllInteractiveViewsClickable(view, true)
295
296
297
298
299
300
301
                        context?.let {
                            showSnackBar(
                                view,
                                getString(
                                    R.string.mozac_feature_addons_failed_to_uninstall,
                                    addon.translateName(it)
                                )
302
                            )
303
                        }
304
                    }
Gabriel Luong's avatar
Gabriel Luong committed
305
306
307
308
309
                }
            )
        }
    }

310
311
    private fun setAllInteractiveViewsClickable(view: View, clickable: Boolean) {
        view.enable_switch.isClickable = clickable
312
        view.settings.isClickable = clickable
313
314
315
316
317
        view.details.isClickable = clickable
        view.permissions.isClickable = clickable
        view.remove_add_on.isClickable = clickable
    }

318
    private fun SwitchMaterial.setState(checked: Boolean) {
Gabriel Luong's avatar
Gabriel Luong committed
319
        val text = if (checked) {
320
            R.string.mozac_feature_addons_enabled
Gabriel Luong's avatar
Gabriel Luong committed
321
        } else {
322
            R.string.mozac_feature_addons_disabled
Gabriel Luong's avatar
Gabriel Luong committed
323
324
325
326
        }
        setText(text)
        isChecked = checked
    }
327
328

    private fun shouldSettingsBeVisible() = !addon.installedState?.optionsPageUrl.isNullOrEmpty()
Gabriel Luong's avatar
Gabriel Luong committed
329
}