Request the phone permission if needed.
authorSoren Stoutner <soren@stoutner.com>
Tue, 17 Aug 2021 05:38:29 +0000 (22:38 -0700)
committerSoren Stoutner <soren@stoutner.com>
Tue, 17 Aug 2021 05:38:29 +0000 (22:38 -0700)
app/src/main/AndroidManifest.xml
app/src/main/java/com/stoutner/privacycell/activities/PrivacyCell.kt
app/src/main/java/com/stoutner/privacycell/dialogs/PhonePermissionDialog.kt [new file with mode: 0644]
app/src/main/res/drawable/phone_permission.xml [new file with mode: 0644]
app/src/main/res/layout/privacy_cell_linearlayout.xml [deleted file]
app/src/main/res/layout/privacy_cell_scrollview.xml [new file with mode: 0644]
app/src/main/res/values-night/colors.xml [new file with mode: 0644]
app/src/main/res/values-night/themes.xml
app/src/main/res/values/colors.xml
app/src/main/res/values/strings.xml
app/src/main/res/values/themes.xml

index d807db0bd540daf6f0016bb1d03194484c1fb169..8922ae35c7ae26e245f6a3a8cd2287b789874d18 100644 (file)
@@ -30,7 +30,7 @@
     <!-- Support Chromebooks that don't have a touch screen. -->
     <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
 
     <!-- Support Chromebooks that don't have a touch screen. -->
     <uses-feature android:name="android.hardware.touchscreen" android:required="false" />
 
-    <!-- App data is automatically backed up to Google cloud servers unless `android:allowBackup="false"` and `android:fullBackupContent="false"` is set. TODO round icon and theme name. -->
+    <!-- App data is automatically backed up to Google cloud servers unless `android:allowBackup="false"` and `android:fullBackupContent="false"` is set. -->
     <application
         android:label="@string/privacy_cell"
         android:icon="@mipmap/privacy_cell"
     <application
         android:label="@string/privacy_cell"
         android:icon="@mipmap/privacy_cell"
index b45701832350f774b513dca3664f393164567380..c8c77120a244e072a0fa8535c280ba29cc6a3d11 100644 (file)
@@ -24,6 +24,7 @@ import android.content.Context
 import android.content.pm.PackageManager
 import android.os.Bundle
 import android.telephony.PhoneStateListener
 import android.content.pm.PackageManager
 import android.os.Bundle
 import android.telephony.PhoneStateListener
+import android.telephony.ServiceState
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyManager
 import android.widget.ImageView
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyManager
 import android.widget.ImageView
@@ -32,58 +33,96 @@ import android.widget.TextView
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.app.ActivityCompat
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.content.res.AppCompatResources
 import androidx.core.app.ActivityCompat
+import androidx.fragment.app.DialogFragment
 
 import com.stoutner.privacycell.R
 
 import com.stoutner.privacycell.R
+import com.stoutner.privacycell.dialogs.PhonePermissionDialog
+
+class PrivacyCell : AppCompatActivity(), PhonePermissionDialog.StoragePermissionDialogListener {
+    // Declare the class variables.
+    private lateinit var context: Context
+    private lateinit var telephonyManager: TelephonyManager
+
+    // Declare the views.
+    lateinit var secureFromStingrayImageView: ImageView
+    lateinit var secureFromStingrayTextView: TextView
+    lateinit var voiceNetworkTextView: TextView
+    lateinit var voiceNetworkDetailsTextView: TextView
+    lateinit var dataNetworkTextView: TextView
+    lateinit var dataNetworkDetailsTextView: TextView
+    lateinit var additionalNetworkInfoTextView: TextView
+    lateinit var additionalNetworkInfoDetailsTextView: TextView
 
 
-class PrivacyCell : AppCompatActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         // Run the default commands.
         super.onCreate(savedInstanceState)
 
         // Set the content view.
     override fun onCreate(savedInstanceState: Bundle?) {
         // Run the default commands.
         super.onCreate(savedInstanceState)
 
         // Set the content view.
-        setContentView(R.layout.privacy_cell_linearlayout)
+        setContentView(R.layout.privacy_cell_scrollview)
 
         // Get handles for the views.
 
         // Get handles for the views.
-        val secureFromStingrayImageView = findViewById<ImageView>(R.id.secure_from_stingray_imageview)
-        val secureFromStingrayTextView = findViewById<TextView>(R.id.secure_from_stingray_textview)
-        val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
-        val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
-        val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
-        val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
-        val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
-        val additionalNetworkInfoDetailsTextView = findViewById<TextView>(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<out String>, 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() {
         // Listen to changes in the cell network state.
         telephonyManager.listen(object : PhoneStateListener() {
-            override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {// Populate the secure from stingray text view.  <https://source.android.com/devices/tech/connect/acts-5g-testing>
-                // Populate the stingray security information.
+            override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
+                // Populate the stingray security information.  <https://source.android.com/devices/tech/connect/acts-5g-testing>
                 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))
                 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.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))
                 } 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.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<String> {
         // Return the string that corresponds to the network type.
         return when(networkType) {
     }
 
     private fun getNetworkType(networkType: Int) : Array<String> {
         // 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))
             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))
             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 (file)
index 0000000..f6a0d38
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Cell <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+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 (file)
index 0000000..983dd21
--- /dev/null
@@ -0,0 +1,12 @@
+<!-- This file comes from the Android Material icon set, where it is called `phone_android`.  It is released under the Apache License 2.0. -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24">
+
+    <path
+        android:fillColor="@color/icon"
+        android:pathData="M16,1L8,1C6.34,1 5,2.34 5,4v16c0,1.66 1.34,3 3,3h8c1.66,0 3,-1.34 3,-3L19,4c0,-1.66 -1.34,-3 -3,-3zM14,21h-4v-1h4v1zM17.25,18L6.75,18L6.75,4h10.5v14z" />
+</vector>
diff --git a/app/src/main/res/layout/privacy_cell_linearlayout.xml b/app/src/main/res/layout/privacy_cell_linearlayout.xml
deleted file mode 100644 (file)
index 167ae8f..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-  Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
-
-  This file is part of Privacy Cell <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>. -->
-
-<ScrollView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_height="match_parent"
-    android:layout_width="wrap_content" >
-
-    <LinearLayout
-        android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:orientation="vertical"
-        android:padding="15dp" >
-
-        <!-- Secure from stingray. -->
-        <ImageView
-            android:id="@+id/secure_from_stingray_imageview"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            tools:ignore="ContentDescription" />
-
-        <TextView
-            android:id="@+id/secure_from_stingray_textview"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center"
-            android:layout_marginTop="10dp"
-            android:textSize="20sp"
-            android:textStyle="bold" />
-
-        <!-- Voice network. -->
-        <TextView
-            android:id="@+id/voice_network"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center"
-            android:layout_marginTop="30dp"
-            android:textColor="@color/blue_700"
-            android:textSize="18sp"
-            android:textStyle="bold" />
-
-        <TextView
-            android:id="@+id/voice_network_details"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center"
-            android:layout_marginBottom="10dp" />
-
-        <!-- Data network. -->
-        <TextView
-            android:id="@+id/data_network"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center"
-            android:textColor="@color/blue_700"
-            android:textSize="18sp"
-            android:textStyle="bold" />
-
-        <TextView
-            android:id="@+id/data_network_details"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center"
-            android:layout_marginBottom="10dp" />
-
-        <!-- Additional network info. -->
-        <TextView
-            android:id="@+id/additional_network_info"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center"
-            android:textColor="@color/blue_700"
-            android:textSize="18sp"
-            android:textStyle="bold" />
-
-        <TextView
-            android:id="@+id/additional_network_info_details"
-            android:layout_height="wrap_content"
-            android:layout_width="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:textAlignment="center" />
-    </LinearLayout>
-</ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/layout/privacy_cell_scrollview.xml b/app/src/main/res/layout/privacy_cell_scrollview.xml
new file mode 100644 (file)
index 0000000..77ad82f
--- /dev/null
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Cell <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>. -->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_height="match_parent"
+    android:layout_width="wrap_content" >
+
+    <LinearLayout
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:orientation="vertical"
+        android:padding="15dp" >
+
+        <!-- Secure from stingray. -->
+        <ImageView
+            android:id="@+id/secure_from_stingray_imageview"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            tools:ignore="ContentDescription" />
+
+        <!-- The text color primary is only displayed if the read phone state permission is not granted. -->
+        <TextView
+            android:id="@+id/secure_from_stingray_textview"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center"
+            android:layout_marginTop="10dp"
+            android:textColor="?android:textColorPrimary"
+            android:textSize="20sp"
+            android:textStyle="bold" />
+
+        <!-- Voice network. -->
+        <TextView
+            android:id="@+id/voice_network"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center"
+            android:layout_marginTop="30dp"
+            android:textColor="@color/blue_text"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+        <TextView
+            android:id="@+id/voice_network_details"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center"
+            android:layout_marginBottom="10dp" />
+
+        <!-- Data network. -->
+        <TextView
+            android:id="@+id/data_network"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center"
+            android:textColor="@color/blue_text"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+        <TextView
+            android:id="@+id/data_network_details"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center"
+            android:layout_marginBottom="10dp" />
+
+        <!-- Additional network info. -->
+        <TextView
+            android:id="@+id/additional_network_info"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center"
+            android:textColor="@color/blue_text"
+            android:textSize="18sp"
+            android:textStyle="bold" />
+
+        <TextView
+            android:id="@+id/additional_network_info_details"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:textAlignment="center" />
+    </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
new file mode 100644 (file)
index 0000000..2293934
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Cell <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>. -->
+
+<resources>
+    <!-- Nicknamed colors. -->
+    <color name="blue_text">@color/violet_500</color>
+    <color name="icon">@color/violet_500</color>
+    <color name="red_text">@color/salmon</color>
+
+    <!-- Raw colors. -->
+    <color name="black">#FF000000</color>
+    <color name="blue_500">#FF2196F3</color>
+    <color name="blue_700">#FF1976D2</color>
+    <color name="blue_900">#FF0D47A1</color>
+    <color name="red_600">#FFE53935</color>
+    <color name="red_700">#FFD32F2F</color>
+    <color name="salmon">#FFFC684E</color>
+    <color name="soft_red">#FFC73625</color>
+    <color name="violet_500">#FF8AB4F8</color>
+    <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
index 674e26cc0b3fc892553e6f3048568eca962e16ae..b8e34ae97900441adec86ab2fe442a646fe3ff01 100644 (file)
@@ -16,7 +16,7 @@
   You should have received a copy of the GNU General Public License
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
   You should have received a copy of the GNU General Public License
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
-<resources xmlns:tools="http://schemas.android.com/tools">
+<resources>
     <style name="Theme.PrivacyCell" parent="Theme.AppCompat.DayNight">
         <!-- Main items. -->
         <item name="android:navigationBarColor">?android:attr/colorBackground</item>
     <style name="Theme.PrivacyCell" parent="Theme.AppCompat.DayNight">
         <!-- Main items. -->
         <item name="android:navigationBarColor">?android:attr/colorBackground</item>
@@ -24,4 +24,9 @@
         <item name="android:windowLightNavigationBar">false</item>
         <item name="android:windowLightStatusBar">false</item>
     </style>
         <item name="android:windowLightNavigationBar">false</item>
         <item name="android:windowLightStatusBar">false</item>
     </style>
+
+    <style name="Theme.PrivacyCellAlertDialog" parent="Theme.AppCompat.DayNight.Dialog.Alert">
+        <!-- Colors. -->
+        <item name="colorAccent">@color/violet_500</item>
+    </style>
 </resources>
\ No newline at end of file
 </resources>
\ No newline at end of file
index 7bcc7820f3d278095f21bf75987dbd385f66e78d..d6d743b12badb8e35d188a6a12026722fc6f965f 100644 (file)
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
 <resources>
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
 <resources>
+    <!-- Nicknamed colors. -->
+    <color name="blue_text">@color/blue_700</color>
+    <color name="icon">@color/blue_700</color>
+    <color name="red_text">@color/red_700</color>
+
+    <!-- Raw colors. -->
     <color name="black">#FF000000</color>
     <color name="blue_500">#FF2196F3</color>
     <color name="blue_700">#FF1976D2</color>
     <color name="blue_900">#FF0D47A1</color>
     <color name="red_600">#FFE53935</color>
     <color name="red_700">#FFD32F2F</color>
     <color name="black">#FF000000</color>
     <color name="blue_500">#FF2196F3</color>
     <color name="blue_700">#FF1976D2</color>
     <color name="blue_900">#FF0D47A1</color>
     <color name="red_600">#FFE53935</color>
     <color name="red_700">#FFD32F2F</color>
+    <color name="salmon">#FFFC684E</color>
+    <color name="soft_red">#FFC73625</color>
+    <color name="violet_500">#FF8AB4F8</color>
     <color name="white">#FFFFFFFF</color>
 </resources>
\ No newline at end of file
     <color name="white">#FFFFFFFF</color>
 </resources>
\ No newline at end of file
index 5a461af95d9d08693ab07ac4966a8c2d788199aa..66cb8e526d90a82d43be1c8e88bd07e87b03e573 100644 (file)
     <string name="lte_nr_nsa_mmwave_detail">Long Term Evolution New Radio Non-Standalone millimeter Wave</string>
 
     <!-- Stingray. -->
     <string name="lte_nr_nsa_mmwave_detail">Long Term Evolution New Radio Non-Standalone millimeter Wave</string>
 
     <!-- Stingray. -->
+    <!-- The `\n\n` code inserts a line break and should be maintained in translations. -->
     <string name="secure_from_stingray">Your device is connected to a standalone 5G network.\n\nIt is secure from stingray IMSI man-in-the-middle attacks.</string>
     <string name="not_secure_from_stingray">Your device is not connected to a standalone 5G network.\n\nIt is not secure from stingray IMSI man-in-the-middle attacks.</string>
     <string name="secure_from_stingray">Your device is connected to a standalone 5G network.\n\nIt is secure from stingray IMSI man-in-the-middle attacks.</string>
     <string name="not_secure_from_stingray">Your device is not connected to a standalone 5G network.\n\nIt is not secure from stingray IMSI man-in-the-middle attacks.</string>
+
+    <!-- Phone permission dialog. -->
+    <string name="phone_permission">Phone Permission</string>
+    <string name="phone_permission_text">Privacy Cell needs the Read Phone State permission to determine the safety level of your cell connection.</string>
+    <string name="ok">OK</string>
 </resources>
\ No newline at end of file
 </resources>
\ No newline at end of file
index e4e0d1b33ff590cf043f3137ba7bc8f334d8b373..ccc93244ab46beb8bf2eb92b03b72cdac72c1e97 100644 (file)
@@ -16,7 +16,7 @@
   You should have received a copy of the GNU General Public License
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
   You should have received a copy of the GNU General Public License
   along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>. -->
 
-<resources xmlns:tools="http://schemas.android.com/tools">
+<resources>
     <style name="Theme.PrivacyCell" parent="Theme.AppCompat.DayNight">
         <!-- Main items. -->
         <item name="android:navigationBarColor">?android:attr/colorBackground</item>
     <style name="Theme.PrivacyCell" parent="Theme.AppCompat.DayNight">
         <!-- Main items. -->
         <item name="android:navigationBarColor">?android:attr/colorBackground</item>
@@ -24,4 +24,9 @@
         <item name="android:windowLightNavigationBar">true</item>
         <item name="android:windowLightStatusBar">true</item>
     </style>
         <item name="android:windowLightNavigationBar">true</item>
         <item name="android:windowLightStatusBar">true</item>
     </style>
+
+    <style name="Theme.PrivacyCellAlertDialog" parent="Theme.AppCompat.DayNight.Dialog.Alert">
+        <!-- Colors. -->
+        <item name="colorAccent">@color/blue_700</item>
+    </style>
 </resources>
\ No newline at end of file
 </resources>
\ No newline at end of file