From e810208a14bb3e58d857e82e58efec24b98b14fd Mon Sep 17 00:00:00 2001 From: partisan <none@noone.no> Date: Sat, 17 May 2025 08:57:57 +0200 Subject: [PATCH] Clean up & Fixes --- .../weforge/xyz/pulse/AboutFragment.kt | 1 - .../xyz/pulse/CallRedirectionService.kt | 7 +- .../weforge/xyz/pulse/MainActivity.kt | 2 - .../xyz/pulse/PopupSettingsFragment.kt | 76 ++++----- .../partisan/weforge/xyz/pulse/PopupWindow.kt | 3 +- .../partisan/weforge/xyz/pulse/Preferences.kt | 14 +- .../partisan/weforge/xyz/pulse/SecretView.kt | 37 +++-- .../weforge/xyz/pulse/ServicesFragment.kt | 21 ++- .../weforge/xyz/pulse/WelcomeActivity.kt | 2 - .../res/layout/fragment_popup_settings.xml | 150 +++++++++--------- 10 files changed, 171 insertions(+), 142 deletions(-) diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/AboutFragment.kt b/app/src/main/java/partisan/weforge/xyz/pulse/AboutFragment.kt index 0f30615..48ec0a1 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/AboutFragment.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/AboutFragment.kt @@ -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 diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/CallRedirectionService.kt b/app/src/main/java/partisan/weforge/xyz/pulse/CallRedirectionService.kt index f9d46b4..105157b 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/CallRedirectionService.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/CallRedirectionService.kt @@ -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) diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/MainActivity.kt b/app/src/main/java/partisan/weforge/xyz/pulse/MainActivity.kt index 91749d7..8a281be 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/MainActivity.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/MainActivity.kt @@ -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 diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/PopupSettingsFragment.kt b/app/src/main/java/partisan/weforge/xyz/pulse/PopupSettingsFragment.kt index 9e607fe..6bd8d97 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/PopupSettingsFragment.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/PopupSettingsFragment.kt @@ -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 { diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/PopupWindow.kt b/app/src/main/java/partisan/weforge/xyz/pulse/PopupWindow.kt index f60b03f..fa81ca6 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/PopupWindow.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/PopupWindow.kt @@ -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 diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/Preferences.kt b/app/src/main/java/partisan/weforge/xyz/pulse/Preferences.kt index ec22994..d9b87a8 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/Preferences.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/Preferences.kt @@ -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) { diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt b/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt index 8450d28..51550e8 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/SecretView.kt @@ -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) } } diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/ServicesFragment.kt b/app/src/main/java/partisan/weforge/xyz/pulse/ServicesFragment.kt index 104ceeb..2d82657 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/ServicesFragment.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/ServicesFragment.kt @@ -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) } ) diff --git a/app/src/main/java/partisan/weforge/xyz/pulse/WelcomeActivity.kt b/app/src/main/java/partisan/weforge/xyz/pulse/WelcomeActivity.kt index 34dbc90..0b5a912 100644 --- a/app/src/main/java/partisan/weforge/xyz/pulse/WelcomeActivity.kt +++ b/app/src/main/java/partisan/weforge/xyz/pulse/WelcomeActivity.kt @@ -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 diff --git a/app/src/main/res/layout/fragment_popup_settings.xml b/app/src/main/res/layout/fragment_popup_settings.xml index 2afe2ae..e11fade 100644 --- a/app/src/main/res/layout/fragment_popup_settings.xml +++ b/app/src/main/res/layout/fragment_popup_settings.xml @@ -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>