Add a higher level warning for antiquated protocols. https://redmine.stoutner.com...
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / dialogs / WebViewDialog.kt
1 /*
2  * Copyright © 2021-2022 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 package com.stoutner.privacycell.dialogs
21
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
30
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
36
37 import com.stoutner.privacycell.R
38
39 // Define the class constants.
40 private const val DIALOG_TYPE = "dialog_type"
41 private const val SCROLL_Y = "scroll_y"
42
43 class WebViewDialog : DialogFragment() {
44     companion object {
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 ANTIQUATED_NETWORK = 6
53         const val NETWORK_UNKNOWN = 7
54         const val NETWORK_GPRS = 8
55         const val NETWORK_EDGE = 9
56         const val NETWORK_UMTS = 10
57         const val NETWORK_CDMA = 11
58         const val NETWORK_EVDO_0 = 12
59         const val NETWORK_EVDO_A = 13
60         const val NETWORK_1xRTT = 14
61         const val NETWORK_HSDPA = 15
62         const val NETWORK_HSUPA = 16
63         const val NETWORK_HSPA = 17
64         const val NETWORK_IDEN = 18
65         const val NETWORK_EVDO_B = 19
66         const val NETWORK_LTE = 20
67         const val NETWORK_EHRPD = 21
68         const val NETWORK_HSPAP = 22
69         const val NETWORK_GSM = 23
70         const val NETWORK_TD_SCDMA = 24
71         const val NETWORK_IWLAN = 25
72         const val NETWORK_NR = 26
73         const val OVERRIDE_NETWORK_NONE = 27
74         const val OVERRIDE_NETWORK_LTE_CA = 28
75         const val OVERRIDE_NETWORK_LTE_ADVANCED_PRO = 29
76         const val OVERRIDE_NETWORK_NR_NSA = 30
77         const val OVERRIDE_NETWORK_NR_NSA_MMWAVE = 31  // Can be removed once the minimum API >= 31.
78         const val OVERRIDE_NETWORK_NR_ADVANCED = 32
79     }
80
81     // Define the class views.
82     private lateinit var webView: WebView
83
84     // Populate the WebView dialog type.
85     fun type(dialogType: Int): WebViewDialog {
86         // Create an arguments bundle.
87         val argumentsBundle = Bundle()
88
89         // Add the dialog type to the bundle.
90         argumentsBundle.putInt(DIALOG_TYPE, dialogType)
91
92         // Create a new instance of the WebView dialog.
93         val webViewDialog = WebViewDialog()
94
95         // Add the arguments bundle to the new dialog.
96         webViewDialog.arguments = argumentsBundle
97
98         // Return the new dialog.
99         return webViewDialog
100     }
101
102     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
103         // Get the dialog type from the arguments bundle.
104         val dialogType = requireArguments().getInt(DIALOG_TYPE)
105
106         // Use a builder to create the alert dialog.
107         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.Theme_PrivacyCellAlertDialog)
108
109         // Set the icon and the title according to the dialog type.
110         when (dialogType) {
111             PERMISSIONS -> {
112                 // Set the icon.
113                 dialogBuilder.setIcon(R.drawable.permissions)
114
115                 // Set the title.
116                 dialogBuilder.setTitle(R.string.permissions)
117             }
118
119             PRIVACY_POLICY -> {
120                 // Set the icon.
121                 dialogBuilder.setIcon(R.drawable.privacy_policy)
122
123                 // Set the title.
124                 dialogBuilder.setTitle(R.string.privacy_policy)
125             }
126
127             CHANGELOG -> {
128                 // Set the icon.
129                 dialogBuilder.setIcon(R.drawable.changelog)
130
131                 // Set the title.
132                 dialogBuilder.setTitle(R.string.changelog)
133             }
134
135             LICENSES -> {
136                 // Set the icon.
137                 dialogBuilder.setIcon(R.drawable.licenses)
138
139                 // Set the title.
140                 dialogBuilder.setTitle(R.string.licenses)
141             }
142
143             CONTRIBUTORS -> {
144                 // Set the icon.
145                 dialogBuilder.setIcon(R.drawable.contributors)
146
147                 // Set the tile.
148                 dialogBuilder.setTitle(R.string.contributors)
149             }
150
151             STINGRAY -> {
152                 // Set the icon.
153                 dialogBuilder.setIcon(R.drawable.secure)
154
155                 // Set the tile.
156                 dialogBuilder.setTitle(R.string.stingrays)
157             }
158
159             ANTIQUATED_NETWORK -> {
160                 // Set the icon.
161                 dialogBuilder.setIcon(R.drawable.antiquated)
162
163                 // Set the title.
164                 dialogBuilder.setTitle(R.string.antiquated_network_title)
165             }
166
167             NETWORK_UNKNOWN -> {
168                 // Set the icon.
169                 dialogBuilder.setIcon(R.drawable.protocols)
170
171                 // Set the title.
172                 dialogBuilder.setTitle(R.string.unknown)
173             }
174
175             NETWORK_GPRS -> {
176                 // Set the icon.
177                 dialogBuilder.setIcon(R.drawable.protocols)
178
179                 // Set the title.
180                 dialogBuilder.setTitle(R.string.gprs)
181             }
182
183             NETWORK_EDGE -> {
184                 // Set the icon.
185                 dialogBuilder.setIcon(R.drawable.protocols)
186
187                 // Set the title.
188                 dialogBuilder.setTitle(R.string.edge)
189             }
190
191             NETWORK_UMTS -> {
192                 // Set the icon.
193                 dialogBuilder.setIcon(R.drawable.protocols)
194
195                 // Set the title.
196                 dialogBuilder.setTitle(R.string.umts)
197             }
198
199             NETWORK_CDMA -> {
200                 // Set the icon.
201                 dialogBuilder.setIcon(R.drawable.protocols)
202
203                 // Set the title.
204                 dialogBuilder.setTitle(R.string.cdma)
205             }
206
207             NETWORK_EVDO_0 -> {
208                 // Set the icon.
209                 dialogBuilder.setIcon(R.drawable.protocols)
210
211                 // Set the title.
212                 dialogBuilder.setTitle(R.string.evdo_0)
213             }
214
215             NETWORK_EVDO_A -> {
216                 // Set the icon.
217                 dialogBuilder.setIcon(R.drawable.protocols)
218
219                 // Set the title.
220                 dialogBuilder.setTitle(R.string.evdo_a)
221             }
222
223             NETWORK_1xRTT -> {
224                 // Set the icon.
225                 dialogBuilder.setIcon(R.drawable.protocols)
226
227                 // Set the title.
228                 dialogBuilder.setTitle(R.string.rtt)
229             }
230
231             NETWORK_HSDPA -> {
232                 // Set the icon.
233                 dialogBuilder.setIcon(R.drawable.protocols)
234
235                 // Set the title.
236                 dialogBuilder.setTitle(R.string.hsdpa)
237             }
238
239             NETWORK_HSUPA -> {
240                 // Set the icon.
241                 dialogBuilder.setIcon(R.drawable.protocols)
242
243                 // Set the title.
244                 dialogBuilder.setTitle(R.string.hsupa)
245             }
246
247             NETWORK_HSPA -> {
248                 // Set the icon.
249                 dialogBuilder.setIcon(R.drawable.protocols)
250
251                 // Set the title.
252                 dialogBuilder.setTitle(R.string.hspa)
253             }
254
255             NETWORK_IDEN -> {
256                 // Set the icon.
257                 dialogBuilder.setIcon(R.drawable.protocols)
258
259                 // Set the title.
260                 dialogBuilder.setTitle(R.string.iden)
261             }
262
263             NETWORK_EVDO_B -> {
264                 // Set the icon.
265                 dialogBuilder.setIcon(R.drawable.protocols)
266
267                 // Set the title.
268                 dialogBuilder.setTitle(R.string.evdo_b)
269             }
270
271             NETWORK_LTE -> {
272                 // Set the icon.
273                 dialogBuilder.setIcon(R.drawable.protocols)
274
275                 // Set the title.
276                 dialogBuilder.setTitle(R.string.lte)
277             }
278
279             NETWORK_EHRPD -> {
280                 // Set the icon.
281                 dialogBuilder.setIcon(R.drawable.protocols)
282
283                 // Set the title.
284                 dialogBuilder.setTitle(R.string.ehrpd)
285             }
286
287             NETWORK_HSPAP -> {
288                 // Set the icon.
289                 dialogBuilder.setIcon(R.drawable.protocols)
290
291                 // Set the title.
292                 dialogBuilder.setTitle(R.string.hspap)
293             }
294
295             NETWORK_GSM -> {
296                 // Set the icon.
297                 dialogBuilder.setIcon(R.drawable.protocols)
298
299                 // Set the title.
300                 dialogBuilder.setTitle(R.string.gsm)
301             }
302
303             NETWORK_TD_SCDMA -> {
304                 // Set the icon.
305                 dialogBuilder.setIcon(R.drawable.protocols)
306
307                 // Set the title.
308                 dialogBuilder.setTitle(R.string.td_scdma)
309             }
310
311             NETWORK_IWLAN -> {
312                 // Set the icon.
313                 dialogBuilder.setIcon(R.drawable.protocols)
314
315                 // Set the title.
316                 dialogBuilder.setTitle(R.string.iwlan)
317             }
318
319             NETWORK_NR -> {
320                 // Set the icon.
321                 dialogBuilder.setIcon(R.drawable.protocols)
322
323                 // Set the title.
324                 dialogBuilder.setTitle(R.string.nr)
325             }
326
327             OVERRIDE_NETWORK_NONE -> {
328                 // Set the icon.
329                 dialogBuilder.setIcon(R.drawable.protocols)
330
331                 // Set the title.
332                 dialogBuilder.setTitle(R.string.none)
333             }
334
335             OVERRIDE_NETWORK_LTE_CA -> {
336                 // Set the icon.
337                 dialogBuilder.setIcon(R.drawable.protocols)
338
339                 // Set the title.
340                 dialogBuilder.setTitle(R.string.lte_ca)
341             }
342
343             OVERRIDE_NETWORK_LTE_ADVANCED_PRO -> {
344                 // Set the icon.
345                 dialogBuilder.setIcon(R.drawable.protocols)
346
347                 // Set the title.
348                 dialogBuilder.setTitle(R.string.lte_advanced_pro)
349             }
350
351             OVERRIDE_NETWORK_NR_NSA -> {
352                 // Set the icon.
353                 dialogBuilder.setIcon(R.drawable.protocols)
354
355                 // Set the title.
356                 dialogBuilder.setTitle(R.string.nr_nsa)
357             }
358
359             OVERRIDE_NETWORK_NR_NSA_MMWAVE -> {
360                 // Set the icon.
361                 dialogBuilder.setIcon(R.drawable.protocols)
362
363                 // Set the title.
364                 dialogBuilder.setTitle(R.string.nr_nsa_mmwave)
365             }
366
367             OVERRIDE_NETWORK_NR_ADVANCED -> {
368                 // Set the icon.
369                 dialogBuilder.setIcon(R.drawable.protocols)
370
371                 // Set the title.
372                 dialogBuilder.setTitle(R.string.nr_advanced)
373             }
374         }
375
376         // Set the view.
377         dialogBuilder.setView(R.layout.webview_dialog)
378
379         // Set a listener on the close button.  Using `null` as the listener closes the dialog without doing anything else.
380         dialogBuilder.setNegativeButton(R.string.close, null)
381
382         // Create an alert dialog from the builder.
383         val alertDialog = dialogBuilder.create()
384
385         // The alert dialog needs to be shown before the contents can be modified.
386         alertDialog.show()
387
388         // Get a handle for the WebView.
389         webView = alertDialog.findViewById(R.id.webview)!!
390
391         // Get the current theme status.
392         val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
393
394         // Check to see if the app is in night mode.
395         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES && WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {  // The app is in night mode.
396             // Apply the dark WebView theme.
397             WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
398         }
399
400         // Create a WebView asset loader.
401         val webViewAssetLoader = WebViewAssetLoader.Builder().addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(requireContext())).build()
402
403         // Set a WebView client.
404         webView.webViewClient = object : WebViewClient() {
405             // Send external links to a web browser.
406             override fun shouldOverrideUrlLoading(view: WebView, webResourceRequest: WebResourceRequest): Boolean {
407                 // Create an intent to view the URL.
408                 val urlIntent = Intent(Intent.ACTION_VIEW)
409
410                 // Add the URL to the intent.
411                 urlIntent.data = webResourceRequest.url
412
413                 // Make it so.
414                 startActivity(urlIntent)
415
416                 // Consume the click.
417                 return true
418             }
419
420             // Process asset requests with the asset loader.
421             override fun shouldInterceptRequest(webView: WebView, webResourceRequest: WebResourceRequest): WebResourceResponse? {
422                 // This allows using the `appassets.androidplatform.net` URL, which handles the loading of SVG files, which otherwise is prevented by the CORS policy.
423                 return webViewAssetLoader.shouldInterceptRequest(webResourceRequest.url)
424             }
425         }
426
427         // Load the WebView data according to the dialog type.
428         when (dialogType) {
429             PERMISSIONS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/permissions.html")
430             PRIVACY_POLICY -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/privacy_policy.html")
431             CHANGELOG -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/changelog.html")
432             LICENSES -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/licenses.html")
433             CONTRIBUTORS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/contributors.html")
434             STINGRAY -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/stingrays.html")
435             ANTIQUATED_NETWORK -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/antiquated_network.html")
436             NETWORK_UNKNOWN -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_unknown.html")
437             NETWORK_GPRS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_gprs.html")
438             NETWORK_EDGE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_edge.html")
439             NETWORK_UMTS -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_umts.html")
440             NETWORK_CDMA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_cdma.html")
441             NETWORK_EVDO_0 -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_evdo_0.html")
442             NETWORK_EVDO_A -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_evdo_a.html")
443             NETWORK_1xRTT -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_1xrtt.html")
444             NETWORK_HSDPA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hsdpa.html")
445             NETWORK_HSUPA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hsupa.html")
446             NETWORK_HSPA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hspa.html")
447             NETWORK_IDEN -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_iden.html")
448             NETWORK_EVDO_B -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_evdo_b.html")
449             NETWORK_LTE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_lte.html")
450             NETWORK_EHRPD -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_ehrpd.html")
451             NETWORK_HSPAP -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_hspap.html")
452             NETWORK_GSM -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_gsm.html")
453             NETWORK_TD_SCDMA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_td_scdma.html")
454             NETWORK_IWLAN -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_iwlan.html")
455             NETWORK_NR -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/network_nr.html")
456             OVERRIDE_NETWORK_NONE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_none.html")
457             OVERRIDE_NETWORK_LTE_CA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_lte_ca.html")
458             OVERRIDE_NETWORK_LTE_ADVANCED_PRO -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) +
459                     "/explanations/override_network_lte_advanced_pro.html")
460             OVERRIDE_NETWORK_NR_NSA -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_nr_nsa.html")
461             // The item below can be removed once the minimum API >= 31.
462             OVERRIDE_NETWORK_NR_NSA_MMWAVE -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_nr_nsa_mmwave.html")
463             OVERRIDE_NETWORK_NR_ADVANCED -> webView.loadUrl("https://appassets.androidplatform.net/assets/" + getString(R.string.asset_directory) + "/explanations/override_network_nr_advanced.html")
464         }
465
466         // Scroll the WebView if the saved instance state is not null.
467         if (savedInstanceState != null) {
468             webView.post {
469                 webView.scrollY = savedInstanceState.getInt(SCROLL_Y)
470             }
471         }
472
473         // Return the alert dialog.
474         return alertDialog
475     }
476
477     override fun onSaveInstanceState(savedInstanceState: Bundle) {
478         // Run the default commands.
479         super.onSaveInstanceState(savedInstanceState)
480
481         // Save the scroll position.
482         savedInstanceState.putInt(SCROLL_Y, webView.scrollY)
483     }
484 }