package com.rizoma.orbita.ui import android.app.Application import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri import android.provider.Settings import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.rizoma.orbita.data.AppRepository import com.rizoma.orbita.data.UsageStore import com.rizoma.orbita.model.AppNode import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class OrbitaViewModel(app: Application) : AndroidViewModel(app) { private val repo = AppRepository(app) private val usage = UsageStore(app) private val rawApps = MutableStateFlow>(emptyList()) /** Nodos ya ponderados y ordenados: fijados primero, luego por calor, luego alfabético. */ val nodes: StateFlow> = combine( rawApps, usage.counts(), usage.pinned(), ) { apps, counts, pinned -> apps.map { node -> node.copy( score = (counts[node.key] ?: 0).toFloat(), pinned = node.key in pinned, ) }.sortedWith( compareByDescending { it.pinned } .thenByDescending { it.score } .thenBy { it.label.lowercase() } ) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) private val pkgReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) = refresh() } init { refresh() val filter = IntentFilter().apply { addAction(Intent.ACTION_PACKAGE_ADDED) addAction(Intent.ACTION_PACKAGE_REMOVED) addAction(Intent.ACTION_PACKAGE_CHANGED) addDataScheme("package") } getApplication().registerReceiver(pkgReceiver, filter) } fun refresh() { viewModelScope.launch(Dispatchers.IO) { rawApps.value = repo.loadApps() } } fun launch(node: AppNode) { val intent = Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_LAUNCHER) .setComponent(ComponentName(node.packageName, node.activityName)) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) runCatching { getApplication().startActivity(intent) } viewModelScope.launch { usage.record(node.key) } } fun togglePin(node: AppNode) { viewModelScope.launch { usage.setPinned(node.key, !node.pinned) } } fun openInfo(node: AppNode) { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", node.packageName, null)) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) runCatching { getApplication().startActivity(intent) } } fun uninstall(node: AppNode) { @Suppress("DEPRECATION") val intent = Intent(Intent.ACTION_DELETE) .setData(Uri.fromParts("package", node.packageName, null)) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) runCatching { getApplication().startActivity(intent) } } override fun onCleared() { runCatching { getApplication().unregisterReceiver(pkgReceiver) } } }