Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
|
079bbb20f5 | ||
|
fc3f6c58ce | ||
|
09de3785b9 |
5 changed files with 54 additions and 30 deletions
16
README.md
16
README.md
|
@ -83,24 +83,24 @@ In the “Add App” screen:
|
|||
|
||||
1. Add the following URL: https://weforge.xyz/partisan/Pulse
|
||||
2. In **Override Source**, select **Forgejo (Codeberg)**
|
||||
3. Tap the “Add” button at the very top, and you’re done!
|
||||
3. Tap the “Add” button at the very top, and you're done!
|
||||
|
||||
## Install directly
|
||||
|
||||
Go to the [Releases page](https://weforge.xyz/partisan/Pulse/releases) and download the latest file with the following format: `app-release.apk`.
|
||||
|
||||
Install it, and you’re done!
|
||||
Install it, and you're done!
|
||||
|
||||
_Please note that when installing directly, the app will not receive automatic updates._
|
||||
|
||||
# Permissions
|
||||
|
||||
- `ACCESS_NETWORK_STATE` – check connectivity
|
||||
- `CALL_PHONE` – make a call via messenger
|
||||
- `READ_CONTACTS` – check if contact has a messenger
|
||||
- `READ_PHONE_NUMBERS` – detect outgoing call
|
||||
- `SYSTEM_ALERT_WINDOW` – show redirecting popup and launch from background
|
||||
- `INTERNET` – check connectivity and verify donates
|
||||
- `ACCESS_NETWORK_STATE` - check connectivity
|
||||
- `CALL_PHONE` - make a call via messenger
|
||||
- `READ_CONTACTS` - check if contact has a messenger
|
||||
- `READ_PHONE_NUMBERS` - detect outgoing call
|
||||
- `SYSTEM_ALERT_WINDOW` - show redirecting popup and launch from background
|
||||
- `INTERNET` - check connectivity and verify donates
|
||||
|
||||
Currently all of the permissions are required.
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@ android {
|
|||
applicationId = "partisan.weforge.xyz.pulse"
|
||||
minSdk = 29
|
||||
targetSdk = 34
|
||||
versionCode = 16
|
||||
versionName = "2.0.2"
|
||||
versionCode = 17
|
||||
versionName = "2.0.3"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ class CallRedirectionService : CallRedirectionService() {
|
|||
initialPhoneAccount: PhoneAccountHandle,
|
||||
allowInteractiveResponse: Boolean,
|
||||
) {
|
||||
Log.d("Redirection", "onPlaceCall triggered: uri=$handle, interactive=$allowInteractiveResponse")
|
||||
val phoneNumber = handle.schemeSpecificPart
|
||||
val numberAlias = getAnonymizedAlias(phoneNumber)
|
||||
Log.d("Redirection", "onPlaceCall triggered: alias=$numberAlias, interactive=$allowInteractiveResponse")
|
||||
|
||||
val capabilities = connectivityManager
|
||||
?.getNetworkCapabilities(connectivityManager?.activeNetwork)
|
||||
|
@ -94,37 +96,30 @@ class CallRedirectionService : CallRedirectionService() {
|
|||
return
|
||||
}
|
||||
|
||||
if (!allowInteractiveResponse) {
|
||||
Log.d("Redirection", "Aborting: interactive response not allowed by system")
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
|
||||
if (prefs.redirectIfRoaming && !isOutsideHomeCountry()) {
|
||||
Log.d("Redirection", "Aborting: redirect only while roaming, but we're inside home country")
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
|
||||
val phoneNumber = handle.schemeSpecificPart
|
||||
Log.d("Redirection", "Resolved phone number: $phoneNumber")
|
||||
|
||||
if (prefs.redirectInternationalOnly && !isInternationalNumber(phoneNumber)) {
|
||||
Log.d("Redirection", "Aborting: number is not international and pref requires it")
|
||||
Log.d("Redirection", "Aborting: number $numberAlias is not international and pref requires it")
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
|
||||
if (prefs.isBlacklistEnabled && !prefs.isContactWhitelisted(phoneNumber)) {
|
||||
Log.d("Redirection", "Aborting: number is not in whitelist while blacklist is enabled")
|
||||
Log.d("Redirection", "Aborting: number $numberAlias is not in whitelist while blacklist is enabled")
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
|
||||
Log.d("Redirection", "Number $numberAlias is not in filters, processing redirection...")
|
||||
|
||||
val records: Array<Record>
|
||||
try {
|
||||
records = getRecordsFromPhoneNumber(phoneNumber)
|
||||
Log.d("Redirection", "Found ${records.size} raw records for contact")
|
||||
Log.d("Redirection", "Found ${records.size} raw redirect apps for number $numberAlias")
|
||||
} catch (exc: SecurityException) {
|
||||
Log.w("Redirection", "SecurityException during record fetch", exc)
|
||||
placeCallUnmodified()
|
||||
|
@ -135,18 +130,20 @@ class CallRedirectionService : CallRedirectionService() {
|
|||
.filter { prefs.isServiceEnabled(it.mimetype) }
|
||||
.sortedBy { prefs.getServicePriority(it.mimetype) }
|
||||
|
||||
Log.d("Redirection", "Filtered to ${enabledRecords.size} enabled records")
|
||||
Log.d("Redirection", "Filtered to ${enabledRecords.size} enabled redirect apps")
|
||||
|
||||
val record = enabledRecords.firstOrNull()
|
||||
if (record == null) {
|
||||
Log.d("Redirection", "Aborting: no suitable record found for redirection")
|
||||
Log.d("Redirection", "Aborting: no suitable redirect apps found for number $numberAlias")
|
||||
placeCallUnmodified()
|
||||
return
|
||||
}
|
||||
|
||||
Log.d("Redirection", "Redirecting call to: ${record.mimetype} → ${record.uri}")
|
||||
|
||||
if (prefs.popupEnabled) {
|
||||
Log.d("Redirection", "Popup ${if (allowInteractiveResponse) "allowed" else "not allowed"} by system; ${if (prefs.popupEnabled) "enabled" else "disabled"} in prefs")
|
||||
|
||||
if (allowInteractiveResponse && prefs.popupEnabled) {
|
||||
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
|
||||
} else {
|
||||
window.call(record.uri)
|
||||
|
@ -203,6 +200,33 @@ class CallRedirectionService : CallRedirectionService() {
|
|||
return results.toTypedArray()
|
||||
}
|
||||
|
||||
private fun getAnonymizedAlias(number: String): String {
|
||||
val prefs = getSharedPreferences("anonymized_numbers", MODE_PRIVATE)
|
||||
|
||||
// Return existing alias if already mapped
|
||||
val existing = prefs.getString(number, null)
|
||||
if (existing != null) return existing
|
||||
|
||||
// Start from current counter
|
||||
var counter = prefs.getInt("counter", 1)
|
||||
var alias: String
|
||||
|
||||
// Find the first unused alias (safety check)
|
||||
while (true) {
|
||||
alias = "#$counter"
|
||||
if (!prefs.all.containsValue(alias)) break
|
||||
counter++
|
||||
}
|
||||
|
||||
// Store new alias and increment counter
|
||||
prefs.edit()
|
||||
.putString(number, alias)
|
||||
.putInt("counter", counter + 1)
|
||||
.apply()
|
||||
|
||||
return alias
|
||||
}
|
||||
|
||||
private fun isInternationalNumber(phoneNumber: String): Boolean {
|
||||
val telephony = getSystemService(TelephonyManager::class.java) ?: return true
|
||||
val simCountryIso = telephony.simCountryIso?.lowercase() ?: return true
|
||||
|
|
|
@ -51,8 +51,8 @@ class MainFragment : Fragment() {
|
|||
emitter =
|
||||
Emitter(duration = 100, TimeUnit.MILLISECONDS)
|
||||
.perSecond(100),
|
||||
speed = 30f,
|
||||
maxSpeed = 40f,
|
||||
speed = 25f,
|
||||
maxSpeed = 30f,
|
||||
damping = 0.85f,
|
||||
spread = 360,
|
||||
position = Position.Relative(0.5, 0.5)
|
||||
|
|
|
@ -38,8 +38,8 @@ class Preferences(private val context: Context) {
|
|||
val isEnabled: Boolean
|
||||
get() = isServiceEnabledByUser &&
|
||||
hasGeneralPermissions(context) &&
|
||||
hasDrawOverlays(context) &&
|
||||
hasCallRedirectionRole(context)
|
||||
hasCallRedirectionRole(context) &&
|
||||
(popupEnabled.not() || hasDrawOverlays(context))
|
||||
|
||||
enum class PopupEffect {
|
||||
NONE, FADE, SCALE, BOUNCE, FLOP, MATRIX, SLIDE_SNAP, GAMER_MODE, RANDOM
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue