From 135083ed1d09126bde43ded4befc444040a13ed8 Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Mon, 16 Aug 2021 22:38:29 -0700 Subject: [PATCH] Request the phone permission if needed. --- app/src/main/AndroidManifest.xml | 2 +- .../privacycell/activities/PrivacyCell.kt | 149 ++++++++++++------ .../dialogs/PhonePermissionDialog.kt | 71 +++++++++ .../main/res/drawable/phone_permission.xml | 12 ++ ...layout.xml => privacy_cell_scrollview.xml} | 8 +- app/src/main/res/values-night/colors.xml | 38 +++++ app/src/main/res/values-night/themes.xml | 7 +- app/src/main/res/values/colors.xml | 9 ++ app/src/main/res/values/strings.xml | 6 + app/src/main/res/values/themes.xml | 7 +- 10 files changed, 254 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/com/stoutner/privacycell/dialogs/PhonePermissionDialog.kt create mode 100644 app/src/main/res/drawable/phone_permission.xml rename app/src/main/res/layout/{privacy_cell_linearlayout.xml => privacy_cell_scrollview.xml} (92%) create mode 100644 app/src/main/res/values-night/colors.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d807db0..8922ae3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,7 +30,7 @@ - + (R.id.secure_from_stingray_imageview) - val secureFromStingrayTextView = findViewById(R.id.secure_from_stingray_textview) - val voiceNetworkTextView = findViewById(R.id.voice_network) - val voiceNetworkDetailsTextView = findViewById(R.id.voice_network_details) - val dataNetworkTextView = findViewById(R.id.data_network) - val dataNetworkDetailsTextView = findViewById(R.id.data_network_details) - val additionalNetworkInfoTextView = findViewById(R.id.additional_network_info) - val additionalNetworkInfoDetailsTextView = findViewById(R.id.additional_network_info_details) - - // TODO. - if (ActivityCompat.checkSelfPermission( - this, - Manifest.permission.READ_PHONE_STATE - ) != PackageManager.PERMISSION_GRANTED - ) { - // TODO: Consider calling - // ActivityCompat#requestPermissions - // here to request the missing permissions, and then overriding - // public void onRequestPermissionsResult(int requestCode, String[] permissions, - // int[] grantResults) - // to handle the case where the user grants the permission. See the documentation - // for ActivityCompat#requestPermissions for more details. - return + secureFromStingrayImageView = findViewById(R.id.secure_from_stingray_imageview) + secureFromStingrayTextView = findViewById(R.id.secure_from_stingray_textview) + voiceNetworkTextView = findViewById(R.id.voice_network) + voiceNetworkDetailsTextView = findViewById(R.id.voice_network_details) + dataNetworkTextView = findViewById(R.id.data_network) + dataNetworkDetailsTextView = findViewById(R.id.data_network_details) + additionalNetworkInfoTextView = findViewById(R.id.additional_network_info) + additionalNetworkInfoDetailsTextView = findViewById(R.id.additional_network_info_details) + + // Get handles for the context and the telephony manager. + context = this + telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + + // Check to see if the read phone state permission has been granted. + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted. + // Populate Privacy Cell. + populatePrivacyCell() + } else { // The phone permission has not been granted. + // Check if the user has previously denied the storage permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first. + // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted when the dialog is shown. + if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown. + // Instantiate the phone permission dialog fragment. + val phonePermissionDialogFragment: DialogFragment = PhonePermissionDialog() + + // Show the phone permission alert dialog. The permission will be requested when the dialog is closed. + phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission)) + } + } else { // Show the permission request directly. + // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0. + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0) + } } + } - // Get a handle for the the telephone Manager and the context. - val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager - val context: Context = this - - // Get the voice network type. - val voiceNetworkType = getNetworkType(telephonyManager.voiceNetworkType) + override fun onCloseStoragePermissionDialog() { + // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0. + ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0) + } - // Populate the voice network text views. - voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0]) - voiceNetworkDetailsTextView.text = voiceNetworkType[1] + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + // Run the default commands. + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + //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). + if (grantResults.isNotEmpty()) { + // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty. + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted. + // Populate Privacy Cell. + populatePrivacyCell() + } else { // The read phone state permission was denied. + // Display the phone permission text on the main activity. + secureFromStingrayTextView.text = getString(R.string.phone_permission_text) + } + } + } + private fun populatePrivacyCell() { // Listen to changes in the cell network state. telephonyManager.listen(object : PhoneStateListener() { - override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {// Populate the secure from stingray text view. - // Populate the stingray security information. + override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) { + // Populate the stingray security information. if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) { // This is a secure 5G NR SA network. // Populate the image view. secureFromStingrayImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.secure_5g_nr_sa)) @@ -92,7 +131,7 @@ class PrivacyCell : AppCompatActivity() { secureFromStingrayTextView.text = getString(R.string.secure_from_stingray) // Set the text color. - secureFromStingrayTextView.setTextColor(getColor(R.color.blue_700)) + secureFromStingrayTextView.setTextColor(getColor(R.color.blue_text)) } else { // This is not a secure 5G NR SA network. // Populate the image view. secureFromStingrayImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.not_secure)) @@ -101,26 +140,38 @@ class PrivacyCell : AppCompatActivity() { secureFromStingrayTextView.text = getString(R.string.not_secure_from_stingray) // Set the text color. - secureFromStingrayTextView.setTextColor(getColor(R.color.red_700)) + secureFromStingrayTextView.setTextColor(getColor(R.color.red_text)) - // Get the strings that correspond to the network information. - val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType) - val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType) + // Get the strings that correspond to the network information. + val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType) + val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType) - // Populate the data network text views. - dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0]) - dataNetworkDetailsTextView.text = dataNetworkType[1] - additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0]) - additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1] + // Populate the data network text views. + dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0]) + dataNetworkDetailsTextView.text = dataNetworkType[1] + additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0]) + additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1] } } - }, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) + + override fun onServiceStateChanged(serviceState: ServiceState) { + // 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). + val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1] + + // Get the voice network type. + val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology) + + // Populate the voice network text views. + voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0]) + voiceNetworkDetailsTextView.text = voiceNetworkType[1] + } + }, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE) } private fun getNetworkType(networkType: Int) : Array { // Return the string that corresponds to the network type. return when(networkType) { - TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), getString(R.string.unknown)) + TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "") TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detal)) TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail)) TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail)) @@ -140,7 +191,7 @@ class PrivacyCell : AppCompatActivity() { TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail)) TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail)) TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail)) - else -> arrayOf(getString(R.string.error), getString(R.string.error)) + else -> arrayOf(getString(R.string.error), "") } } diff --git a/app/src/main/java/com/stoutner/privacycell/dialogs/PhonePermissionDialog.kt b/app/src/main/java/com/stoutner/privacycell/dialogs/PhonePermissionDialog.kt new file mode 100644 index 0000000..f6a0d38 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacycell/dialogs/PhonePermissionDialog.kt @@ -0,0 +1,71 @@ +/* + * Copyright © 2021 Soren Stoutner . + * + * This file is part of Privacy Cell . + * + * Privacy Cell is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Privacy Cell is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Privacy Browser. If not, see . + */ + +package com.stoutner.privacycell.dialogs + +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import android.os.Bundle + +import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.DialogFragment + +import com.stoutner.privacycell.R + +class PhonePermissionDialog : DialogFragment() { + // Declare the listener. + private lateinit var storagePermissionDialogListener: StoragePermissionDialogListener + + // The public interface is used to send information back to the parent activity. + interface StoragePermissionDialogListener { + fun onCloseStoragePermissionDialog() + } + + override fun onAttach(context: Context) { + // Run the default commands. + super.onAttach(context) + + // Get a handle for the listener from the launching context. + storagePermissionDialogListener = context as StoragePermissionDialogListener + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + // Use a builder to create the alert dialog. + val dialogBuilder: AlertDialog.Builder = AlertDialog.Builder(requireContext(), R.style.Theme_PrivacyCellAlertDialog) + + // Set the icon. + dialogBuilder.setIcon(R.drawable.phone_permission) + + // Set the title. + dialogBuilder.setTitle(R.string.phone_permission) + + // Set the text. + dialogBuilder.setMessage(R.string.phone_permission_text) + + // Set the close button listener. + dialogBuilder.setNegativeButton(R.string.ok) { _: DialogInterface, _: Int -> + // Call the storage permission dialog listener. + storagePermissionDialogListener.onCloseStoragePermissionDialog() + } + + // Return the alert dialog. + return dialogBuilder.create() + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/phone_permission.xml b/app/src/main/res/drawable/phone_permission.xml new file mode 100644 index 0000000..983dd21 --- /dev/null +++ b/app/src/main/res/drawable/phone_permission.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/layout/privacy_cell_linearlayout.xml b/app/src/main/res/layout/privacy_cell_scrollview.xml similarity index 92% rename from app/src/main/res/layout/privacy_cell_linearlayout.xml rename to app/src/main/res/layout/privacy_cell_scrollview.xml index 167ae8f..77ad82f 100644 --- a/app/src/main/res/layout/privacy_cell_linearlayout.xml +++ b/app/src/main/res/layout/privacy_cell_scrollview.xml @@ -38,6 +38,7 @@ android:layout_gravity="center_horizontal" tools:ignore="ContentDescription" /> + @@ -56,7 +58,7 @@ android:layout_gravity="center_horizontal" android:textAlignment="center" android:layout_marginTop="30dp" - android:textColor="@color/blue_700" + android:textColor="@color/blue_text" android:textSize="18sp" android:textStyle="bold" /> @@ -75,7 +77,7 @@ android:layout_width="wrap_content" android:layout_gravity="center_horizontal" android:textAlignment="center" - android:textColor="@color/blue_700" + android:textColor="@color/blue_text" android:textSize="18sp" android:textStyle="bold" /> @@ -94,7 +96,7 @@ android:layout_width="wrap_content" android:layout_gravity="center_horizontal" android:textAlignment="center" - android:textColor="@color/blue_700" + android:textColor="@color/blue_text" android:textSize="18sp" android:textStyle="bold" /> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml new file mode 100644 index 0000000..2293934 --- /dev/null +++ b/app/src/main/res/values-night/colors.xml @@ -0,0 +1,38 @@ + + + + + + + @color/violet_500 + @color/violet_500 + @color/salmon + + + #FF000000 + #FF2196F3 + #FF1976D2 + #FF0D47A1 + #FFE53935 + #FFD32F2F + #FFFC684E + #FFC73625 + #FF8AB4F8 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 674e26c..b8e34ae 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Privacy Browser. If not, see . --> - + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 7bcc782..d6d743b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -19,11 +19,20 @@ along with Privacy Browser. If not, see . --> + + @color/blue_700 + @color/blue_700 + @color/red_700 + + #FF000000 #FF2196F3 #FF1976D2 #FF0D47A1 #FFE53935 #FFD32F2F + #FFFC684E + #FFC73625 + #FF8AB4F8 #FFFFFFFF \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5a461af..66cb8e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -80,6 +80,12 @@ Long Term Evolution New Radio Non-Standalone millimeter Wave + Your device is connected to a standalone 5G network.\n\nIt is secure from stingray IMSI man-in-the-middle attacks. Your device is not connected to a standalone 5G network.\n\nIt is not secure from stingray IMSI man-in-the-middle attacks. + + + Phone Permission + Privacy Cell needs the Read Phone State permission to determine the safety level of your cell connection. + OK \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index e4e0d1b..ccc9324 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Privacy Browser. If not, see . --> - + + + \ No newline at end of file -- 2.45.2