Added individual service toggle and priority switch
This commit is contained in:
parent
707cd39ef8
commit
b09b6578bb
13 changed files with 328 additions and 83 deletions
|
@ -4,29 +4,39 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'partisan.weforge.xyz.pulse'
|
namespace = 'partisan.weforge.xyz.pulse'
|
||||||
compileSdk 34
|
compileSdk = 34
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "partisan.weforge.xyz.pulse"
|
applicationId = "partisan.weforge.xyz.pulse"
|
||||||
minSdk 29
|
minSdk = 29
|
||||||
targetSdk 34
|
targetSdk = 34
|
||||||
versionCode 9
|
versionCode = 9
|
||||||
versionName "1.2.0"
|
versionName = "1.3.0"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
signingConfigs {
|
||||||
|
release {
|
||||||
|
storeFile file("release-key.jks")
|
||||||
|
storePassword RELEASE_STORE_PASSWORD
|
||||||
|
keyAlias "release-key"
|
||||||
|
keyPassword RELEASE_STORE_PASSWORD
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled = false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
signingConfig signingConfigs.release
|
||||||
|
proguardFiles(getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
|
@ -34,11 +44,11 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding = true
|
||||||
}
|
}
|
||||||
|
|
||||||
lint {
|
lint {
|
||||||
disable 'MissingTranslation'
|
disable += 'MissingTranslation'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,20 +17,19 @@ class CallRedirectionService : CallRedirectionService() {
|
||||||
private const val TELEGRAM_MIMETYPE = "$PREFIX/vnd.org.telegram.messenger.android.call"
|
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 THREEMA_MIMETYPE = "$PREFIX/vnd.ch.threema.app.call"
|
||||||
private const val WHATSAPP_MIMETYPE = "$PREFIX/vnd.com.whatsapp.voip.call"
|
private const val WHATSAPP_MIMETYPE = "$PREFIX/vnd.com.whatsapp.voip.call"
|
||||||
private val MIMETYPE_TO_WEIGHT = mapOf(
|
|
||||||
SIGNAL_MIMETYPE to 0,
|
val ALL_MIMETYPES = arrayOf(
|
||||||
TELEGRAM_MIMETYPE to 1,
|
SIGNAL_MIMETYPE,
|
||||||
THREEMA_MIMETYPE to 2,
|
TELEGRAM_MIMETYPE,
|
||||||
WHATSAPP_MIMETYPE to 48,
|
THREEMA_MIMETYPE,
|
||||||
)
|
|
||||||
private val FALLBACK_MIMETYPES = arrayOf(
|
|
||||||
WHATSAPP_MIMETYPE,
|
WHATSAPP_MIMETYPE,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val MIMETYPE_TO_DST_NAME = mapOf(
|
private val MIMETYPE_TO_DST_NAME = mapOf(
|
||||||
SIGNAL_MIMETYPE to R.string.destination_signal,
|
SIGNAL_MIMETYPE to R.string.destination_signal,
|
||||||
TELEGRAM_MIMETYPE to R.string.destination_telegram,
|
TELEGRAM_MIMETYPE to R.string.destination_telegram,
|
||||||
THREEMA_MIMETYPE to R.string.destination_threema,
|
THREEMA_MIMETYPE to R.string.destination_threema,
|
||||||
WHATSAPP_MIMETYPE to R.string.fallback_destination_whatsapp,
|
WHATSAPP_MIMETYPE to R.string.destination_whatsapp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +62,7 @@ class CallRedirectionService : CallRedirectionService() {
|
||||||
placeCallUnmodified()
|
placeCallUnmodified()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val records: Array<Record>
|
val records: Array<Record>
|
||||||
try {
|
try {
|
||||||
records = getRecordsFromPhoneNumber(handle.schemeSpecificPart)
|
records = getRecordsFromPhoneNumber(handle.schemeSpecificPart)
|
||||||
|
@ -70,11 +70,18 @@ class CallRedirectionService : CallRedirectionService() {
|
||||||
placeCallUnmodified()
|
placeCallUnmodified()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val record = records.minByOrNull { MIMETYPE_TO_WEIGHT[it.mimetype] ?: 0 }
|
|
||||||
if (record == null || (record.mimetype in FALLBACK_MIMETYPES && !prefs.isFallbackChecked)) {
|
// Filter to enabled services only
|
||||||
|
val enabledRecords = records
|
||||||
|
.filter { prefs.isServiceEnabled(it.mimetype) }
|
||||||
|
.sortedBy { prefs.getServicePriority(it.mimetype) }
|
||||||
|
|
||||||
|
val record = enabledRecords.firstOrNull()
|
||||||
|
if (record == null) {
|
||||||
placeCallUnmodified()
|
placeCallUnmodified()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
|
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,9 +116,8 @@ class CallRedirectionService : CallRedirectionService() {
|
||||||
ContactsContract.Data.CONTENT_URI,
|
ContactsContract.Data.CONTENT_URI,
|
||||||
arrayOf(ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE),
|
arrayOf(ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE),
|
||||||
"${ContactsContract.Data.CONTACT_ID} = ? AND " +
|
"${ContactsContract.Data.CONTACT_ID} = ? AND " +
|
||||||
"${ContactsContract.Data.MIMETYPE} IN " +
|
"${ContactsContract.Data.MIMETYPE} IN (${ALL_MIMETYPES.joinToString(",") { "?" }})",
|
||||||
"(${MIMETYPE_TO_WEIGHT.keys.joinToString(",") { "?" }})",
|
arrayOf(contactId, *ALL_MIMETYPES),
|
||||||
arrayOf(contactId, *MIMETYPE_TO_WEIGHT.keys.toTypedArray()),
|
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
cursor?.apply {
|
cursor?.apply {
|
||||||
|
|
|
@ -10,7 +10,17 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.widget.doAfterTextChanged
|
import androidx.core.widget.doAfterTextChanged
|
||||||
import java.lang.NumberFormatException
|
import java.lang.NumberFormatException
|
||||||
|
import android.text.InputType
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
import partisan.weforge.xyz.pulse.getServicePriority
|
||||||
|
import partisan.weforge.xyz.pulse.setServicePriority
|
||||||
|
import partisan.weforge.xyz.pulse.isServiceEnabled
|
||||||
|
import partisan.weforge.xyz.pulse.setServiceEnabled
|
||||||
import partisan.weforge.xyz.pulse.databinding.ActivityMainBinding
|
import partisan.weforge.xyz.pulse.databinding.ActivityMainBinding
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
@ -55,7 +65,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
redirectionDelay.value = (prefs.redirectionDelay / 1000).toFloat()
|
redirectionDelay.value = (prefs.redirectionDelay / 1000).toFloat()
|
||||||
popupPosition.editText?.setText(prefs.popupPosition.toString())
|
popupPosition.editText?.setText(prefs.popupPosition.toString())
|
||||||
fallback.isChecked = prefs.isFallbackChecked
|
|
||||||
toggle.isChecked = prefs.isEnabled
|
toggle.isChecked = prefs.isEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,9 +85,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
prefs.popupPosition = it?.toString()?.toInt() ?: return@doAfterTextChanged
|
prefs.popupPosition = it?.toString()?.toInt() ?: return@doAfterTextChanged
|
||||||
} catch (exc: NumberFormatException) {}
|
} catch (exc: NumberFormatException) {}
|
||||||
}
|
}
|
||||||
fallback.setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
prefs.isFallbackChecked = isChecked
|
|
||||||
}
|
|
||||||
toggle.setOnCheckedChangeListener { _, isChecked ->
|
toggle.setOnCheckedChangeListener { _, isChecked ->
|
||||||
if (isChecked && !hasPermissions()) {
|
if (isChecked && !hasPermissions()) {
|
||||||
toggle.isChecked = false
|
toggle.isChecked = false
|
||||||
|
@ -87,6 +93,64 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
prefs.isEnabled = isChecked
|
prefs.isEnabled = isChecked
|
||||||
}
|
}
|
||||||
|
val services = listOf(
|
||||||
|
ServiceEntry("vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call", R.string.destination_signal, this@MainActivity.isServiceEnabled("vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call")),
|
||||||
|
ServiceEntry("vnd.android.cursor.item/vnd.org.telegram.messenger.android.call", R.string.destination_telegram, this@MainActivity.isServiceEnabled("vnd.android.cursor.item/vnd.org.telegram.messenger.android.call")),
|
||||||
|
ServiceEntry("vnd.android.cursor.item/vnd.ch.threema.app.call", R.string.destination_threema, this@MainActivity.isServiceEnabled("vnd.android.cursor.item/vnd.ch.threema.app.call")),
|
||||||
|
ServiceEntry("vnd.android.cursor.item/vnd.com.whatsapp.voip.call", R.string.destination_whatsapp, this@MainActivity.isServiceEnabled("vnd.android.cursor.item/vnd.com.whatsapp.voip.call")),
|
||||||
|
)
|
||||||
|
|
||||||
|
val adapter = ServiceAdapter(
|
||||||
|
context = this@MainActivity,
|
||||||
|
services = services.toMutableList(),
|
||||||
|
onReordered = { updatedList ->
|
||||||
|
updatedList.forEachIndexed { index, entry ->
|
||||||
|
setServicePriority(entry.mimetype, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
binding.serviceRecycler.adapter = adapter
|
||||||
|
binding.serviceRecycler.layoutManager = LinearLayoutManager(this@MainActivity)
|
||||||
|
|
||||||
|
val touchHelper = ItemTouchHelper(adapter.dragHelper)
|
||||||
|
touchHelper.attachToRecyclerView(binding.serviceRecycler)
|
||||||
|
|
||||||
|
adapter.setDragStartListener { viewHolder ->
|
||||||
|
touchHelper.startDrag(viewHolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
// binding.serviceConfigList.removeAllViews()
|
||||||
|
// for ((mimetype, labelRes) in mimetypes) {
|
||||||
|
// val checkbox = CheckBox(this@MainActivity).apply {
|
||||||
|
// text = getString(labelRes)
|
||||||
|
// isChecked = this@MainActivity.isServiceEnabled(mimetype)
|
||||||
|
// setOnCheckedChangeListener { _, checked ->
|
||||||
|
// this@MainActivity.setServiceEnabled(mimetype, checked)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// val priorityInput = EditText(this@MainActivity).apply {
|
||||||
|
// inputType = InputType.TYPE_CLASS_NUMBER
|
||||||
|
// setEms(4)
|
||||||
|
// hint = "Priority"
|
||||||
|
// setText(this@MainActivity.getServicePriority(mimetype).toString())
|
||||||
|
// setOnFocusChangeListener { _, hasFocus ->
|
||||||
|
// if (!hasFocus) {
|
||||||
|
// val value = text.toString().toIntOrNull()
|
||||||
|
// if (value != null) this@MainActivity.setServicePriority(mimetype, value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// val row = LinearLayout(this@MainActivity).apply {
|
||||||
|
// orientation = LinearLayout.HORIZONTAL
|
||||||
|
// setPadding(0, 16, 0, 16)
|
||||||
|
// addView(checkbox, LinearLayout.LayoutParams(0, WRAP_CONTENT, 1f))
|
||||||
|
// addView(priorityInput, LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// binding.serviceConfigList.addView(row)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@ class PopupWindow(
|
||||||
R.string.destination_telegram,
|
R.string.destination_telegram,
|
||||||
R.string.destination_threema,
|
R.string.destination_threema,
|
||||||
)
|
)
|
||||||
if (prefs.isFallbackChecked) destinations.add(R.string.fallback_destination_whatsapp)
|
|
||||||
setDescription(destinations.random())
|
setDescription(destinations.random())
|
||||||
add()
|
add()
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ class Preferences(ctx: Context) {
|
||||||
private const val ENABLED = "enabled"
|
private const val ENABLED = "enabled"
|
||||||
private const val REDIRECTION_DELAY = "redirection_delay"
|
private const val REDIRECTION_DELAY = "redirection_delay"
|
||||||
private const val POPUP_POSITION = "popup_position_y"
|
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_REDIRECTION_DELAY = 2000L
|
||||||
private const val DEFAULT_POPUP_POSITION = 333
|
private const val DEFAULT_POPUP_POSITION = 333
|
||||||
|
@ -32,7 +31,26 @@ class Preferences(ctx: Context) {
|
||||||
get() = prefs.getInt(POPUP_POSITION, DEFAULT_POPUP_POSITION)
|
get() = prefs.getInt(POPUP_POSITION, DEFAULT_POPUP_POSITION)
|
||||||
set(value) = prefs.edit { putInt(POPUP_POSITION, value) }
|
set(value) = prefs.edit { putInt(POPUP_POSITION, value) }
|
||||||
|
|
||||||
var isFallbackChecked: Boolean
|
private fun makeKeyEnabled(mimetype: String) = "enabled_$mimetype"
|
||||||
get() = prefs.getBoolean(FALLBACK_CHECKED, false)
|
private fun makeKeyPriority(mimetype: String) = "priority_$mimetype"
|
||||||
set(value) = prefs.edit { putBoolean(FALLBACK_CHECKED, value) }
|
|
||||||
|
/** Whether this service is enabled */
|
||||||
|
fun isServiceEnabled(mimetype: String): Boolean {
|
||||||
|
return prefs.getBoolean(makeKeyEnabled(mimetype), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Current priority for this service (lower = higher priority) */
|
||||||
|
fun getServicePriority(mimetype: String): Int {
|
||||||
|
return prefs.getInt(makeKeyPriority(mimetype), Int.MAX_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Enable or disable individual service */
|
||||||
|
fun setServiceEnabled(mimetype: String, enabled: Boolean) {
|
||||||
|
prefs.edit().putBoolean(makeKeyEnabled(mimetype), enabled).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Change priority for an individual service */
|
||||||
|
fun setServicePriority(mimetype: String, priority: Int) {
|
||||||
|
prefs.edit().putInt(makeKeyPriority(mimetype), priority).apply()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package partisan.weforge.xyz.pulse
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.CheckBox
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class ServiceAdapter(
|
||||||
|
private val context: Context,
|
||||||
|
private val services: MutableList<ServiceEntry>,
|
||||||
|
private val onReordered: (List<ServiceEntry>) -> Unit
|
||||||
|
) : RecyclerView.Adapter<ServiceAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
val label: TextView = view.findViewById(R.id.label)
|
||||||
|
val checkbox: CheckBox = view.findViewById(R.id.checkbox)
|
||||||
|
val handle: ImageView = view.findViewById(R.id.handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
val view = LayoutInflater.from(parent.context)
|
||||||
|
.inflate(R.layout.item_service, parent, false)
|
||||||
|
return ViewHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
val entry = services[position]
|
||||||
|
|
||||||
|
holder.label.setText(entry.labelRes)
|
||||||
|
holder.checkbox.isChecked = entry.enabled
|
||||||
|
|
||||||
|
holder.checkbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
entry.enabled = isChecked
|
||||||
|
context.setServiceEnabled(entry.mimetype, isChecked)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.handle.setOnTouchListener { _, event ->
|
||||||
|
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||||
|
dragStartListener?.invoke(holder)
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = services.size
|
||||||
|
|
||||||
|
private var dragStartListener: ((RecyclerView.ViewHolder) -> Unit)? = null
|
||||||
|
|
||||||
|
val dragHelper = object : ItemTouchHelper.Callback() {
|
||||||
|
override fun getMovementFlags(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder
|
||||||
|
): Int {
|
||||||
|
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
|
||||||
|
return makeMovementFlags(dragFlags, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMove(
|
||||||
|
recyclerView: RecyclerView,
|
||||||
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
|
target: RecyclerView.ViewHolder
|
||||||
|
): Boolean {
|
||||||
|
val from = viewHolder.adapterPosition
|
||||||
|
val to = target.adapterPosition
|
||||||
|
services.add(to, services.removeAt(from))
|
||||||
|
notifyItemMoved(from, to)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
|
// No swipe actions
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
super.clearView(recyclerView, viewHolder)
|
||||||
|
onReordered(services)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDragStartListener(listener: (RecyclerView.ViewHolder) -> Unit) {
|
||||||
|
dragStartListener = listener
|
||||||
|
}
|
||||||
|
}
|
33
app/src/main/java/partisan/weforge/xyz/pulse/ServicePrefs.kt
Normal file
33
app/src/main/java/partisan/weforge/xyz/pulse/ServicePrefs.kt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package partisan.weforge.xyz.pulse
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
|
||||||
|
private fun makeKeyEnabled(mimetype: String) = "enabled_$mimetype"
|
||||||
|
private fun makeKeyPriority(mimetype: String) = "priority_$mimetype"
|
||||||
|
|
||||||
|
data class ServiceEntry(
|
||||||
|
val mimetype: String,
|
||||||
|
val labelRes: Int,
|
||||||
|
var enabled: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Context.isServiceEnabled(mimetype: String): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
return prefs.getBoolean(makeKeyEnabled(mimetype), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.setServiceEnabled(mimetype: String, enabled: Boolean) {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
prefs.edit().putBoolean(makeKeyEnabled(mimetype), enabled).apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getServicePriority(mimetype: String): Int {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
return prefs.getInt(makeKeyPriority(mimetype), Int.MAX_VALUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.setServicePriority(mimetype: String, priority: Int) {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
prefs.edit().putInt(makeKeyPriority(mimetype), priority).apply()
|
||||||
|
}
|
9
app/src/main/res/drawable/ic_drag_handle.xml
Normal file
9
app/src/main/res/drawable/ic_drag_handle.xml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#666"
|
||||||
|
android:pathData="M7,10h2v2H7v-2zm0,-4h2v2H7V6zm0,8h2v2H7v-2zm4,-8h2v2h-2V6zm0,4h2v2h-2v-2zm0,4h2v2h-2v-2z" />
|
||||||
|
</vector>
|
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -12,9 +13,9 @@
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/description"
|
android:text="@string/description"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:id="@+id/scrollView"
|
android:id="@+id/scrollView"
|
||||||
|
@ -22,10 +23,10 @@
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:layout_marginVertical="16dp"
|
android:layout_marginVertical="16dp"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/toggle"
|
app:layout_constraintTop_toBottomOf="@id/description"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintBottom_toTopOf="@id/toggle"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/description">
|
app:layout_constraintEnd_toEndOf="parent">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -36,65 +37,54 @@
|
||||||
android:id="@+id/redirectionDelay"
|
android:id="@+id/redirectionDelay"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:contentDescription="@string/redirection_delay_description"
|
|
||||||
android:stepSize="0.5"
|
android:stepSize="0.5"
|
||||||
android:valueFrom="2"
|
android:valueFrom="2"
|
||||||
android:valueTo="4" />
|
android:valueTo="4"
|
||||||
|
android:contentDescription="@string/redirection_delay_description" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/description2"
|
android:id="@+id/description2"
|
||||||
android:textSize="12sp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/redirection_delay_description" />
|
android:text="@string/redirection_delay_description"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_marginVertical="8dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="8dp" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/popupPosition"
|
android:id="@+id/popupPosition"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
app:endIconMode="custom"
|
app:endIconMode="custom"
|
||||||
app:endIconDrawable="@drawable/ic_baseline_check_circle_24"
|
app:endIconDrawable="@drawable/ic_baseline_check_circle_24"
|
||||||
android:hint="@string/popup_position"
|
android:hint="@string/popup_position">
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:inputType="number"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="number" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_marginVertical="8dp"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginVertical="8dp" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_marginVertical="8dp"
|
android:layout_marginVertical="8dp"
|
||||||
android:background="?android:attr/listDivider" />
|
android:background="?android:attr/listDivider" />
|
||||||
|
|
||||||
<CheckBox
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/fallback"
|
android:id="@+id/serviceRecycler"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layoutDirection="rtl"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/fallback"
|
android:scrollbars="vertical" />
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/description3"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/fallback_description"
|
|
||||||
android:textSize="12sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
|
@ -105,7 +95,6 @@
|
||||||
android:paddingVertical="12dp"
|
android:paddingVertical="12dp"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
30
app/src/main/res/layout/item_service.xml
Normal file
30
app/src/main/res/layout/item_service.xml
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/handle"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@drawable/ic_drag_handle"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:paddingEnd="8dp" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/checkbox"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:text="Label"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_gravity="center_vertical" />
|
||||||
|
</LinearLayout>
|
|
@ -1,10 +1,10 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Pulse</string>
|
<string name="app_name">Pulse</string>
|
||||||
<string name="description">L\'application essaiera de rediriger les appels sortants vers Signal/Telegram/Threema s\'ils sont disponibles. Pour fonctionner, l\'application nécessite de nombreuses permissions. Cliquez sur le bouton et accordez les autorisations nécéssaires jusqu\'à ce qu\'il soit activé.</string>
|
<string name="description">L\'application essaiera de rediriger les appels sortants vers Signal/Telegram/Threema/WhatsApp s\'ils sont disponibles. Pour fonctionner, l\'application nécessite de nombreuses permissions. Cliquez sur le bouton et accordez les autorisations nécéssaires jusqu\'à ce qu\'il soit activé.</string>
|
||||||
<string name="popup">Redirection vers %1$s</string>
|
<string name="popup">Redirection vers %1$s</string>
|
||||||
<string name="destination_signal">Signal</string>
|
<string name="destination_signal">Signal</string>
|
||||||
<string name="destination_telegram">Telegram</string>
|
<string name="destination_telegram">Telegram</string>
|
||||||
<string name="destination_threema">Threema</string>
|
<string name="destination_threema">Threema</string>
|
||||||
<string name="redirection_delay_description">Délai avant qu\'un appel ne soit redirigé.</string>
|
<string name="destination_whatsapp">WhatsApp</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Pulse</string>
|
<string name="app_name">Pulse</string>
|
||||||
<string name="description">Приложение будет пытаться перенаправить исходящие вызовы в Signal/Telegram/Threema. Для работы ему нужно много разрешений. Кликайте на переключатель и выдавайте разрешения пока он не включится.</string>
|
<string name="description">Приложение будет пытаться перенаправить исходящие вызовы в Signal/Telegram/Threema/WhatsApp. Для работы ему нужно много разрешений. Кликайте на переключатель и выдавайте разрешения пока он не включится.</string>
|
||||||
<string name="popup">Перенаправление в %1$s</string>
|
<string name="popup">Перенаправление в %1$s</string>
|
||||||
<string name="destination_signal">Signal</string>
|
<string name="destination_signal">Signal</string>
|
||||||
<string name="destination_telegram">Telegram</string>
|
<string name="destination_telegram">Telegram</string>
|
||||||
<string name="destination_threema">Threema</string>
|
<string name="destination_threema">Threema</string>
|
||||||
|
<string name="destination_whatsapp">WhatsApp</string>
|
||||||
<string name="redirection_delay_description">Задержка до того, как звонок будет перенаправлен.</string>
|
<string name="redirection_delay_description">Задержка до того, как звонок будет перенаправлен.</string>
|
||||||
<string name="popup_position">Позиция всплывающего окна</string>
|
<string name="popup_position">Позиция всплывающего окна</string>
|
||||||
<string name="fallback">Обратная совместимость</string>
|
<string name="fallback">Обратная совместимость</string>
|
||||||
<string name="fallback_description">Перенаправлять в WhatsApp, если другие недоступны.</string>
|
|
||||||
<string name="fallback_destination_whatsapp">WhatsApp</string>
|
|
||||||
</resources>
|
</resources>
|
|
@ -1,14 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Pulse</string>
|
<string name="app_name">Pulse</string>
|
||||||
<string name="description">The app will try to redirect outgoing calls to Signal/Telegram/Threema if available. For work it requires many permissions. Click on the toggle and grant permissions until it turns ON.</string>
|
<string name="description">The app will try to redirect outgoing calls to Signal/Telegram/Threema/WhatsApp 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="popup">Redirecting to %1$s</string>
|
||||||
<string name="destination_signal">Signal</string>
|
<string name="destination_signal">Signal</string>
|
||||||
<string name="destination_telegram">Telegram</string>
|
<string name="destination_telegram">Telegram</string>
|
||||||
<string name="destination_threema">Threema</string>
|
<string name="destination_threema">Threema</string>
|
||||||
|
<string name="destination_whatsapp">WhatsApp</string>
|
||||||
<string name="redirection_delay_description">The 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="popup_position">Popup position</string>
|
||||||
<string name="fallback">Fallback</string>
|
<string name="fallback">Fallback</string>
|
||||||
<string name="fallback_description">Redirect to WhatsApp if no other available.</string>
|
|
||||||
<string name="fallback_destination_whatsapp">WhatsApp</string>
|
|
||||||
</resources>
|
</resources>
|
Loading…
Add table
Add a link
Reference in a new issue