diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3fdf003c491f19b33e446cbaf4142ccc1ba5c644..2a52077585e628831a5cf0167d6e5975509b487c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,7 +47,6 @@ android { } dependencies { - implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.activity.compose) @@ -58,6 +57,11 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.androidx.navigation.runtime.ktx) implementation(libs.androidx.navigation.compose) + + // Notification and Background Task Dependencies + implementation("androidx.work:work-runtime-ktx:2.8.0") // WorkManager + implementation("androidx.core:core:1.10.0") // Core functionality, including notifications + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -65,4 +69,4 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) -} \ No newline at end of file +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6c7e6cab950943cc83497c1a1c9786671d647c56..e44d47c06872b0f6ded87be67a102756aec53f98 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,16 +2,17 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> + <!-- Permissions --> <uses-permission android:name="android.permission.VIBRATE" android:required="false"/> - <uses-permission android:name="android.permission.WAKE_LOCK"/> - <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> - <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> - <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/> - <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/> - <uses-permission android:name="android.permission.USE_EXACT_ALARM"/> - <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/> - - + <uses-permission android:name="android.permission.WAKE_LOCK" android:required="false"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" android:required="false"/> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" android:required="false"/> + <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" android:required="false"/> + <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" android:required="false"/> + <uses-permission android:name="android.permission.USE_EXACT_ALARM" android:required="false"/> + <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" android:required="false"/> + <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" android:required="false"/> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" android:required="false"/> <application android:allowBackup="true" @@ -23,16 +24,20 @@ android:supportsRtl="true" android:theme="@style/Theme.MyApplication" tools:targetApi="31"> + + <!-- Main Activity --> <activity android:name=".MainActivity" android:exported="true" android:theme="@style/Theme.MyApplication"> <intent-filter> <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + + </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/app/src/main/java/com/pomo/myapplication/MainActivity.kt b/app/src/main/java/com/pomo/myapplication/MainActivity.kt index 835c6d09129720be76204f0faeec0cd72f7e26cf..fe3559dfbc7742f6bedf3202760304e166ce9c8a 100644 --- a/app/src/main/java/com/pomo/myapplication/MainActivity.kt +++ b/app/src/main/java/com/pomo/myapplication/MainActivity.kt @@ -1,31 +1,50 @@ package com.pomo.myapplication -import android.Manifest.permission.POST_NOTIFICATIONS +import android.Manifest import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.core.content.ContextCompat import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.pomo.myapplication.ui.theme.MyApplicationTheme -import com.pomo.myapplication.ui.theme.ToDoScreen class MainActivity : ComponentActivity() { - val postNotificationsPermission = POST_NOTIFICATIONS - lateinit var notificationHelper: NotificationHelper var timeLeftForNotification: String? = null + val postNotificationsPermission = Manifest.permission.POST_NOTIFICATIONS + private lateinit var notificationHelper: NotificationHelper + private lateinit var permissionLauncher: ActivityResultLauncher<String> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) notificationHelper = NotificationHelper(this) + // Initialize the permission launcher + permissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show() + notificationHelper.updateNotification() + } else { + Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show() + } + } + + // Check and request notification permission + checkAndRequestNotificationPermission() + setContent { MyApplicationTheme { Surface( @@ -33,36 +52,29 @@ class MainActivity : ComponentActivity() { color = MaterialTheme.colorScheme.background ) { val navController = rememberNavController() - SetupNavGraph(navController = navController) + SetupNavGraph(navController) } } } } - companion object { - const val postNotificationsPermission: String = POST_NOTIFICATIONS - const val PERMISSION_REQUEST_CODE = 1001 - } - - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array<out String>, - grantResults: IntArray - ) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == PERMISSION_REQUEST_CODE) { - if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { - timeLeftForNotification?.let { + private fun checkAndRequestNotificationPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + when { + ContextCompat.checkSelfPermission(this, postNotificationsPermission) == PackageManager.PERMISSION_GRANTED -> { notificationHelper.updateNotification() } - } else { - // Permission denied, handle accordingly + else -> { + permissionLauncher.launch(postNotificationsPermission) + } } + } else { + notificationHelper.updateNotification() } } @Composable - fun SetupNavGraph(navController: NavHostController) { + private fun SetupNavGraph(navController: NavHostController) { NavHost( navController = navController, startDestination = "timer_screen" @@ -79,12 +91,8 @@ class MainActivity : ComponentActivity() { } } } -} -@Composable - fun TimerDisplay(time: String) { - Text( - text = time, - style = MaterialTheme.typography.displayLarge - ) + companion object { + const val PERMISSION_REQUEST_CODE= 1001 } +} \ No newline at end of file diff --git a/app/src/main/java/com/pomo/myapplication/NotificationUtils.kt b/app/src/main/java/com/pomo/myapplication/NotificationUtils.kt index d2d1cbefdad9e43cedcc9283501d4e88b0d8e34d..2f0e467a454b163edb14655ad85b082309f5dbee 100644 --- a/app/src/main/java/com/pomo/myapplication/NotificationUtils.kt +++ b/app/src/main/java/com/pomo/myapplication/NotificationUtils.kt @@ -36,9 +36,12 @@ class NotificationHelper(private val context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val name = "Timer Notification" val descriptionText = "Shows notifications when the timer is running or has finished" - val importance = NotificationManager.IMPORTANCE_DEFAULT + val importance = NotificationManager.IMPORTANCE_HIGH // Use high importance for heads-up notifications val channel = NotificationChannel(CHANNEL_ID, name, importance).apply { description = descriptionText + enableLights(true) + enableVibration(true) + vibrationPattern = longArrayOf(1000, 1000) // Optional: Vibration pattern } val notificationManager: NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -58,6 +61,7 @@ class NotificationHelper(private val context: Context) { if (isTimerRunning && remainingTime > 0) { remainingTime -= 1000 // Decrease by 1 second timeLeft = formatTime(remainingTime) + updateNotification() // Update notification during countdown handler.postDelayed(this, 1000) // Schedule next update } else { isTimerRunning = false @@ -84,7 +88,7 @@ class NotificationHelper(private val context: Context) { val notification = buildNotification( title = "Timer abgelaufen", contentText = "Der Timer ist abgelaufen!", - ) + ) showNotification(notification) } @@ -128,16 +132,14 @@ class NotificationHelper(private val context: Context) { val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE) - val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_launcher_foreground) // Ensure you have this drawable resource + return NotificationCompat.Builder(context, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(title) .setContentText(contentText) - .setPriority(NotificationCompat.PRIORITY_HIGH) + .setPriority(NotificationCompat.PRIORITY_HIGH) // Set priority to high for heads-up notifications .setContentIntent(pendingIntent) .setAutoCancel(true) - - - return notificationBuilder.build() + .build() } private fun showNotification(notification: Notification) { @@ -147,7 +149,6 @@ class NotificationHelper(private val context: Context) { } } catch (e: SecurityException) { e.printStackTrace() - // Optionally, you could also show a toast or log the issue } } } diff --git a/app/src/main/java/com/pomo/myapplication/Permissions.kt b/app/src/main/java/com/pomo/myapplication/Permissions.kt index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c4a31c50753cce8d9999e0fbb347363b4f34e241 100644 --- a/app/src/main/java/com/pomo/myapplication/Permissions.kt +++ b/app/src/main/java/com/pomo/myapplication/Permissions.kt @@ -0,0 +1,62 @@ +package com.pomo.myapplication + +import android.Manifest +import android.annotation.SuppressLint +import android.content.pm.PackageManager +import android.os.Bundle +import android.widget.Toast +import androidx.activity.ComponentActivity +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.compose.setContent +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.core.app.ActivityCompat + +class PermissionRequestScreen : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + PermissionScreen() + } + } + + @Composable + fun PermissionButton(permission: String, text: String) { + val context = LocalContext.current + val permissionLauncher = rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> + if (isGranted) { + // Berechtigung gewährt + Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show() + } else { + // Berechtigung verweigert + Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show() + } + } + + Button(onClick = { + if (ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) { + Toast.makeText(context, "Permission already granted", Toast.LENGTH_SHORT).show() + } else { + permissionLauncher.launch(permission) + } + }) { + Text(text) + } + } + + @SuppressLint("InlinedApi") + @Composable + fun PermissionScreen() { + PermissionButton(permission = Manifest.permission.VIBRATE, text = "Request VIBRATE Permission") + PermissionButton(permission = Manifest.permission.WAKE_LOCK, text = "Request WAKE_LOCK Permission") + PermissionButton(permission = Manifest.permission.FOREGROUND_SERVICE, text = "Request FOREGROUND_SERVICE Permission") + PermissionButton(permission = Manifest.permission.POST_NOTIFICATIONS, text = "Request POST_NOTIFICATIONS Permission") + PermissionButton(permission = Manifest.permission.ACCESS_NOTIFICATION_POLICY, text = "Request ACCESS_NOTIFICATION_POLICY Permission") + PermissionButton(permission = Manifest.permission.USE_FULL_SCREEN_INTENT, text = "Request USE_FULLSCREEN_INTENT Permission") + PermissionButton(permission = Manifest.permission.USE_EXACT_ALARM, text = "Request USE_EXACT_ALARM Permission") + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pomo/myapplication/TimerScreen.kt b/app/src/main/java/com/pomo/myapplication/TimerScreen.kt index 701b66e593aed8cc6c95adc79a033435110dc5d8..77a765ffc2a92f79f0391f018a29f2defcb5130f 100644 --- a/app/src/main/java/com/pomo/myapplication/TimerScreen.kt +++ b/app/src/main/java/com/pomo/myapplication/TimerScreen.kt @@ -2,18 +2,37 @@ package com.pomo.myapplication import android.content.pm.PackageManager import androidx.activity.ComponentActivity -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.List -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.core.app.ActivityCompat -import com.pomo.myapplication.ui.theme.MotivationalQuotes -import kotlinx.coroutines.* +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable fun TimerScreen( @@ -21,25 +40,26 @@ fun TimerScreen( notificationHelper: NotificationHelper, activity: ComponentActivity ) { - var timeLeftInMillis by remember { mutableLongStateOf(25 * 60 * 1000L) } + val coroutineScope = rememberCoroutineScope() + var timeLeftInMillis by remember { mutableStateOf(25 * 60 * 1000L) } var timerRunning by remember { mutableStateOf(false) } - var customMinutes by remember { mutableIntStateOf(0) } - val timeFormatted = remember(timeLeftInMillis) { - val minutes = (timeLeftInMillis / 1000) / 60 - val seconds = (timeLeftInMillis / 1000) % 60 - String.format("%02d:%02d", minutes, seconds) - } - - var showDialog by remember { mutableStateOf(false) } + var customMinutes by remember { mutableStateOf(0) } var inputMinutes by remember { mutableStateOf("") } + var showDialog by remember { mutableStateOf(false) } + var lastSelectedDuration by remember { mutableStateOf(25) } - var job: Job? by remember { mutableStateOf(null) } + val timeFormatted by remember(timeLeftInMillis) { + derivedStateOf { + val minutes = (timeLeftInMillis / 1000) / 60 + val seconds = (timeLeftInMillis / 1000) % 60 + String.format("%02d:%02d", minutes, seconds) + } + } fun startTimer() { - job?.cancel() - job = CoroutineScope(Dispatchers.Main).launch { - timerRunning = true - notificationHelper.startTimerCountdown(timeLeftInMillis) + timerRunning = true + notificationHelper.startTimerCountdown(timeLeftInMillis) + coroutineScope.launch { while (timeLeftInMillis > 0 && timerRunning) { delay(1000L) timeLeftInMillis -= 1000L @@ -60,21 +80,22 @@ fun TimerScreen( } fun stopTimer() { - job?.cancel() timerRunning = false } fun resetTimer() { - job?.cancel() - timeLeftInMillis = 0 + timeLeftInMillis = lastSelectedDuration * 60 * 1000L timerRunning = false + notificationHelper.cancelNotification() } fun setTimer(minutes: Int) { - job?.cancel() - timeLeftInMillis = minutes * 60 * 1000L - timerRunning = false - notificationHelper.cancelNotification() + if (minutes > 0) { + timeLeftInMillis = minutes * 60 * 1000L + lastSelectedDuration = minutes + timerRunning = false + notificationHelper.cancelNotification() + } } fun setCustomTimer() { @@ -86,8 +107,6 @@ fun TimerScreen( } } - val motivationalQuote = remember { MotivationalQuotes.getRandomQuote() } - Box( modifier = Modifier.fillMaxSize() ) { @@ -98,13 +117,6 @@ fun TimerScreen( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { - Text( - text = motivationalQuote, - style = MaterialTheme.typography.titleLarge, - color = Color.Gray, - modifier = Modifier.padding(16.dp) - ) - TimerDisplay(time = timeFormatted) Spacer(modifier = Modifier.height(20.dp)) Row { @@ -112,14 +124,14 @@ fun TimerScreen( onClick = { if (ActivityCompat.checkSelfPermission( activity, - MainActivity.postNotificationsPermission + (activity as MainActivity).postNotificationsPermission ) == PackageManager.PERMISSION_GRANTED ) { startTimer() } else { ActivityCompat.requestPermissions( activity, - arrayOf(MainActivity.postNotificationsPermission), + arrayOf(activity.postNotificationsPermission), MainActivity.PERMISSION_REQUEST_CODE ) } @@ -132,6 +144,7 @@ fun TimerScreen( ) { Text(text = "Start") } + Spacer(modifier = Modifier.width(8.dp)) Button( onClick = { stopTimer() }, @@ -149,7 +162,6 @@ fun TimerScreen( } } - Spacer(modifier = Modifier.height(20.dp)) Row { Button( @@ -188,7 +200,8 @@ fun TimerScreen( Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(8.dp)) - Button(onClick = { showDialog = true }, + Button( + onClick = { showDialog = true }, colors = ButtonDefaults.buttonColors( containerColor = Color.Magenta, contentColor = Color.White @@ -220,3 +233,11 @@ fun TimerScreen( } } } + +@Composable +fun TimerDisplay(time: String) { + Text( + text = time, + style = MaterialTheme.typography.displayLarge + ) +} diff --git a/app/src/main/java/com/pomo/myapplication/ToDoList.kt b/app/src/main/java/com/pomo/myapplication/ToDoList.kt new file mode 100644 index 0000000000000000000000000000000000000000..ce55def12ef74ca89c4a4e21dd9866000d8c13b1 --- /dev/null +++ b/app/src/main/java/com/pomo/myapplication/ToDoList.kt @@ -0,0 +1,252 @@ +package com.pomo.myapplication + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog + +@Composable +fun ToDoScreen(onBackClick: () -> Unit) { + val tasks = remember { mutableStateListOf<Task>() } + var showDialog by remember { mutableStateOf(false) } + + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Top + ) { + Row( + modifier = Modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = "Back" + ) + } + Spacer(modifier = Modifier.weight(1f)) + Text( + text = "To-Do", + style = MaterialTheme.typography.headlineLarge, + modifier = Modifier.align(Alignment.CenterVertically) + ) + Spacer(modifier = Modifier.weight(1f)) + IconButton(onClick = { showDialog = true }) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add Task" + ) + } + } + + Spacer(modifier = Modifier.height(20.dp)) + + // Display the list of tasks + LazyColumn { + items(tasks) { task -> + TaskItem( + task = task, + onComplete = { completedTask -> + tasks[tasks.indexOf(completedTask)] = completedTask.copy(isCompleted = !completedTask.isCompleted) + }, + onRemove = { removedTask -> + tasks.remove(removedTask) + } + ) + } + } + } + + if (showDialog) { + TaskInputDialog( + onDismiss = { showDialog = false }, + onAddTask = { taskText, taskTime -> + tasks.add(Task(taskText, taskTime, isCompleted = false)) + showDialog = false + } + ) + } + } +} + +@Composable +fun TaskItem(task: Task, onComplete: (Task) -> Unit, onRemove: (Task) -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + .border(1.dp, Color.Black) + .background(Color.White) + .padding(8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = task.description, + style = MaterialTheme.typography.bodyLarge, + color = if (task.isCompleted) Color.Gray else Color.Black + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Time: ${task.time} min", + style = MaterialTheme.typography.bodyMedium, + color = Color.Gray + ) + } + IconButton(onClick = { onComplete(task) }) { + Icon( + imageVector = if (task.isCompleted) Icons.Filled.Refresh else Icons.Filled.Check, + contentDescription = if (task.isCompleted) "Undo Completion" else "Complete Task" + ) + } + IconButton(onClick = { onRemove(task) }) { + Icon( + imageVector = Icons.Filled.Delete, + contentDescription = "Remove Task" + ) + } + } +} + + + + +@Composable +fun TaskInputDialog(onDismiss: () -> Unit, onAddTask: (String, String) -> Unit) { + var taskText by remember { mutableStateOf("") } + var taskTime by remember { mutableStateOf("") } + + Dialog(onDismissRequest = onDismiss) { + Surface( + modifier = Modifier.padding(16.dp), + color = MaterialTheme.colorScheme.surface, + shape = MaterialTheme.shapes.medium + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text(text = "Add New Task", style = MaterialTheme.typography.titleMedium) + + // Task Field with Label and Border + Column(modifier = Modifier.fillMaxWidth()) { + Text(text = "Task", style = MaterialTheme.typography.bodyMedium) + Spacer(modifier = Modifier.height(4.dp)) + Box( + modifier = Modifier + .fillMaxWidth() + .border(1.dp, Color.Black) + .padding(4.dp) + ) { + BasicTextField( + value = taskText, + onValueChange = { taskText = it }, + modifier = Modifier.fillMaxWidth(), + textStyle = MaterialTheme.typography.bodyLarge.copy(color = Color.Black) + ) + } + } + + // Time Field with Label, Border and Numeric Input + Column(modifier = Modifier.fillMaxWidth()) { + Text(text = "Time (minutes)", style = MaterialTheme.typography.bodyMedium) + Spacer(modifier = Modifier.height(4.dp)) + Box( + modifier = Modifier + .fillMaxWidth() + .border(1.dp, Color.Black) + .padding(4.dp) + ) { + BasicTextField( + value = taskTime, + onValueChange = { newValue -> + // Only allow numeric input + if (newValue.all { it.isDigit() }) { + taskTime = newValue + } + }, + modifier = Modifier.fillMaxWidth(), + textStyle = MaterialTheme.typography.bodyLarge.copy(color = Color.Black) + ) + } + } + + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "Cancel", + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.clickable(onClick = onDismiss) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = "Add", + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.clickable { + onAddTask(taskText, taskTime) + } + ) + } + } + } + } +} + +data class Task( + val description: String, + val time: String, + val isCompleted: Boolean +) + + +@Preview(showBackground = true) +@Composable +fun PreviewToDoScreen() { + ToDoScreen(onBackClick = {}) +} diff --git a/app/src/main/java/com/pomo/myapplication/ui/theme/ToDoList.kt b/app/src/main/java/com/pomo/myapplication/ui/theme/ToDoList.kt deleted file mode 100644 index 979b4cb0628d6c47ef945c804309f6fbdbe51d50..0000000000000000000000000000000000000000 --- a/app/src/main/java/com/pomo/myapplication/ui/theme/ToDoList.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.pomo.myapplication.ui.theme - -import androidx.compose.foundation.layout.* -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp - - -@Composable -fun ToDoScreen(onBackClick: () -> Unit) { - Surface( - modifier = Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background - ) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Top - ) { - Row( - modifier = Modifier - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - IconButton(onClick = onBackClick) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = "Back" - ) - } - Spacer(modifier = Modifier.weight(1f)) - Text( - text = "To-Do", - style = MaterialTheme.typography.headlineMedium, - modifier = Modifier.align(Alignment.CenterVertically) - ) - Spacer(modifier = Modifier.weight(1f)) - } - - Spacer(modifier = Modifier.height(20.dp)) - - } - } -} - -@Preview(showBackground = true) -@Composable -fun PreviewToDoListScreen() { - ToDoScreen(onBackClick = {}) -}