2 * Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
6 * Privacy Cell is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Cell is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Cell. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacycell.dialogs
22 import android.app.Dialog
23 import android.content.Intent
24 import android.content.res.Configuration
25 import android.os.Bundle
26 import android.webkit.WebResourceRequest
27 import android.webkit.WebResourceResponse
28 import android.webkit.WebView
29 import android.webkit.WebViewClient
31 import androidx.appcompat.app.AlertDialog
32 import androidx.fragment.app.DialogFragment
33 import androidx.webkit.WebSettingsCompat
34 import androidx.webkit.WebViewAssetLoader
35 import androidx.webkit.WebViewFeature
37 import com.stoutner.privacycell.R
39 // Define the class constants.
40 private const val DIALOG_TYPE = "dialog_type"
41 private const val SCROLL_Y = "scroll_y"
43 class WebViewDialog : DialogFragment() {
45 // Define the public constants.
46 const val PERMISSIONS = 0
47 const val PRIVACY_POLICY = 1
48 const val CHANGELOG = 2
49 const val LICENSES = 3
50 const val CONTRIBUTORS = 4
51 const val STINGRAY = 5
52 const val NETWORK_UNKNOWN = 6
53 const val NETWORK_GPRS = 7
54 const val NETWORK_EDGE = 8
55 const val NETWORK_UMTS = 9
56 const val NETWORK_CDMA = 10
57 const val NETWORK_EVDO_0 = 11
58 const val NETWORK_EVDO_A = 12
59 const val NETWORK_1xRTT = 13
60 const val NETWORK_HSDPA = 14
61 const val NETWORK_HSUPA = 15
62 const val NETWORK_HSPA = 16
63 const val NETWORK_IDEN = 17
64 const val NETWORK_EVDO_B = 18
65 const val NETWORK_LTE = 19
66 const val NETWORK_EHRPD = 20
67 const val NETWORK_HSPAP = 21
68 const val NETWORK_GSM = 22
69 const val NETWORK_TD_SCDMA = 23
70 const val NETWORK_IWLAN = 24
71 const val NETWORK_NR = 25
72 const val OVERRIDE_NETWORK_NONE = 26
73 const val OVERRIDE_NETWORK_LTE_CA = 27
74 const val OVERRIDE_NETWORK_LTE_ADVANCED_PRO = 28
75 const val OVERRIDE_NETWORK_NR_NSA = 29
76 const val OVERRIDE_NETWORK_NR_NSA_MMWAVE = 30 // Can be removed once the minimum API >= 31.
77 const val OVERRIDE_NETWORK_NR_ADVANCED = 31
80 // Define the class views.
81 private lateinit var webView: WebView
83 // Populate the WebView dialog type.
84 fun type(dialogType: Int): WebViewDialog {
85 // Create an arguments bundle.
86 val argumentsBundle = Bundle()
88 // Add the dialog type to the bundle.
89 argumentsBundle.putInt(DIALOG_TYPE, dialogType)
91 // Create a new instance of the WebView dialog.
92 val webViewDialog = WebViewDialog()
94 // Add the arguments bundle to the new dialog.
95 webViewDialog.arguments = argumentsBundle
97 // Return the new dialog.
101 override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
102 // Get the dialog type from the arguments bundle.
103 val dialogType = requireArguments().getInt(DIALOG_TYPE)
105 // Use a builder to create the alert dialog.
106 val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.Theme_PrivacyCellAlertDialog)
108 // Set the icon and the title according to the dialog type.
112 dialogBuilder.setIcon(R.drawable.permissions)
115 dialogBuilder.setTitle(R.string.permissions)
120 dialogBuilder.setIcon(R.drawable.privacy_policy)
123 dialogBuilder.setTitle(R.string.privacy_policy)
128 dialogBuilder.setIcon(R.drawable.changelog)
131 dialogBuilder.setTitle(R.string.changelog)
136 dialogBuilder.setIcon(R.drawable.licenses)
139 dialogBuilder.setTitle(R.string.licenses)
144 dialogBuilder.setIcon(R.drawable.contributors)
147 dialogBuilder.setTitle(R.string.contributors)
152 dialogBuilder.setIcon(R.drawable.secure_5g_nr_sa)
155 dialogBuilder.setTitle(R.string.stingrays)
160 dialogBuilder.setIcon(R.drawable.privacy_policy)
163 dialogBuilder.setTitle(R.string.unknown)
168 dialogBuilder.setIcon(R.drawable.privacy_policy)
171 dialogBuilder.setTitle(R.string.gprs)
176 dialogBuilder.setIcon(R.drawable.privacy_policy)
179 dialogBuilder.setTitle(R.string.edge)
184 dialogBuilder.setIcon(R.drawable.privacy_policy)
187 dialogBuilder.setTitle(R.string.umts)
192 dialogBuilder.setIcon(R.drawable.privacy_policy)
195 dialogBuilder.setTitle(R.string.cdma)
200 dialogBuilder.setIcon(R.drawable.privacy_policy)
203 dialogBuilder.setTitle(R.string.evdo_0)
208 dialogBuilder.setIcon(R.drawable.privacy_policy)
211 dialogBuilder.setTitle(R.string.evdo_a)
216 dialogBuilder.setIcon(R.drawable.privacy_policy)
219 dialogBuilder.setTitle(R.string.rtt)
224 dialogBuilder.setIcon(R.drawable.privacy_policy)
227 dialogBuilder.setTitle(R.string.hsdpa)
232 dialogBuilder.setIcon(R.drawable.privacy_policy)
235 dialogBuilder.setTitle(R.string.hsupa)
240 dialogBuilder.setIcon(R.drawable.privacy_policy)
243 dialogBuilder.setTitle(R.string.hspa)
248 dialogBuilder.setIcon(R.drawable.privacy_policy)
251 dialogBuilder.setTitle(R.string.iden)
256 dialogBuilder.setIcon(R.drawable.privacy_policy)
259 dialogBuilder.setTitle(R.string.evdo_b)
264 dialogBuilder.setIcon(R.drawable.privacy_policy)
267 dialogBuilder.setTitle(R.string.lte)
272 dialogBuilder.setIcon(R.drawable.privacy_policy)
275 dialogBuilder.setTitle(R.string.ehrpd)
280 dialogBuilder.setIcon(R.drawable.privacy_policy)
283 dialogBuilder.setTitle(R.string.hspap)
288 dialogBuilder.setIcon(R.drawable.privacy_policy)
291 dialogBuilder.setTitle(R.string.gsm)
294 NETWORK_TD_SCDMA -> {
296 dialogBuilder.setIcon(R.drawable.privacy_policy)
299 dialogBuilder.setTitle(R.string.td_scdma)
304 dialogBuilder.setIcon(R.drawable.privacy_policy)
307 dialogBuilder.setTitle(R.string.iwlan)
312 dialogBuilder.setIcon(R.drawable.privacy_policy)
315 dialogBuilder.setTitle(R.string.nr)
318 OVERRIDE_NETWORK_NONE -> {
320 dialogBuilder.setIcon(R.drawable.privacy_policy)
323 dialogBuilder.setTitle(R.string.none)
326 OVERRIDE_NETWORK_LTE_CA -> {
328 dialogBuilder.setIcon(R.drawable.privacy_policy)
331 dialogBuilder.setTitle(R.string.lte_ca)
334 OVERRIDE_NETWORK_LTE_ADVANCED_PRO -> {
336 dialogBuilder.setIcon(R.drawable.privacy_policy)
339 dialogBuilder.setTitle(R.string.lte_ca)
342 OVERRIDE_NETWORK_NR_NSA -> {
344 dialogBuilder.setIcon(R.drawable.privacy_policy)
347 dialogBuilder.setTitle(R.string.nr_nsa)
350 OVERRIDE_NETWORK_NR_NSA_MMWAVE -> {
352 dialogBuilder.setIcon(R.drawable.privacy_policy)
355 dialogBuilder.setTitle(R.string.nr_nsa_mmwave)
358 OVERRIDE_NETWORK_NR_ADVANCED -> {
360 dialogBuilder.setIcon(R.drawable.privacy_policy)
363 dialogBuilder.setTitle(R.string.nr_advanced)
368 dialogBuilder.setView(R.layout.webview_dialog)
370 // Set a listener on the close button. Using `null` as the listener closes the dialog without doing anything else.
371 dialogBuilder.setNegativeButton(R.string.close, null)
373 // Create an alert dialog from the builder.
374 val alertDialog = dialogBuilder.create()
376 // The alert dialog needs to be shown before the contents can be modified.
379 // Get a handle for the WebView.
380 webView = alertDialog.findViewById(R.id.webview)!!
382 // Get the current theme status.
383 val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
385 // Check to see if the app is in night mode.
386 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { // The app is in night mode.
387 // Apply the dark WebView theme.
388 WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
391 // Create a WebView asset loader.
392 val webViewAssetLoader = WebViewAssetLoader.Builder().addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(requireContext())).build()
394 // Set a WebView client.
395 webView.webViewClient = object : WebViewClient() {
396 // Send external links to a web browser.
397 override fun shouldOverrideUrlLoading(view: WebView, webResourceRequest: WebResourceRequest): Boolean {
398 // Create an intent to view the URL.
399 val urlIntent = Intent(Intent.ACTION_VIEW)
401 // Add the URL to the intent.
402 urlIntent.data = webResourceRequest.url
405 startActivity(urlIntent)
407 // Consume the click.
411 // Process asset requests with the asset loader.
412 override fun shouldInterceptRequest(webView: WebView, webResourceRequest: WebResourceRequest): WebResourceResponse? {
413 // This allows using the `appassets.androidplatform.net` URL, which handles the loading of SVG files, which otherwise is prevented by the CORS policy.
414 return webViewAssetLoader.shouldInterceptRequest(webResourceRequest.url)
418 // Load the WebView data according to the dialog type.
420 PERMISSIONS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/permissions.html")
421 PRIVACY_POLICY -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/privacy_policy.html")
422 CHANGELOG -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/changelog.html")
423 LICENSES -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/licenses.html")
424 CONTRIBUTORS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/contributors.html")
425 STINGRAY -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/stingrays.html")
426 NETWORK_UNKNOWN -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_unknown.html")
427 NETWORK_GPRS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_gprs.html")
428 NETWORK_EDGE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_edge.html")
429 NETWORK_UMTS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_umts.html")
430 NETWORK_CDMA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_cdma.html")
431 NETWORK_EVDO_0 -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_evdo_0.html")
432 NETWORK_EVDO_A -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_evdo_a.html")
433 NETWORK_1xRTT -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_1xrtt.html")
434 NETWORK_HSDPA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hsdpa.html")
435 NETWORK_HSUPA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hsupa.html")
436 NETWORK_HSPA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hspa.html")
437 NETWORK_IDEN -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_iden.html")
438 NETWORK_EVDO_B -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_evdo_b.html")
439 NETWORK_LTE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_lte.html")
440 NETWORK_EHRPD -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_ehrpd.html")
441 NETWORK_HSPAP -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hspap.html")
442 NETWORK_GSM -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_gsm.html")
443 NETWORK_TD_SCDMA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_td_scdma.html")
444 NETWORK_IWLAN -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_iwlan.html")
445 NETWORK_NR -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_nr.html")
446 OVERRIDE_NETWORK_NONE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_none.html")
447 OVERRIDE_NETWORK_LTE_CA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_lte_ca.html")
448 OVERRIDE_NETWORK_LTE_ADVANCED_PRO -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) +
449 "/explanations/override_network_lte_advanced_pro.html")
450 OVERRIDE_NETWORK_NR_NSA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_nr_nsa.html")
451 // The item below can be removed once the minimum API >= 31.
452 OVERRIDE_NETWORK_NR_NSA_MMWAVE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_nr_nsa_mmwave.html")
453 OVERRIDE_NETWORK_NR_ADVANCED -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_nr_advanced.html")
456 // Scroll the WebView if the saved instance state is not null.
457 if (savedInstanceState != null) {
459 webView.scrollY = savedInstanceState.getInt(SCROLL_Y)
463 // Return the alert dialog.
467 override fun onSaveInstanceState(savedInstanceState: Bundle) {
468 // Run the default commands.
469 super.onSaveInstanceState(savedInstanceState)
471 // Save the scroll position.
472 savedInstanceState.putInt(SCROLL_Y, webView.scrollY)