From 48a9998c26a4923179382dec9906a574e42cadf2 Mon Sep 17 00:00:00 2001
From: lucky <>
Date: Fri, 4 Feb 2022 14:22:57 +0300
Subject: [PATCH] rename to Red
show redirection destination
fix dialer end call
add priority Signal > Telegram
---
README.md | 28 ++--
app/build.gradle | 6 +-
.../{re => red}/ExampleInstrumentedTest.kt | 4 +-
app/src/main/AndroidManifest.xml | 6 +-
.../me/lucky/re/CallRedirectionService.kt | 100 --------------
app/src/main/java/me/lucky/re/DialogWindow.kt | 67 ----------
.../java/me/lucky/{re => red}/Application.kt | 2 +-
.../me/lucky/red/CallRedirectionService.kt | 124 ++++++++++++++++++
.../java/me/lucky/{re => red}/MainActivity.kt | 4 +-
app/src/main/java/me/lucky/red/PopupWindow.kt | 107 +++++++++++++++
.../java/me/lucky/{re => red}/Preferences.kt | 2 +-
app/src/main/res/layout/popup.xml | 1 -
app/src/main/res/values-night/themes.xml | 2 +-
app/src/main/res/values-ru/strings.xml | 8 +-
app/src/main/res/values/strings.xml | 8 +-
app/src/main/res/values/themes.xml | 2 +-
.../me/lucky/{re => red}/ExampleUnitTest.kt | 2 +-
build.gradle | 2 +-
data/{re.svg => red.svg} | 0
.../metadata/android/en-US/changelogs/2.txt | 4 +
.../android/en-US/full_description.txt | 6 +-
.../android/en-US/images/featureGraphic.png | Bin 14709 -> 16521 bytes
.../en-US/images/phoneScreenshots/1.png | Bin 60719 -> 62368 bytes
.../android/en-US/short_description.txt | 2 +-
fastlane/metadata/android/en-US/title.txt | 2 +-
.../android/ru-RU/full_description.txt | 15 ++-
.../android/ru-RU/short_description.txt | 2 +-
fastlane/metadata/android/ru-RU/title.txt | 2 +-
settings.gradle | 2 +-
29 files changed, 291 insertions(+), 219 deletions(-)
rename app/src/androidTest/java/me/lucky/{re => red}/ExampleInstrumentedTest.kt (87%)
delete mode 100644 app/src/main/java/me/lucky/re/CallRedirectionService.kt
delete mode 100644 app/src/main/java/me/lucky/re/DialogWindow.kt
rename app/src/main/java/me/lucky/{re => red}/Application.kt (92%)
create mode 100644 app/src/main/java/me/lucky/red/CallRedirectionService.kt
rename app/src/main/java/me/lucky/{re => red}/MainActivity.kt (97%)
create mode 100644 app/src/main/java/me/lucky/red/PopupWindow.kt
rename app/src/main/java/me/lucky/{re => red}/Preferences.kt (95%)
rename app/src/test/java/me/lucky/{re => red}/ExampleUnitTest.kt (93%)
rename data/{re.svg => red.svg} (100%)
create mode 100644 fastlane/metadata/android/en-US/changelogs/2.txt
diff --git a/README.md b/README.md
index 75ceb26..55a5c94 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,35 @@
-# Re
+# Red
-Redirect outgoing calls to Signal or Telegram.
+Redirect outgoing calls to Signal/Telegram.
[](https://f-droid.org/packages/me.lucky.re/)
-
-[comment]: <> ([ ( src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png")
-
-[comment]: <> ( alt="Get it on Google Play")
-
-[comment]: <> ( height="80">](https://play.google.com/store/apps/details?id=me.lucky.re))
+ height="80">](https://f-droid.org/packages/me.lucky.red/)
+[](https://play.google.com/store/apps/details?id=me.lucky.red)
-Tiny app to redirect outgoing calls to Signal or Telegram if available.
+Tiny app to redirect outgoing calls to Signal/Telegram if available.
-You can cancel redirection by clicking on `Redirecting` popup.
+You can cancel redirection by clicking on `Redirecting to..` popup.
## Permissions
* ACCESS_NETWORK_STATE - check internet is available
* CALL_PHONE - make a call via messenger
* READ_CONTACTS - check contact has a messenger record
-* SYSTEM_ALERT_WINDOW - show redirecting popup and launch activity from background
+* SYSTEM_ALERT_WINDOW - show redirecting popup and launch an activity from background
* CALL_REDIRECTION - process outgoing call
+All permissions are mandatory.
+
## License
[![GNU GPLv3 Image](https://www.gnu.org/graphics/gplv3-127x51.png)](https://www.gnu.org/licenses/gpl-3.0.en.html)
diff --git a/app/build.gradle b/app/build.gradle
index dd2d459..325b6a5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,11 +7,11 @@ android {
compileSdk 32
defaultConfig {
- applicationId "me.lucky.re"
+ applicationId "me.lucky.red"
minSdk 29
targetSdk 32
- versionCode 1
- versionName "1.0.0"
+ versionCode 2
+ versionName "1.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
diff --git a/app/src/androidTest/java/me/lucky/re/ExampleInstrumentedTest.kt b/app/src/androidTest/java/me/lucky/red/ExampleInstrumentedTest.kt
similarity index 87%
rename from app/src/androidTest/java/me/lucky/re/ExampleInstrumentedTest.kt
rename to app/src/androidTest/java/me/lucky/red/ExampleInstrumentedTest.kt
index 9c1318c..2579b90 100644
--- a/app/src/androidTest/java/me/lucky/re/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/me/lucky/red/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package me.lucky.re
+package me.lucky.red
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("me.lucky.re", appContext.packageName)
+ assertEquals("me.lucky.red", appContext.packageName)
}
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ecbf4db..496a2a3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+ package="me.lucky.red">
@@ -9,14 +9,12 @@
+ android:theme="@style/Theme.Red">
+ try {
+ records = getRecordsFromPhoneNumber(handle.schemeSpecificPart)
+ } catch (exc: SecurityException) {
+ placeCallUnmodified()
+ return
+ }
+ val record = records.minByOrNull { MIMETYPES[it.mimetype] ?: 0 }
+ if (record == null) {
+ placeCallUnmodified()
+ return
+ }
+ window.show(record.uri, when (record.mimetype) {
+ SIGNAL_MIMETYPE -> R.string.signal
+ TELEGRAM_MIMETYPE -> R.string.telegram
+ else -> return
+ })
+ }
+
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ private fun getContactIdByPhoneNumber(phoneNumber: String): String? {
+ var result: String? = null
+ val cursor = contentResolver.query(
+ Uri.withAppendedPath(
+ ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(phoneNumber)
+ ),
+ arrayOf(ContactsContract.PhoneLookup._ID),
+ null,
+ null,
+ null,
+ )
+ cursor?.apply {
+ if (moveToFirst())
+ result = getString(getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID))
+ close()
+ }
+ return result
+ }
+
+ private data class Record(val uri: Uri, val mimetype: String)
+
+ @RequiresPermission(Manifest.permission.READ_CONTACTS)
+ private fun getRecordsFromPhoneNumber(phoneNumber: String): Array {
+ val results = mutableSetOf()
+ val contactId = getContactIdByPhoneNumber(phoneNumber) ?: return results.toTypedArray()
+ val cursor = contentResolver.query(
+ ContactsContract.Data.CONTENT_URI,
+ arrayOf(ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE),
+ "${ContactsContract.Data.CONTACT_ID} = ? AND " +
+ "${ContactsContract.Data.MIMETYPE} IN " +
+ "(${MIMETYPES.keys.joinToString(",") { "?" }})",
+ arrayOf(contactId, *MIMETYPES.keys.toTypedArray()),
+ null,
+ )
+ cursor?.apply {
+ while (moveToNext())
+ results.add(Record(
+ Uri.withAppendedPath(
+ ContactsContract.Data.CONTENT_URI,
+ Uri.encode(getString(getColumnIndexOrThrow(ContactsContract.Data._ID))),
+ ),
+ getString(getColumnIndexOrThrow(ContactsContract.Data.MIMETYPE)),
+ ))
+ close()
+ }
+ return results.toTypedArray()
+ }
+
+ @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE)
+ private fun hasInternet(): Boolean {
+ val capabilities = connectivityManager
+ ?.getNetworkCapabilities(connectivityManager?.activeNetwork) ?: return false
+ return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
+ capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
+ }
+}
diff --git a/app/src/main/java/me/lucky/re/MainActivity.kt b/app/src/main/java/me/lucky/red/MainActivity.kt
similarity index 97%
rename from app/src/main/java/me/lucky/re/MainActivity.kt
rename to app/src/main/java/me/lucky/red/MainActivity.kt
index bb5f569..8d571c9 100644
--- a/app/src/main/java/me/lucky/re/MainActivity.kt
+++ b/app/src/main/java/me/lucky/red/MainActivity.kt
@@ -1,4 +1,4 @@
-package me.lucky.re
+package me.lucky.red
import android.Manifest
import android.app.role.RoleManager
@@ -9,7 +9,7 @@ import android.provider.Settings
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
-import me.lucky.re.databinding.ActivityMainBinding
+import me.lucky.red.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
companion object {
diff --git a/app/src/main/java/me/lucky/red/PopupWindow.kt b/app/src/main/java/me/lucky/red/PopupWindow.kt
new file mode 100644
index 0000000..6156826
--- /dev/null
+++ b/app/src/main/java/me/lucky/red/PopupWindow.kt
@@ -0,0 +1,107 @@
+package me.lucky.red
+
+import android.Manifest
+import android.content.Intent
+import android.graphics.PixelFormat
+import android.media.AudioManager
+import android.net.Uri
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.WindowManager
+import android.widget.TextView
+import androidx.annotation.RequiresPermission
+import java.util.*
+import kotlin.concurrent.timerTask
+
+class PopupWindow(private val service: CallRedirectionService) {
+ companion object {
+ private const val CANCEL_DELAY = 2000L
+ }
+
+ private val windowManager = service
+ .applicationContext
+ .getSystemService(WindowManager::class.java)
+ private val audioManager = service
+ .applicationContext
+ .getSystemService(AudioManager::class.java)
+ @Suppress("InflateParams")
+ private val view = LayoutInflater
+ .from(service.applicationContext)
+ .inflate(R.layout.popup, null)
+ private val layoutParams = WindowManager.LayoutParams().apply {
+ format = PixelFormat.TRANSLUCENT
+ flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ gravity = Gravity.BOTTOM
+ width = WindowManager.LayoutParams.WRAP_CONTENT
+ height = WindowManager.LayoutParams.WRAP_CONTENT
+ y = 333
+ }
+ private var timer: Timer? = null
+
+ init {
+ view.setOnClickListener {
+ timer?.cancel()
+ service.placeCallUnmodified()
+ remove()
+ }
+ }
+
+ fun show(uri: Uri, destinationId: Int) {
+ if (!remove()) {
+ service.placeCallUnmodified()
+ return
+ }
+ timer?.cancel()
+ timer = Timer()
+ timer?.schedule(timerTask {
+ if (!remove()) {
+ service.placeCallUnmodified()
+ return@timerTask
+ }
+ if (audioManager?.mode != AudioManager.MODE_IN_CALL) {
+ service.placeCallUnmodified()
+ return@timerTask
+ }
+ try {
+ call(uri)
+ } catch (exc: SecurityException) {
+ service.placeCallUnmodified()
+ return@timerTask
+ }
+ service.cancelCall()
+ }, CANCEL_DELAY)
+ view.findViewById(R.id.description).text = String.format(
+ service.getString(R.string.popup),
+ service.getString(destinationId),
+ )
+ if (!add()) {
+ timer?.cancel()
+ service.placeCallUnmodified()
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.CALL_PHONE)
+ private fun call(data: Uri) {
+ Intent(Intent.ACTION_VIEW).let {
+ it.data = data
+ it.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ service.startActivity(it)
+ }
+ }
+
+ private fun add(): Boolean {
+ try {
+ windowManager?.addView(view, layoutParams)
+ } catch (exc: WindowManager.BadTokenException) { return false }
+ return true
+ }
+
+ private fun remove(): Boolean {
+ try {
+ windowManager?.removeView(view)
+ } catch (exc: IllegalArgumentException) {
+ } catch (exc: WindowManager.BadTokenException) { return false }
+ return true
+ }
+}
diff --git a/app/src/main/java/me/lucky/re/Preferences.kt b/app/src/main/java/me/lucky/red/Preferences.kt
similarity index 95%
rename from app/src/main/java/me/lucky/re/Preferences.kt
rename to app/src/main/java/me/lucky/red/Preferences.kt
index 14d679f..1c617e0 100644
--- a/app/src/main/java/me/lucky/re/Preferences.kt
+++ b/app/src/main/java/me/lucky/red/Preferences.kt
@@ -1,4 +1,4 @@
-package me.lucky.re
+package me.lucky.red
import android.content.Context
import androidx.core.content.edit
diff --git a/app/src/main/res/layout/popup.xml b/app/src/main/res/layout/popup.xml
index 434a2f1..03b30b0 100644
--- a/app/src/main/res/layout/popup.xml
+++ b/app/src/main/res/layout/popup.xml
@@ -14,7 +14,6 @@
android:layout_height="wrap_content"
android:background="@color/popup"
android:padding="16dp"
- android:text="@string/info"
android:textColor="@color/black"
android:textSize="16sp" />
diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml
index 684c95e..89f8cb1 100644
--- a/app/src/main/res/values-night/themes.xml
+++ b/app/src/main/res/values-night/themes.xml
@@ -1,6 +1,6 @@
-