Clean up & Fixes

This commit is contained in:
partisan 2025-05-17 08:57:57 +02:00
parent 27cd0a829e
commit e810208a14
10 changed files with 171 additions and 142 deletions

View file

@ -6,7 +6,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import androidx.fragment.app.Fragment
import partisan.weforge.xyz.pulse.databinding.FragmentAboutBinding

View file

@ -82,7 +82,12 @@ class CallRedirectionService : CallRedirectionService() {
return
}
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
if (prefs.popupEnabled) {
window.show(record.uri, MIMETYPE_TO_DST_NAME[record.mimetype] ?: return)
} else {
window.call(record.uri)
cancelCall()
}
}
@RequiresPermission(Manifest.permission.READ_CONTACTS)

View file

@ -2,8 +2,6 @@ package partisan.weforge.xyz.pulse
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import partisan.weforge.xyz.pulse.databinding.ActivityMainBinding
import androidx.appcompat.app.ActionBarDrawerToggle

View file

@ -8,8 +8,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.core.content.getSystemService
import partisan.weforge.xyz.pulse.databinding.FragmentPopupSettingsBinding
@ -33,48 +31,42 @@ class PopupSettingsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val services = listOf(
ServiceEntry(
"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call",
R.string.destination_signal,
requireContext().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,
requireContext().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,
requireContext().isServiceEnabled("vnd.android.cursor.item/vnd.ch.threema.app.call")
),
ServiceEntry(
"vnd.android.cursor.item/vnd.com.whatsapp.voip.call",
R.string.destination_whatsapp,
requireContext().isServiceEnabled("vnd.android.cursor.item/vnd.com.whatsapp.voip.call")
),
)
prefs = Preferences(requireContext())
window = PopupWindow(requireContext(), null)
val adapter = ServiceAdapter(
context = requireContext(),
services = services.toMutableList(),
onReordered = { updatedList ->
updatedList.forEachIndexed { index, entry ->
requireContext().setServicePriority(entry.mimetype, index)
}
}
)
binding.serviceRecycler.adapter = adapter
binding.serviceRecycler.layoutManager = LinearLayoutManager(requireContext())
val touchHelper = ItemTouchHelper(adapter.dragHelper)
touchHelper.attachToRecyclerView(binding.serviceRecycler)
adapter.setDragStartListener { viewHolder ->
touchHelper.startDrag(viewHolder)
binding.popupEnabledCheckbox.isChecked = prefs.popupEnabled
binding.popupEnabledCheckbox.setOnCheckedChangeListener { _, isChecked ->
prefs.popupEnabled = isChecked
updateControls(isChecked)
}
binding.popupPreview.setOnClickListener {
window.preview()
}
binding.redirectionDelay.value = (prefs.redirectionDelay / 1000).toFloat()
binding.redirectionDelay.setLabelFormatter {
String.format("%.1f", it)
}
binding.redirectionDelay.addOnChangeListener { _, value, _ ->
prefs.redirectionDelay = (value * 1000).toLong()
}
val screenHeight = getScreenHeightPx()
binding.popupHeightSlider.valueFrom = 0f
binding.popupHeightSlider.valueTo = screenHeight.toFloat()
binding.popupHeightSlider.value = prefs.popupPosition.toFloat()
binding.popupHeightSlider.addOnChangeListener { _, value, _ ->
prefs.popupPosition = value.toInt().coerceIn(0, screenHeight)
}
updateControls(prefs.popupEnabled)
}
private fun updateControls(enabled: Boolean) {
binding.redirectionDelay.isEnabled = enabled
binding.popupHeightSlider.isEnabled = enabled
binding.popupPreview.isEnabled = enabled
}
private fun getScreenHeightPx(): Int {

View file

@ -23,7 +23,6 @@ import kotlin.concurrent.timerTask
import android.util.Log
import android.content.res.ColorStateList
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.utilities.MaterialDynamicColors
import com.google.android.material.color.MaterialColors
class PopupWindow(
@ -121,7 +120,7 @@ class PopupWindow(
}
@RequiresPermission(Manifest.permission.CALL_PHONE)
private fun call(data: Uri) {
fun call(data: Uri) {
Intent(Intent.ACTION_VIEW).apply {
this.data = data
flags = Intent.FLAG_ACTIVITY_NEW_TASK

View file

@ -11,6 +11,7 @@ class Preferences(private val context: Context) {
private const val POPUP_POSITION = "popup_position_y"
private const val POPUP_ENABLED = "popup_enabled"
private const val BLACKLISTED_CONTACTS = "blacklisted_contacts"
private val SERVICE_ORDER_KEY = "service_order"
private const val DEFAULT_REDIRECTION_DELAY = 2000L
private const val DEFAULT_POPUP_POSITION = 333
@ -51,7 +52,18 @@ class Preferences(private val context: Context) {
}
fun getServicePriority(mimetype: String): Int {
return prefs.getInt(makeKeyPriority(mimetype), Int.MAX_VALUE)
val order = getServiceOrder()
val index = order.indexOf(mimetype)
return if (index != -1) index else Int.MAX_VALUE
}
fun getServiceOrder(): List<String> {
val stored = prefs.getString(SERVICE_ORDER_KEY, null)
return stored?.split("|")?.filter { it.isNotBlank() } ?: emptyList()
}
fun setServiceOrder(order: List<String>) {
prefs.edit().putString(SERVICE_ORDER_KEY, order.joinToString("|")).apply()
}
fun setServiceEnabled(mimetype: String, enabled: Boolean) {

View file

@ -7,7 +7,6 @@ import android.util.AttributeSet
import android.view.Choreographer
import android.view.MotionEvent
import android.view.View
import com.google.android.material.color.MaterialColors
import kotlin.math.*
import kotlin.random.Random
@ -64,7 +63,7 @@ class SecretView @JvmOverloads constructor(
private var score = 0
private val bullets = mutableListOf<Bullet>()
private val mediumBulletsToFire = mutableListOf<Bullet>()
private val enemyBullets = mutableListOf<Bullet>()
private val enemies = mutableListOf<Enemy>()
private val rockets = mutableListOf<Rocket>()
private val stars = mutableListOf<Star>()
@ -164,9 +163,9 @@ class SecretView @JvmOverloads constructor(
}
rockets.removeIf { it.x < 0 || it.x > viewWidth || it.y < 0 || it.y > viewHeight }
bullets.addAll(mediumBulletsToFire)
mediumBulletsToFire.clear()
enemyBullets.forEach { it.y += it.dy * deltaMs / 16f }
enemyBullets.removeIf { it.y > viewHeight }
checkCollisions()
spawnEnemies(deltaMs)
}
@ -190,6 +189,14 @@ class SecretView @JvmOverloads constructor(
}
}
for (b in enemyBullets) {
if (hypot(b.x - playerX, b.y - (viewHeight - 100f)) < 20f) {
explosions.add(Explosion(playerX, viewHeight - 100f))
gameOver = true
return
}
}
val rocketIter = rockets.iterator()
while (rocketIter.hasNext()) {
val rocket = rocketIter.next()
@ -258,7 +265,7 @@ class SecretView @JvmOverloads constructor(
// The group sync isn't working as enemies gradually get out of sync, but whatever, it's fine.
val enemy = when (currentWaveType) {
"easy" -> EnemyEasy(x, baseY)
"medium" -> EnemyMedium(x, baseY, sharedOffset, sharedFireTime) { mediumBulletsToFire.add(it) }
"medium" -> EnemyMedium(x, baseY, sharedOffset, sharedFireTime) { enemyBullets.add(it) }
"hard" -> EnemyHard(x, baseY, { rockets.add(it) }, sharedOffset, sharedFireTime)
else -> EnemyEasy(x, baseY)
}
@ -271,6 +278,7 @@ class SecretView @JvmOverloads constructor(
private fun resetGame() {
bullets.clear()
enemyBullets.clear()
enemies.clear()
rockets.clear()
explosions.clear()
@ -280,6 +288,8 @@ class SecretView @JvmOverloads constructor(
enemiesLeftInWave = 0
gameOver = false
playerX = viewWidth / 2f
lastLogicTime = System.nanoTime()
Choreographer.getInstance().postFrameCallback(this)
}
override fun onDraw(canvas: Canvas) {
@ -289,6 +299,8 @@ class SecretView @JvmOverloads constructor(
canvas.drawColor(Color.parseColor("#121212"))
stars.forEach { canvas.drawCircle(it.x, it.y, it.radius, starPaint) }
enemyBullets.forEach { canvas.drawLine(it.x, it.y, it.x, it.y + 20f, rocketPaint) }
rockets.forEach { rocket ->
rocket.trail.forEachIndexed { index, (x, y) ->
val alpha = ((1f - index / 20f.toFloat()) * 255).toInt()
@ -328,17 +340,14 @@ class SecretView @JvmOverloads constructor(
} else {
canvas.drawText("Game Over", viewWidth / 2f, viewHeight / 2f - 60f, textPaint)
canvas.drawText("Score: $score", viewWidth / 2f, viewHeight / 2f + 10f, textPaint)
canvas.drawRoundRect(retryRect, 20f, 20f, retryPaint)
canvas.drawText("Retry", retryRect.centerX(), retryRect.centerY() + 16f, retryTextPaint)
canvas.drawText("Tap to restart", retryRect.centerX(), retryRect.centerY() + 16f, retryTextPaint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (gameOver && event.action == MotionEvent.ACTION_DOWN) {
if (retryRect.contains(event.x, event.y)) {
resetGame()
return true
}
resetGame()
return true
}
when (event.action) {
@ -419,7 +428,7 @@ class SecretView @JvmOverloads constructor(
override var y: Float,
private val offset: Float,
private var fireTimer: Long,
val fireBullet: (Bullet) -> Unit
val fireEnemyBullet: (Bullet) -> Unit
) : Enemy {
override fun update(deltaMs: Long) {
y += 2.5f * deltaMs / 16f
@ -427,7 +436,7 @@ class SecretView @JvmOverloads constructor(
fireTimer -= deltaMs
if (fireTimer <= 0) {
fireBullet(Bullet(x, y + 30f, dy = 10f))
fireEnemyBullet(Bullet(x, y + 30f, dy = 10f))
fireTimer = Random.nextLong(3000L, 6000L)
}
}

View file

@ -26,7 +26,7 @@ class ServiceSettingsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val services = listOf(
val available = listOf(
ServiceEntry(
"vnd.android.cursor.item/vnd.org.thoughtcrime.securesms.call",
R.string.destination_signal,
@ -49,13 +49,24 @@ class ServiceSettingsFragment : Fragment() {
),
)
val storedOrder = Preferences(requireContext()).getServiceOrder()
val ordered = storedOrder.mapNotNull { mime ->
available.find { it.mimetype == mime }
}.toMutableList()
// Add any missing services that weren't stored (e.g., first run)
val missing = available.filterNot { s -> ordered.any { it.mimetype == s.mimetype } }
ordered += missing
val prefs = Preferences(requireContext())
val adapter = ServiceAdapter(
context = requireContext(),
services = services.toMutableList(),
services = ordered,
onReordered = { updatedList ->
updatedList.forEachIndexed { index, entry ->
requireContext().setServicePriority(entry.mimetype, index)
}
val newOrder = updatedList.map { it.mimetype }
prefs.setServiceOrder(newOrder)
}
)

View file

@ -1,9 +1,7 @@
package partisan.weforge.xyz.pulse
import android.Manifest
import android.app.role.RoleManager
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.provider.Settings
import android.view.View

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@ -8,82 +8,88 @@
android:padding="32dp"
tools:context=".PopupSettingsFragment">
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/popup_settings_description"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<CheckBox
android:id="@+id/popupEnabledCheckbox"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/popup_enabled"
android:textSize="14sp"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@id/description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
android:layout_height="wrap_content">
<Button
android:id="@+id/popupPreview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@id/popupEnabledCheckbox"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/popup_settings_description"
android:textSize="16sp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.slider.Slider
android:id="@+id/redirectionDelay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stepSize="0.5"
android:valueFrom="2"
android:valueTo="4"
android:layout_marginTop="8dp"
android:contentDescription="@string/redirection_delay_description" />
<CheckBox
android:id="@+id/popupEnabledCheckbox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/popup_enabled"
android:textSize="14sp"
app:layout_constraintTop_toBottomOf="@id/description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/popupPreview"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/delayDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/redirection_delay_description"
android:textSize="12sp"
android:layout_marginTop="4dp" />
<Button
android:id="@+id/popupPreview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/test"
app:layout_constraintTop_toTopOf="@id/popupEnabledCheckbox"
app:layout_constraintBottom_toBottomOf="@id/popupEnabledCheckbox"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginStart="8dp" />
<com.google.android.material.slider.Slider
android:id="@+id/popupHeightSlider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="0"
android:valueTo="100"
android:stepSize="1"
android:layout_marginTop="16dp"
android:contentDescription="@string/popup_position" />
<com.google.android.material.slider.Slider
android:id="@+id/redirectionDelay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:stepSize="0.5"
android:valueFrom="2"
android:valueTo="4"
android:contentDescription="@string/redirection_delay_description"
app:layout_constraintTop_toBottomOf="@id/popupEnabledCheckbox"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/heightDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/popup_position"
android:textSize="12sp"
android:layout_marginTop="4dp" />
<TextView
android:id="@+id/delayDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/redirection_delay_description"
android:textSize="12sp"
app:layout_constraintTop_toBottomOf="@id/redirectionDelay"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="4dp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="?android:attr/listDivider" />
<com.google.android.material.slider.Slider
android:id="@+id/popupHeightSlider"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:valueFrom="0"
android:valueTo="100"
android:stepSize="1"
android:contentDescription="@string/popup_position"
app:layout_constraintTop_toBottomOf="@id/delayDescription"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/serviceRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
android:layout_marginTop="8dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/heightDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/popup_position"
android:textSize="12sp"
app:layout_constraintTop_toBottomOf="@id/popupHeightSlider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="4dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>