Skip to content
This repository has been archived by the owner on Jun 22, 2022. It is now read-only.

Commit

Permalink
popup window preview
Browse files Browse the repository at this point in the history
  • Loading branch information
lucky authored and lucky committed Jun 21, 2022
1 parent f7bed71 commit 2ff2448
Show file tree
Hide file tree
Showing 16 changed files with 111 additions and 59 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/gradle-wrapper-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
name: "Validate Gradle Wrapper"
on: [push, pull_request]

jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: gradle/wrapper-validation-action@v1
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@

Redirect outgoing calls to Signal/Telegram/Threema.

[comment]: <> ([<img)

[comment]: <> ( src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png")

[comment]: <> ( alt="Get it on F-Droid")

[comment]: <> ( height="80">]&#40;https://f-droid.org/packages/me.lucky.red/&#41;)
[<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.red/)
[<img
src="https://play.google.com/intl/en_us/badges/images/generic/en-play-badge.png"
alt="Get it on Google Play"
Expand Down
12 changes: 6 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ android {
applicationId "me.lucky.red"
minSdk 29
targetSdk 32
versionCode 8
versionName "1.0.7"
versionCode 9
versionName "1.0.8"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
Expand Down Expand Up @@ -39,10 +39,10 @@ android {
}

dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
Expand Down
31 changes: 16 additions & 15 deletions app/src/main/java/me/lucky/red/CallRedirectionService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.provider.ContactsContract
import android.telecom.CallRedirectionService
import android.telecom.PhoneAccountHandle
import androidx.annotation.RequiresPermission
import java.lang.ref.WeakReference

class CallRedirectionService : CallRedirectionService() {
companion object {
Expand All @@ -16,7 +17,7 @@ class CallRedirectionService : CallRedirectionService() {
private const val TELEGRAM_MIMETYPE = "$PREFIX/vnd.org.telegram.messenger.android.call"
private const val THREEMA_MIMETYPE = "$PREFIX/vnd.ch.threema.app.call"
private const val WHATSAPP_MIMETYPE = "$PREFIX/vnd.com.whatsapp.voip.call"
private val MIMETYPES = mapOf(
private val MIMETYPE_TO_WEIGHT = mapOf(
SIGNAL_MIMETYPE to 0,
TELEGRAM_MIMETYPE to 1,
THREEMA_MIMETYPE to 2,
Expand All @@ -25,9 +26,15 @@ class CallRedirectionService : CallRedirectionService() {
private val FALLBACK_MIMETYPES = arrayOf(
WHATSAPP_MIMETYPE,
)
private val MIMETYPE_TO_DST_NAME = mapOf(
SIGNAL_MIMETYPE to R.string.destination_signal,
TELEGRAM_MIMETYPE to R.string.destination_telegram,
THREEMA_MIMETYPE to R.string.destination_threema,
WHATSAPP_MIMETYPE to R.string.fallback_destination_whatsapp,
)
}

lateinit var prefs: Preferences
private lateinit var prefs: Preferences
private lateinit var window: PopupWindow
private var connectivityManager: ConnectivityManager? = null

Expand All @@ -43,7 +50,7 @@ class CallRedirectionService : CallRedirectionService() {

private fun init() {
prefs = Preferences(this)
window = PopupWindow(this)
window = PopupWindow(this, WeakReference(this))
connectivityManager = getSystemService(ConnectivityManager::class.java)
}

Expand All @@ -52,7 +59,7 @@ class CallRedirectionService : CallRedirectionService() {
initialPhoneAccount: PhoneAccountHandle,
allowInteractiveResponse: Boolean,
) {
if (!prefs.isServiceEnabled || !hasInternet() || !allowInteractiveResponse) {
if (!prefs.isEnabled || !hasInternet() || !allowInteractiveResponse) {
placeCallUnmodified()
return
}
Expand All @@ -63,18 +70,12 @@ class CallRedirectionService : CallRedirectionService() {
placeCallUnmodified()
return
}
val record = records.minByOrNull { MIMETYPES[it.mimetype] ?: 0 }
val record = records.minByOrNull { MIMETYPE_TO_WEIGHT[it.mimetype] ?: 0 }
if (record == null || (record.mimetype in FALLBACK_MIMETYPES && !prefs.isFallbackChecked)) {
placeCallUnmodified()
return
}
window.show(record.uri, when (record.mimetype) {
SIGNAL_MIMETYPE -> R.string.destination_signal
TELEGRAM_MIMETYPE -> R.string.destination_telegram
THREEMA_MIMETYPE -> R.string.destination_threema
WHATSAPP_MIMETYPE -> R.string.fallback_destination_whatsapp
else -> return
})
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
}

@RequiresPermission(Manifest.permission.READ_CONTACTS)
Expand All @@ -83,7 +84,7 @@ class CallRedirectionService : CallRedirectionService() {
val cursor = contentResolver.query(
Uri.withAppendedPath(
ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phoneNumber)
Uri.encode(phoneNumber),
),
arrayOf(ContactsContract.PhoneLookup._ID),
null,
Expand All @@ -109,8 +110,8 @@ class CallRedirectionService : CallRedirectionService() {
arrayOf(ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE),
"${ContactsContract.Data.CONTACT_ID} = ? AND " +
"${ContactsContract.Data.MIMETYPE} IN " +
"(${MIMETYPES.keys.joinToString(",") { "?" }})",
arrayOf(contactId, *MIMETYPES.keys.toTypedArray()),
"(${MIMETYPE_TO_WEIGHT.keys.joinToString(",") { "?" }})",
arrayOf(contactId, *MIMETYPE_TO_WEIGHT.keys.toTypedArray()),
null,
)
cursor?.apply {
Expand Down
14 changes: 12 additions & 2 deletions app/src/main/java/me/lucky/red/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding
private lateinit var prefs: Preferences
private lateinit var window: PopupWindow
private var roleManager: RoleManager? = null

private val registerForCallRedirectionRole =
Expand All @@ -42,14 +43,20 @@ class MainActivity : AppCompatActivity() {
setup()
}

override fun onDestroy() {
super.onDestroy()
window.cancel()
}

private fun init() {
prefs = Preferences(this)
window = PopupWindow(this, null)
roleManager = getSystemService(RoleManager::class.java)
binding.apply {
redirectionDelay.value = (prefs.redirectionDelay / 1000).toFloat()
popupPosition.editText?.setText(prefs.popupPosition.toString())
fallback.isChecked = prefs.isFallbackChecked
toggle.isChecked = prefs.isServiceEnabled
toggle.isChecked = prefs.isEnabled
}
}

Expand All @@ -61,6 +68,9 @@ class MainActivity : AppCompatActivity() {
redirectionDelay.addOnChangeListener { _, value, _ ->
prefs.redirectionDelay = (value * 1000).toLong()
}
popupPosition.setEndIconOnClickListener {
window.preview()
}
popupPosition.editText?.doAfterTextChanged {
try {
prefs.popupPosition = it?.toString()?.toInt() ?: return@doAfterTextChanged
Expand All @@ -75,7 +85,7 @@ class MainActivity : AppCompatActivity() {
requestPermissions()
return@setOnCheckedChangeListener
}
prefs.isServiceEnabled = isChecked
prefs.isEnabled = isChecked
}
}
}
Expand Down
54 changes: 36 additions & 18 deletions app/src/main/java/me/lucky/red/PopupWindow.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.lucky.red

import android.Manifest
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.media.AudioManager
Expand All @@ -10,39 +11,52 @@ import android.view.LayoutInflater
import android.view.WindowManager
import android.widget.TextView
import androidx.annotation.RequiresPermission
import java.lang.ref.WeakReference
import java.util.*
import kotlin.concurrent.timerTask

class PopupWindow(private val service: CallRedirectionService) {
private val windowManager = service
.applicationContext
.getSystemService(WindowManager::class.java)
private val audioManager = service
.applicationContext
.getSystemService(AudioManager::class.java)
class PopupWindow(
private val ctx: Context,
private val service: WeakReference<CallRedirectionService>?,
) {
private val prefs = Preferences(ctx)
private val windowManager = ctx.getSystemService(WindowManager::class.java)
private val audioManager = ctx.getSystemService(AudioManager::class.java)
@Suppress("InflateParams")
private val view = LayoutInflater
.from(service.applicationContext)
.inflate(R.layout.popup, null)
private val view = LayoutInflater.from(ctx).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 = service.prefs.popupPosition
y = prefs.popupPosition
}
private var timer: Timer? = null

init {
view.setOnClickListener {
cancel()
service.placeCallUnmodified()
service?.get()?.placeCallUnmodified()
}
}

fun preview() {
remove()
layoutParams.y = prefs.popupPosition
val destinations = mutableListOf(
R.string.destination_signal,
R.string.destination_telegram,
R.string.destination_threema,
)
if (prefs.isFallbackChecked) destinations.add(R.string.fallback_destination_whatsapp)
setDescription(destinations.random())
add()
}

fun show(uri: Uri, destinationId: Int) {
val service = service?.get() ?: return
if (!remove()) {
service.placeCallUnmodified()
return
Expand All @@ -65,23 +79,27 @@ class PopupWindow(private val service: CallRedirectionService) {
return@timerTask
}
service.cancelCall()
}, service.prefs.redirectionDelay)
view.findViewById<TextView>(R.id.description).text = String.format(
service.getString(R.string.popup),
service.getString(destinationId),
)
}, prefs.redirectionDelay)
setDescription(destinationId)
if (!add()) {
timer?.cancel()
service.placeCallUnmodified()
}
}

private fun setDescription(id: Int) {
view.findViewById<TextView>(R.id.description).text = ctx.getString(
R.string.popup,
ctx.getString(id),
)
}

@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)
ctx.startActivity(it)
}
}

Expand Down
11 changes: 7 additions & 4 deletions app/src/main/java/me/lucky/red/Preferences.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@ import androidx.preference.PreferenceManager

class Preferences(ctx: Context) {
companion object {
private const val SERVICE_ENABLED = "service_enabled"
private const val ENABLED = "enabled"
private const val REDIRECTION_DELAY = "redirection_delay"
private const val POPUP_POSITION = "popup_position_y"
private const val FALLBACK_CHECKED = "fallback_checked"

private const val DEFAULT_REDIRECTION_DELAY = 2000L
private const val DEFAULT_POPUP_POSITION = 333

// migration
private const val SERVICE_ENABLED = "service_enabled"
}

private val prefs = PreferenceManager.getDefaultSharedPreferences(ctx)

var isServiceEnabled: Boolean
get() = prefs.getBoolean(SERVICE_ENABLED, false)
set(value) = prefs.edit { putBoolean(SERVICE_ENABLED, value) }
var isEnabled: Boolean
get() = prefs.getBoolean(ENABLED, prefs.getBoolean(SERVICE_ENABLED, false))
set(value) = prefs.edit { putBoolean(ENABLED, value) }

var redirectionDelay: Long
get() = prefs.getLong(REDIRECTION_DELAY, DEFAULT_REDIRECTION_DELAY)
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_baseline_check_circle_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
</vector>
2 changes: 2 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@

<com.google.android.material.textfield.TextInputLayout
android:id="@+id/popupPosition"
app:endIconMode="custom"
app:endIconDrawable="@drawable/ic_baseline_check_circle_24"
android:hint="@string/popup_position"
android:layout_width="match_parent"
android:layout_height="wrap_content">
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<string name="destination_signal">Signal</string>
<string name="destination_telegram">Telegram</string>
<string name="destination_threema">Threema</string>
<string name="redirection_delay_description">Delay before a call will be redirected.</string>
<string name="redirection_delay_description">The delay before a call will be redirected.</string>
<string name="popup_position">Popup position</string>
<string name="fallback">Fallback</string>
<string name="fallback_description">Redirect to WhatsApp if no other available.</string>
Expand Down
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
classpath 'com.android.tools.build:gradle:7.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0"

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
1 change: 1 addition & 0 deletions fastlane/metadata/android/en-US/changelogs/9.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
popup window preview
Binary file modified fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion fastlane/metadata/android/ru-RU/full_description.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Минимальное приложение для перенаправления исходящих вызовов в Signal/Telegram/Threema.
Мини приложение для перенаправления исходящих вызовов в Signal/Telegram/Threema.

Вы можете отменить перенаправление, кликнув на всплывающее сообщение "Перенаправление в..".

Expand Down
6 changes: 3 additions & 3 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Tue Jun 14 23:11:06 MSK 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
distributionSha256Sum=f581709a9c35e9cb92e16f585d2c4bc99b2b1a5f85d2badbd3dc6bff59e1e6dd
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

0 comments on commit 2ff2448

Please sign in to comment.