/* * Copyright (C) 2023 MURENA SAS * Copyright (C) 2021 E FOUNDATION * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package foundation.e.advancedprivacy.features.internetprivacy import android.os.Bundle import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.Toast import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.LinearLayoutManager import foundation.e.advancedprivacy.R import foundation.e.advancedprivacy.common.NavToolbarFragment import foundation.e.advancedprivacy.common.ToggleAppsAdapter import foundation.e.advancedprivacy.common.setToolTipForAsterisk import foundation.e.advancedprivacy.databinding.FragmentInternetActivityPolicyBinding import foundation.e.advancedprivacy.domain.entities.FeatureState import kotlinx.coroutines.launch import org.koin.androidx.viewmodel.ext.android.viewModel import java.util.Locale class InternetPrivacyFragment : NavToolbarFragment(R.layout.fragment_internet_activity_policy) { private val viewModel: InternetPrivacyViewModel by viewModel() private var _binding: FragmentInternetActivityPolicyBinding? = null private val binding get() = _binding!! private fun displayToast(message: String) { Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT) .show() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentInternetActivityPolicyBinding.bind(view) binding.apps.apply { layoutManager = LinearLayoutManager(requireContext()) setHasFixedSize(true) adapter = ToggleAppsAdapter(R.layout.ipscrambling_item_app_toggle) { packageName -> viewModel.submitAction( InternetPrivacyViewModel.Action.ToggleAppIpScrambled(packageName) ) } } binding.radioUseRealIp.radiobutton.setOnClickListener { viewModel.submitAction(InternetPrivacyViewModel.Action.UseRealIPAction) } binding.radioUseHiddenIp.radiobutton.setOnClickListener { viewModel.submitAction(InternetPrivacyViewModel.Action.UseHiddenIPAction) } setToolTipForAsterisk( textView = binding.ipscramblingSelectApps, textId = R.string.ipscrambling_select_app, tooltipTextId = R.string.ipscrambling_app_list_infos ) binding.ipscramblingSelectLocation.apply { adapter = ArrayAdapter( requireContext(), android.R.layout.simple_spinner_item, viewModel.availablesLocationsIds.map { if (it == "") { getString(R.string.ipscrambling_any_location) } else { Locale("", it).displayCountry } } ).apply { setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) } onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected( parentView: AdapterView<*>, selectedItemView: View?, position: Int, id: Long ) { viewModel.submitAction( InternetPrivacyViewModel.Action.SelectLocationAction( position ) ) } override fun onNothingSelected(parentView: AdapterView<*>?) {} } } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { render(viewModel.state.value) viewModel.state.collect(::render) } } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.singleEvents.collect { event -> when (event) { is InternetPrivacyViewModel.SingleEvent.ErrorEvent -> { displayToast(getString(event.errorResId, *event.args.toTypedArray())) } } } } } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.doOnStartedState() } } } private fun render(state: InternetPrivacyState) { binding.radioUseHiddenIp.radiobutton.apply { isChecked = state.mode.isChecked isEnabled = state.mode != FeatureState.STARTING } binding.radioUseRealIp.radiobutton.apply { isChecked = !state.mode.isChecked isEnabled = state.mode != FeatureState.STOPPING } binding.ipscramblingSelectLocation.setSelection(state.selectedLocationPosition) // TODO: this should not be mandatory. binding.apps.post { (binding.apps.adapter as ToggleAppsAdapter?)?.setData( list = state.getApps(), isEnabled = state.mode == FeatureState.ON ) } val viewIdsToHide = listOf( binding.ipscramblingLocationLabel, binding.selectLocationContainer, binding.ipscramblingSelectLocation, binding.ipscramblingSelectApps, binding.apps ) when { state.mode.isLoading || state.availableApps.isEmpty() -> { binding.loader.visibility = View.VISIBLE viewIdsToHide.forEach { it.visibility = View.GONE } } else -> { binding.loader.visibility = View.GONE viewIdsToHide.forEach { it.visibility = View.VISIBLE } } } } override fun onDestroyView() { super.onDestroyView() _binding = null } }