From ce7469698bf5956b678d14efec327002f896fc82 Mon Sep 17 00:00:00 2001 From: loloakira <6253351+loloakira@users.noreply.github.com> Date: Thu, 4 Jun 2026 23:30:20 +0200 Subject: [PATCH] feat(widget): add interactive notes widget Add a third home-screen widget, "Interactive notes", that shows notes as a scrollable single column of rounded cards. Each card shows the note title and a multi-line content excerpt (the whole note when it is short). Tapping a card opens that specific note; a floating button in the bottom-right creates a new note in the widget's configured category/account. The favourite star is a non-clickable indicator. The per-widget configuration adds sort options: a "favourites on top" toggle and a newest-first / oldest-first order. The widget is reconfigurable via long-press on Android 12+ and provides a live previewLayout for the widget picker. Reuses the existing NotesListWidgetData storage (no database migration) and the existing excerpt generation; sort options are kept in per-widget SharedPreferences. The two existing widgets and the shared data layer are left unchanged. AI-assistant: Claude Code 2.1.162 (Claude Opus 4.8) Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: loloakira <6253351+loloakira@users.noreply.github.com> --- app/src/main/AndroidManifest.xml | 27 +++ .../notes/persistence/NotesRepository.java | 2 + .../InteractiveNoteListWidget.kt | 171 ++++++++++++++++ ...veNoteListWidgetConfigurationActivity.java | 175 +++++++++++++++++ .../InteractiveNoteListWidgetFactory.kt | 182 ++++++++++++++++++ .../InteractiveNoteListWidgetService.java | 17 ++ .../InteractiveWidgetPreferences.kt | 46 +++++ .../widget/interactivelist/WidgetSortOrder.kt | 12 ++ .../interactive_note_list_widget_preview.webp | Bin 0 -> 27184 bytes .../res/drawable/widget_card_background.xml | 12 ++ .../res/drawable/widget_fab_background.xml | 11 ++ .../widget_interactive_background.xml | 12 ++ ...ivity_interactive_widget_configuration.xml | 61 ++++++ .../res/layout/widget_interactive_entry.xml | 73 +++++++ .../layout/widget_interactive_note_list.xml | 49 +++++ .../res/layout/widget_interactive_preview.xml | 128 ++++++++++++ app/src/main/res/values-night/colors.xml | 1 + app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/dimens.xml | 3 + app/src/main/res/values/strings.xml | 6 + ...ractive_note_list_widget_provider_info.xml | 20 ++ 21 files changed, 1009 insertions(+) create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidget.kt create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetConfigurationActivity.java create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetFactory.kt create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetService.java create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveWidgetPreferences.kt create mode 100644 app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/WidgetSortOrder.kt create mode 100644 app/src/main/res/drawable/interactive_note_list_widget_preview.webp create mode 100644 app/src/main/res/drawable/widget_card_background.xml create mode 100644 app/src/main/res/drawable/widget_fab_background.xml create mode 100644 app/src/main/res/drawable/widget_interactive_background.xml create mode 100644 app/src/main/res/layout/activity_interactive_widget_configuration.xml create mode 100644 app/src/main/res/layout/widget_interactive_entry.xml create mode 100644 app/src/main/res/layout/widget_interactive_note_list.xml create mode 100644 app/src/main/res/layout/widget_interactive_preview.xml create mode 100644 app/src/main/res/xml/interactive_note_list_widget_provider_info.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 111a0dc01..3b1cd56f6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -159,6 +159,14 @@ + + + + + + + + + + + + + + + + + + { updateSingleNoteWidgets(context); updateNoteListWidgets(context); + updateInteractiveNoteListWidgets(context); }); } diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidget.kt b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidget.kt new file mode 100644 index 000000000..6ef26c5da --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidget.kt @@ -0,0 +1,171 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.widget.interactivelist + +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.content.res.ColorStateList +import android.os.Build +import android.os.Bundle +import android.util.Log +import android.widget.RemoteViews +import androidx.core.net.toUri +import it.niedermann.owncloud.notes.R +import it.niedermann.owncloud.notes.branding.BrandingUtil +import it.niedermann.owncloud.notes.edit.EditNoteActivity +import it.niedermann.owncloud.notes.persistence.NotesRepository +import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData +import it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType +import it.niedermann.owncloud.notes.shared.model.NavigationCategory +import it.niedermann.owncloud.notes.shared.util.WidgetUtil +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class InteractiveNoteListWidget : AppWidgetProvider() { + private val executor: ExecutorService = Executors.newCachedThreadPool() + + override fun onUpdate( + context: Context, + appWidgetManager: AppWidgetManager, + appWidgetIds: IntArray + ) { + super.onUpdate(context, appWidgetManager, appWidgetIds) + updateAppWidget(context, appWidgetManager, appWidgetIds) + } + + override fun onReceive(context: Context, intent: Intent) { + super.onReceive(context, intent) + val awm = AppWidgetManager.getInstance(context) + + if (intent.action == null) { + Log.w(TAG, "Intent action is null") + return + } + + if (intent.action != AppWidgetManager.ACTION_APPWIDGET_UPDATE) { + Log.w(TAG, "Intent action is not ACTION_APPWIDGET_UPDATE") + return + } + + if (!intent.hasExtra(AppWidgetManager.EXTRA_APPWIDGET_ID)) { + updateAppWidget( + context, + awm, + awm.getAppWidgetIds(ComponentName(context, InteractiveNoteListWidget::class.java)) + ) + } + + val appWidgetIds = intArrayOf(intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) ?: -1) + + updateAppWidget( + context, + awm, + appWidgetIds + ) + } + + override fun onDeleted(context: Context, appWidgetIds: IntArray) { + super.onDeleted(context, appWidgetIds) + val repo = NotesRepository.getInstance(context) + + for (appWidgetId in appWidgetIds) { + executor.execute { + repo.removeNoteListWidget(appWidgetId) + InteractiveWidgetPreferences.remove(context, appWidgetId) + } + } + } + + companion object { + private val TAG: String = InteractiveNoteListWidget::class.java.simpleName + + fun updateAppWidget(context: Context, awm: AppWidgetManager, appWidgetIds: IntArray) { + val repo = NotesRepository.getInstance(context) + appWidgetIds.forEach { appWidgetId -> + repo.getNoteListWidgetData(appWidgetId)?.let { data -> + val serviceIntent = Intent(context, InteractiveNoteListWidgetService::class.java).apply { + putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) + setData(toUri(Intent.URI_INTENT_SCHEME).toUri()) + } + + val openTemplateIntent = Intent(context, EditNoteActivity::class.java).apply { + setPackage(context.packageName) + } + val openTemplateFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + val openTemplatePendingIntent = + PendingIntent.getActivity(context, 0, openTemplateIntent, openTemplateFlags) + + val createNotePendingIntent = + PendingIntent.getActivity( + context, + appWidgetId, + createNoteIntent(context, data), + WidgetUtil.pendingIntentFlagCompat(PendingIntent.FLAG_UPDATE_CURRENT) + ) + + val views = RemoteViews(context.packageName, R.layout.widget_interactive_note_list).apply { + setRemoteAdapter(R.id.interactive_note_list_lv, serviceIntent) + setPendingIntentTemplate(R.id.interactive_note_list_lv, openTemplatePendingIntent) + setEmptyView( + R.id.interactive_note_list_lv, + R.id.interactive_note_list_placeholder_tv + ) + setOnClickPendingIntent(R.id.interactive_create_note_button, createNotePendingIntent) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + setColorStateList( + R.id.interactive_create_note_button, + "setBackgroundTintList", + ColorStateList.valueOf(BrandingUtil.readBrandMainColor(context)) + ) + } + } + + awm.run { + updateAppWidget(appWidgetId, views) + notifyAppWidgetViewDataChanged(intArrayOf(appWidgetId), R.id.interactive_note_list_lv) + } + } + } + } + + private fun createNoteIntent(context: Context, data: NotesListWidgetData): Intent { + val navigationCategory = if (data.mode == NotesListWidgetData.MODE_DISPLAY_STARRED) { + NavigationCategory(ENavigationCategoryType.FAVORITES) + } else { + NavigationCategory(data.accountId, data.category) + } + + val bundle = Bundle().apply { + putSerializable(EditNoteActivity.PARAM_CATEGORY, navigationCategory) + putLong(EditNoteActivity.PARAM_ACCOUNT_ID, data.accountId) + } + + return Intent(context, EditNoteActivity::class.java).apply { + setPackage(context.packageName) + putExtras(bundle) + setData("interactive-create://${data.id}".toUri()) + } + } + + @JvmStatic + fun updateInteractiveNoteListWidgets(context: Context) { + val intent = Intent(context, InteractiveNoteListWidget::class.java).apply { + action = AppWidgetManager.ACTION_APPWIDGET_UPDATE + } + context.sendBroadcast(intent) + } + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetConfigurationActivity.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetConfigurationActivity.java new file mode 100644 index 000000000..a3f218564 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetConfigurationActivity.java @@ -0,0 +1,175 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.widget.interactivelist; + +import android.appwidget.AppWidgetManager; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProvider; + +import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; +import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException; +import com.nextcloud.android.sso.helper.SingleAccountHelper; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import it.niedermann.owncloud.notes.LockedActivity; +import it.niedermann.owncloud.notes.NotesApplication; +import it.niedermann.owncloud.notes.R; +import it.niedermann.owncloud.notes.branding.BrandingUtil; +import it.niedermann.owncloud.notes.databinding.ActivityInteractiveWidgetConfigurationBinding; +import it.niedermann.owncloud.notes.main.navigation.NavigationAdapter; +import it.niedermann.owncloud.notes.main.navigation.NavigationClickListener; +import it.niedermann.owncloud.notes.main.navigation.NavigationItem; +import it.niedermann.owncloud.notes.persistence.NotesRepository; +import it.niedermann.owncloud.notes.persistence.entity.Account; +import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData; +import it.niedermann.owncloud.notes.widget.notelist.NoteListViewModel; + +import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_ALL; +import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_CATEGORY; +import static it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData.MODE_DISPLAY_STARRED; +import static it.niedermann.owncloud.notes.shared.model.ENavigationCategoryType.RECENT; + +public class InteractiveNoteListWidgetConfigurationActivity extends LockedActivity { + private static final String TAG = InteractiveNoteListWidgetConfigurationActivity.class.getSimpleName(); + + private final ExecutorService executor = Executors.newCachedThreadPool(); + + private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + + private Account localAccount = null; + + private ActivityInteractiveWidgetConfigurationBinding binding; + private NoteListViewModel viewModel; + private NavigationAdapter adapterCategories; + private NotesRepository repo = null; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setResult(RESULT_CANCELED); + + repo = NotesRepository.getInstance(this); + final var args = getIntent().getExtras(); + + if (args != null) { + appWidgetId = args.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID); + } + + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + Log.d(TAG, "INVALID_APPWIDGET_ID"); + finish(); + } + + viewModel = new ViewModelProvider(this).get(NoteListViewModel.class); + binding = ActivityInteractiveWidgetConfigurationBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + binding.favoritesFirstCheckbox.setChecked(InteractiveWidgetPreferences.isFavoritesFirst(this, appWidgetId)); + if (InteractiveWidgetPreferences.getSortOrder(this, appWidgetId) == WidgetSortOrder.OLDEST_FIRST) { + binding.sortOldestFirst.setChecked(true); + } else { + binding.sortNewestFirst.setChecked(true); + } + + adapterCategories = new NavigationAdapter(this, new NavigationClickListener() { + @Override + public void onItemClick(NavigationItem item) { + final boolean favoritesFirst = binding.favoritesFirstCheckbox.isChecked(); + final WidgetSortOrder sortOrder = binding.sortOldestFirst.isChecked() + ? WidgetSortOrder.OLDEST_FIRST + : WidgetSortOrder.NEWEST_FIRST; + + final var data = new NotesListWidgetData(); + + data.setId(appWidgetId); + if (item.type != null) { + switch (item.type) { + case RECENT: { + data.setMode(MODE_DISPLAY_ALL); + break; + } + case FAVORITES: { + data.setMode(MODE_DISPLAY_STARRED); + break; + } + case UNCATEGORIZED: { + data.setMode(MODE_DISPLAY_CATEGORY); + data.setCategory(null); + break; + } + case DEFAULT_CATEGORY: + default: { + if (item.getClass() == NavigationItem.CategoryNavigationItem.class) { + data.setMode(MODE_DISPLAY_CATEGORY); + data.setCategory(((NavigationItem.CategoryNavigationItem) item).category); + } else { + data.setMode(MODE_DISPLAY_ALL); + Log.e(TAG, "Unknown item navigation type. Fallback to show " + RECENT); + } + } + } + } else { + data.setMode(MODE_DISPLAY_ALL); + Log.e(TAG, "Unknown item navigation type. Fallback to show " + RECENT); + } + + data.setAccountId(localAccount.getId()); + data.setThemeMode(NotesApplication.getAppTheme(getApplicationContext()).getModeId()); + + executor.submit(() -> { + repo.createOrUpdateNoteListWidgetData(data); + InteractiveWidgetPreferences.save(getApplicationContext(), appWidgetId, favoritesFirst, sortOrder); + + final var updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, getApplicationContext(), InteractiveNoteListWidget.class) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + setResult(RESULT_OK, updateIntent); + getApplicationContext().sendBroadcast(updateIntent); + finish(); + }); + } + + @Override + public void onIconClick(NavigationItem item) { + onItemClick(item); + } + }); + + binding.recyclerView.setAdapter(adapterCategories); + + executor.submit(() -> { + try { + this.localAccount = repo.getAccountByName(SingleAccountHelper.getCurrentSingleSignOnAccount(this).name); + } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) { + Log.w(TAG, "Account not found", e); + Toast.makeText(this, R.string.widget_not_logged_in, Toast.LENGTH_LONG).show(); + Log.w(TAG, "onCreate: user not logged in"); + finish(); + } + runOnUiThread(() -> viewModel.getAdapterCategories(localAccount.getId()).observe(this, (navigationItems) -> adapterCategories.setItems(navigationItems))); + }); + } + + @Override + public void applyBrand(int color) { + if (binding == null) { + return; + } + + final var util = BrandingUtil.of(color, this); + util.platform.themeCheckbox(binding.favoritesFirstCheckbox); + util.platform.themeRadioButton(binding.sortNewestFirst); + util.platform.themeRadioButton(binding.sortOldestFirst); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetFactory.kt b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetFactory.kt new file mode 100644 index 000000000..d2eaa7f8e --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetFactory.kt @@ -0,0 +1,182 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.widget.interactivelist + +import android.appwidget.AppWidgetManager +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.util.Log +import android.view.View +import android.widget.RemoteViews +import android.widget.RemoteViewsService.RemoteViewsFactory +import androidx.core.content.ContextCompat +import androidx.core.net.toUri +import com.nextcloud.android.common.ui.util.PlatformThemeUtil +import it.niedermann.owncloud.notes.R +import it.niedermann.owncloud.notes.edit.EditNoteActivity +import it.niedermann.owncloud.notes.persistence.NotesRepository +import it.niedermann.owncloud.notes.persistence.entity.Note +import it.niedermann.owncloud.notes.persistence.entity.NotesListWidgetData +import it.niedermann.owncloud.notes.shared.util.NoteUtil + +class InteractiveNoteListWidgetFactory internal constructor(private val context: Context, intent: Intent) : + RemoteViewsFactory { + private val appWidgetId: Int = intent.getIntExtra( + AppWidgetManager.EXTRA_APPWIDGET_ID, + AppWidgetManager.INVALID_APPWIDGET_ID + ) + private val repo: NotesRepository = NotesRepository.getInstance(context) + private val dbNotes: MutableList = ArrayList() + private var data: NotesListWidgetData? = null + + override fun onCreate() = Unit + + override fun onDataSetChanged() { + dbNotes.clear() + try { + data = repo.getNoteListWidgetData(appWidgetId) + if (data == null) { + Log.w(TAG, "Widget data is null") + return + } + val widgetData = data ?: return + + when (widgetData.mode) { + NotesListWidgetData.MODE_DISPLAY_ALL -> dbNotes.addAll( + repo.searchRecentByModified( + widgetData.accountId, "%" + ) + ) + + NotesListWidgetData.MODE_DISPLAY_STARRED -> dbNotes.addAll( + repo.searchFavoritesByModified( + widgetData.accountId, "%" + ) + ) + + else -> { + if (widgetData.category != null) { + dbNotes.addAll( + repo.searchCategoryByModified( + widgetData.accountId, + "%", + widgetData.category + ) + ) + } else { + dbNotes.addAll( + repo.searchUncategorizedByModified( + widgetData.accountId, + "%" + ) + ) + } + } + } + + applySorting() + } catch (e: Exception) { + Log.w(TAG, "Error caught at onDataSetChanged: $e") + } + } + + private fun applySorting() { + val byDate: Comparator = if (InteractiveWidgetPreferences.getSortOrder(context, appWidgetId) == WidgetSortOrder.OLDEST_FIRST) { + compareBy { it.modified?.timeInMillis ?: 0L } + } else { + compareByDescending { it.modified?.timeInMillis ?: 0L } + } + val comparator = if (InteractiveWidgetPreferences.isFavoritesFirst(context, appWidgetId)) { + compareByDescending { it.favorite }.then(byDate) + } else { + byDate + } + dbNotes.sortWith(comparator) + } + + override fun onDestroy() = Unit + + override fun getCount(): Int { + return dbNotes.size + } + + private fun getOpenNoteIntent(note: Note): Intent { + val bundle = Bundle().apply { + putLong(EditNoteActivity.PARAM_NOTE_ID, note.id) + putLong(EditNoteActivity.PARAM_ACCOUNT_ID, note.accountId) + } + + return Intent(context, EditNoteActivity::class.java).apply { + setPackage(context.packageName) + putExtras(bundle) + data = toUri(Intent.URI_INTENT_SCHEME).toUri() + } + } + + override fun getViewAt(position: Int): RemoteViews? { + val note = dbNotes.getOrNull(position) ?: return null + + return RemoteViews(context.packageName, R.layout.widget_interactive_entry).apply { + setOnClickFillInIntent(R.id.interactive_entry, getOpenNoteIntent(note)) + + setTextViewText(R.id.interactive_entry_title, note.title) + + if (note.excerpt.isEmpty()) { + setViewVisibility(R.id.interactive_entry_excerpt, View.GONE) + } else { + setViewVisibility(R.id.interactive_entry_excerpt, View.VISIBLE) + setTextViewText( + R.id.interactive_entry_excerpt, + note.excerpt.replace(NoteUtil.EXCERPT_LINE_SEPARATOR, "\n") + ) + } + + if (note.category.isEmpty()) { + setViewVisibility(R.id.interactive_entry_category, View.GONE) + } else { + setViewVisibility(R.id.interactive_entry_category, View.VISIBLE) + setTextViewText(R.id.interactive_entry_category, note.category) + + val textColorId = if (PlatformThemeUtil.isDarkMode(context)) { + R.color.text_color + } else { + R.color.category_border + } + val textColor = ContextCompat.getColor(context, textColorId) + setTextColor(R.id.interactive_entry_category, textColor) + } + + if (note.favorite) { + setViewVisibility(R.id.interactive_entry_fav_icon, View.VISIBLE) + setImageViewResource(R.id.interactive_entry_fav_icon, R.drawable.ic_star_yellow_24dp) + } else { + setViewVisibility(R.id.interactive_entry_fav_icon, View.GONE) + } + } + } + + override fun getLoadingView(): RemoteViews? { + return null + } + + override fun getViewTypeCount(): Int { + return 1 + } + + override fun getItemId(position: Int): Long { + return dbNotes[position].id + } + + override fun hasStableIds(): Boolean { + return true + } + + companion object { + private val TAG: String = InteractiveNoteListWidgetFactory::class.java.simpleName + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetService.java b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetService.java new file mode 100644 index 000000000..8bb8ab353 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveNoteListWidgetService.java @@ -0,0 +1,17 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.widget.interactivelist; + +import android.content.Intent; +import android.widget.RemoteViewsService; + +public class InteractiveNoteListWidgetService extends RemoteViewsService { + @Override + public RemoteViewsFactory onGetViewFactory(Intent intent) { + return new InteractiveNoteListWidgetFactory(this.getApplicationContext(), intent); + } +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveWidgetPreferences.kt b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveWidgetPreferences.kt new file mode 100644 index 000000000..46518622f --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/InteractiveWidgetPreferences.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.widget.interactivelist + +import android.content.Context +import androidx.core.content.edit + +object InteractiveWidgetPreferences { + private const val PREFS_NAME = "interactive_note_list_widget" + private const val KEY_FAVORITES_FIRST = "favorites_first_" + private const val KEY_SORT_ORDER = "sort_order_" + + @JvmStatic + fun save(context: Context, appWidgetId: Int, favoritesFirst: Boolean, sortOrder: WidgetSortOrder) { + prefs(context).edit { + putBoolean(KEY_FAVORITES_FIRST + appWidgetId, favoritesFirst) + putString(KEY_SORT_ORDER + appWidgetId, sortOrder.name) + } + } + + @JvmStatic + fun isFavoritesFirst(context: Context, appWidgetId: Int): Boolean = + prefs(context).getBoolean(KEY_FAVORITES_FIRST + appWidgetId, false) + + @JvmStatic + fun getSortOrder(context: Context, appWidgetId: Int): WidgetSortOrder { + val stored = prefs(context).getString(KEY_SORT_ORDER + appWidgetId, null) + ?: return WidgetSortOrder.NEWEST_FIRST + return runCatching { WidgetSortOrder.valueOf(stored) }.getOrDefault(WidgetSortOrder.NEWEST_FIRST) + } + + @JvmStatic + fun remove(context: Context, appWidgetId: Int) { + prefs(context).edit { + remove(KEY_FAVORITES_FIRST + appWidgetId) + remove(KEY_SORT_ORDER + appWidgetId) + } + } + + private fun prefs(context: Context) = + context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) +} diff --git a/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/WidgetSortOrder.kt b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/WidgetSortOrder.kt new file mode 100644 index 000000000..645b449e7 --- /dev/null +++ b/app/src/main/java/it/niedermann/owncloud/notes/widget/interactivelist/WidgetSortOrder.kt @@ -0,0 +1,12 @@ +/* + * Nextcloud Notes - Android Client + * + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-3.0-or-later + */ +package it.niedermann.owncloud.notes.widget.interactivelist + +enum class WidgetSortOrder { + NEWEST_FIRST, + OLDEST_FIRST +} diff --git a/app/src/main/res/drawable/interactive_note_list_widget_preview.webp b/app/src/main/res/drawable/interactive_note_list_widget_preview.webp new file mode 100644 index 0000000000000000000000000000000000000000..367115b15bdc7b637ec27a3defac6380e190a74e GIT binary patch literal 27184 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 z z04GqfX+cor!Ymq!VQ%X&WgC1rTuttIrF-o$@<(w9gB&n78Eb5+_PJPIE(Mz$>HJ$g zxSj#}%1AB-3twTogz%aahoxrAU%!DlH*4+U+JRKl&vMkE+3L@Zgm_s(j$ zAE{b}GBo-BGn)>3Q>~|c6+CKFoJ7>LwXg^ScYG`;Mk4fpC8lM09NF9xcX%1uDN5W& z_VVvZZ)~2AXnlwa#%mpY;4^iOcuJd;kH{_(-!k1&*}-&h5Onq@X9BfYjY__}sAM*+ z(Mc}cwIXBAZn!h}J07IBRGiKzi24cV=6XK@f6F$c%I$Wd3U>=^LuCawd6@^DSNqN? zOA{B)@O*2E2gGH&i&}nv(m3Ut6_TkJZB^uRE6!6GQQF z!2g1@8EDKu*Ge%IJ<^X`P(hDS{^oE3`+K zI&Y;!<&t?$)zGzR>fo@dC9bJ_)l;oNRfOt_7yuBy4G#QNB)!07cjt#-C$Z~kLyY8+gukPOkpW~;qG6-T;Pk8r#K&BNQEXgz|<>Gm6 z*(G21m{+v+`ZB33497cpjc_QC;sNJGHhXNwXN2jStD5I6x~1}UZ)o7KOSAA{`GteU z0l<#Es+bbk_=@r8qP;|blH^9_ + + + + + diff --git a/app/src/main/res/drawable/widget_fab_background.xml b/app/src/main/res/drawable/widget_fab_background.xml new file mode 100644 index 000000000..321ed54d6 --- /dev/null +++ b/app/src/main/res/drawable/widget_fab_background.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/drawable/widget_interactive_background.xml b/app/src/main/res/drawable/widget_interactive_background.xml new file mode 100644 index 000000000..aa3c3d2d4 --- /dev/null +++ b/app/src/main/res/drawable/widget_interactive_background.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_interactive_widget_configuration.xml b/app/src/main/res/layout/activity_interactive_widget_configuration.xml new file mode 100644 index 000000000..3ff0153ba --- /dev/null +++ b/app/src/main/res/layout/activity_interactive_widget_configuration.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/widget_interactive_entry.xml b/app/src/main/res/layout/widget_interactive_entry.xml new file mode 100644 index 000000000..3a5cb7875 --- /dev/null +++ b/app/src/main/res/layout/widget_interactive_entry.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/widget_interactive_note_list.xml b/app/src/main/res/layout/widget_interactive_note_list.xml new file mode 100644 index 000000000..c72bfdb58 --- /dev/null +++ b/app/src/main/res/layout/widget_interactive_note_list.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/widget_interactive_preview.xml b/app/src/main/res/layout/widget_interactive_preview.xml new file mode 100644 index 000000000..afd8ed8d6 --- /dev/null +++ b/app/src/main/res/layout/widget_interactive_preview.xml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 0857691c8..e681da78d 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -35,6 +35,7 @@ #dd000000 #d8d8d8 + #2b2b2b #222222 #ffffff diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f3a39be8a..a72fd5899 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -47,6 +47,7 @@ #ddffffff #222222 + #ffffff #ededed #666666 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index b42e9c41b..379270717 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -76,6 +76,9 @@ 4dp 26dp 20dp + 48dp + @dimen/spacer_2x + 11dp 48dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1d0c39640..9694440db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -244,6 +244,12 @@ Note not found Please login to Notes before using this widget Star icon is used to denote an item as a favorite + Interactive notes + Create note + Favourites on top + Sort order + Newest first + Oldest first Select note diff --git a/app/src/main/res/xml/interactive_note_list_widget_provider_info.xml b/app/src/main/res/xml/interactive_note_list_widget_provider_info.xml new file mode 100644 index 000000000..cd3f58f23 --- /dev/null +++ b/app/src/main/res/xml/interactive_note_list_widget_provider_info.xml @@ -0,0 +1,20 @@ + + +