diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f8c11f441..3538868ce 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -221,6 +221,10 @@ + + + diff --git a/app/src/main/java/com/orgzly/android/data/DataRepository.kt b/app/src/main/java/com/orgzly/android/data/DataRepository.kt index d27570804..f427efb05 100644 --- a/app/src/main/java/com/orgzly/android/data/DataRepository.kt +++ b/app/src/main/java/com/orgzly/android/data/DataRepository.kt @@ -613,7 +613,7 @@ class DataRepository @Inject constructor( } else { db.runInTransaction(Callable { - moveSubtrees(noteIds, Place.UNDER, target.noteId) + moveSubtrees(noteIds, target.place, target.noteId) }) } } @@ -1280,6 +1280,22 @@ class DataRepository @Inject constructor( return db.note().getNoteAndAncestors(noteId) } + fun getNoteAtPath(fullPath: String): NoteView? { + val (bookName, path) = run { + val pathParts = fullPath.split("/") + if (pathParts.isEmpty()) return null + pathParts[0] to pathParts.drop(1).joinToString("/") + } + return if (path.split("/").any { it.isNotEmpty() }) + getNotes(bookName) + .filter { ("/$path").endsWith("/" + it.note.title) } + .firstOrNull { view -> + getNoteAndAncestors(view.note.id) + .joinToString("/") { it.title } == path + } + else null + } + fun getNotesAndSubtrees(ids: Set): List { return db.note().getNotesForSubtrees(ids) } diff --git a/app/src/main/java/com/orgzly/android/di/AppComponent.kt b/app/src/main/java/com/orgzly/android/di/AppComponent.kt index f5b3efb86..d89a48fa4 100644 --- a/app/src/main/java/com/orgzly/android/di/AppComponent.kt +++ b/app/src/main/java/com/orgzly/android/di/AppComponent.kt @@ -7,6 +7,7 @@ import com.orgzly.android.TimeChangeBroadcastReceiver import com.orgzly.android.di.module.ApplicationModule import com.orgzly.android.di.module.DataModule import com.orgzly.android.di.module.DatabaseModule +import com.orgzly.android.external.actionhandlers.ExternalAccessActionHandler import com.orgzly.android.reminders.NoteReminders import com.orgzly.android.reminders.RemindersBroadcastReceiver import com.orgzly.android.sync.SyncWorker @@ -86,4 +87,5 @@ interface AppComponent { fun inject(arg: RemindersBroadcastReceiver) fun inject(arg: NotificationBroadcastReceiver) fun inject(arg: SharingShortcutsManager) + fun inject(arg: ExternalAccessActionHandler) } \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/external/ExternalAccessReceiver.kt b/app/src/main/java/com/orgzly/android/external/ExternalAccessReceiver.kt new file mode 100644 index 000000000..97b8ec116 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/ExternalAccessReceiver.kt @@ -0,0 +1,27 @@ +package com.orgzly.android.external + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.google.gson.GsonBuilder +import com.orgzly.android.external.actionhandlers.* +import com.orgzly.android.external.types.Response + +class ExternalAccessReceiver : BroadcastReceiver() { + val actionHandlers = listOf( + GetOrgInfo(), + RunSearch(), + EditNotes(), + EditSavedSearches(), + ManageWidgets() + ) + + override fun onReceive(context: Context?, intent: Intent?) { + val response = actionHandlers.asSequence() + .mapNotNull { it.handle(intent!!, context!!) } + .firstOrNull() + ?: Response(false, "Invalid action") + val gson = GsonBuilder().serializeNulls().create() + resultData = gson.toJson(response) + } +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/EditNotes.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/EditNotes.kt new file mode 100644 index 000000000..94d1cd749 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/EditNotes.kt @@ -0,0 +1,53 @@ +package com.orgzly.android.external.actionhandlers + +import android.content.Intent +import com.orgzly.android.external.types.ExternalHandlerFailure + +class EditNotes : ExternalAccessActionHandler() { + override val actions = listOf( + action(::addNote, "ADD_NOTE"), + action(::editNote, "EDIT_NOTE"), + action(::refileNote, "REFILE_NOTE", "REFILE_NOTES"), + action(::moveNote, "MOVE_NOTE", "MOVE_NOTES"), + action(::deleteNote, "DELETE_NOTE", "DELETE_NOTES") + ) + + private fun addNote(intent: Intent): String { + val place = intent.getNotePlace() + val newNote = intent.getNotePayload() + val note = dataRepository.createNote(newNote, place) + return "${note.id}" + } + + private fun editNote(intent: Intent) { + val noteView = intent.getNote() + val newNote = intent.getNotePayload(title=noteView.note.title) + dataRepository.updateNote(noteView.note.id, newNote) + } + + private fun refileNote(intent: Intent) { + val notes = intent.getNoteIds() + val place = intent.getNotePlace() + dataRepository.refileNotes(notes, place) + } + + private fun moveNote(intent: Intent) { + val notes = intent.getNoteIds() + with(dataRepository) { when (intent.getStringExtra("DIRECTION")) { + "UP" -> moveNote(intent.getBook().id, notes, -1) + "DOWN" -> moveNote(intent.getBook().id, notes, 1) + "LEFT" -> promoteNotes(notes) + "RIGHT" -> demoteNotes(notes) + else -> throw ExternalHandlerFailure("invalid direction") + } } + } + + private fun deleteNote(intent: Intent) { + intent.getNoteIds().groupBy { + dataRepository.getNoteView(it)?.bookName + ?: throw ExternalHandlerFailure("invalid note id $it") + }.forEach { (bookName, notes) -> + dataRepository.deleteNotes(dataRepository.getBook(bookName)!!.id, notes.toSet()) + } + } +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/EditSavedSearches.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/EditSavedSearches.kt new file mode 100644 index 000000000..4f8ced4ef --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/EditSavedSearches.kt @@ -0,0 +1,45 @@ +package com.orgzly.android.external.actionhandlers + +import android.content.Intent +import com.orgzly.android.db.entity.SavedSearch +import com.orgzly.android.external.types.ExternalHandlerFailure + +class EditSavedSearches : ExternalAccessActionHandler() { + override val actions = listOf( + action(::addSavedSearch, "ADD_SAVED_SEARCH"), + action(::editSavedSearch, "EDIT_SAVED_SEARCH"), + action(::moveSavedSearch, "MOVE_SAVED_SEARCH"), + action(::deleteSavedSearch, "DELETE_SAVED_SEARCH"), + ) + + private fun addSavedSearch(intent: Intent): String { + val savedSearch = intent.getNewSavedSearch() + val id = dataRepository.createSavedSearch(savedSearch) + return "$id" + } + + private fun editSavedSearch(intent: Intent) { + val savedSearch = intent.getSavedSearch() + val newSavedSearch = intent.getNewSavedSearch(allowBlank = true) + dataRepository.updateSavedSearch(SavedSearch( + savedSearch.id, + newSavedSearch.name.ifBlank { savedSearch.name }, + newSavedSearch.query.ifBlank { savedSearch.query }, + savedSearch.position + )) + } + + private fun moveSavedSearch(intent: Intent) { + val savedSearch = intent.getSavedSearch() + when (intent.getStringExtra("DIRECTION")) { + "UP" -> dataRepository.moveSavedSearchUp(savedSearch.id) + "DOWN" -> dataRepository.moveSavedSearchDown(savedSearch.id) + else -> throw ExternalHandlerFailure("invalid direction") + } + } + + private fun deleteSavedSearch(intent: Intent) { + val savedSearch = intent.getSavedSearch() + dataRepository.deleteSavedSearches(setOf(savedSearch.id)) + } +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalAccessActionHandler.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalAccessActionHandler.kt new file mode 100644 index 000000000..a7cce4f05 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalAccessActionHandler.kt @@ -0,0 +1,45 @@ +package com.orgzly.android.external.actionhandlers + +import android.content.Context +import android.content.Intent +import com.orgzly.android.App +import com.orgzly.android.data.DataRepository +import com.orgzly.android.external.types.Response +import javax.inject.Inject + +abstract class ExternalAccessActionHandler : ExternalIntentParser { + @Inject + override lateinit var dataRepository: DataRepository + + init { + @Suppress("LeakingThis") + App.appComponent.inject(this) + } + + abstract val actions: List Any>>> + private val fullNameActions by lazy { + actions.flatten().toMap().mapKeys { (key, _) -> "com.orgzly.android.$key" } + } + + fun action(f: (Intent, Context) -> Any, vararg names: String) = names.map { it to f } + + @JvmName("intentAction") + fun action(f: (Intent) -> Any, vararg names: String) = + action({ i, _ -> f(i) }, *names) + + @JvmName("contextAction") + fun action(f: (Context) -> Any, vararg names: String) = + action({ _, c -> f(c) }, *names) + + fun action(f: () -> Any, vararg names: String) = + action({ _, _ -> f() }, *names) + + + fun handle(intent: Intent, context: Context) = try { + fullNameActions[intent.action!!] + ?.let { it(intent, context) } + ?.let { Response(true, if (it is Unit) null else it) } + } catch (e: Exception) { + Response(false, e.message) + } +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalIntentParser.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalIntentParser.kt new file mode 100644 index 000000000..f8478c30b --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalIntentParser.kt @@ -0,0 +1,135 @@ +package com.orgzly.android.external.actionhandlers + +import android.content.Intent +import com.google.gson.JsonElement +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.orgzly.android.data.DataRepository +import com.orgzly.android.db.entity.NoteView +import com.orgzly.android.db.entity.SavedSearch +import com.orgzly.android.external.types.ExternalHandlerFailure +import com.orgzly.android.query.user.InternalQueryParser +import com.orgzly.android.ui.NotePlace +import com.orgzly.android.ui.Place +import com.orgzly.android.ui.note.NotePayload +import com.orgzly.org.OrgProperties + +interface ExternalIntentParser { + val dataRepository: DataRepository + + fun Intent.getNotePayload(title: String? = null): NotePayload { + val rawJson = getStringExtra("NOTE_PAYLOAD") + val json = try { + JsonParser.parseString(rawJson) + .let { if (it.isJsonObject) it.asJsonObject else null }!! + } catch (e: Exception) { + throw ExternalHandlerFailure("failed to parse json: ${e.message}\n$rawJson") + } + return NotePayload( + json.getString("title") ?: title + ?: throw ExternalHandlerFailure("no title supplied!\n$rawJson"), + json.getString("content"), + json.getString("state"), + json.getString("priority"), + json.getString("scheduled"), + json.getString("deadline"), + json.getString("closed"), + (json.getString("tags") ?: "") + .split(" +".toRegex()) + .filter { it.isNotEmpty() }, + OrgProperties().apply { + json["properties"]?.asMap?.forEach { (k, v) -> this[k] = v } + } + ) + } + + private fun getNoteByQuery(rawQuery: String?): NoteView { + if (rawQuery == null) + throw ExternalHandlerFailure("couldn't find note") + val query = InternalQueryParser().parse(rawQuery) + val notes = dataRepository.selectNotesFromQuery(query) + if (notes.isEmpty()) + throw ExternalHandlerFailure("couldn't find note") + if (notes.size > 1) + throw ExternalHandlerFailure("query \"$rawQuery\" gave multiple results") + return notes[0] + } + + fun Intent.getNote(prefix: String = "") = + dataRepository.getNoteView(getLongExtra("${prefix}NOTE_ID", -1)) + ?: dataRepository.getNoteAtPath(getStringExtra("${prefix}NOTE_PATH") ?: "") + ?: getNoteByQuery(getStringExtra("${prefix}NOTE_QUERY")) + + fun Intent.getNoteAndProps(prefix: String = "") = getNote(prefix).let { + it to dataRepository.getNoteProperties(it.note.id) + } + + fun Intent.getBook(prefix: String = "") = + dataRepository.getBook(getLongExtra("${prefix}BOOK_ID", -1)) + ?: dataRepository.getBook(getStringExtra("${prefix}BOOK_NAME") ?: "") + ?: throw ExternalHandlerFailure("couldn't find book") + + fun Intent.getNotePlace() = try { + getNote(prefix="PARENT_").let { noteView -> + val place = try { + Place.valueOf(getStringExtra("PLACEMENT") ?: "") + } catch (e: IllegalArgumentException) { Place.UNDER } + dataRepository.getBook(noteView.bookName)?.let { book -> + NotePlace(book.id, noteView.note.id, place) + } + } + } catch (e: ExternalHandlerFailure) { null } ?: try { + NotePlace(getBook(prefix="PARENT_").id) + } catch (e: ExternalHandlerFailure) { + throw ExternalHandlerFailure("couldn't find parent note/book") + } + + fun Intent.getNoteIds(allowSingle: Boolean = true, allowEmpty: Boolean = false): Set { + val id = if (allowSingle) getLongExtra("NOTE_ID", -1) else null + val ids = getLongArrayExtra("NOTE_IDS")?.toTypedArray() ?: emptyArray() + val path = + if (allowSingle) + getStringExtra("NOTE_PATH") + ?.let { dataRepository.getNoteAtPath(it)?.note?.id } + else null + val paths = (getStringArrayExtra("NOTE_PATHS") ?: emptyArray()) + .mapNotNull { dataRepository.getNoteAtPath(it)?.note?.id } + .toTypedArray() + return listOfNotNull(id, *ids, path, *paths).filter { it >= 0 }.toSet().also { + if (it.isEmpty() && !allowEmpty) + throw ExternalHandlerFailure("no notes specified") + } + } + + fun Intent.getSavedSearch() = + dataRepository.getSavedSearch(getLongExtra("SAVED_SEARCH_ID", -1)) + ?: dataRepository.getSavedSearches() + .find { it.name == getStringExtra("SAVED_SEARCH_NAME") } + ?: throw ExternalHandlerFailure("couldn't find saved search") + + fun Intent.getNewSavedSearch(allowBlank: Boolean = false): SavedSearch { + val name = getStringExtra("SAVED_SEARCH_NEW_NAME") + val query = getStringExtra("SAVED_SEARCH_NEW_QUERY") + if (!allowBlank && (name.isNullOrBlank() || query.isNullOrBlank())) + throw ExternalHandlerFailure("invalid parameters for new saved search") + return SavedSearch(0, name ?: "", query ?: "", 0) + } + + private fun JsonObject.getString(name: String) = this[name]?.let { + if (it.isJsonPrimitive && it.asJsonPrimitive.isString) + it.asJsonPrimitive.asString + else null + } + + private val JsonElement.asMap: Map? + get() = if (this.isJsonObject) { + this.asJsonObject + .entrySet() + .map { + if (it.value.isJsonPrimitive) + it.key to it.value.asJsonPrimitive.asString + else return null + } + .toMap() + } else null +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/GetOrgInfo.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/GetOrgInfo.kt new file mode 100644 index 000000000..2f7442f41 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/GetOrgInfo.kt @@ -0,0 +1,21 @@ +package com.orgzly.android.external.actionhandlers + +import android.content.Intent +import com.orgzly.android.external.types.* + +class GetOrgInfo : ExternalAccessActionHandler() { + override val actions = listOf( + action(::getBooks, "GET_BOOKS"), + action(::getSavedSearches, "GET_SAVED_SEARCHES"), + action(::getNote, "GET_NOTE") + ) + + private fun getBooks() = + dataRepository.getBooks().map(Book::from).toTypedArray() + + private fun getSavedSearches() = + dataRepository.getSavedSearches().map(SavedSearch::from).toTypedArray() + + private fun getNote(intent: Intent) = + Note.from(intent.getNoteAndProps()) +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/ManageWidgets.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/ManageWidgets.kt new file mode 100644 index 000000000..fb9374bfc --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/ManageWidgets.kt @@ -0,0 +1,36 @@ +package com.orgzly.android.external.actionhandlers + +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import com.orgzly.android.AppIntent +import com.orgzly.android.db.entity.SavedSearch +import com.orgzly.android.external.types.ExternalHandlerFailure +import com.orgzly.android.widgets.ListWidgetProvider + +class ManageWidgets : ExternalAccessActionHandler() { + override val actions = listOf( + action(::getWidgets, "GET_WIDGETS"), + action(::setWidget, "SET_WIDGET") + ) + + private fun getWidgets(context: Context): Map { + val widgetManager = AppWidgetManager.getInstance(context) + val componentName = ComponentName(context.packageName, ListWidgetProvider::class.java.name) + return widgetManager.getAppWidgetIds(componentName) + .associateWith { ListWidgetProvider.getSavedSearch(context, it, dataRepository) } + } + + private fun setWidget(intent: Intent, context: Context) { + val widgetId = intent.getIntExtra("WIDGET_ID", -1) + if (widgetId < 0) throw ExternalHandlerFailure("invalid widget id") + val savedSearch = intent.getSavedSearch() + + context.sendBroadcast(Intent(context, ListWidgetProvider::class.java).apply { + action = AppIntent.ACTION_SET_LIST_WIDGET_SELECTION + putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId) + putExtra(AppIntent.EXTRA_SAVED_SEARCH_ID, savedSearch.id) + }) + } +} diff --git a/app/src/main/java/com/orgzly/android/external/actionhandlers/RunSearch.kt b/app/src/main/java/com/orgzly/android/external/actionhandlers/RunSearch.kt new file mode 100644 index 000000000..6542f7cc0 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/actionhandlers/RunSearch.kt @@ -0,0 +1,21 @@ +package com.orgzly.android.external.actionhandlers + +import android.content.Intent +import com.orgzly.android.external.types.ExternalHandlerFailure +import com.orgzly.android.external.types.Note +import com.orgzly.android.query.user.InternalQueryParser + +class RunSearch : ExternalAccessActionHandler() { + override val actions = listOf( + action(::runSearch, "SEARCH") + ) + + private fun runSearch(intent: Intent): List { + val searchTerm = intent.getStringExtra("QUERY") + if (searchTerm.isNullOrBlank()) throw ExternalHandlerFailure("invalid search term") + val query = InternalQueryParser().parse(searchTerm) + val notes = dataRepository.selectNotesFromQuery(query) + val notesWithProps = notes.map { it to dataRepository.getNoteProperties(it.note.id) } + return notesWithProps.map(Note::from) + } +} diff --git a/app/src/main/java/com/orgzly/android/external/types/Book.kt b/app/src/main/java/com/orgzly/android/external/types/Book.kt new file mode 100644 index 000000000..31b624aaf --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/types/Book.kt @@ -0,0 +1,10 @@ +package com.orgzly.android.external.types + +import com.orgzly.android.db.entity.BookView + +data class Book(val id: Long, val title: String) { + companion object { + fun from(view: BookView) = + Book(view.book.id, view.book.name) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/external/types/ExternalHandlerFailure.kt b/app/src/main/java/com/orgzly/android/external/types/ExternalHandlerFailure.kt new file mode 100644 index 000000000..81030ead5 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/types/ExternalHandlerFailure.kt @@ -0,0 +1,3 @@ +package com.orgzly.android.external.types + +class ExternalHandlerFailure(msg: String) : Exception(msg) \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/external/types/Note.kt b/app/src/main/java/com/orgzly/android/external/types/Note.kt new file mode 100644 index 000000000..66c3fd03b --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/types/Note.kt @@ -0,0 +1,82 @@ +package com.orgzly.android.external.types + +import com.orgzly.android.db.entity.NoteProperty +import com.orgzly.android.db.entity.NoteView + +data class Note( + val id: Long, + val title: String, + val content: String?, + val tags: List, + val inheritedTags: List, + val bookName: String, + val scheduled: Timestamp?, + val deadline: Timestamp?, + val closed: Timestamp?, + val priority: String?, + val state: String?, + val createdAt: Long?, + val properties: Map +) { + companion object { + fun from(view: NoteView, props: List): Note { + val note = view.note + return Note( + note.id, + note.title, + note.content, + note.tags?.split(" +".toRegex()) + ?.filter { it.isNotEmpty() } + ?: emptyList(), + view.getInheritedTagsList() + .filter { it.isNotEmpty() }, + view.bookName, + Timestamp.from( + view.scheduledRangeString, + view.scheduledTimeTimestamp, + view.scheduledTimeString, + view.scheduledTimeEndString, + ), + Timestamp.from( + view.deadlineRangeString, + view.deadlineTimeTimestamp, + view.deadlineTimeString, + view.deadlineTimeEndString, + ), + Timestamp.from( + view.closedRangeString, + view.closedTimeTimestamp, + view.closedTimeString, + view.closedTimeEndString, + ), + note.priority, + note.state, + note.createdAt, + props.associate { it.name to it.value } + ) + } + + fun from(noteAndProps: Pair>) = + from(noteAndProps.first, noteAndProps.second) + } + + data class Timestamp( + val rangeString: String, + val timeTimestamp: Long, + val timeString: String?, + val timeEndString: String? = null, + ) { + companion object { + fun from( + rangeString: String?, + timeTimestamp: Long?, + timeString: String?, + timeEndString: String?, + ): Timestamp? { + return if (rangeString != null && timeTimestamp != null) + Timestamp(rangeString, timeTimestamp, timeString, timeEndString) + else null + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/external/types/Response.kt b/app/src/main/java/com/orgzly/android/external/types/Response.kt new file mode 100644 index 000000000..41bf95860 --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/types/Response.kt @@ -0,0 +1,8 @@ +package com.orgzly.android.external.types + +import java.io.Serializable + +data class Response(val success: Boolean = true, val result: Any? = null) { + constructor(success: Boolean, result: List) : + this(success, result.toTypedArray()) +} \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/external/types/SavedSearch.kt b/app/src/main/java/com/orgzly/android/external/types/SavedSearch.kt new file mode 100644 index 000000000..4200e3e5b --- /dev/null +++ b/app/src/main/java/com/orgzly/android/external/types/SavedSearch.kt @@ -0,0 +1,8 @@ +package com.orgzly.android.external.types + +data class SavedSearch(val id: Long, val name: String, val position: Int, val query: String) { + companion object { + fun from(search: com.orgzly.android.db.entity.SavedSearch) = + SavedSearch(search.id, search.name, search.position, search.query) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/orgzly/android/widgets/ListWidgetProvider.java b/app/src/main/java/com/orgzly/android/widgets/ListWidgetProvider.java index fd42a9d60..b541eedc1 100644 --- a/app/src/main/java/com/orgzly/android/widgets/ListWidgetProvider.java +++ b/app/src/main/java/com/orgzly/android/widgets/ListWidgetProvider.java @@ -242,6 +242,10 @@ private void setSelectionFromIntent(Context context, Intent intent) { } private SavedSearch getSavedSearch(Context context, int appWidgetId) { + return getSavedSearch(context, appWidgetId, dataRepository); + } + + public static SavedSearch getSavedSearch(Context context, int appWidgetId, DataRepository dataRepository) { long filterId = context.getSharedPreferences(PREFERENCES_ID, Context.MODE_PRIVATE) .getLong(getFilterPreferenceKey(appWidgetId), -1); @@ -265,7 +269,7 @@ private void setFilter(Context context, int appWidgetId, long id) { editor.apply(); } - private String getFilterPreferenceKey(int appWidgetId) { + private static String getFilterPreferenceKey(int appWidgetId) { return "widget-filter-" + appWidgetId; }