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 { + val stored = prefs.getString(SERVICE_ORDER_KEY, null) + return stored?.split("|")?.filter { it.isNotBlank() } ?: emptyList() + } + + fun setServiceOrder(order: List) { + 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() - private val mediumBulletsToFire = mutableListOf() + private val enemyBullets = mutableListOf() private val enemies = mutableListOf() private val rockets = mutableListOf() private val stars = mutableListOf() @@ -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 @@ - - - - + android:layout_height="wrap_content"> -