rename to Red
show redirection destination fix dialer end call add priority Signal > Telegram
This commit is contained in:
parent
9b3c582337
commit
48a9998c26
29 changed files with 291 additions and 219 deletions
28
README.md
28
README.md
|
@ -1,37 +1,35 @@
|
|||
# Re
|
||||
# Red
|
||||
|
||||
Redirect outgoing calls to Signal or Telegram.
|
||||
Redirect outgoing calls to Signal/Telegram.
|
||||
|
||||
[<img
|
||||
src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
|
||||
alt="Get it on F-Droid"
|
||||
height="80">](https://f-droid.org/packages/me.lucky.re/)
|
||||
|
||||
[comment]: <> ([<img)
|
||||
|
||||
[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/)
|
||||
[<img
|
||||
src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png"
|
||||
alt="Get it on Google Play"
|
||||
height="80">](https://play.google.com/store/apps/details?id=me.lucky.red)
|
||||
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/x13a/Re/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png"
|
||||
src="https://raw.githubusercontent.com/x13a/Red/main/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png"
|
||||
width="30%"
|
||||
height="30%">
|
||||
|
||||
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
|
||||
[](https://www.gnu.org/licenses/gpl-3.0.en.html)
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="me.lucky.re">
|
||||
package="me.lucky.red">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
|
@ -9,14 +9,12 @@
|
|||
<uses-feature android:name="android.hardware.telephony" android:required="true" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:name=".Application"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Re">
|
||||
android:theme="@style/Theme.Red">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
package me.lucky.re
|
||||
|
||||
import android.database.Cursor
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.telecom.CallRedirectionService
|
||||
import android.telecom.PhoneAccountHandle
|
||||
|
||||
class CallRedirectionService : CallRedirectionService() {
|
||||
companion object {
|
||||
private const val SIGNAL_MIMETYPE = "vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call"
|
||||
private const val TELEGRAM_MIMETYPE = "vnd.android.cursor.item/vnd.org.telegram.messenger.android.call"
|
||||
}
|
||||
|
||||
private lateinit var prefs: Preferences
|
||||
private lateinit var dialog: DialogWindow
|
||||
private var connectivityManager: ConnectivityManager? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
prefs = Preferences(this)
|
||||
dialog = DialogWindow(this)
|
||||
connectivityManager = getSystemService(ConnectivityManager::class.java)
|
||||
}
|
||||
|
||||
override fun onPlaceCall(
|
||||
handle: Uri,
|
||||
initialPhoneAccount: PhoneAccountHandle,
|
||||
allowInteractiveResponse: Boolean,
|
||||
) {
|
||||
if (!prefs.isServiceEnabled || !hasInternet()) {
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
val uri = getUriFromPhoneNumber(handle.schemeSpecificPart)
|
||||
if (uri != null) {
|
||||
dialog.show(uri)
|
||||
return
|
||||
}
|
||||
placeCallUnmodified()
|
||||
}
|
||||
|
||||
private fun getContactIdByPhoneNumber(phoneNumber: String): String? {
|
||||
var result: String? = null
|
||||
val cursor: Cursor?
|
||||
try {
|
||||
cursor = contentResolver.query(
|
||||
Uri.withAppendedPath(
|
||||
ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
|
||||
Uri.encode(phoneNumber)
|
||||
),
|
||||
arrayOf(ContactsContract.PhoneLookup._ID),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
} catch (exc: SecurityException) { return null }
|
||||
cursor?.apply {
|
||||
if (moveToFirst()) {
|
||||
result = getString(getColumnIndexOrThrow(ContactsContract.PhoneLookup._ID))
|
||||
}
|
||||
close()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getUriFromPhoneNumber(phoneNumber: String): Uri? {
|
||||
val contactId = getContactIdByPhoneNumber(phoneNumber) ?: return null
|
||||
var result: Uri? = null
|
||||
val cursor: Cursor?
|
||||
try {
|
||||
cursor = contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
arrayOf(ContactsContract.Data._ID),
|
||||
"${ContactsContract.Data.CONTACT_ID} = ? AND " +
|
||||
"${ContactsContract.Data.MIMETYPE} IN (?, ?)",
|
||||
arrayOf(contactId, SIGNAL_MIMETYPE, TELEGRAM_MIMETYPE),
|
||||
null,
|
||||
)
|
||||
} catch (exc: SecurityException) { return null }
|
||||
cursor?.apply {
|
||||
if (moveToFirst()) {
|
||||
result = Uri.withAppendedPath(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
Uri.encode(getString(getColumnIndexOrThrow(ContactsContract.Data._ID))),
|
||||
)
|
||||
}
|
||||
close()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun hasInternet(): Boolean {
|
||||
return connectivityManager
|
||||
?.getNetworkCapabilities(connectivityManager?.activeNetwork)
|
||||
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
package me.lucky.re
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.PixelFormat
|
||||
import android.net.Uri
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.WindowManager
|
||||
import java.util.*
|
||||
import kotlin.concurrent.timerTask
|
||||
|
||||
class DialogWindow(private val service: CallRedirectionService) {
|
||||
companion object {
|
||||
private const val CANCEL_DELAY = 3000L
|
||||
}
|
||||
|
||||
private val windowManager = service
|
||||
.applicationContext
|
||||
.getSystemService(WindowManager::class.java)
|
||||
@Suppress("InflateParams")
|
||||
private val floatView = LayoutInflater
|
||||
.from(service.applicationContext)
|
||||
.inflate(R.layout.popup, null)
|
||||
private val layoutParams: WindowManager.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 = 384
|
||||
}
|
||||
private var data: Uri? = null
|
||||
private var timer: Timer? = null
|
||||
|
||||
init {
|
||||
floatView.setOnClickListener {
|
||||
timer?.cancel()
|
||||
service.placeCallUnmodified()
|
||||
remove()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(uri: Uri?) {
|
||||
remove()
|
||||
timer?.cancel()
|
||||
timer = Timer()
|
||||
timer?.schedule(timerTask {
|
||||
service.cancelCall()
|
||||
remove()
|
||||
Intent(Intent.ACTION_VIEW).also {
|
||||
it.data = data
|
||||
it.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
service.startActivity(it)
|
||||
}
|
||||
data = null
|
||||
}, CANCEL_DELAY)
|
||||
data = uri
|
||||
windowManager?.addView(floatView, layoutParams)
|
||||
}
|
||||
|
||||
private fun remove() {
|
||||
try {
|
||||
windowManager?.removeView(floatView)
|
||||
} catch (exc: IllegalArgumentException) {}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package me.lucky.re
|
||||
package me.lucky.red
|
||||
|
||||
import android.app.Application
|
||||
import com.google.android.material.color.DynamicColors
|
124
app/src/main/java/me/lucky/red/CallRedirectionService.kt
Normal file
124
app/src/main/java/me/lucky/red/CallRedirectionService.kt
Normal file
|
@ -0,0 +1,124 @@
|
|||
package me.lucky.red
|
||||
|
||||
import android.Manifest
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import android.telecom.CallRedirectionService
|
||||
import android.telecom.PhoneAccountHandle
|
||||
import androidx.annotation.RequiresPermission
|
||||
|
||||
class CallRedirectionService : CallRedirectionService() {
|
||||
companion object {
|
||||
private const val SIGNAL_MIMETYPE =
|
||||
"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call"
|
||||
private const val TELEGRAM_MIMETYPE =
|
||||
"vnd.android.cursor.item/vnd.org.telegram.messenger.android.call"
|
||||
private val MIMETYPES = mapOf(
|
||||
SIGNAL_MIMETYPE to 0,
|
||||
TELEGRAM_MIMETYPE to 1,
|
||||
)
|
||||
}
|
||||
|
||||
private lateinit var prefs: Preferences
|
||||
private lateinit var window: PopupWindow
|
||||
private var connectivityManager: ConnectivityManager? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
init()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
prefs = Preferences(this)
|
||||
window = PopupWindow(this)
|
||||
connectivityManager = getSystemService(ConnectivityManager::class.java)
|
||||
}
|
||||
|
||||
override fun onPlaceCall(
|
||||
handle: Uri,
|
||||
initialPhoneAccount: PhoneAccountHandle,
|
||||
allowInteractiveResponse: Boolean,
|
||||
) {
|
||||
if (!prefs.isServiceEnabled || !hasInternet() || !allowInteractiveResponse) {
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
val records: Array<Record>
|
||||
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<Record> {
|
||||
val results = mutableSetOf<Record>()
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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 {
|
107
app/src/main/java/me/lucky/red/PopupWindow.kt
Normal file
107
app/src/main/java/me/lucky/red/PopupWindow.kt
Normal file
|
@ -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<TextView>(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
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package me.lucky.re
|
||||
package me.lucky.red
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Re" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<style name="Theme.Red" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_200</item>
|
||||
<item name="colorOnPrimary">@color/black</item>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Re</string>
|
||||
<string name="description">Приложение будет от всей души пытаться перенаправить исходящие вызовы в Сигнал или Телеграм, но иногда у него может не получаться. Для работы ему нужны тонны разрешений. Кликайте на переключатель и выдавайте разрешения пока он не включится.</string>
|
||||
<string name="info">Перенаправление</string>
|
||||
<string name="app_name">Red</string>
|
||||
<string name="description">Приложение будет пытаться перенаправить исходящие вызовы в Signal/Telegram. Для работы ему нужно много разрешений. Кликайте на переключатель и выдавайте разрешения пока он не включится.</string>
|
||||
<string name="popup">Перенаправление в %1$s</string>
|
||||
<string name="signal">Signal</string>
|
||||
<string name="telegram">Telegram</string>
|
||||
</resources>
|
|
@ -1,6 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Re</string>
|
||||
<string name="description">The app will try to redirect outgoing calls to Signal or Telegram if available. For work it will require many permissions. Click on toggle and grant permissions until it turns ON.</string>
|
||||
<string name="info">Redirecting</string>
|
||||
<string name="app_name">Red</string>
|
||||
<string name="description">The app will try to redirect outgoing calls to Signal/Telegram if available. For work it requires many permissions. Click on the toggle and grant permissions until it turns ON.</string>
|
||||
<string name="popup">Redirecting to %1$s</string>
|
||||
<string name="signal">Signal</string>
|
||||
<string name="telegram">Telegram</string>
|
||||
</resources>
|
|
@ -1,6 +1,6 @@
|
|||
<resources>
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.Re" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<style name="Theme.Red" parent="Theme.Material3.DayNight.NoActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package me.lucky.re
|
||||
package me.lucky.red
|
||||
|
||||
import org.junit.Test
|
||||
|
|
@ -5,7 +5,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:7.0.4"
|
||||
classpath 'com.android.tools.build:gradle:7.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
|
Before Width: | Height: | Size: 876 B After Width: | Height: | Size: 876 B |
4
fastlane/metadata/android/en-US/changelogs/2.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/2.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
rename to Red
|
||||
show redirection destination
|
||||
fix dialer end call
|
||||
add priority Signal > Telegram
|
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
|
@ -9,5 +9,7 @@ Permissions:
|
|||
* SYSTEM_ALERT_WINDOW - show redirecting popup and launch an activity from background
|
||||
* CALL_REDIRECTION - process outgoing call
|
||||
|
||||
All permissions are mandatory.
|
||||
|
||||
It is Free Open Source Software.
|
||||
License: GPL-3
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
Binary file not shown.
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB |
|
@ -1 +1 @@
|
|||
Redirect outgoing calls to Signal or Telegram
|
||||
Redirect outgoing calls to Signal/Telegram
|
||||
|
|
|
@ -1 +1 @@
|
|||
Re
|
||||
Red
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
Минимальное приложение для перенаправления исходящих звонков в Сигнал или Телеграм, если возможно.
|
||||
Минимальное приложение для перенаправления исходящих вызовов в Signal/Telegram.
|
||||
|
||||
Вы можете отменить перенаправление, кликнув на всплывающий блок "Перенаправление".
|
||||
Вы можете отменить перенаправление, кликнув на всплывающее сообщение "Перенаправление в..".
|
||||
|
||||
Разрешения:
|
||||
* ACCESS_NETWORK_STATE - проверить доступность интернета
|
||||
* CALL_PHONE - сделать звонок через мессенджер
|
||||
* ACCESS_NETWORK_STATE - проверить наличие интернета
|
||||
* CALL_PHONE - позвонить через мессенджер
|
||||
* READ_CONTACTS - проверить контакт на наличие записи из мессенджера
|
||||
* SYSTEM_ALERT_WINDOW - показать всплывающий блок о перенаправлении и запустить активити из фона
|
||||
* CALL_REDIRECTION - обработать исходящий звонок
|
||||
* SYSTEM_ALERT_WINDOW - показать всплывающее сообщение о перенаправлении и запустить активити из
|
||||
фона
|
||||
* CALL_REDIRECTION - обработать исходящий вызов
|
||||
|
||||
Все разрешения обязательны для работы приложения.
|
||||
|
||||
Это свободное программное обеспечение с открытым исходным кодом.
|
||||
Лицензия: GPL-3
|
||||
|
|
|
@ -1 +1 @@
|
|||
Перенаправить исходящие звонки в Сигнал или Телеграм
|
||||
Перенаправить исходящие вызовы в Signal/Telegram
|
||||
|
|
|
@ -1 +1 @@
|
|||
Re
|
||||
Red
|
||||
|
|
|
@ -5,5 +5,5 @@ dependencyResolutionManagement {
|
|||
mavenCentral()
|
||||
}
|
||||
}
|
||||
rootProject.name = "Re"
|
||||
rootProject.name = "Red"
|
||||
include ':app'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue