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 Browser. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacycell.activities
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
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
44 import com.google.android.material.navigation.NavigationView
46 import com.stoutner.privacycell.R
47 import com.stoutner.privacycell.dialogs.PhonePermissionDialog
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
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
65 override fun onCreate(savedInstanceState: Bundle?) {
66 // Run the default commands.
67 super.onCreate(savedInstanceState)
69 // Set the content view.
70 setContentView(R.layout.privacy_cell_drawerlayout)
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)
85 // Get handles for the context and the telephony manager.
87 telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
89 // Set the support action bar.
90 setSupportActionBar(toolbar)
92 // Get a handle for the action bar.
93 val actionBar = supportActionBar!!
95 // Set a custom view on the action bar.
96 actionBar.setCustomView(R.layout.app_bar_textview)
98 // Display the custom view.
99 actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
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)
104 // Listen for touches on the navigation menu.
105 navigationView.setNavigationItemSelectedListener(this)
107 // Add a drawer listener.
108 drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
109 override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
113 override fun onDrawerOpened(drawerView: View) {
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()
122 override fun onDrawerStateChanged(newState: Int) {
128 override fun onPostCreate(savedInstanceState: Bundle?) {
129 // Run the default commands.
130 super.onPostCreate(savedInstanceState)
132 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
133 actionBarDrawerToggle.syncState()
136 override fun onResume() {
137 // Run the default commands.
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()
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))
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)
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)
167 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
168 // Run the default commands.
169 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
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)
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))
194 secureFromStingrayTextView.text = getString(R.string.secure_from_stingray)
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))
203 secureFromStingrayTextView.text = getString(R.string.not_secure_from_stingray)
205 // Set the text color.
206 secureFromStingrayTextView.setTextColor(getColor(R.color.red_text))
208 // Get the strings that correspond to the network information.
209 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
210 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
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]
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]
224 // Get the voice network type.
225 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
227 // Populate the voice network text views.
228 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
229 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
231 }, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
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), "")
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), "")
273 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {