Compare commits

..

No commits in common. "main" and "v2.0.0" have entirely different histories.
main ... v2.0.0

13 changed files with 53 additions and 138 deletions

View file

@ -73,7 +73,7 @@ Redirecting outgoing calls to E2EE apps.
In the app, search for "Pulse" and install it. 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 ## Using Obtainium
@ -91,7 +91,7 @@ Go to the [Releases page](https://weforge.xyz/partisan/Pulse/releases) and downl
Install it, and youre done! 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 # Permissions

View file

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

View file

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

View file

@ -1,20 +1,17 @@
package partisan.weforge.xyz.pulse package partisan.weforge.xyz.pulse
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import java.util.concurrent.TimeUnit
import nl.dionsegijn.konfetti.core.Party import nl.dionsegijn.konfetti.core.Party
import nl.dionsegijn.konfetti.core.Position import nl.dionsegijn.konfetti.core.Position
import nl.dionsegijn.konfetti.core.emitter.Emitter import nl.dionsegijn.konfetti.core.emitter.Emitter
import nl.dionsegijn.konfetti.xml.KonfettiView import nl.dionsegijn.konfetti.xml.KonfettiView
import java.util.concurrent.TimeUnit
class MainFragment : Fragment() { class MainFragment : Fragment() {
@ -27,9 +24,9 @@ class MainFragment : Fragment() {
} }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
val view = inflater.inflate(R.layout.fragment_main, container, false) val view = inflater.inflate(R.layout.fragment_main, container, false)
prefs = Preferences(requireContext()) prefs = Preferences(requireContext())
@ -47,16 +44,14 @@ class MainFragment : Fragment() {
if (isNowChecked && SystemClock.elapsedRealtime() - lastConfettiTime > 500) { if (isNowChecked && SystemClock.elapsedRealtime() - lastConfettiTime > 500) {
konfetti.start( konfetti.start(
Party( Party(
emitter = emitter = Emitter(duration = 100, TimeUnit.MILLISECONDS).perSecond(100),
Emitter(duration = 100, TimeUnit.MILLISECONDS) speed = 30f,
.perSecond(100), maxSpeed = 40f,
speed = 30f, damping = 0.85f,
maxSpeed = 40f, spread = 360,
damping = 0.85f, position = Position.Relative(0.5, 0.5)
spread = 360, )
position = Position.Relative(0.5, 0.5)
)
) )
lastConfettiTime = SystemClock.elapsedRealtime() lastConfettiTime = SystemClock.elapsedRealtime()
} }
@ -67,49 +62,6 @@ 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 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

@ -133,11 +133,7 @@ class PopupSettingsFragment : Fragment() {
binding.popupEffectSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { binding.popupEffectSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
val selectedEffect = allEffects[position] val selectedEffect = allEffects[position]
if (!prefs.isDonationActivated && if (!prefs.isDonationActivated && selectedEffect !in prefs.getAvailablePopupEffects()) {
selectedEffect !in prefs.getAvailablePopupEffects() &&
selectedEffect != Preferences.PopupEffect.NONE &&
selectedEffect != Preferences.PopupEffect.RANDOM
) {
Toast.makeText(requireContext(), getString(R.string.donate_lock), Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), getString(R.string.donate_lock), Toast.LENGTH_SHORT).show()
binding.popupEffectSpinner.setSelection(prefs.popupEffect.ordinal) binding.popupEffectSpinner.setSelection(prefs.popupEffect.ordinal)
} else { } else {

View file

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

View file

@ -34,17 +34,4 @@
app:iconGravity="textTop" app:iconGravity="textTop"
app:iconPadding="0dp" app:iconPadding="0dp"
app:cornerRadius="48dp" /> 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> </FrameLayout>

View file

@ -47,10 +47,6 @@
<item>Random</item> <item>Random</item>
</string-array> </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 --> <!-- Donate screen -->
<string name="donate_title">Support Pulse Development 💖</string> <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> <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

@ -17,6 +17,6 @@ Fixes:
Misc: Misc:
- Updated Gradle to v8.14.1 - Updated Gradle to v8.14.1
- Updated screenshots to reflect the new look of the app - Updated screenshots to include new looks of the app.
- Updated description - Updated description.
- Temporarily added "v2.0" to the store icon to indicate the new version - Added temporary "v2.0" to store icon to indicate new version.

View file

@ -1,2 +0,0 @@
v2.0.1
- Fixed lock warning on NONE and RANDOM popup effects

View file

@ -1,3 +0,0 @@
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

View file

@ -3,7 +3,6 @@ Redirect calls to Signal, Telegram, Threema, or WhatsApp.
--- ---
**Features:** **Features:**
- Material You design - Material You design
- Popup with cancel option - Popup with cancel option
- Extensive settings panel: - Extensive settings panel:
@ -12,17 +11,15 @@ Redirect calls to Signal, Telegram, Threema, or WhatsApp.
- Allowlist specific contacts - Allowlist specific contacts
- Change per-service priority - Change per-service priority
- Customize popup position, animation, and duration - Customize popup position, animation, and duration
- etc ...
**Supports:** **Supports:**
- Signal - Signal
- Telegram - Telegram
- Threema - Threema
- WhatsApp - WhatsApp
**Permissions required:** **Permissions required:**
- `CALL_PHONE` - initiate calls via messenger - `CALL_PHONE` - initiate calls via messenger
- `READ_CONTACTS` - check contact compatibility - `READ_CONTACTS` - check contact compatibility
- `READ_PHONE_NUMBERS` - detect outgoing call - `READ_PHONE_NUMBERS` - detect outgoing call
@ -32,8 +29,5 @@ Redirect calls to Signal, Telegram, Threema, or WhatsApp.
Currently all of the permissions are required. Currently all of the permissions are required.
---
**License:** GPL-3.0 **License:** GPL-3.0
Free and open source Free and open source

View file

@ -1 +1 @@
Redirecting outgoing calls to E2EE apps Redirecting outgoing calls to E2EE apps.