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