Skip to content

Commit

Permalink
Clean up external receiver error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
NatKarmios committed Aug 24, 2021
1 parent e6f3230 commit 479e6d4
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 99 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.orgzly.android.external.actionhandlers

import android.content.Intent
import com.orgzly.android.external.types.Response
import com.orgzly.android.external.types.ExternalHandlerFailure

class EditNotes : ExternalAccessActionHandler() {
override val actions = listOf(
Expand All @@ -12,52 +12,42 @@ class EditNotes : ExternalAccessActionHandler() {
action(::deleteNote, "DELETE_NOTE", "DELETE_NOTES")
)

private fun addNote(intent: Intent): Response {
private fun addNote(intent: Intent): String {
val place = intent.getNotePlace()
?: return Response(false, "Could not find parent note/book")
val newNote = intent.getNotePayload()
?: return Response(false, "Invalid payload")
val note = dataRepository.createNote(newNote, place)
return Response(true, "${note.id}")
return "${note.id}"
}

private fun editNote(intent: Intent): Response {
private fun editNote(intent: Intent) {
val noteView = intent.getNote()
?: return Response(false, "Couldn't find note")
val newNote = intent.getNotePayload(title=noteView.note.title)
?: return Response(false, "Invalid payload")
dataRepository.updateNote(noteView.note.id, newNote)
return Response()
}

private fun refileNote(intent: Intent): Response {
private fun refileNote(intent: Intent) {
val notes = intent.getNoteIds()
if (notes.isEmpty())
return Response(false, "No notes specified")
val place = intent.getNotePlace()
?: return Response(false, "Couldn't find note")
dataRepository.refileNotes(notes, place)
return Response()
}

private fun moveNote(intent: Intent): Response {
private fun moveNote(intent: Intent) {
val notes = intent.getNoteIds()
if (notes.isEmpty()) return Response(false, "No notes specified")
with(dataRepository) { when (intent.getStringExtra("DIRECTION")) {
"UP" -> intent.getBook()?.id?.let { moveNote(it, notes, -1) }
"DOWN" -> intent.getBook()?.id?.let { moveNote(it, notes, 1) }
"UP" -> moveNote(intent.getBook().id, notes, -1)
"DOWN" -> moveNote(intent.getBook().id, notes, 1)
"LEFT" -> promoteNotes(notes)
"RIGHT" -> demoteNotes(notes)
else -> return Response(false, "Invalid direction")
else -> throw ExternalHandlerFailure("invalid direction")
} }
return Response()
}

private fun deleteNote(intent: Intent): Response {
val book = intent.getBook() ?: return Response(false, "Couldn't find specified book")
val notes = intent.getNoteIds()
if (notes.isEmpty()) return Response(false, "No notes specified")
dataRepository.deleteNotes(book.id, notes)
return Response()
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())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.orgzly.android.external.actionhandlers

import android.content.Intent
import com.orgzly.android.db.entity.SavedSearch
import com.orgzly.android.external.types.Response
import com.orgzly.android.external.types.ExternalHandlerFailure

class EditSavedSearches : ExternalAccessActionHandler() {
override val actions = listOf(
Expand All @@ -12,37 +12,36 @@ class EditSavedSearches : ExternalAccessActionHandler() {
action(::deleteSavedSearch, "DELETE_SAVED_SEARCH"),
)

private fun addSavedSearch(intent: Intent) =
intent.getNewSavedSearch()?.let {
val id = dataRepository.createSavedSearch(it)
Response(true, "$id")
} ?: Response(false, "Invalid saved search details")
private fun addSavedSearch(intent: Intent): String {
val savedSearch = intent.getNewSavedSearch()
val id = dataRepository.createSavedSearch(savedSearch)
return "$id"
}

private fun editSavedSearch(intent: Intent) = intent.getSavedSearch()?.let { savedSearch ->
intent.getNewSavedSearch(allowBlank = true)?.let { newSavedSearch ->
dataRepository.updateSavedSearch(SavedSearch(
savedSearch.id,
(if (newSavedSearch.name.isBlank()) savedSearch.name
else newSavedSearch.name),
(if (newSavedSearch.query.isBlank()) savedSearch.query
else newSavedSearch.query),
savedSearch.position
))
return Response()
} ?: Response(false, "Invalid saved search details")
} ?: Response(false, "Couldn't find saved search")
private fun editSavedSearch(intent: Intent) {
val savedSearch = intent.getSavedSearch()
val newSavedSearch = intent.getNewSavedSearch(allowBlank = true)
dataRepository.updateSavedSearch(SavedSearch(
savedSearch.id,
(if (newSavedSearch.name.isBlank()) savedSearch.name
else newSavedSearch.name),
(if (newSavedSearch.query.isBlank()) savedSearch.query
else newSavedSearch.query),
savedSearch.position
))
}

private fun moveSavedSearch(intent: Intent) = intent.getSavedSearch()?.let { savedSearch ->
private fun moveSavedSearch(intent: Intent) {
val savedSearch = intent.getSavedSearch()
when (intent.getStringExtra("DIRECTION")) {
"UP" -> dataRepository.moveSavedSearchUp(savedSearch.id)
"DOWN" -> dataRepository.moveSavedSearchDown(savedSearch.id)
else -> return Response(false, "Invalid direction")
else -> throw ExternalHandlerFailure("invalid direction")
}
return Response()
} ?: Response(false, "Couldn't find saved search")
}

private fun deleteSavedSearch(intent: Intent) = intent.getSavedSearch()?.let { savedSearch ->
private fun deleteSavedSearch(intent: Intent) {
val savedSearch = intent.getSavedSearch()
dataRepository.deleteSavedSearches(setOf(savedSearch.id))
return Response()
} ?: Response(false, "Couldn't find saved search")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.google.gson.JsonParser
import com.orgzly.android.App
import com.orgzly.android.data.DataRepository
import com.orgzly.android.db.entity.SavedSearch
import com.orgzly.android.external.types.ExternalHandlerFailure
import com.orgzly.android.external.types.Response
import com.orgzly.android.ui.NotePlace
import com.orgzly.android.ui.Place
Expand All @@ -25,12 +26,12 @@ abstract class ExternalAccessActionHandler {
App.appComponent.inject(this)
}

abstract val actions: List<List<Pair<String, (Intent, Context) -> Response>>>
abstract val actions: List<List<Pair<String, (Intent, Context) -> Any>>>
private val fullNameActions by lazy {
actions.flatten().toMap().mapKeys { (key, _) -> "com.orgzly.android.$key" }
}

fun Intent.getNotePayload(title: String? = null): NotePayload? {
fun Intent.getNotePayload(title: String? = null): NotePayload {
val rawJson = getStringExtra("NOTE_PAYLOAD")
val json = try {
JsonParser.parseString(rawJson)
Expand All @@ -56,19 +57,27 @@ abstract class ExternalAccessActionHandler {
json["properties"]?.asMap?.forEach { (k, v) -> this[k] = v }
}
)
} catch (e: NullPointerException) { null }
} catch (e: NullPointerException) {
throw ExternalHandlerFailure("invalid payload")
}
}

fun Intent.getNotePlace() = 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)
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)
}
}
} ?: getBook(prefix="PARENT")?.let { book -> NotePlace(book.id) }
} catch (e: ExternalHandlerFailure) { null } ?: try {
NotePlace(getBook(prefix="PARENT_").id)
} catch (e: ExternalHandlerFailure) {
throw ExternalHandlerFailure("could not find parent note/book")
}

fun Intent.getNoteIds(allowSingle: Boolean = true): Set<Long> {
fun Intent.getNoteIds(allowSingle: Boolean = true, allowEmpty: Boolean = false): Set<Long> {
val id = if (allowSingle) getLongExtra("NOTE_ID", -1) else null
val ids = getLongArrayExtra("NOTE_IDS")?.toTypedArray() ?: emptyArray()
val path =
Expand All @@ -79,26 +88,33 @@ abstract class ExternalAccessActionHandler {
val paths = (getStringArrayExtra("NOTE_PATHS") ?: emptyArray())
.mapNotNull { dataRepository.getNoteAtPath(it)?.note?.id }
.toTypedArray()
return listOfNotNull(id, *ids, path, *paths).filter { it >= 0 }.toSet()
return listOfNotNull(id, *ids, path, *paths).filter { it >= 0 }.toSet().also {
if (it.isEmpty() && !allowEmpty)
throw ExternalHandlerFailure("no notes specified")
}
}

fun Intent.getNote(prefix: String = "") =
dataRepository.getNoteView(getLongExtra("${prefix}NOTE_ID", -1))
?: dataRepository.getNoteAtPath(getStringExtra("${prefix}NOTE_PATH") ?: "")
?: throw ExternalHandlerFailure("couldn't find note")

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.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? {
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())) return null
if (!allowBlank && (name.isNullOrBlank() || query.isNullOrBlank()))
throw ExternalHandlerFailure("invalid parameters for new saved search")
return SavedSearch(0, name ?: "", query ?: "", 0)
}

Expand All @@ -120,20 +136,25 @@ abstract class ExternalAccessActionHandler {
.toMap()
} else null

fun action(f: (Intent, Context) -> Response, vararg names: String) = names.map { it to f }
fun action(f: (Intent, Context) -> Any, vararg names: String) = names.map { it to f }

@JvmName("intentAction")
fun action(f: (Intent) -> Response, vararg names: String) =
fun action(f: (Intent) -> Any, vararg names: String) =
action({ i, _ -> f(i) }, *names)

@JvmName("contextAction")
fun action(f: (Context) -> Response, vararg names: String) =
fun action(f: (Context) -> Any, vararg names: String) =
action({ _, c -> f(c) }, *names)

fun action(f: () -> Response, vararg names: String) =
fun action(f: () -> Any, vararg names: String) =
action({ _, _ -> f() }, *names)


fun handle(intent: Intent, context: Context) =
fullNameActions[intent.action!!]?.let { it(intent, context) }
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: ExternalHandlerFailure) {
Response(false, e.message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,12 @@ class GetOrgInfo : ExternalAccessActionHandler() {
action(::getNote, "GET_NOTE")
)

private fun getBooks() = Response(
true,
dataRepository.getBooks()
.map(Book::from).toTypedArray()
)
private fun getBooks() =
dataRepository.getBooks().map(Book::from).toTypedArray()

private fun getSavedSearches() = Response(
true,
dataRepository.getSavedSearches()
.map(SavedSearch::from).toTypedArray()
)
private fun getSavedSearches() =
dataRepository.getSavedSearches().map(SavedSearch::from).toTypedArray()

private fun getNote(intent: Intent) =
intent.getNote()
?.let { Response(true, Note.from(it)) }
?: Response(false, "Couldn't find note at specified path!")
Note.from(intent.getNote())
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import android.content.ComponentName
import android.content.Context
import android.content.Intent
import com.orgzly.android.AppIntent
import com.orgzly.android.external.types.Response
import com.orgzly.android.db.entity.SavedSearch
import com.orgzly.android.external.types.ExternalHandlerFailure
import com.orgzly.android.widgets.ListWidgetProvider

class ManageWidgets : ExternalAccessActionHandler() {
Expand All @@ -14,27 +15,23 @@ class ManageWidgets : ExternalAccessActionHandler() {
action(::setWidget, "SET_WIDGET")
)

private fun getWidgets(context: Context): Response {
private fun getWidgets(context: Context): Map<Int, SavedSearch> {
val widgetManager = AppWidgetManager.getInstance(context)
val componentName = ComponentName(context.packageName, ListWidgetProvider::class.java.name)
val widgetData = widgetManager.getAppWidgetIds(componentName)
return widgetManager.getAppWidgetIds(componentName)
.map { it to ListWidgetProvider.getSavedSearch(context, it, dataRepository) }
.toMap()
return Response(true, widgetData)
}

private fun setWidget(intent: Intent, context: Context): Response {
private fun setWidget(intent: Intent, context: Context) {
val widgetId = intent.getIntExtra("WIDGET_ID", -1)
if (widgetId < 0) return Response(false, "invalid widget ID")
if (widgetId < 0) throw ExternalHandlerFailure("invalid widget id")
val savedSearch = intent.getSavedSearch()
?: return Response(false, "invalid saved search ID")

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)
})

return Response()
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.orgzly.android.external.actionhandlers

import android.content.Context
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
import com.orgzly.android.external.types.*

class RunSearch : ExternalAccessActionHandler() {
override val actions = listOf(
action(::runSearch, "SEARCH")
)

private fun runSearch(intent: Intent): Response {
private fun runSearch(intent: Intent): List<Note> {
val searchTerm = intent.getStringExtra("QUERY")
if (searchTerm.isNullOrBlank()) return Response(false, "Invalid search term!")
if (searchTerm.isNullOrBlank()) throw ExternalHandlerFailure("invalid search term")
val query = InternalQueryParser().parse(searchTerm)
val notes = dataRepository.selectNotesFromQuery(query)
return Response(true, notes.map(Note::from).toTypedArray())
return notes.map(Note::from)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.orgzly.android.external.types

class ExternalHandlerFailure(msg: String) : Exception(msg)

0 comments on commit 479e6d4

Please sign in to comment.