Bump the target API to 31. https://redmine.stoutner.com/issues/772
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / dialogs / WebViewDialog.kt
1 /*
2  * Copyright © 2021 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 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
78     }
79
80     // Define the class views.
81     private lateinit var webView: WebView
82
83     // Populate the WebView dialog type.
84     fun type(dialogType: Int): WebViewDialog {
85         // Create an arguments bundle.
86         val argumentsBundle = Bundle()
87
88         // Add the dialog type to the bundle.
89         argumentsBundle.putInt(DIALOG_TYPE, dialogType)
90
91         // Create a new instance of the WebView dialog.
92         val webViewDialog = WebViewDialog()
93
94         // Add the arguments bundle to the new dialog.
95         webViewDialog.arguments = argumentsBundle
96
97         // Return the new dialog.
98         return webViewDialog
99     }
100
101     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
102         // Get the dialog type from the arguments bundle.
103         val dialogType = requireArguments().getInt(DIALOG_TYPE)
104
105         // Use a builder to create the alert dialog.
106         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.Theme_PrivacyCellAlertDialog)
107
108         // Set the icon and the title according to the dialog type.
109         when (dialogType) {
110             PERMISSIONS -> {
111                 // Set the icon.
112                 dialogBuilder.setIcon(R.drawable.permissions)
113
114                 // Set the title.
115                 dialogBuilder.setTitle(R.string.permissions)
116             }
117
118             PRIVACY_POLICY -> {
119                 // Set the icon.
120                 dialogBuilder.setIcon(R.drawable.privacy_policy)
121
122                 // Set the title.
123                 dialogBuilder.setTitle(R.string.privacy_policy)
124             }
125
126             CHANGELOG -> {
127                 // Set the icon.
128                 dialogBuilder.setIcon(R.drawable.changelog)
129
130                 // Set the title.
131                 dialogBuilder.setTitle(R.string.changelog)
132             }
133
134             LICENSES -> {
135                 // Set the icon.
136                 dialogBuilder.setIcon(R.drawable.licenses)
137
138                 // Set the title.
139                 dialogBuilder.setTitle(R.string.licenses)
140             }
141
142             CONTRIBUTORS -> {
143                 // Set the icon.
144                 dialogBuilder.setIcon(R.drawable.contributors)
145
146                 // Set the tile.
147                 dialogBuilder.setTitle(R.string.contributors)
148             }
149
150             STINGRAY -> {
151                 // Set the icon.
152                 dialogBuilder.setIcon(R.drawable.secure_5g_nr_sa)
153
154                 // Set the tile.
155                 dialogBuilder.setTitle(R.string.stingrays)
156             }
157
158             NETWORK_UNKNOWN -> {
159                 // Set the icon.
160                 dialogBuilder.setIcon(R.drawable.privacy_policy)
161
162                 // Set the title.
163                 dialogBuilder.setTitle(R.string.unknown)
164             }
165
166             NETWORK_GPRS -> {
167                 // Set the icon.
168                 dialogBuilder.setIcon(R.drawable.privacy_policy)
169
170                 // Set the title.
171                 dialogBuilder.setTitle(R.string.gprs)
172             }
173
174             NETWORK_EDGE -> {
175                 // Set the icon.
176                 dialogBuilder.setIcon(R.drawable.privacy_policy)
177
178                 // Set the title.
179                 dialogBuilder.setTitle(R.string.edge)
180             }
181
182             NETWORK_UMTS -> {
183                 // Set the icon.
184                 dialogBuilder.setIcon(R.drawable.privacy_policy)
185
186                 // Set the title.
187                 dialogBuilder.setTitle(R.string.umts)
188             }
189
190             NETWORK_CDMA -> {
191                 // Set the icon.
192                 dialogBuilder.setIcon(R.drawable.privacy_policy)
193
194                 // Set the title.
195                 dialogBuilder.setTitle(R.string.cdma)
196             }
197
198             NETWORK_EVDO_0 -> {
199                 // Set the icon.
200                 dialogBuilder.setIcon(R.drawable.privacy_policy)
201
202                 // Set the title.
203                 dialogBuilder.setTitle(R.string.evdo_0)
204             }
205
206             NETWORK_EVDO_A -> {
207                 // Set the icon.
208                 dialogBuilder.setIcon(R.drawable.privacy_policy)
209
210                 // Set the title.
211                 dialogBuilder.setTitle(R.string.evdo_a)
212             }
213
214             NETWORK_1xRTT -> {
215                 // Set the icon.
216                 dialogBuilder.setIcon(R.drawable.privacy_policy)
217
218                 // Set the title.
219                 dialogBuilder.setTitle(R.string.rtt)
220             }
221
222             NETWORK_HSDPA -> {
223                 // Set the icon.
224                 dialogBuilder.setIcon(R.drawable.privacy_policy)
225
226                 // Set the title.
227                 dialogBuilder.setTitle(R.string.hsdpa)
228             }
229
230             NETWORK_HSUPA -> {
231                 // Set the icon.
232                 dialogBuilder.setIcon(R.drawable.privacy_policy)
233
234                 // Set the title.
235                 dialogBuilder.setTitle(R.string.hsupa)
236             }
237
238             NETWORK_HSPA -> {
239                 // Set the icon.
240                 dialogBuilder.setIcon(R.drawable.privacy_policy)
241
242                 // Set the title.
243                 dialogBuilder.setTitle(R.string.hspa)
244             }
245
246             NETWORK_IDEN -> {
247                 // Set the icon.
248                 dialogBuilder.setIcon(R.drawable.privacy_policy)
249
250                 // Set the title.
251                 dialogBuilder.setTitle(R.string.iden)
252             }
253
254             NETWORK_EVDO_B -> {
255                 // Set the icon.
256                 dialogBuilder.setIcon(R.drawable.privacy_policy)
257
258                 // Set the title.
259                 dialogBuilder.setTitle(R.string.evdo_b)
260             }
261
262             NETWORK_LTE -> {
263                 // Set the icon.
264                 dialogBuilder.setIcon(R.drawable.privacy_policy)
265
266                 // Set the title.
267                 dialogBuilder.setTitle(R.string.lte)
268             }
269
270             NETWORK_EHRPD -> {
271                 // Set the icon.
272                 dialogBuilder.setIcon(R.drawable.privacy_policy)
273
274                 // Set the title.
275                 dialogBuilder.setTitle(R.string.ehrpd)
276             }
277
278             NETWORK_HSPAP -> {
279                 // Set the icon.
280                 dialogBuilder.setIcon(R.drawable.privacy_policy)
281
282                 // Set the title.
283                 dialogBuilder.setTitle(R.string.hspap)
284             }
285
286             NETWORK_GSM -> {
287                 // Set the icon.
288                 dialogBuilder.setIcon(R.drawable.privacy_policy)
289
290                 // Set the title.
291                 dialogBuilder.setTitle(R.string.gsm)
292             }
293
294             NETWORK_TD_SCDMA -> {
295                 // Set the icon.
296                 dialogBuilder.setIcon(R.drawable.privacy_policy)
297
298                 // Set the title.
299                 dialogBuilder.setTitle(R.string.td_scdma)
300             }
301
302             NETWORK_IWLAN -> {
303                 // Set the icon.
304                 dialogBuilder.setIcon(R.drawable.privacy_policy)
305
306                 // Set the title.
307                 dialogBuilder.setTitle(R.string.iwlan)
308             }
309
310             NETWORK_NR -> {
311                 // Set the icon.
312                 dialogBuilder.setIcon(R.drawable.privacy_policy)
313
314                 // Set the title.
315                 dialogBuilder.setTitle(R.string.nr)
316             }
317
318             OVERRIDE_NETWORK_NONE -> {
319                 // Set the icon.
320                 dialogBuilder.setIcon(R.drawable.privacy_policy)
321
322                 // Set the title.
323                 dialogBuilder.setTitle(R.string.none)
324             }
325
326             OVERRIDE_NETWORK_LTE_CA -> {
327                 // Set the icon.
328                 dialogBuilder.setIcon(R.drawable.privacy_policy)
329
330                 // Set the title.
331                 dialogBuilder.setTitle(R.string.lte_ca)
332             }
333
334             OVERRIDE_NETWORK_LTE_ADVANCED_PRO -> {
335                 // Set the icon.
336                 dialogBuilder.setIcon(R.drawable.privacy_policy)
337
338                 // Set the title.
339                 dialogBuilder.setTitle(R.string.lte_ca)
340             }
341
342             OVERRIDE_NETWORK_NR_NSA -> {
343                 // Set the icon.
344                 dialogBuilder.setIcon(R.drawable.privacy_policy)
345
346                 // Set the title.
347                 dialogBuilder.setTitle(R.string.nr_nsa)
348             }
349
350             OVERRIDE_NETWORK_NR_NSA_MMWAVE -> {
351                 // Set the icon.
352                 dialogBuilder.setIcon(R.drawable.privacy_policy)
353
354                 // Set the title.
355                 dialogBuilder.setTitle(R.string.nr_nsa_mmwave)
356             }
357
358             OVERRIDE_NETWORK_NR_ADVANCED -> {
359                 // Set the icon.
360                 dialogBuilder.setIcon(R.drawable.privacy_policy)
361
362                 // Set the title.
363                 dialogBuilder.setTitle(R.string.nr_advanced)
364             }
365         }
366
367         // Set the view.
368         dialogBuilder.setView(R.layout.webview_dialog)
369
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)
372
373         // Create an alert dialog from the builder.
374         val alertDialog = dialogBuilder.create()
375
376         // The alert dialog needs to be shown before the contents can be modified.
377         alertDialog.show()
378
379         // Get a handle for the WebView.
380         webView = alertDialog.findViewById(R.id.webview)!!
381
382         // Get the current theme status.
383         val currentThemeStatus = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
384
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)
389         }
390
391         // Create a WebView asset loader.
392         val webViewAssetLoader = WebViewAssetLoader.Builder().addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(requireContext())).build()
393
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)
400
401                 // Add the URL to the intent.
402                 urlIntent.data = webResourceRequest.url
403
404                 // Make it so.
405                 startActivity(urlIntent)
406
407                 // Consume the click.
408                 return true
409             }
410
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)
415             }
416         }
417
418         // Load the WebView data according to the dialog type.
419         when (dialogType) {
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")
454         }
455
456         // Scroll the WebView if the saved instance state is not null.
457         if (savedInstanceState != null) {
458             webView.post {
459                 webView.scrollY = savedInstanceState.getInt(SCROLL_Y)
460             }
461         }
462
463         // Return the alert dialog.
464         return alertDialog
465     }
466
467     override fun onSaveInstanceState(savedInstanceState: Bundle) {
468         // Run the default commands.
469         super.onSaveInstanceState(savedInstanceState)
470
471         // Save the scroll position.
472         savedInstanceState.putInt(SCROLL_Y, webView.scrollY)
473     }
474 }