diff --git a/Reef/build.gradle.kts b/Reef/build.gradle.kts index 5dcb541..9265616 100644 --- a/Reef/build.gradle.kts +++ b/Reef/build.gradle.kts @@ -38,6 +38,7 @@ android { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } + kotlin.compilerOptions { jvmTarget.set(JvmTarget.JVM_17) } @@ -51,6 +52,7 @@ android { buildToolsVersion = "36.1.0" ndkVersion = "29.0.14033849 rc4" compileSdkMinor = 1 + } dependencies { diff --git a/Reef/src/main/AndroidManifest.xml b/Reef/src/main/AndroidManifest.xml index 6d48538..12e6efd 100644 --- a/Reef/src/main/AndroidManifest.xml +++ b/Reef/src/main/AndroidManifest.xml @@ -62,7 +62,6 @@ android:exported="true" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:foregroundServiceType="specialUse" - android:process=":blocker_process" tools:ignore="AccessibilityPolicy"> @@ -72,7 +71,6 @@ android:resource="@xml/blocker_configuration" /> - - - diff --git a/Reef/src/main/java/dev/pranav/reef/MainActivity.kt b/Reef/src/main/java/dev/pranav/reef/MainActivity.kt index 5ff5f05..fdc8fcd 100644 --- a/Reef/src/main/java/dev/pranav/reef/MainActivity.kt +++ b/Reef/src/main/java/dev/pranav/reef/MainActivity.kt @@ -264,17 +264,17 @@ class MainActivity: ComponentActivity() { R.string.hour_min_short_suffix, hours, minutes - ) + " today" + ) + " " + getString(R.string.today) hours > 0 -> getString( R.string.hours_short_format, hours - ) + " today" + ) + " " + getString(R.string.today) minutes > 0 -> getString( R.string.minutes_short_format, minutes - ) + " today" + ) + " " + getString(R.string.today) else -> getString(R.string.less_than_one_minute) } @@ -761,25 +761,25 @@ private fun ReefBottomNavBar( ) { BottomNavItem( icon = Icons.Outlined.Home, - label = "Home", + label = stringResource(R.string.nav_home), selected = selectedItem == 0, onClick = { onItemSelected(0) } ) BottomNavItem( icon = Icons.Rounded.BarChart, - label = "Stats", + label = stringResource(R.string.nav_stats), selected = selectedItem == 1, onClick = { onItemSelected(1) } ) BottomNavItem( icon = Icons.Rounded.SelfImprovement, - label = "Focus", + label = stringResource(R.string.nav_focus), selected = selectedItem == 2, onClick = { onItemSelected(2) } ) BottomNavItem( icon = Icons.Outlined.Settings, - label = "Settings", + label = stringResource(R.string.nav_settings), selected = selectedItem == 3, onClick = { onItemSelected(3) } ) diff --git a/Reef/src/main/java/dev/pranav/reef/MainScreen.kt b/Reef/src/main/java/dev/pranav/reef/MainScreen.kt index 921399f..7ca425b 100644 --- a/Reef/src/main/java/dev/pranav/reef/MainScreen.kt +++ b/Reef/src/main/java/dev/pranav/reef/MainScreen.kt @@ -1,5 +1,6 @@ package dev.pranav.reef +import android.content.Context import android.content.Intent import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState @@ -105,7 +106,8 @@ fun HomeContent( TimeLimitsCard( modifier = Modifier.weight(1f), onClick = onNavigateToWhitelist, - whitelistedCount = whitelistedAppsCount + whitelistedCount = whitelistedAppsCount, + context = context ) } @@ -193,7 +195,7 @@ private fun FocusModeCard( Spacer(Modifier.height(24.dp)) Text( - text = "Focus Mode", + text = stringResource(R.string.focus_mode), style = MaterialTheme.typography.headlineLarge.copy( fontWeight = FontWeight.Bold ), @@ -204,7 +206,7 @@ private fun FocusModeCard( Spacer(Modifier.height(8.dp)) Text( - text = "Slide to start a deep work\nsession", + text = stringResource(R.string.slide_description), style = MaterialTheme.typography.bodyLarge, color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.7f), lineHeight = 22.sp, @@ -281,7 +283,7 @@ private fun FocusTogglePill( verticalAlignment = Alignment.CenterVertically ) { Text( - text = "Release", + text = stringResource(R.string.release), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onPrimaryContainer.copy( alpha = (1f - progress).coerceIn(0.3f, 0.7f) @@ -296,7 +298,7 @@ private fun FocusTogglePill( verticalAlignment = Alignment.CenterVertically ) { Text( - text = "Slide", + text = stringResource(R.string.slide), style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onPrimaryContainer.copy( alpha = (1f - progress).coerceIn(0.3f, 0.7f) @@ -368,7 +370,7 @@ private fun AppUsageCard( Column { Text( - text = "App Usage", + text = stringResource(R.string.app_usage), style = MaterialTheme.typography.titleLarge.copy( fontWeight = FontWeight.Bold ), @@ -389,7 +391,8 @@ private fun AppUsageCard( private fun TimeLimitsCard( modifier: Modifier = Modifier, onClick: () -> Unit, - whitelistedCount: Int = 0 + whitelistedCount: Int = 0, + context: Context, ) { Card( onClick = onClick, @@ -423,7 +426,7 @@ private fun TimeLimitsCard( Column { Text( - text = "Whitelist", + text = stringResource(R.string.whitelist_apps), style = MaterialTheme.typography.titleLarge.copy( fontWeight = FontWeight.Bold ), @@ -431,7 +434,11 @@ private fun TimeLimitsCard( color = MaterialTheme.colorScheme.onTertiaryContainer ) Text( - text = "$whitelistedCount apps allowed", + text = context.resources.getQuantityString( + R.plurals.whitelisted_apps, + whitelistedCount, + whitelistedCount + ), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onTertiaryContainer.copy(alpha = 0.7f) ) @@ -475,14 +482,14 @@ private fun RoutinesCard(onClick: () -> Unit) { Column(modifier = Modifier.weight(1f)) { Text( - text = "Routines", + text = stringResource(R.string.routines), style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Bold ), color = MaterialTheme.colorScheme.onSurface ) Text( - text = "Scheduled: \"Deep Work\" at 2:00 PM", + text = stringResource(R.string.sample_routine), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) @@ -565,7 +572,7 @@ private fun PomodoroTimerCard( text = if (isActive) { currentTimerState.lowercase().replaceFirstChar { it.uppercase() } } else { - "Pomodoro Timer" + stringResource(R.string.pomodoro) }, style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Bold @@ -574,9 +581,13 @@ private fun PomodoroTimerCard( ) Text( text = if (isActive) { - if (isPaused) "Paused • $currentTimeLeft" else "In progress • $currentTimeLeft" + if (isPaused) { + stringResource(R.string.paused_focus_session, currentTimeLeft) + } else { + stringResource(R.string.in_progress_focus_session, currentTimeLeft) + } } else { - "Start a focus session" + stringResource(R.string.start_focus_session) }, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant diff --git a/Reef/src/main/java/dev/pranav/reef/screens/CreateRoutineScreen.kt b/Reef/src/main/java/dev/pranav/reef/screens/CreateRoutineScreen.kt index c9190a5..48fc7a8 100644 --- a/Reef/src/main/java/dev/pranav/reef/screens/CreateRoutineScreen.kt +++ b/Reef/src/main/java/dev/pranav/reef/screens/CreateRoutineScreen.kt @@ -667,6 +667,7 @@ private fun AppSelectorDialog( Column { listOf( pluralStringResource(R.plurals.minutes_label, 0, 0) to 0, + pluralStringResource(R.plurals.minutes_label, 5, 5) to 5, pluralStringResource(R.plurals.minutes_label, 15, 15) to 15, pluralStringResource(R.plurals.minutes_label, 30, 30) to 30, pluralStringResource(R.plurals.hours_label, 1, 1) to 60, diff --git a/Reef/src/main/java/dev/pranav/reef/ui/appusage/AppUsageViewModel.kt b/Reef/src/main/java/dev/pranav/reef/ui/appusage/AppUsageViewModel.kt index edb0adf..d206178 100644 --- a/Reef/src/main/java/dev/pranav/reef/ui/appusage/AppUsageViewModel.kt +++ b/Reef/src/main/java/dev/pranav/reef/ui/appusage/AppUsageViewModel.kt @@ -275,8 +275,8 @@ class AppUsageViewModel( repeat(7) { i -> val cal = Calendar.getInstance() - val daysAgo = (_weekOffset.intValue * 7) + (6 - i) - cal.add(Calendar.DAY_OF_YEAR, -daysAgo) + val daysAgo = (_weekOffset.intValue - 1) * 7 + i + 1 + cal.add(Calendar.DAY_OF_YEAR, daysAgo) cal.set(Calendar.HOUR_OF_DAY, 0) cal.set(Calendar.MINUTE, 0) @@ -316,8 +316,8 @@ class AppUsageViewModel( private fun checkPreviousWeekData() { viewModelScope.launch(Dispatchers.IO) { val calendar = Calendar.getInstance() - val daysToSubtract = ((_weekOffset.intValue - 1) * 7) + 6 - calendar.add(Calendar.DAY_OF_YEAR, -daysToSubtract) + val daysToSubtract = (_weekOffset.intValue - 1) * 7 + calendar.add(Calendar.DAY_OF_YEAR, daysToSubtract) val maxWeeksBack = 13 if (_weekOffset.intValue <= -maxWeeksBack) { @@ -326,28 +326,29 @@ class AppUsageViewModel( } var hasData = false - repeat(7) { + for (i in 0 until 7) { val start = calendar.clone() as Calendar - start.set(Calendar.HOUR_OF_DAY, 0); start.set(Calendar.MINUTE, 0); start.set( - Calendar.SECOND, - 0 - ); start.set(Calendar.MILLISECOND, 0) + start.set(Calendar.HOUR_OF_DAY, 0) + start.set(Calendar.MINUTE, 0) + start.set(Calendar.SECOND,0) + start.set(Calendar.MILLISECOND, 0) + val end = start.clone() as Calendar - end.set(Calendar.HOUR_OF_DAY, 23); end.set( - Calendar.MINUTE, - 59 - ); end.set(Calendar.SECOND, 59); end.set(Calendar.MILLISECOND, 999) + end.set(Calendar.HOUR_OF_DAY, 23) + end.set(Calendar.MINUTE,59) + end.set(Calendar.SECOND, 59) + end.set(Calendar.MILLISECOND, 999) if (ScreenUsageHelper.calculateUsage( context, usageStatsManager, start.timeInMillis, end.timeInMillis - ).values.sum() > 0 + ).values.any { it > 0} ) { hasData = true - return@repeat + break } - calendar.add(Calendar.DAY_OF_YEAR, 1) + calendar.add(Calendar.DAY_OF_YEAR, -1) } withContext(Dispatchers.Main) { _canGoPrevious.value = hasData } diff --git a/Reef/src/main/java/dev/pranav/reef/util/ScreenUsageHelper.kt b/Reef/src/main/java/dev/pranav/reef/util/ScreenUsageHelper.kt index 88bfd6c..cd154f0 100644 --- a/Reef/src/main/java/dev/pranav/reef/util/ScreenUsageHelper.kt +++ b/Reef/src/main/java/dev/pranav/reef/util/ScreenUsageHelper.kt @@ -1,12 +1,14 @@ package dev.pranav.reef.util import android.app.usage.UsageEvents +import android.app.usage.UsageEventsQuery import android.app.usage.UsageStatsManager import android.content.Context import android.os.Build import android.util.Log import java.util.Calendar + object ScreenUsageHelper { private const val TAG = "ScreenUsageHelper" @@ -27,8 +29,6 @@ object ScreenUsageHelper { end: Long, targetPackage: String? = null ): Map { - val usageMap = mutableMapOf() - try { val eventBasedUsage = calculateUsageFromEvents(usageStatsManager, start, end, targetPackage) @@ -51,116 +51,69 @@ object ScreenUsageHelper { end: Long, targetPackage: String? = null ): Map { - val usageMap = mutableMapOf() - val packageForegroundTime = mutableMapOf() - var currentForegroundPackage: String? = null - var foregroundStartTime: Long = 0 + val packageForegroundTimes = mutableMapOf() + val packageStartTimes = mutableMapOf() - val lookbackStart = start - (24 * 60 * 60 * 1000) + var usageEvents: UsageEvents + val event = UsageEvents.Event() runCatching { - val usageEvents = usageStatsManager.queryEvents(lookbackStart, end) - - while (usageEvents.hasNextEvent()) { - val event = UsageEvents.Event() - usageEvents.getNextEvent(event) - - if (event.timeStamp < start) { - if (event.eventType == UsageEvents.Event.ACTIVITY_RESUMED || - (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - event.eventType == UsageEvents.Event.ACTIVITY_RESUMED) - ) { - currentForegroundPackage = event.packageName - foregroundStartTime = start + val lookBackStart = start - (2 * 60 * 60 * 1000) + + usageEvents = queryEvents(usageStatsManager, lookBackStart, start, targetPackage) + + while (usageEvents.hasNextEvent() && usageEvents.getNextEvent(event)) { + when (event.eventType) { + UsageEvents.Event.ACTIVITY_RESUMED -> { + packageStartTimes[event.packageName] = start } - continue - } - if (targetPackage != null && event.packageName != targetPackage) { - if (currentForegroundPackage == targetPackage && - (event.eventType == UsageEvents.Event.ACTIVITY_RESUMED || - (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && - event.eventType == UsageEvents.Event.ACTIVITY_RESUMED)) - ) { - val duration = event.timeStamp - foregroundStartTime - if (duration > 0) { - packageForegroundTime[targetPackage] = - packageForegroundTime.getOrDefault(targetPackage, 0L) + duration - } - currentForegroundPackage = null + UsageEvents.Event.ACTIVITY_PAUSED -> { + packageStartTimes.remove(event.packageName) } - continue } + } + } + + runCatching { + usageEvents = queryEvents(usageStatsManager, start, end) + while (usageEvents.hasNextEvent() && usageEvents.getNextEvent(event)) { val packageName = event.packageName val timestamp = event.timeStamp when (event.eventType) { UsageEvents.Event.ACTIVITY_RESUMED -> { - if (currentForegroundPackage != null && currentForegroundPackage != packageName) { - val duration = timestamp - foregroundStartTime - if (duration > 0 && foregroundStartTime >= start) { - packageForegroundTime[currentForegroundPackage!!] = - packageForegroundTime.getOrDefault( - currentForegroundPackage!!, - 0L - ) + duration - } + packageStartTimes[packageName] = timestamp + if (packageStartTimes.count() == 3) { + val noisyPackage = packageStartTimes.minByOrNull { it.value } + packageStartTimes.remove(noisyPackage!!.key) } - currentForegroundPackage = packageName - foregroundStartTime = timestamp } - UsageEvents.Event.ACTIVITY_PAUSED, UsageEvents.Event.ACTIVITY_STOPPED -> { - if (currentForegroundPackage == packageName) { - val duration = timestamp - foregroundStartTime - if (duration > 0 && foregroundStartTime >= start) { - packageForegroundTime[packageName] = - packageForegroundTime.getOrDefault(packageName, 0L) + duration - } - currentForegroundPackage = null - } - } - - UsageEvents.Event.SCREEN_INTERACTIVE -> { - if (currentForegroundPackage != null) { - foregroundStartTime = timestamp - } - } - - UsageEvents.Event.SCREEN_NON_INTERACTIVE -> { - if (currentForegroundPackage != null) { - val duration = timestamp - foregroundStartTime - if (duration > 0 && foregroundStartTime >= start) { - packageForegroundTime[currentForegroundPackage!!] = - packageForegroundTime.getOrDefault( - currentForegroundPackage!!, - 0L - ) + duration - } - currentForegroundPackage = null + UsageEvents.Event.ACTIVITY_PAUSED -> { + packageStartTimes[packageName]?.let { startTime -> + val duration = timestamp - startTime + packageForegroundTimes[packageName] = + packageForegroundTimes.getOrDefault(packageName, 0L) + duration + packageStartTimes.remove(packageName) } } } } - if (currentForegroundPackage != null && foregroundStartTime >= start) { - val duration = end - foregroundStartTime - if (duration > 0) { - packageForegroundTime[currentForegroundPackage!!] = - packageForegroundTime.getOrDefault( - currentForegroundPackage!!, - 0L - ) + duration + if (packageStartTimes.isNotEmpty()) { + val latestPackage = packageStartTimes.maxByOrNull { it.value } + packageStartTimes[latestPackage!!.key]?.let { startTime -> + val duration = end - startTime + packageForegroundTimes[latestPackage.key] = + packageForegroundTimes.getOrDefault(targetPackage, 0L) + duration } } - } - packageForegroundTime.forEach { (pkg, time) -> - usageMap[pkg] = time } - return usageMap.filterValues { it > 0L } + return packageForegroundTimes.filterValues { it > 0L } } private fun calculateUsageFromStats( @@ -208,24 +161,23 @@ object ScreenUsageHelper { return fetchUsageInMs(usageStatsManager, start, end).mapValues { it.value / 1000 } } - fun getDailyUsage(usageStatsManager: UsageStatsManager, packageName: String): Long { - val cal = Calendar.getInstance() - cal.set(Calendar.HOUR_OF_DAY, 0) - cal.set(Calendar.MINUTE, 0) - cal.set(Calendar.SECOND, 0) - cal.set(Calendar.MILLISECOND, 0) - val startOfDay = cal.timeInMillis - val now = System.currentTimeMillis() + private fun queryEvents(usageStatsManager: UsageStatsManager, start: Long, end: Long, targetPackage: String? = null) : UsageEvents { + var usageEvents: UsageEvents + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) { + usageEvents = usageStatsManager.queryEvents(start, end) + } else { + val query = UsageEventsQuery.Builder( + start, + end, + ).setEventTypes(*intArrayOf(UsageEvents.Event.ACTIVITY_RESUMED, UsageEvents.Event.ACTIVITY_PAUSED)) - return fetchUsageInMs(usageStatsManager, startOfDay, now, packageName)[packageName] ?: 0L - } + if (targetPackage != null) { + query.setPackageNames(targetPackage) + } + + usageEvents = usageStatsManager.queryEvents(query.build())!! + } - fun getTodayUsageMap(usageStatsManager: UsageStatsManager): Map { - val cal = Calendar.getInstance() - cal.set(Calendar.HOUR_OF_DAY, 0) - cal.set(Calendar.MINUTE, 0) - cal.set(Calendar.SECOND, 0) - cal.set(Calendar.MILLISECOND, 0) - return fetchUsageInMs(usageStatsManager, cal.timeInMillis, System.currentTimeMillis()) + return usageEvents } } diff --git a/Reef/src/main/res/values-es/strings.xml b/Reef/src/main/res/values-es/strings.xml new file mode 100644 index 0000000..182a6f2 --- /dev/null +++ b/Reef/src/main/res/values-es/strings.xml @@ -0,0 +1,317 @@ + + + Reef + Servicio de Reef + Reef es una aplicación de código abierto de productividad diseñada para ayudarte a reducir el tiempo de pantalla y mejorar tu productividad. + Servicio de accesibilidad + Reef usa el servicio de accesibilidad para bloquear distracciones y ayudarte a mantener la concentración. + Reef usa el servicio de accesiblidad para forzar los límites de tiempo de pantalla por aplicación. + Estoy de acuerdo + Tiempo restante: %1$s + Modo concentración + Uso de aplicaciones + Monitoriza el tiempo de pantalla + Acceso al uso + Reef usa el acceso al uso para monitorizar el uso de aplicaciones. No se recopila ni se comparte ningún tipo de información. + Estadísticas de uso de aplicaciones + Reef usa el permiso de acceso al uso para ayudarte a controlar el tiempo en pantalla. Ningún dato será envíado desde tu teléfono. + + Permiso de notificaciones + Por favor, concede el permiso de notificaciones para continuar. + Excepción de optimización de batería + Por favor, concede las excepciones de optimización de batería para continuar. + Programar alarmas exactas + Permite a Reef programar alarmas exactas para las rutinas. Esto asegura que el bloqueo de apps empiece y termine a la hora exacta que configures. + + Apps permitidas + Selecciona apps para excluir del modo concentración + + %1$s app permitida + %1$s apps permitidas + + + Establecer límite + Eliminar límite + Límite de uso diario + Icono de aplicación + Promedio diario + Pico de uso + Semanal + + Rutinas + Programar sesiones de concentración + + Sin rutinas + Icono de rutina + Activo + Nombre de rutina + Horario + Diario + Semanal + Manual + Hora de inicio: + Hora de fin: + Selecciona los días: + Lunes + Martes + Miércoles + Jueves + Viernes + Sábado + Domingo + Límites de aplicaciones + Añadir app + No hay límites añadidos. Toca \'Añadir app\' para limitar apps específicas + Guardar rutina + Eliminar rutina + Automatiza tu bienestar digital + Crea rutinas inteligentes para gestionar límites automáticamente en días y horas específicos + Aún no hay rutinas + Crea tu primera rutina para automatizar límites de uso de aplicaciones según tu horario + Crear rutina + Acerca de + Atrás + Siguiente + Empezar + Gestiona tu enfoque + Ajustes + + Unirse a la comunidad + Únete a nuestro Discord para conectar con otros usuarios, dar feedback y estar al día de las novedades. + Unirse a Discord + Quizás luego + Apoyar el desarrollo + Si te gusta Reef, considera apoyar su desarrollo. Tus contribuciones ayudan a mantener la app gratuita y de código abierto. + Cualquier aporte ayuda a mantener vivo y mejorando este proyecto. + + Temporizador + Activar modo \'No Molestar\' al concentrarse + Silencia notificaciones durante el modo concentración + Pomodoro + Configurar sesiones y descansos + Bloqueo de aplicaciones + Configurar apps bloqueadas + Info. de la app y créditos + Ajustes Pomodoro + Ajustes Pomodoro + Duraciones + Duraciones + Duración de la sesión de concentración + Descanso corto + Duración del descanso corto + Descanso largo + Duración del descanso largo + Ciclos antes del descanso largo + min. + ciclos + Notificaciones + Sonido + Sonar al cambiar de fase + Elegir sonido + Seleccionar tono de notificación + Vibración + Vibrar al cambiar de fase + Elegir sonido de transición + Automatización + Iniciar descansos de manera automática + Iniciar descanso al terminar la sesión de concentración + Iniciar automáticamente la sesión de concentración + Iniciar sesión de concentración al terminar el descanso + Sonido de transición + Reproducir sonido al cambiar de fase + Vibración al transicionar + Vibrar al cambiar de fase + Seleccionar sonido + Elige un sonido de notificación + + Notificaciones + Configurar alertas y recordatorios + Ajustes de notificación + Recordatorios de concentración + Recibir aviso para iniciar sesión de concentración + Alertas de descanso + Avisar cuando toque descansar + Resumen diario + Recibir informe de tiempo en pantalla a las 21:00 + Tiempo de pantalla diario + Hoy usaste tu teléfono %1$s + Avisos de límite + Avisar al acercarse al límite de uso una app + General + + Editar rutina + ¿Seguro que quieres eliminar \'%1$s\'? No se puede deshacer. + Eliminar + Cancelar + Restablecer + Quitar límite de app + OK + Seleccionar app + Buscar aplicación + No se encontraron apps + Ninguna app coincide con tu búsqueda + Poner límite a %1$s + + + %d minuto + %d minutos + + + + %1$d hora + %1$d horas + + + Introduce un nombre para la rutina + + Activar rutina + ¿Quieres activar \'%1$s\' ahora? + No se aplicaron límites de apps + + + %1$d límite aplicado + %1$d límites aplicados + + + ¡Rutina \'%1$s\' activada! %2$s + Activar + Editar + Sin límites de apps + + Horario desconocido + \ de %1$s a %2$s + a las %1$s + Todos los días + Entre semana + Fines de semana + Activación manual + m + %1$d m + %1$d h + %1$d h %2$d m + < 1 m + 0 + + Modo concentración + Temporizador + Pomodoro + Aumentar horas + Aumentar minutos + Reducir horas + Reducir minutos + Horas + Minutos + Modo Estricto + Modo flexible + No se permite pausar + Pausa y reanuda cuando quieras + Iniciar + Concentración + Descanso corto + Descanso largo + Ciclos + Iniciar Pomodoro + Reducir + Aumentar + Ciclo %1$d/%2$d + Pausado + Tómate un descanso, las aplicaciones han sido desbloqueadas + Sin interrupciones + Reanudar + Pausar + + Límite de uso diario + Historial de uso + %1$d min/día de media + + Versión %1$s + App de productividad de código abierto para reducir el tiempo en pantalla y mejorar tu bienestar digital + Desarrollador + Desarrollador Open Source enfocado en herramientas de productividad y seguridad. + Ver perfil de GitHub + Ayuda a mantener y crecer este proyecto + Donar + Mantén Reef gratis y sin anuncios + Apoyar desarrollo + Reef es totalmente gratis, sin anuncios, rastreo ni costes ocultos. Si te resulta útil, considera una pequeña donación. Tu apoyo lo es todo y mantiene vivo el proyecto. ¡Gracias! + Quizás luego + Donar + Enlaces + Comunidad de Discord + Código fuente + Reportar problema + Software Libre y de Código Abierto + + 1 app rastreada + %1$d apps rastreadas + Ordenar + Por tiempo + Por nombre (A–Z) + Cargando datos de uso… + Ver las %1$d apps + Hoy + Últimos 7 días + 7 Días + Semana anterior + Semana siguiente + %1$d%% + + Apps permitidas + No puedes usar Reef sin activar el servicio de accesibilidad + Permisos necesarios + Para que Reef funcione correctamente, concede los siguientes permisos. + Concedido + Conceder + + Parar + + Pausar + Reanudar + Sesión de concentración completada + ¡Bien hecho! Has completado tu sesión de concentración. + Toca Reanudar para empezar + + App bloqueada + %1$s está bloqueada por tu rutina activa + %1$s ha alcanzado su límite + Distracción bloqueada + Estabas usando %1$s + + Bloqueador de contenido + Muestra recordatorios de tiempo y apps bloqueadas. + Sin datos de uso + Rutina activada + Rutina desactivada + %1$s ha terminado + Recordatorio de límite + + %1$s se bloqueará en %2$d minuto + %1$s se bloqueará en %2$d minutos + + + Alertas de rutina + Notificaciones sobre activación y fin de rutinas. + + Recordatorios sobre el uso de pantalla. + Recordatorios de tiempo en pantalla + + Modo concentración + Notificaciones relacionadas con sesiones de concentración. + + ¡Se acabó el descanso! + Hora de volver al trabajo. Tu sesión de concentración empieza ahora. + Deslizar + Soltar + Desliza para comenzar una\nsesión de trabajo + Por ejemplo, configura la rutina \"Trabajo\" a las 14:00 + + Comienza una sesión de concentración + En pausa • %1$s + En progreso • %1$s + + + Inicio + Estadísticas + Concentración + Ajustes + \ No newline at end of file diff --git a/Reef/src/main/res/values/strings.xml b/Reef/src/main/res/values/strings.xml index 84fe7bb..604ad19 100644 --- a/Reef/src/main/res/values/strings.xml +++ b/Reef/src/main/res/values/strings.xml @@ -23,6 +23,11 @@ Whitelist apps Select apps to exclude from focus mode + + %1$s app allowed + %1$s apps allowed + + Set Limit Remove Limit @@ -308,4 +313,20 @@ Break Over! Time to get back to work. Your focus session is starting now. - + + Slide + Release + Slide to start a deep work\nsession + "For example, schedule \"Deep Work\" routine at 2:00 PM" + + Start a focus session + Paused • %1$s + In progress • %1$s + + + Home + Stats + Focus + Settings + + \ No newline at end of file diff --git a/appintro/build.gradle.kts b/appintro/build.gradle.kts index 746d0f8..b022757 100644 --- a/appintro/build.gradle.kts +++ b/appintro/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.compose) @@ -35,6 +37,10 @@ android { buildFeatures { compose = true } + + kotlin.compilerOptions { + jvmTarget.set(JvmTarget.JVM_11) + } } dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bc7ee2c..0704527 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "9.1.0-alpha03" +agp = "9.1.0-alpha05" coreSplashscreen = "1.2.0" kotlin = "2.3.0" coreKtx = "1.17.0" @@ -8,10 +8,10 @@ material = "1.14.0-alpha08" activity = "1.12.2" googleServices = "4.4.4" gson = "2.13.2" -composeBom = "2025.12.01" -material3 = "1.5.0-alpha11" -uiTextGoogleFonts = "1.10.0" -vico = "2.3.6" +composeBom = "2026.01.00" +material3 = "1.5.0-alpha12" +uiTextGoogleFonts = "1.10.1" +vico = "2.4.1" junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" @@ -50,7 +50,7 @@ androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx" [plugins] android-application = { id = "com.android.application", version.ref = "agp" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version = "2.3.0" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } android-library = { id = "com.android.library", version.ref = "agp" }