eec29e4f08b3626b2b8f1ef625859ca9a578cfcd
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / activities / PrivacyCell.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 Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacycell.activities
21
22 import android.Manifest
23 import android.content.Context
24 import android.content.pm.PackageManager
25 import android.os.Bundle
26 import android.telephony.PhoneStateListener
27 import android.telephony.ServiceState
28 import android.telephony.TelephonyDisplayInfo
29 import android.telephony.TelephonyManager
30 import android.view.MenuItem
31 import android.view.View
32 import android.widget.ImageView
33 import android.widget.TextView
34
35 import androidx.appcompat.app.ActionBar
36 import androidx.appcompat.app.ActionBarDrawerToggle
37 import androidx.appcompat.app.AppCompatActivity
38 import androidx.appcompat.content.res.AppCompatResources
39 import androidx.appcompat.widget.Toolbar
40 import androidx.core.app.ActivityCompat
41 import androidx.core.view.GravityCompat
42 import androidx.drawerlayout.widget.DrawerLayout
43
44 import com.google.android.material.navigation.NavigationView
45
46 import com.stoutner.privacycell.R
47 import com.stoutner.privacycell.dialogs.PhonePermissionDialog
48 import com.stoutner.privacycell.dialogs.WebViewDialog
49
50 class PrivacyCell : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, PhonePermissionDialog.StoragePermissionDialogListener {
51     // Declare the class variables.
52     private lateinit var context: Context
53     private lateinit var telephonyManager: TelephonyManager
54     private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
55
56     // Declare the views.
57     private lateinit var drawerLayout: DrawerLayout
58     private lateinit var secureFromStingrayImageView: ImageView
59     private lateinit var secureFromStingrayTextView: TextView
60     private lateinit var voiceNetworkTextView: TextView
61     private lateinit var voiceNetworkDetailsTextView: TextView
62     private lateinit var dataNetworkTextView: TextView
63     private lateinit var dataNetworkDetailsTextView: TextView
64     private lateinit var additionalNetworkInfoTextView: TextView
65     private lateinit var additionalNetworkInfoDetailsTextView: TextView
66
67     override fun onCreate(savedInstanceState: Bundle?) {
68         // Run the default commands.
69         super.onCreate(savedInstanceState)
70
71         // Set the content view.
72         setContentView(R.layout.privacy_cell_drawerlayout)
73
74         // Get handles for the views.
75         drawerLayout = findViewById(R.id.drawerlayout)
76         val toolbar = findViewById<Toolbar>(R.id.toolbar)
77         secureFromStingrayImageView = findViewById(R.id.secure_from_stingray_imageview)
78         secureFromStingrayTextView = findViewById(R.id.secure_from_stingray_textview)
79         voiceNetworkTextView = findViewById(R.id.voice_network)
80         voiceNetworkDetailsTextView = findViewById(R.id.voice_network_details)
81         dataNetworkTextView = findViewById(R.id.data_network)
82         dataNetworkDetailsTextView = findViewById(R.id.data_network_details)
83         additionalNetworkInfoTextView = findViewById(R.id.additional_network_info)
84         additionalNetworkInfoDetailsTextView = findViewById(R.id.additional_network_info_details)
85         val navigationView = findViewById<NavigationView>(R.id.navigationview)
86
87         // Get handles for the context and the telephony manager.
88         context = this
89         telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
90
91         // Set the support action bar.
92         setSupportActionBar(toolbar)
93
94         // Get a handle for the action bar.
95         val actionBar = supportActionBar!!
96
97         // Set a custom view on the action bar.
98         actionBar.setCustomView(R.layout.app_bar_textview)
99
100         // Display the custom view.
101         actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
102
103         // Define a hamburger icon at the start of the app bar.  It will be populated in `onPostCreate()`.
104         actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
105
106         // Listen for touches on the navigation menu.
107         navigationView.setNavigationItemSelectedListener(this)
108
109         // Add a drawer listener.
110         drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
111             override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
112                 // Do nothing.
113             }
114
115             override fun onDrawerOpened(drawerView: View) {
116                 // Do nothing.
117             }
118
119             override fun onDrawerClosed(drawerView: View) {
120                 // Reset the drawer icon when the drawer is closed.  Otherwise, it is an arrow if the drawer is open when the app is restarted.
121                 actionBarDrawerToggle.syncState()
122             }
123
124             override fun onDrawerStateChanged(newState: Int) {
125                 // Do nothing.
126             }
127         })
128     }
129
130     override fun onPostCreate(savedInstanceState: Bundle?) {
131         // Run the default commands.
132         super.onPostCreate(savedInstanceState)
133
134         // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished.  This creates the navigation drawer icon.
135         actionBarDrawerToggle.syncState()
136     }
137
138     override fun onResume() {
139         // Run the default commands.
140         super.onResume()
141
142         // Check to see if the read phone state permission has been granted.  These commands need to be run on every resume so that the listener gets reassigned as it is automatically paused.
143         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
144             // Populate Privacy Cell.
145             populatePrivacyCell()
146         } else {  // The phone permission has not been granted.
147             // Check if the user has previously denied the storage permission.
148             if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) {  // Show a dialog explaining the request first.
149                 // Check to see if a phone permission dialog is already displayed.  This happens if the app is restarted when the dialog is shown.
150                 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) {  // No dialog is currently shown.
151                     // Instantiate the phone permission dialog fragment.
152                     val phonePermissionDialogFragment = PhonePermissionDialog()
153
154                     // Show the phone permission alert dialog.  The permission will be requested when the dialog is closed.
155                     phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
156                 }
157             } else {  // Show the permission request directly.
158                 // Request the read phone state permission.  There is only one permission request in the app, so it has a request code of 0.
159                 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
160             }
161         }
162     }
163
164     override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
165         // Run the commands that correspond to the selected menu item.
166         when (menuItem.itemId) {
167             R.id.permissions -> {  // Permissions.
168                 // Instantiate the permissions dialog fragment.
169                 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
170
171                 // Show the alert dialog.
172                 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
173             }
174
175             R.id.privacy_policy -> {  // Privacy Policy.
176                 // Instantiate the privacy policy dialog fragment.
177                 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
178
179                 // Show the alert dialog.
180                 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
181             }
182
183             R.id.changelog -> {  // Changelog.
184                 // Instantiate the changelog dialog fragment.
185                 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
186
187                 // Show the alert dialog.
188                 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
189             }
190
191             R.id.licenses -> {  // Licenses.
192                 // Instantiate the licenses dialog fragment.
193                 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
194
195                 // Show the alert dialog.
196                 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
197             }
198
199             R.id.contributors -> {  // Contributors.
200                 // Instantiate the contributors dialog fragment.
201                 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
202
203                 // Show the alert dialog.
204                 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
205             }
206         }
207
208         // Close the navigation drawer.
209         drawerLayout.closeDrawer(GravityCompat.START)
210
211         // Consume the click.
212         return true
213     }
214
215     override fun onCloseStoragePermissionDialog() {
216         // Request the read phone state permission.  There is only one permission request in the app, so it has a request code of 0.
217         ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
218     }
219
220     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
221         // Run the default commands.
222         super.onRequestPermissionsResult(requestCode, permissions, grantResults)
223
224         //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
225         if (grantResults.isNotEmpty()) {
226             // Check to see if the read phone state permission was granted.  If the dialog was canceled the grant results will be empty.
227             if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The read phone state permission was granted.
228                 // Populate Privacy Cell.
229                 populatePrivacyCell()
230             } else {  // The read phone state permission was denied.
231                 // Display the phone permission text on the main activity.
232                 secureFromStingrayTextView.text = getString(R.string.phone_permission_text)
233             }
234         }
235     }
236
237     private fun populatePrivacyCell() {
238         // Listen to changes in the cell network state.
239         telephonyManager.listen(object : PhoneStateListener() {
240             override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
241                 // Populate the stingray security information.  <https://source.android.com/devices/tech/connect/acts-5g-testing>
242                 if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) {  // This is a secure 5G NR SA network.
243                     // Populate the image view.
244                     secureFromStingrayImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.secure_5g_nr_sa))
245
246                     // Set the text.
247                     secureFromStingrayTextView.text = getString(R.string.secure_from_stingray)
248
249                     // Set the text color.
250                     secureFromStingrayTextView.setTextColor(getColor(R.color.blue_text))
251                 } else {  // This is not a secure 5G NR SA network.
252                     // Populate the image view.
253                     secureFromStingrayImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.not_secure))
254
255                     // Set the text.
256                     secureFromStingrayTextView.text = getString(R.string.not_secure_from_stingray)
257
258                     // Set the text color.
259                     secureFromStingrayTextView.setTextColor(getColor(R.color.red_text))
260
261                     // Get the strings that correspond to the network information.
262                     val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
263                     val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
264
265                     // Populate the data network text views.
266                     dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
267                     dataNetworkDetailsTextView.text = dataNetworkType[1]
268                     additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
269                     additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
270                 }
271             }
272
273             override fun onServiceStateChanged(serviceState: ServiceState) {
274                 // Get the network registration info for the voice network, which is the second of the three entries (the first appears to be Wi-Fi and the third appears to be the cell data network).
275                 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
276
277                 // Get the voice network type.
278                 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
279
280                 // Populate the voice network text views.
281                 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
282                 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
283             }
284         }, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
285     }
286
287     private fun getNetworkType(networkType: Int) : Array<String> {
288         // Return the string that corresponds to the network type.
289         return when(networkType) {
290             TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "")
291             TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detal))
292             TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail))
293             TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail))
294             TelephonyManager.NETWORK_TYPE_CDMA -> arrayOf(getString(R.string.cdma), getString(R.string.cdma_detail))
295             TelephonyManager.NETWORK_TYPE_EVDO_0 -> arrayOf(getString(R.string.evdo_0), getString(R.string.evdo_0_detail))
296             TelephonyManager.NETWORK_TYPE_EVDO_A -> arrayOf(getString(R.string.evdo_a), getString(R.string.evdo_a_detail))
297             TelephonyManager.NETWORK_TYPE_1xRTT -> arrayOf(getString(R.string.rtt), getString(R.string.rtt_detail))
298             TelephonyManager.NETWORK_TYPE_HSDPA -> arrayOf(getString(R.string.hsdpa), getString(R.string.hsdpa_detail))
299             TelephonyManager.NETWORK_TYPE_HSUPA -> arrayOf(getString(R.string.hsupa), getString(R.string.hsupa_detail))
300             TelephonyManager.NETWORK_TYPE_HSPA -> arrayOf(getString(R.string.hspa), getString(R.string.hspa_detail))
301             TelephonyManager.NETWORK_TYPE_IDEN -> arrayOf(getString(R.string.iden), getString(R.string.iden_detail))
302             TelephonyManager.NETWORK_TYPE_EVDO_B -> arrayOf(getString(R.string.evdo_b), getString(R.string.evdo_b_detail))
303             TelephonyManager.NETWORK_TYPE_LTE -> arrayOf(getString(R.string.lte), getString(R.string.lte_detail))
304             TelephonyManager.NETWORK_TYPE_EHRPD -> arrayOf(getString(R.string.ehrpd), getString(R.string.ehrpd_detail))
305             TelephonyManager.NETWORK_TYPE_HSPAP -> arrayOf(getString(R.string.hspap), getString(R.string.hspap_detail))
306             TelephonyManager.NETWORK_TYPE_GSM -> arrayOf(getString(R.string.gsm), getString(R.string.gsm_detail))
307             TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail))
308             TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail))
309             TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail))
310             else -> arrayOf(getString(R.string.error), "")
311         }
312     }
313
314     private fun getAdditionalNetworkInfo(overrideNetworkType: Int) : Array<String> {
315         // Return the string that corresponds to the override network type.
316         return when(overrideNetworkType) {
317             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> arrayOf(getString(R.string.none), "")
318             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
319             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
320             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.lte_nr_nsa), getString(R.string.lte_nr_nsa_detail))
321             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> arrayOf(getString(R.string.lte_nr_nsa_mmwave), getString(R.string.lte_nr_nsa_mmwave_detail))
322             else -> arrayOf(getString(R.string.error), "")
323         }
324     }
325 }