commit
7cc7d5e390
8 changed files with 120 additions and 47 deletions
44
README.md
44
README.md
|
@ -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 you’re done!
|
||||
|
||||
## Install directly
|
||||
|
@ -91,16 +91,16 @@ Go to the [Releases page](https://weforge.xyz/partisan/Pulse/releases) and downl
|
|||
|
||||
Install it, and you’re 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.
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
3
fastlane/metadata/android/en-US/changelogs/17.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/17.txt
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue