/* 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 import android.widget.Switch import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.Navigation import androidx.navigation.findNavController import kotlinx.android.synthetic.main.activity_addons.view.* import kotlinx.android.synthetic.main.fragment_installed_add_on_details.view.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.map import mozilla.components.feature.addons.Addon import mozilla.components.feature.addons.ui.translate import mozilla.components.feature.addons.ui.translatedName import mozilla.components.lib.state.ext.flowScoped import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.showToolbar /** * An activity to show the details of a installed add-on. */ class InstalledAddonDetailsFragment : Fragment() { private lateinit var addon: Addon private var scope: CoroutineScope? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { if (!::addon.isInitialized) { addon = AddonDetailsFragmentArgs.fromBundle(requireNotNull(arguments)).addon } return inflater.inflate(R.layout.fragment_installed_add_on_details, container, false).also { bind(it) } } @UseExperimental(ExperimentalCoroutinesApi::class) override fun onStart() { super.onStart() scope = requireContext().components.core.store.flowScoped { flow -> flow.ifChanged { it.extensions } .map { it.extensions.filterValues { extension -> extension.id == addon.id } } .ifChanged() .collect { val addonState = it[addon.id] if (addonState != null && addonState.enabled != addon.isEnabled()) { view?.let { view -> val newState = addon.installedState?.copy(enabled = addonState.enabled) this.addon = addon.copy(installedState = newState) view.enable_switch.setState(addon.isEnabled()) } } } } } override fun onStop() { super.onStop() scope?.cancel() } private fun bind(view: View) { val title = addon.translatableName.translate() showToolbar(title) bindEnableSwitch(view) bindSettings(view) bindDetails(view) bindPermissions(view) bindRemoveButton(view) } private fun bindEnableSwitch(view: View) { val switch = view.enable_switch switch.setState(addon.isEnabled()) switch.setOnCheckedChangeListener { v, isChecked -> val addonManager = v.context.components.addonManager switch.isClickable = false if (isChecked) { addonManager.enableAddon( addon, onSuccess = { runIfFragmentIsAttached { switch.isClickable = true switch.setText(R.string.mozac_feature_addons_settings_on) view.settings.isVisible = true this.addon = it showSnackBar( view, getString( R.string.mozac_feature_addons_successfully_enabled, addon.translatedName ) ) } }, onError = { runIfFragmentIsAttached { switch.isClickable = true showSnackBar( view, getString( R.string.mozac_feature_addons_failed_to_enable, addon.translatedName ) ) } } ) } else { addonManager.disableAddon( addon, onSuccess = { runIfFragmentIsAttached { switch.isClickable = true switch.setText(R.string.mozac_feature_addons_settings_off) view.settings.isVisible = false this.addon = it showSnackBar( view, getString( R.string.mozac_feature_addons_successfully_disabled, addon.translatedName ) ) } }, onError = { runIfFragmentIsAttached { switch.isClickable = true showSnackBar( view, getString( R.string.mozac_feature_addons_failed_to_disable, addon.translatedName ) ) } } ) } } } private fun bindSettings(view: View) { view.settings.apply { isVisible = !addon.installedState?.optionsPageUrl.isNullOrEmpty() setOnClickListener { val directions = InstalledAddonDetailsFragmentDirections.actionInstalledAddonFragmentToAddonInternalSettingsFragment( addon ) 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) } } private fun bindRemoveButton(view: View) { view.remove_add_on.setOnClickListener { requireContext().components.addonManager.uninstallAddon( addon, onSuccess = { runIfFragmentIsAttached { showSnackBar( view, getString( R.string.mozac_feature_addons_successfully_uninstalled, addon.translatedName ) ) view.findNavController().popBackStack() } }, onError = { _, _ -> runIfFragmentIsAttached { showSnackBar( view, getString( R.string.mozac_feature_addons_failed_to_uninstall, addon.translatedName ) ) } } ) } } private fun Switch.setState(checked: Boolean) { val text = if (checked) { R.string.mozac_feature_addons_settings_on } else { R.string.mozac_feature_addons_settings_off } setText(text) isChecked = checked } }