package com.peachpasswords.qs import android.content.Context import android.content.Intent import android.graphics.drawable.Icon import android.os.Build import android.service.quicksettings.Tile import android.service.quicksettings.TileService import android.widget.Toast import com.peachpasswords.MainActivity import com.peachpasswords.R import com.peachpasswords.vault.VaultEntry import com.peachpasswords.vault.VaultRepository import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject /** * Quick Settings tile. Active when the vault is unlocked; tapping it copies the * most recently used entry's password to the clipboard. Locked → launches Peach * to the lock screen. The vault is a process singleton, so the tile sees the * live unlock state without its own session. */ @AndroidEntryPoint class PeachTileService : TileService() { @Inject lateinit var vault: VaultRepository override fun onStartListening() { super.onStartListening() updateTile() } override fun onClick() { super.onClick() if (vault.isUnlocked.value) { val recent = mostRecentPasswordEntry(vault.entries.value) if (recent?.password != null) { copyToClipboard(recent.title, recent.password!!) Toast.makeText(this, "Copied ${recent.title} password", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "No entries to copy", Toast.LENGTH_SHORT).show() } updateTile() } else { launchApp() } } private fun launchApp() { val intent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK } if (Build.VERSION.SDK_INT >= 34) { startActivityAndCollapse( android.app.PendingIntent.getActivity( this, 0, intent, android.app.PendingIntent.FLAG_IMMUTABLE or android.app.PendingIntent.FLAG_UPDATE_CURRENT, ), ) } else { @Suppress("DEPRECATION", "StartActivityAndCollapseDeprecated") startActivityAndCollapse(intent) } } private fun copyToClipboard(label: String, value: String) { // Shared IS_SENSITIVE + 30s auto-clear path (MF-8). com.peachpasswords.shared.PeachClipboard.copy(this, label, value) } private fun updateTile() { val tile = qsTile ?: return val unlocked = vault.isUnlocked.value tile.state = if (unlocked) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE tile.label = "Peach" tile.contentDescription = if (unlocked) "Copy recent password" else "Unlock Peach" if (Build.VERSION.SDK_INT >= 29) { tile.subtitle = if (unlocked) "tap to copy" else "locked" } tile.icon = Icon.createWithResource(this, R.drawable.ic_peach_mark) tile.updateTile() } } internal fun mostRecentPasswordEntry(entries: List): VaultEntry? = entries .filter { !it.password.isNullOrEmpty() } .maxByOrNull { it.lastUsedAt ?: it.modifiedAt }