[趣味開発]タスク管理通知アプリの開発 アプリ編

# [趣味開発]タスク管理通知アプリの開発 アプリ編
## はじめに
https://qiita.com/yydevelop/items/d81b0a9fe0595a7956c4
の続き

## チェックリスト管理画面の追加

MainActivity.kt
```kotlin
package com.example.taskreminder

import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.taskreminder.ui.theme.TaskReminderTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            TaskReminderTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    TaskInputScreen(
                        modifier = Modifier.padding(innerPadding),
                        sharedPreferences = getSharedPreferences("TaskReminderPrefs", Context.MODE_PRIVATE)
                    )
                }
            }
        }
    }
}

@Composable
fun TaskInputScreen(modifier: Modifier = Modifier, sharedPreferences: SharedPreferences) {
    var task by remember { mutableStateOf("") }
    val context = LocalContext.current

    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center
    ) {
        TextField(
            value = task,
            onValueChange = { task = it },
            label = { Text("タスクを入力") },
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(modifier = Modifier.height(8.dp))
        Button(
            onClick = {
                val editor = sharedPreferences.edit()
                editor.putString("task", task)
                editor.apply()

                // ウィジェットを更新
                val appWidgetManager = AppWidgetManager.getInstance(context)
                val widgetComponent = ComponentName(context, TaskReminderWidget::class.java)
                val appWidgetIds = appWidgetManager.getAppWidgetIds(widgetComponent)
                for (appWidgetId in appWidgetIds) {
                    updateAppWidget(context, appWidgetManager, appWidgetId, task)
                }

                task = ""
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("タスクを追加")
        }
    }
}

@Preview(showBackground = true)
@Composable
fun TaskInputScreenPreview() {
    // プレビュー用のダミーのSharedPreferencesを使う
    val context = LocalContext.current
    val sharedPreferences = context.getSharedPreferences("TaskReminderPrefs", Context.MODE_PRIVATE)
    
    TaskReminderTheme {
        TaskInputScreen(sharedPreferences = sharedPreferences)
    }
}
```

### 画面確認
![](https://storage.googleapis.com/zenn-user-upload/6ccf008dc48c-20241006.png)

## 入力したテキストをウィジェットに反映

MainActivity.kt
```kotlin
package com.example.taskreminder

import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.taskreminder.ui.theme.TaskReminderTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            TaskReminderTheme {
                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                    TaskInputScreen(
                        modifier = Modifier.padding(innerPadding),
                        sharedPreferences = getSharedPreferences("TaskReminderPrefs", Context.MODE_PRIVATE)
                    )
                }
            }
        }
    }
}

@Composable
fun TaskInputScreen(modifier: Modifier = Modifier, sharedPreferences: SharedPreferences) {
    var task by remember { mutableStateOf("") }
    val context = LocalContext.current

    Column(
        modifier = modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center
    ) {
        TextField(
            value = task,
            onValueChange = { task = it },
            label = { Text("タスクを入力") },
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(modifier = Modifier.height(8.dp))
        Button(
            onClick = {
                val editor = sharedPreferences.edit()
                editor.putString("task", task)
                editor.apply()

                // ウィジェットを更新
                val appWidgetManager = AppWidgetManager.getInstance(context)
                val widgetComponent = ComponentName(context, TaskReminderWidget::class.java)
                val appWidgetIds = appWidgetManager.getAppWidgetIds(widgetComponent)
                for (appWidgetId in appWidgetIds) {
                    updateAppWidget(context, appWidgetManager, appWidgetId) // 修正箇所: task引数を削除
                }

                task = ""
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("タスクを追加")
        }
    }
}

@Preview(showBackground = true)
@Composable
fun TaskInputScreenPreview() {
    // プレビュー用のダミーのSharedPreferencesを使う
    val context = LocalContext.current
    val sharedPreferences = context.getSharedPreferences("TaskReminderPrefs", Context.MODE_PRIVATE)

    TaskReminderTheme {
        TaskInputScreen(sharedPreferences = sharedPreferences)
    }
}
```

TaskReminderWidget.kt
```kotlin
package com.example.taskreminder

import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.widget.RemoteViews
import android.widget.Toast

class TaskReminderWidget : AppWidgetProvider() {

    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)

        if (intent.action == "TASK_COMPLETED") {
            // タスク完了時に SharedPreferences に完了状態を保存
            val sharedPreferences = context.getSharedPreferences("TaskReminderPrefs", Context.MODE_PRIVATE)
            val editor = sharedPreferences.edit()
            editor.putBoolean("taskCompleted", true) // タスクが完了したことを保存
            editor.apply()

            val appWidgetManager = AppWidgetManager.getInstance(context)
            val thisAppWidget = ComponentName(context.packageName, javaClass.name)
            val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
            for (appWidgetId in appWidgetIds) {
                updateAppWidget(context, appWidgetManager, appWidgetId)
            }

            // タスク完了の通知をユーザーに表示
            Toast.makeText(context, "タスクが完了しました", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
        }
    }

    override fun onEnabled(context: Context) {
        // 初回ウィジェット追加時の処理
    }

    override fun onDisabled(context: Context) {
        // 最後のウィジェットが削除された時の処理
    }
}

internal fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
    val views = RemoteViews(context.packageName, R.layout.task_reminder_widget)

    // SharedPreferencesからタスクと完了状態を取得
    val sharedPreferences = context.getSharedPreferences("TaskReminderPrefs", Context.MODE_PRIVATE)
    val task = sharedPreferences.getString("task", "今日のタスク: 未設定")
    val taskCompleted = sharedPreferences.getBoolean("taskCompleted", false)

    // 完了状態に応じて表示を変更
    if (taskCompleted) {
        views.setTextViewText(R.id.widgetTaskText, "$task - 完了")
    } else {
        views.setTextViewText(R.id.widgetTaskText, task)
    }

    // 完了ボタンのPendingIntentを設定
    val intent = Intent(context, TaskReminderWidget::class.java)
    intent.action = "TASK_COMPLETED"
    val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
    views.setOnClickPendingIntent(R.id.completeTaskButton, pendingIntent)

    appWidgetManager.updateAppWidget(appWidgetId, views)
}

```

### 画面確認
ウィジェットに連携がされるように

![](https://storage.googleapis.com/zenn-user-upload/031558ed98da-20241006.png)