PeachTileService.kt

3.0 KB · raw

1package com.peachpasswords.qs23import android.content.Context4import android.content.Intent5import android.graphics.drawable.Icon6import android.os.Build7import android.service.quicksettings.Tile8import android.service.quicksettings.TileService9import android.widget.Toast10import com.peachpasswords.MainActivity11import com.peachpasswords.R12import com.peachpasswords.vault.VaultEntry13import com.peachpasswords.vault.VaultRepository14import dagger.hilt.android.AndroidEntryPoint15import javax.inject.Inject1617/**18 * Quick Settings tile. Active when the vault is unlocked; tapping it copies the19 * most recently used entry's password to the clipboard. Locked → launches Peach20 * to the lock screen. The vault is a process singleton, so the tile sees the21 * live unlock state without its own session.22 */23@AndroidEntryPoint24class PeachTileService : TileService() {2526    @Inject lateinit var vault: VaultRepository2728    override fun onStartListening() {29        super.onStartListening()30        updateTile()31    }3233    override fun onClick() {34        super.onClick()35        if (vault.isUnlocked.value) {36            val recent = mostRecentPasswordEntry(vault.entries.value)37            if (recent?.password != null) {38                copyToClipboard(recent.title, recent.password!!)39                Toast.makeText(this, "Copied password", Toast.LENGTH_SHORT).show()40            } else {41                Toast.makeText(this, "No entries to copy", Toast.LENGTH_SHORT).show()42            }43            updateTile()44        } else {45            launchApp()46        }47    }4849    private fun launchApp() {50        val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }51        if (Build.VERSION.SDK_INT >= 34) {52            startActivityAndCollapse(53                android.app.PendingIntent.getActivity(54                    this, 0, intent,55                    android.app.PendingIntent.FLAG_IMMUTABLE or android.app.PendingIntent.FLAG_UPDATE_CURRENT,56                ),57            )58        } else {59            @Suppress("DEPRECATION", "StartActivityAndCollapseDeprecated")60            startActivityAndCollapse(intent)61        }62    }6364    private fun copyToClipboard(label: String, value: String) {65        // Shared IS_SENSITIVE + 30s auto-clear path (MF-8).66        com.peachpasswords.shared.PeachClipboard.copy(this, label, value)67    }6869    private fun updateTile() {70        val tile = qsTile ?: return71        val unlocked = vault.isUnlocked.value72        tile.state = if (unlocked) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE73        tile.label = "Peach"74        tile.contentDescription = if (unlocked) "Copy recent password" else "Unlock Peach"75        if (Build.VERSION.SDK_INT >= 29) {76            tile.subtitle = if (unlocked) "tap to copy" else "locked"77        }78        tile.icon = Icon.createWithResource(this, R.drawable.ic_peach_mark)79        tile.updateTile()80    }81}8283internal fun mostRecentPasswordEntry(entries: List<VaultEntry>): VaultEntry? =84    entries85        .filter { !it.password.isNullOrEmpty() }86        .maxByOrNull { it.lastUsedAt ?: it.modifiedAt }