diff options
| author | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-05-02 21:25:17 +0200 |
|---|---|---|
| committer | Guillaume Jacquart <guillaume.jacquart@hoodbrains.com> | 2023-05-02 22:00:35 +0200 |
| commit | a8874167f663885f2d3371801cf03681576ac817 (patch) | |
| tree | 5be07b8768142efeade536d4135f2250c1ac9071 /app/src/main/java/foundation/e/privacycentralapp/widget | |
| parent | a0ee04ea9dbc0802c828afdf660eb37dc6fa350f (diff) | |
| download | advanced-privacy-a8874167f663885f2d3371801cf03681576ac817.tar.gz | |
1200: rename everything to AdvancedPrivacy
Diffstat (limited to 'app/src/main/java/foundation/e/privacycentralapp/widget')
3 files changed, 0 insertions, 579 deletions
diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt deleted file mode 100644 index 3abe21b..0000000 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/Widget.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. - */ - -package foundation.e.privacycentralapp - -import android.appwidget.AppWidgetManager -import android.appwidget.AppWidgetProvider -import android.content.Context -import android.os.Bundle -import foundation.e.privacycentralapp.domain.usecases.GetQuickPrivacyStateUseCase -import foundation.e.privacycentralapp.domain.usecases.TrackersStatisticsUseCase -import foundation.e.privacycentralapp.widget.State -import foundation.e.privacycentralapp.widget.render -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.sample -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import java.time.temporal.ChronoUnit - -/** - * Implementation of App Widget functionality. - */ -class Widget : AppWidgetProvider() { - - override fun onUpdate( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetIds: IntArray - ) { - render(context, state.value, appWidgetManager) - } - - override fun onEnabled(context: Context) { - // Enter relevant functionality for when the first widget is created - } - - override fun onDisabled(context: Context) { - // Enter relevant functionality for when the last widget is disabled - } - - companion object { - private var updateWidgetJob: Job? = null - - private var state: StateFlow<State> = MutableStateFlow(State()) - - private const val DARK_TEXT_KEY = "foundation.e.blisslauncher.WIDGET_OPTION_DARK_TEXT" - var isDarkText = false - - @OptIn(FlowPreview::class) - private fun initState( - getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - trackersStatisticsUseCase: TrackersStatisticsUseCase, - coroutineScope: CoroutineScope - ): StateFlow<State> { - - return combine( - getPrivacyStateUseCase.quickPrivacyState, - getPrivacyStateUseCase.trackerMode, - getPrivacyStateUseCase.isLocationHidden, - getPrivacyStateUseCase.ipScramblingMode, - ) { quickPrivacyState, trackerMode, isLocationHidden, ipScramblingMode -> - - State( - quickPrivacyState = quickPrivacyState, - trackerMode = trackerMode, - isLocationHidden = isLocationHidden, - ipScramblingMode = ipScramblingMode - ) - }.sample(50) - .combine( - merge( - trackersStatisticsUseCase.listenUpdates() - .onStart { emit(Unit) } - .debounce(5000), - flow { - while (true) { - emit(Unit) - delay(ChronoUnit.HOURS.duration.toMillis()) - } - } - - ) - ) { state, _ -> - state.copy( - dayStatistics = trackersStatisticsUseCase.getDayTrackersCalls(), - activeTrackersCount = trackersStatisticsUseCase.getDayTrackersCount() - ) - }.stateIn( - scope = coroutineScope, - started = SharingStarted.Eagerly, - initialValue = State() - ) - } - - @OptIn(DelicateCoroutinesApi::class) - fun startListening( - appContext: Context, - getPrivacyStateUseCase: GetQuickPrivacyStateUseCase, - trackersStatisticsUseCase: TrackersStatisticsUseCase, - ) { - state = initState( - getPrivacyStateUseCase, - trackersStatisticsUseCase, - GlobalScope - ) - - updateWidgetJob?.cancel() - updateWidgetJob = GlobalScope.launch(Dispatchers.Main) { - state.collect { - render(appContext, it, AppWidgetManager.getInstance(appContext)) - } - } - } - } - - override fun onAppWidgetOptionsChanged( - context: Context, - appWidgetManager: AppWidgetManager, - appWidgetId: Int, - newOptions: Bundle? - ) { - super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions) - if (newOptions != null) { - isDarkText = newOptions.getBoolean(DARK_TEXT_KEY) - } - render(context, state.value, appWidgetManager) - } -} diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt deleted file mode 100644 index e01f47f..0000000 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetCommandReceiver.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. - */ - -package foundation.e.privacycentralapp.widget - -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import foundation.e.privacycentralapp.PrivacyCentralApplication - -class WidgetCommandReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - val getQuickPrivacyStateUseCase = (context?.applicationContext as? PrivacyCentralApplication)?.dependencyContainer?.getQuickPrivacyStateUseCase - - when (intent?.action) { - ACTION_TOGGLE_TRACKERS -> getQuickPrivacyStateUseCase?.toggleTrackers() - ACTION_TOGGLE_LOCATION -> getQuickPrivacyStateUseCase?.toggleLocation() - ACTION_TOGGLE_IPSCRAMBLING -> getQuickPrivacyStateUseCase?.toggleIpScrambling() - else -> {} - } - } - - companion object { - const val ACTION_TOGGLE_TRACKERS = "toggle_trackers" - const val ACTION_TOGGLE_LOCATION = "toggle_location" - const val ACTION_TOGGLE_IPSCRAMBLING = "toggle_ipscrambling" - } -} diff --git a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt b/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt deleted file mode 100644 index fccfd48..0000000 --- a/app/src/main/java/foundation/e/privacycentralapp/widget/WidgetUI.kt +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2022 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 <https://www.gnu.org/licenses/>. - */ - -package foundation.e.privacycentralapp.widget - -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_IMMUTABLE -import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.appwidget.AppWidgetManager -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.view.View -import android.widget.RemoteViews -import foundation.e.privacycentralapp.R -import foundation.e.privacycentralapp.Widget -import foundation.e.privacycentralapp.Widget.Companion.isDarkText -import foundation.e.privacycentralapp.common.extensions.dpToPxF -import foundation.e.privacycentralapp.domain.entities.InternetPrivacyMode -import foundation.e.privacycentralapp.domain.entities.QuickPrivacyState -import foundation.e.privacycentralapp.domain.entities.TrackerMode -import foundation.e.privacycentralapp.main.MainActivity -import foundation.e.privacycentralapp.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_IPSCRAMBLING -import foundation.e.privacycentralapp.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_LOCATION -import foundation.e.privacycentralapp.widget.WidgetCommandReceiver.Companion.ACTION_TOGGLE_TRACKERS - -data class State( - val quickPrivacyState: QuickPrivacyState = QuickPrivacyState.DISABLED, - val trackerMode: TrackerMode = TrackerMode.VULNERABLE, - val isLocationHidden: Boolean = false, - val ipScramblingMode: InternetPrivacyMode = InternetPrivacyMode.REAL_IP_LOADING, - val dayStatistics: List<Pair<Int, Int>> = emptyList(), - val activeTrackersCount: Int = 0, -) - -fun render( - context: Context, - state: State, - appWidgetManager: AppWidgetManager, -) { - val views = RemoteViews(context.packageName, R.layout.widget) - applyDarkText(context, state, views) - views.apply { - val openPIntent = PendingIntent.getActivity( - context, - REQUEST_CODE_DASHBOARD, - Intent(context, MainActivity::class.java), - FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT - ) - setOnClickPendingIntent(R.id.settings_btn, openPIntent) - setOnClickPendingIntent(R.id.widget_container, openPIntent) - - setTextViewText( - R.id.state_label, - context.getString( - when (state.quickPrivacyState) { - QuickPrivacyState.DISABLED -> R.string.widget_state_title_off - QuickPrivacyState.FULL_ENABLED -> R.string.widget_state_title_on - QuickPrivacyState.ENABLED -> R.string.widget_state_title_custom - } - ) - ) - - setImageViewResource( - R.id.toggle_trackers, - if (state.trackerMode == TrackerMode.VULNERABLE) - R.drawable.ic_switch_disabled - else R.drawable.ic_switch_enabled - ) - - setOnClickPendingIntent( - R.id.toggle_trackers, - PendingIntent.getBroadcast( - context, - REQUEST_CODE_TOGGLE_TRACKERS, - Intent(context, WidgetCommandReceiver::class.java).apply { - action = ACTION_TOGGLE_TRACKERS - }, - FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT - ) - ) - - setTextViewText( - R.id.state_trackers, - context.getString( - when (state.trackerMode) { - TrackerMode.DENIED -> R.string.widget_state_trackers_on - TrackerMode.VULNERABLE -> R.string.widget_state_trackers_off - TrackerMode.CUSTOM -> R.string.widget_state_trackers_custom - } - ) - ) - - setImageViewResource( - R.id.toggle_location, - if (state.isLocationHidden) R.drawable.ic_switch_enabled - else R.drawable.ic_switch_disabled - ) - - setOnClickPendingIntent( - R.id.toggle_location, - PendingIntent.getBroadcast( - context, - REQUEST_CODE_TOGGLE_LOCATION, - Intent(context, WidgetCommandReceiver::class.java).apply { - action = ACTION_TOGGLE_LOCATION - }, - FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT - ) - ) - - setTextViewText( - R.id.state_geolocation, - context.getString( - if (state.isLocationHidden) R.string.widget_state_geolocation_on - else R.string.widget_state_geolocation_off - ) - ) - - setImageViewResource( - R.id.toggle_ipscrambling, - if (state.ipScramblingMode.isChecked) R.drawable.ic_switch_enabled - else R.drawable.ic_switch_disabled - ) - - setOnClickPendingIntent( - R.id.toggle_ipscrambling, - PendingIntent.getBroadcast( - context, - REQUEST_CODE_TOGGLE_IPSCRAMBLING, - Intent(context, WidgetCommandReceiver::class.java).apply { - action = ACTION_TOGGLE_IPSCRAMBLING - }, - FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT - ) - ) - - setTextViewText( - R.id.state_ip_address, - context.getString( - if (state.ipScramblingMode == InternetPrivacyMode.HIDE_IP) R.string.widget_state_ipaddress_on - else R.string.widget_state_ipaddress_off - ) - ) - - val loading = state.ipScramblingMode.isLoading - - setViewVisibility(R.id.state_ip_address, if (loading) View.GONE else View.VISIBLE) - - setViewVisibility(R.id.state_ip_address_loader, if (loading) View.VISIBLE else View.GONE) - - if (state.dayStatistics.all { it.first == 0 && it.second == 0 }) { - setViewVisibility(R.id.graph, View.GONE) - setViewVisibility(R.id.graph_legend, View.GONE) - setViewVisibility(R.id.graph_empty, View.VISIBLE) - setViewVisibility(R.id.graph_legend_values, View.GONE) - setViewVisibility(R.id.graph_view_trackers_btn, View.GONE) - } else { - setViewVisibility(R.id.graph, View.VISIBLE) - setViewVisibility(R.id.graph_legend, View.VISIBLE) - setViewVisibility(R.id.graph_empty, View.GONE) - setViewVisibility(R.id.graph_legend_values, View.VISIBLE) - setViewVisibility(R.id.graph_view_trackers_btn, View.VISIBLE) - - val pIntent = PendingIntent.getActivity( - context, - REQUEST_CODE_TRACKERS, - MainActivity.createTrackersIntent(context), - FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT - ) - - setOnClickPendingIntent(R.id.graph_view_trackers_btn, pIntent) - - val graphHeightPx = 26.dpToPxF(context) - val maxValue = - state.dayStatistics - .map { it.first + it.second } - .maxOrNull() - .let { if (it == null || it == 0) 1 else it } - val ratio = graphHeightPx / maxValue - - state.dayStatistics.forEachIndexed { index, (blocked, leaked) -> - // blocked (the bar below) - val middlePadding = graphHeightPx - blocked * ratio - setViewPadding(blockedBarIds[index], 0, middlePadding.toInt(), 0, 0) - - // leaked (the bar above) - val topPadding = graphHeightPx - (blocked + leaked) * ratio - setViewPadding(leakedBarIds[index], 0, topPadding.toInt(), 0, 0) - - val highlightPIntent = PendingIntent.getActivity( - context, REQUEST_CODE_HIGHLIGHT + index, - MainActivity.createHighlightLeaksIntent(context, index), - FLAG_IMMUTABLE or FLAG_UPDATE_CURRENT - ) - setOnClickPendingIntent(containerBarIds[index], highlightPIntent) - } - - setTextViewText( - R.id.graph_legend, - context.getString( - R.string.widget_graph_trackers_legend, - state.activeTrackersCount.toString() - ) - ) - } - } - - appWidgetManager.updateAppWidget(ComponentName(context, Widget::class.java), views) -} - -private val containerBarIds = listOf( - R.id.widget_graph_bar_container_0, - R.id.widget_graph_bar_container_1, - R.id.widget_graph_bar_container_2, - R.id.widget_graph_bar_container_3, - R.id.widget_graph_bar_container_4, - R.id.widget_graph_bar_container_5, - R.id.widget_graph_bar_container_6, - R.id.widget_graph_bar_container_7, - R.id.widget_graph_bar_container_8, - R.id.widget_graph_bar_container_9, - R.id.widget_graph_bar_container_10, - R.id.widget_graph_bar_container_11, - R.id.widget_graph_bar_container_12, - R.id.widget_graph_bar_container_13, - R.id.widget_graph_bar_container_14, - R.id.widget_graph_bar_container_15, - R.id.widget_graph_bar_container_16, - R.id.widget_graph_bar_container_17, - R.id.widget_graph_bar_container_18, - R.id.widget_graph_bar_container_19, - R.id.widget_graph_bar_container_20, - R.id.widget_graph_bar_container_21, - R.id.widget_graph_bar_container_22, - R.id.widget_graph_bar_container_23, -) - -private val blockedBarIds = listOf( - R.id.widget_graph_bar_0, - R.id.widget_graph_bar_1, - R.id.widget_graph_bar_2, - R.id.widget_graph_bar_3, - R.id.widget_graph_bar_4, - R.id.widget_graph_bar_5, - R.id.widget_graph_bar_6, - R.id.widget_graph_bar_7, - R.id.widget_graph_bar_8, - R.id.widget_graph_bar_9, - R.id.widget_graph_bar_10, - R.id.widget_graph_bar_11, - R.id.widget_graph_bar_12, - R.id.widget_graph_bar_13, - R.id.widget_graph_bar_14, - R.id.widget_graph_bar_15, - R.id.widget_graph_bar_16, - R.id.widget_graph_bar_17, - R.id.widget_graph_bar_18, - R.id.widget_graph_bar_19, - R.id.widget_graph_bar_20, - R.id.widget_graph_bar_21, - R.id.widget_graph_bar_22, - R.id.widget_graph_bar_23 -) - -private val leakedBarIds = listOf( - R.id.widget_leaked_graph_bar_0, - R.id.widget_leaked_graph_bar_1, - R.id.widget_leaked_graph_bar_2, - R.id.widget_leaked_graph_bar_3, - R.id.widget_leaked_graph_bar_4, - R.id.widget_leaked_graph_bar_5, - R.id.widget_leaked_graph_bar_6, - R.id.widget_leaked_graph_bar_7, - R.id.widget_leaked_graph_bar_8, - R.id.widget_leaked_graph_bar_9, - R.id.widget_leaked_graph_bar_10, - R.id.widget_leaked_graph_bar_11, - R.id.widget_leaked_graph_bar_12, - R.id.widget_leaked_graph_bar_13, - R.id.widget_leaked_graph_bar_14, - R.id.widget_leaked_graph_bar_15, - R.id.widget_leaked_graph_bar_16, - R.id.widget_leaked_graph_bar_17, - R.id.widget_leaked_graph_bar_18, - R.id.widget_leaked_graph_bar_19, - R.id.widget_leaked_graph_bar_20, - R.id.widget_leaked_graph_bar_21, - R.id.widget_leaked_graph_bar_22, - R.id.widget_leaked_graph_bar_23 -) - -private const val REQUEST_CODE_DASHBOARD = 1 -private const val REQUEST_CODE_TRACKERS = 3 -private const val REQUEST_CODE_TOGGLE_TRACKERS = 4 -private const val REQUEST_CODE_TOGGLE_LOCATION = 5 -private const val REQUEST_CODE_TOGGLE_IPSCRAMBLING = 6 -private const val REQUEST_CODE_HIGHLIGHT = 100 - -fun applyDarkText(context: Context, state: State, views: RemoteViews) { - views.apply { - listOf( - R.id.state_label, - R.id.graph_legend_blocked, - R.id.graph_legend_allowed, - - ) - .forEach { - setTextColor( - it, - context.getColor(if (isDarkText) R.color.on_surface_disabled_light else R.color.on_primary_medium_emphasis) - ) - } - setTextColor( - R.id.widget_title, - context.getColor(if (isDarkText) R.color.on_surface_medium_emphasis_light else R.color.on_surface_high_emphasis) - ) - listOf( - R.id.state_trackers, - R.id.state_geolocation, - R.id.state_ip_address, - R.id.graph_legend, - R.id.graph_view_trackers_btn - ) - .forEach { - setTextColor( - it, - context.getColor(if (isDarkText) R.color.on_surface_medium_emphasis_light else R.color.on_primary_high_emphasis) - ) - } - - listOf( - R.id.trackers_label, - R.id.geolocation_label, - R.id.ip_address_label, - R.id.graph_empty - - ) - .forEach { - setTextColor( - it, - context.getColor(if (isDarkText) R.color.on_surface_disabled_light else R.color.on_primary_disabled) - ) - } - setTextViewCompoundDrawables( - R.id.graph_view_trackers_btn, - 0, - 0, - if (isDarkText) R.drawable.ic_chevron_right_24dp_light else R.drawable.ic_chevron_right_24dp, - 0 - ) - setImageViewResource( - R.id.settings_btn, - if (isDarkText) R.drawable.ic_settings_light else R.drawable.ic_settings - ) - setImageViewResource( - R.id.state_icon, - if (isDarkText) { - if (state.quickPrivacyState.isEnabled()) R.drawable.ic_shield_on_light - else R.drawable.ic_shield_off_light - } else { - if (state.quickPrivacyState.isEnabled()) R.drawable.ic_shield_on_white - else R.drawable.ic_shield_off_white - } - ) - } -} |
