Merge pull request 'v2.0.2' (#4) from work into main

Reviewed-on: #4
This commit is contained in:
Internet Addict 2025-06-03 19:21:19 +00:00
commit 7cc7d5e390
8 changed files with 120 additions and 47 deletions

View file

@ -48,22 +48,22 @@ Redirecting outgoing calls to E2EE apps.
# Features
- Material You design
- Popup with cancel option
- Material You design
- Popup with cancel option
- Extensive settings panel:
- Toggle per-service support
- Redirection only on Wi-Fi/Data
- Allowlist specific contacts
- Change per-service priority
- Customize popup position, animation, and duration
- Toggle per-service support
- Redirection only on Wi-Fi/Data
- Allowlist specific contacts
- Change per-service priority
- Customize popup position, animation, and duration
- ...
# Supports
- Signal
- Telegram
- Threema
- WhatsApp
- Signal
- Telegram
- Threema
- WhatsApp
# How to Install
@ -73,7 +73,7 @@ Redirecting outgoing calls to E2EE apps.
In the app, search for "Pulse" and install it.
*Pulse uses the IzzyOnDroid repo. Some F-Droid clients, such as F-Droid itself, do not include it by default. Please add the IzzyOnDroid repo: https://apt.izzysoft.de/fdroid/repo*
_Pulse uses the IzzyOnDroid repo. Some F-Droid clients, such as F-Droid itself, do not include it by default. Please add the IzzyOnDroid repo: https://apt.izzysoft.de/fdroid/repo_
## Using Obtainium
@ -81,8 +81,8 @@ In the app, search for "Pulse" and install it.
In the “Add App” screen:
1. Add the following URL: https://weforge.xyz/partisan/Pulse
2. In **Override Source**, select **Forgejo (Codeberg)**
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 youre done!
## Install directly
@ -91,16 +91,16 @@ Go to the [Releases page](https://weforge.xyz/partisan/Pulse/releases) and downl
Install it, and youre done!
*Please note that when installing directly, the app will not receive automatic updates.*
_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.
@ -120,4 +120,4 @@ Licensed under the [Public Domain](https://www.svgrepo.com/page/licensing/#PD).
## Original Author
[This software](https://github.com/x13a/Red) was originally developed by [x13a](https://github.com/x13a), but it has been archived by the owner on Jun 22, 2022.
[This software](https://github.com/x13a/Red) was originally developed by [x13a](https://github.com/x13a), but it has been archived by the owner on Jun 22, 2022.

View file

@ -11,8 +11,8 @@ android {
applicationId = "partisan.weforge.xyz.pulse"
minSdk = 29
targetSdk = 34
versionCode = 15
versionName = "2.0.1"
versionCode = 16
versionName = "2.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

View file

@ -89,7 +89,9 @@ class ContactsFragment : Fragment() {
}
private fun getContacts(): List<ContactEntry> {
val results = mutableListOf<ContactEntry>()
val resolver: ContentResolver = requireContext().contentResolver
val projection = arrayOf(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
@ -103,16 +105,16 @@ class ContactsFragment : Fragment() {
"${ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME} ASC"
)
val results = mutableListOf<ContactEntry>()
cursor?.use {
val nameIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val numberIndex = it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
val nameIndex = it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)
val numberIndex = it.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)
while (it.moveToNext()) {
val name = it.getString(nameIndex) ?: continue
val number = it.getString(numberIndex) ?: continue
results.add(ContactEntry(name, number))
val name = it.getString(nameIndex)
val number = it.getString(numberIndex)
if (!name.isNullOrBlank() && !number.isNullOrBlank()) {
results.add(ContactEntry(name, number))
}
}
}

View file

@ -1,17 +1,20 @@
package partisan.weforge.xyz.pulse
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import com.google.android.material.button.MaterialButton
import java.util.concurrent.TimeUnit
import nl.dionsegijn.konfetti.core.Party
import nl.dionsegijn.konfetti.core.Position
import nl.dionsegijn.konfetti.core.emitter.Emitter
import nl.dionsegijn.konfetti.xml.KonfettiView
import java.util.concurrent.TimeUnit
class MainFragment : Fragment() {
@ -24,9 +27,9 @@ class MainFragment : Fragment() {
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view = inflater.inflate(R.layout.fragment_main, container, false)
prefs = Preferences(requireContext())
@ -44,14 +47,16 @@ class MainFragment : Fragment() {
if (isNowChecked && SystemClock.elapsedRealtime() - lastConfettiTime > 500) {
konfetti.start(
Party(
emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).perSecond(100),
speed = 30f,
maxSpeed = 40f,
damping = 0.85f,
spread = 360,
position = Position.Relative(0.5, 0.5)
)
Party(
emitter =
Emitter(duration = 100, TimeUnit.MILLISECONDS)
.perSecond(100),
speed = 30f,
maxSpeed = 40f,
damping = 0.85f,
spread = 360,
position = Position.Relative(0.5, 0.5)
)
)
lastConfettiTime = SystemClock.elapsedRealtime()
}
@ -62,6 +67,49 @@ class MainFragment : Fragment() {
}
}
val warningText = view.findViewById<TextView>(R.id.warningText)
val warnings = mutableListOf<String>()
// 1. Check if contacts are available
val contactCursor =
requireContext()
.contentResolver
.query(
android.provider.ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
null,
null,
null
)
if (contactCursor != null) {
contactCursor.use {
if (!it.moveToFirst()) {
warnings.add(getString(R.string.warn_no_contacts))
}
}
} else {
warnings.add(getString(R.string.warn_no_contacts))
}
// 2. Check internet connectivity
if (!hasInternet()) {
warnings.add(getString(R.string.warn_no_internet))
}
// Show warning if needed
if (warnings.isNotEmpty()) {
warningText.text = warnings.joinToString("\n")
warningText.visibility = View.VISIBLE
}
return view
}
private fun hasInternet(): Boolean {
val cm = requireContext().getSystemService(ConnectivityManager::class.java)
val capabilities = cm?.getNetworkCapabilities(cm.activeNetwork) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}
}

View file

@ -127,10 +127,13 @@ class SecretView @JvmOverloads constructor(
com.google.android.material.R.attr.colorPrimaryVariant,
com.google.android.material.R.attr.colorSecondary
)
context.obtainStyledAttributes(colorAttrs).use {
playerPaint.color = it.getColor(0, Color.CYAN)
enemyPaint.color = it.getColor(0, Color.CYAN)
colorSecondary = it.getColor(1, Color.GREEN)
val ta = context.obtainStyledAttributes(colorAttrs)
try {
playerPaint.color = ta.getColor(0, Color.CYAN)
enemyPaint.color = ta.getColor(0, Color.CYAN)
colorSecondary = ta.getColor(1, Color.GREEN)
} finally {
ta.recycle()
}
Choreographer.getInstance().postFrameCallback(this)

View file

@ -34,4 +34,17 @@
app:iconGravity="textTop"
app:iconPadding="0dp"
app:cornerRadius="48dp" />
<TextView
android:id="@+id/warningText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:padding="16dp"
android:textColor="@android:color/white"
android:background="#AAFF5252"
android:textSize="14sp"
android:text=""
android:visibility="gone" />
</FrameLayout>

View file

@ -47,6 +47,10 @@
<item>Random</item>
</string-array>
<!-- Missing perms -->
<string name="warn_no_contacts">Unable to access contacts.</string>
<string name="warn_no_internet">No internet connection.</string>
<!-- Donate screen -->
<string name="donate_title">Support Pulse Development 💖</string>
<string name="donate_description">Pulse is free and open-source. You can support future development through Ko-fi. As a thank-you, donors get special popup animation effects!</string>

View file

@ -0,0 +1,3 @@
v2.0.2
- Added warning text in case the app does not have sufficient permissions
- Fixed a bug related to tapping the Pulse logo in the About section, specific to MIUI