package com.anas4tech.noteable import android.content.Context import android.content.Intent import android.view.View import android.widget.RemoteViews import android.widget.RemoteViewsService import androidx.core.net.toUri class NotesWidgetRemoteViewsService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = NotesWidgetRemoteViewsFactory(applicationContext) } private class NotesWidgetRemoteViewsFactory( private val context: Context ) : RemoteViewsService.RemoteViewsFactory { private var notes: List = emptyList() override fun onCreate() = Unit override fun onDataSetChanged() { val data = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE) val count = data.getInt("notes_count", 0).coerceIn(0, MAX_NOTES) notes = (0 until count).map { index -> val checkCount = data.getInt("note_${index}_check_count", 0) .coerceIn(0, MAX_CHECKLIST_ITEMS) val checks = (0 until checkCount).mapNotNull { itemIndex -> val itemId = data.getInt("note_${index}_check_${itemIndex}_id", -1) if (itemId <= 0) return@mapNotNull null WidgetCheckItem( id = itemId, content = data.getString("note_${index}_check_${itemIndex}_content", "") ?: "", completed = data.getBoolean("widget_check_${itemId}_completed", false) ) } WidgetNote( id = data.getInt("note_${index}_id", -1), title = data.getString("note_${index}_title", "") ?: "", body = data.getString("note_${index}_body", "") ?: "", checklist = checks ) } } override fun onDestroy() { notes = emptyList() } override fun getCount(): Int = notes.size override fun getViewAt(position: Int): RemoteViews? { val note = notes.getOrNull(position) ?: return null val data = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE) val palette = WidgetTheme.palette(context, data) return RemoteViews(context.packageName, R.layout.noteable_widget_item).apply { if (!palette.followsSystem) { setTextColor(R.id.widget_note_title, palette.primaryText) setTextColor(R.id.widget_note_body, palette.secondaryText) CHECKBOX_VIEW_IDS.forEach { setTextColor(it, palette.secondaryText) } } setTextViewText(R.id.widget_note_title, note.title.ifEmpty { "Untitled note" }) setViewVisibility(R.id.widget_note_title, if (note.title.isEmpty()) View.GONE else View.VISIBLE) setTextViewText(R.id.widget_note_body, note.body) setViewVisibility(R.id.widget_note_body, if (note.body.isEmpty()) View.GONE else View.VISIBLE) // Fixed checkbox views are more reliable in RemoteViews ListView rows // than dynamically adding nested RemoteViews at runtime. for (itemIndex in 0 until MAX_CHECKLIST_ITEMS) { val viewId = CHECKBOX_VIEW_IDS[itemIndex] val item = note.checklist.getOrNull(itemIndex) setViewVisibility(viewId, if (item == null) View.GONE else View.VISIBLE) if (item != null) { // RemoteViews does not support CheckBox on all supported API levels. // Render the checkbox state as text and keep the row clickable. val checkboxMark = if (item.completed) "☑ " else "☐ " setTextViewText(viewId, checkboxMark + item.content) setOnClickFillInIntent( viewId, Intent().apply { action = WidgetItemClickReceiver.ACTION_TOGGLE_CHECK putExtra(WidgetItemClickReceiver.EXTRA_NOTE_ID, note.id) putExtra(WidgetItemClickReceiver.EXTRA_ITEM_ID, item.id) } ) } } setOnClickFillInIntent( R.id.widget_note_row, Intent().apply { action = WidgetItemClickReceiver.ACTION_OPEN_NOTE putExtra(WidgetItemClickReceiver.EXTRA_NOTE_ID, note.id) this.data = "noteable://open-note/${note.id}?source=list_view".toUri() } ) } } override fun getLoadingView(): RemoteViews? = null override fun getViewTypeCount(): Int = 1 override fun getItemId(position: Int): Long = notes.getOrNull(position)?.id?.toLong() ?: position.toLong() override fun hasStableIds(): Boolean = true private data class WidgetNote( val id: Int, val title: String, val body: String, val checklist: List ) private data class WidgetCheckItem(val id: Int, val content: String, val completed: Boolean) private companion object { const val PREFERENCES = "HomeWidgetPreferences" const val MAX_NOTES = 10 const val MAX_CHECKLIST_ITEMS = 4 val CHECKBOX_VIEW_IDS = intArrayOf( R.id.widget_check_item_0, R.id.widget_check_item_1, R.id.widget_check_item_2, R.id.widget_check_item_3 ) } }