Skip to content

Commit 8917bfc

Browse files
committed
Add remaining "essential" external actions
1 parent 1314430 commit 8917bfc

File tree

9 files changed

+215
-89
lines changed

9 files changed

+215
-89
lines changed

app/src/main/java/com/orgzly/android/data/DataRepository.kt

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ class DataRepository @Inject constructor(
599599

600600
} else {
601601
db.runInTransaction(Callable {
602-
moveSubtrees(noteIds, Place.UNDER, target.noteId)
602+
moveSubtrees(noteIds, target.place, target.noteId)
603603
})
604604
}
605605
}
@@ -1239,12 +1239,14 @@ class DataRepository @Inject constructor(
12391239
}
12401240

12411241
fun getNoteAtPath(bookName: String, path: String) =
1242-
getNotes(bookName)
1243-
.filter { ("/$path").endsWith("/" + it.note.title) }
1244-
.firstOrNull { view ->
1245-
getNoteAndAncestors(view.note.id)
1246-
.joinToString("/") { it.title } == path
1247-
}
1242+
if (path.split("/").any { it.isNotEmpty() })
1243+
getNotes(bookName)
1244+
.filter { ("/$path").endsWith("/" + it.note.title) }
1245+
.firstOrNull { view ->
1246+
getNoteAndAncestors(view.note.id)
1247+
.joinToString("/") { it.title } == path
1248+
}
1249+
else null
12481250

12491251
fun getNotesAndSubtrees(ids: Set<Long>): List<Note> {
12501252
return db.note().getNotesForSubtrees(ids)

app/src/main/java/com/orgzly/android/external/ExternalAccessReceiver.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class ExternalAccessReceiver : BroadcastReceiver() {
1212
GetOrgInfo(),
1313
RunSearch(),
1414
EditNotes(),
15+
EditSavedSearches(),
1516
ManageWidgets()
1617
)
1718

Lines changed: 45 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,63 @@
11
package com.orgzly.android.external.actionhandlers
22

3-
import android.content.Context
43
import android.content.Intent
5-
import com.google.gson.JsonElement
6-
import com.google.gson.JsonObject
7-
import com.google.gson.JsonParseException
8-
import com.google.gson.JsonParser
94
import com.orgzly.android.external.types.Response
10-
import com.orgzly.android.ui.NotePlace
11-
import com.orgzly.android.ui.Place
12-
import com.orgzly.android.ui.note.NotePayload
13-
import com.orgzly.org.OrgProperties
145

156
class EditNotes : ExternalAccessActionHandler() {
167
override val actions = listOf(
17-
action(::addNote, "ADD_NOTE", "ADD_NOTES"),
18-
action(::deleteNote, "DELETE_NOTE", "DELETE_NOTES")
8+
action(::addNote, "ADD_NOTE", "ADD_NOTES"),
9+
action(::editNote, "EDIT_NOTE"),
10+
action(::refileNote, "REFILE_NOTE", "REFILE_NOTES"),
11+
action(::moveNote, "MOVE_NOTE", "MOVE_NOTES"),
12+
action(::deleteNote, "DELETE_NOTE", "DELETE_NOTES")
1913
)
2014

21-
fun addNote(intent: Intent): Response {
22-
val book = getBook(intent) ?: return Response(false, "Couldn't find specified book")
23-
val newNote = notePayloadFromJson(intent.getStringExtra("PAYLOAD") ?: "")
15+
private fun addNote(intent: Intent): Response {
16+
val place = intent.getNotePlace()
17+
?: return Response(false, "Could not find parent note")
18+
val newNote = intent.getNotePayload()
2419
?: return Response(false, "Invalid payload")
25-
val path = intent.getStringExtra("PATH") ?: ""
26-
27-
val place = if (path.split("/").any { it.isNotEmpty() }) {
28-
dataRepository.getNoteAtPath(book.name, path)?.let {
29-
NotePlace(book.id, it.note.id, Place.UNDER)
30-
}
31-
} else null
32-
place ?: return Response(false, "Couldn't find parent note at path")
33-
dataRepository.createNote(newNote, place)
34-
return Response(true, null)
20+
val note = dataRepository.createNote(newNote, place)
21+
return Response(true, "${note.id}")
3522
}
3623

37-
// <editor-fold desc="Helpers">
38-
39-
private fun JsonObject.getString(name: String) = this[name]?.let {
40-
if (it.isJsonPrimitive && it.asJsonPrimitive.isString)
41-
it.asJsonPrimitive.asString
42-
else null
24+
private fun editNote(intent: Intent): Response {
25+
val noteView = intent.getNote()
26+
?: return Response(false, "Couldn't find note")
27+
val newNote = intent.getNotePayload(title=noteView.note.title)
28+
?: return Response(false, "Invalid payload")
29+
dataRepository.updateNote(noteView.note.id, newNote)
30+
return Response()
4331
}
4432

45-
private val JsonElement.asMap: Map<String, String>?
46-
get() = if (this.isJsonObject) {
47-
this.asJsonObject
48-
.entrySet()
49-
.map {
50-
if (it.value.isJsonPrimitive)
51-
it.key to it.value.asJsonPrimitive.asString
52-
else return null
53-
}
54-
.toMap()
55-
} else null
33+
private fun refileNote(intent: Intent): Response {
34+
val notes = intent.getNoteIds()
35+
if (notes.isEmpty())
36+
return Response(false, "No notes specified")
37+
val place = intent.getNotePlace()
38+
?: return Response(false, "Couldn't find note")
39+
dataRepository.refileNotes(notes, place)
40+
return Response()
41+
}
5642

57-
private fun notePayloadFromJson(rawJson: String): NotePayload? {
58-
val json = try {
59-
JsonParser.parseString(rawJson)
60-
.let { if (it.isJsonObject) it.asJsonObject else null }
61-
} catch (e: JsonParseException) {
62-
null
63-
}
64-
return try {
65-
json!!
66-
NotePayload(
67-
json.getString("title")!!,
68-
json.getString("content"),
69-
json.getString("state"),
70-
json.getString("priority"),
71-
json.getString("scheduled"),
72-
json.getString("deadline"),
73-
json.getString("closed"),
74-
(json.getString("tags") ?: "")
75-
.split(" +".toRegex())
76-
.filter { it.isNotEmpty() },
77-
OrgProperties().apply {
78-
json["properties"]?.asMap?.forEach { (k, v) -> this[k] = v }
79-
}
80-
)
81-
} catch (e: NullPointerException) { null }
43+
private fun moveNote(intent: Intent): Response {
44+
val notes = intent.getNoteIds()
45+
if (notes.isEmpty()) return Response(false, "No notes specified")
46+
with(dataRepository) { when (intent.getStringExtra("DIRECTION")) {
47+
"UP" -> intent.getBook()?.id?.let { moveNote(it, notes, -1) }
48+
"DOWN" -> intent.getBook()?.id?.let { moveNote(it, notes, 1) }
49+
"LEFT" -> promoteNotes(notes)
50+
"RIGHT" -> demoteNotes(notes)
51+
else -> return Response(false, "Invalid direction")
52+
} }
53+
return Response()
8254
}
8355

84-
// </editor-fold>
56+
private fun deleteNote(intent: Intent): Response {
57+
val book = intent.getBook() ?: return Response(false, "Couldn't find specified book")
58+
val notes = intent.getNoteIds()
59+
if (notes.isEmpty()) return Response(false, "No notes specified")
60+
dataRepository.deleteNotes(book.id, notes)
61+
return Response()
62+
}
8563
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.orgzly.android.external.actionhandlers
2+
3+
import android.content.Intent
4+
import com.orgzly.android.db.entity.SavedSearch
5+
import com.orgzly.android.external.types.Response
6+
7+
class EditSavedSearches : ExternalAccessActionHandler() {
8+
override val actions = listOf(
9+
action(::addSavedSearch, "ADD_SAVED_SEARCH"),
10+
action(::editSavedSearch, "EDIT_SAVED_SEARCH"),
11+
action(::moveSavedSearch, "MOVE_SAVED_SEARCH"),
12+
action(::deleteSavedSearch, "DELETE_SAVED_SEARCH"),
13+
)
14+
15+
private fun addSavedSearch(intent: Intent) =
16+
intent.getNewSavedSearch()?.let {
17+
val id = dataRepository.createSavedSearch(it)
18+
Response(true, "$id")
19+
} ?: Response(false, "Invalid saved search details")
20+
21+
private fun editSavedSearch(intent: Intent) = intent.getSavedSearch()?.let { savedSearch ->
22+
intent.getNewSavedSearch(allowBlank = true)?.let { newSavedSearch ->
23+
dataRepository.updateSavedSearch(SavedSearch(
24+
savedSearch.id,
25+
(if (newSavedSearch.name.isBlank()) savedSearch.name
26+
else newSavedSearch.name),
27+
(if (newSavedSearch.query.isBlank()) savedSearch.query
28+
else newSavedSearch.query),
29+
savedSearch.position
30+
))
31+
return Response()
32+
} ?: Response(false, "Invalid saved search details")
33+
} ?: Response(false, "Couldn't find saved search")
34+
35+
private fun moveSavedSearch(intent: Intent) = intent.getSavedSearch()?.let { savedSearch ->
36+
when (intent.getStringExtra("DIRECTION")) {
37+
"UP" -> dataRepository.moveSavedSearchUp(savedSearch.id)
38+
"DOWN" -> dataRepository.moveSavedSearchDown(savedSearch.id)
39+
else -> return Response(false, "Invalid direction")
40+
}
41+
return Response()
42+
} ?: Response(false, "Couldn't find saved search")
43+
44+
private fun deleteSavedSearch(intent: Intent) = intent.getSavedSearch()?.let { savedSearch ->
45+
dataRepository.deleteSavedSearches(setOf(savedSearch.id))
46+
return Response()
47+
} ?: Response(false, "Couldn't find saved search")
48+
}

app/src/main/java/com/orgzly/android/external/actionhandlers/ExternalAccessActionHandler.kt

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@ package com.orgzly.android.external.actionhandlers
22

33
import android.content.Context
44
import android.content.Intent
5+
import com.google.gson.JsonElement
6+
import com.google.gson.JsonObject
7+
import com.google.gson.JsonParseException
8+
import com.google.gson.JsonParser
59
import com.orgzly.android.App
610
import com.orgzly.android.data.DataRepository
711
import com.orgzly.android.db.entity.Book
12+
import com.orgzly.android.db.entity.SavedSearch
813
import com.orgzly.android.external.types.Response
14+
import com.orgzly.android.ui.NotePlace
15+
import com.orgzly.android.ui.Place
16+
import com.orgzly.android.ui.note.NotePayload
17+
import com.orgzly.org.OrgProperties
918
import javax.inject.Inject
1019

1120
abstract class ExternalAccessActionHandler {
@@ -21,9 +30,100 @@ abstract class ExternalAccessActionHandler {
2130
actions.flatten().toMap().mapKeys { (key, _) -> "com.orgzly.android.$key" }
2231
}
2332

24-
fun getBook(intent: Intent) =
25-
dataRepository.getBook(intent.getLongExtra("BOOK_ID", -1))
26-
?: dataRepository.getBook(intent.getStringExtra("BOOK_NAME") ?: "")
33+
fun Intent.getNotePayload(title: String? = null): NotePayload? {
34+
val rawJson = getStringExtra("NOTE_PAYLOAD")
35+
val json = try {
36+
JsonParser.parseString(rawJson)
37+
.let { if (it.isJsonObject) it.asJsonObject else null }
38+
} catch (e: JsonParseException) {
39+
null
40+
}
41+
42+
return try {
43+
json!!
44+
NotePayload(
45+
(json.getString("title") ?: title)!!,
46+
json.getString("content"),
47+
json.getString("state"),
48+
json.getString("priority"),
49+
json.getString("scheduled"),
50+
json.getString("deadline"),
51+
json.getString("closed"),
52+
(json.getString("tags") ?: "")
53+
.split(" +".toRegex())
54+
.filter { it.isNotEmpty() },
55+
OrgProperties().apply {
56+
json["properties"]?.asMap?.forEach { (k, v) -> this[k] = v }
57+
}
58+
)
59+
} catch (e: NullPointerException) { null }
60+
}
61+
62+
fun Intent.getNotePlace(book_: Book? = null) = (book_ ?: getBook(prefix="PARENT_"))
63+
?.let { book -> getNote(book, prefix="PARENT_")?.let { noteView ->
64+
val place = try {
65+
Place.valueOf(getStringExtra("PLACEMENT") ?: "")
66+
} catch (e: IllegalArgumentException) { Place.UNDER }
67+
NotePlace(book.id, noteView.note.id, place)
68+
} }
69+
70+
fun Intent.getNoteIds(allowSingle: Boolean = true): Set<Long> {
71+
val id = if (allowSingle) getLongExtra("NOTE_ID", -1) else null
72+
val ids = getLongArrayExtra("NOTE_IDS")?.toTypedArray() ?: emptyArray()
73+
val book = getBook()?.name
74+
val (path, paths) = if (book != null) {
75+
val path =
76+
if (allowSingle)
77+
getStringExtra("NOTE_PATH")
78+
?.let { dataRepository.getNoteAtPath(book, it)?.note?.id }
79+
else null
80+
val paths = (getStringArrayExtra("NOTE_PATHS") ?: emptyArray())
81+
.mapNotNull { dataRepository.getNoteAtPath(book, it)?.note?.id }
82+
.toTypedArray()
83+
path to paths
84+
} else null to emptyArray()
85+
return listOfNotNull(id, *ids, path, *paths).filter { it >= 0 }.toSet()
86+
}
87+
88+
fun Intent.getNote(book: Book? = null, prefix: String = "") =
89+
dataRepository.getNoteView(getLongExtra("${prefix}NOTE_ID", -1))
90+
?: (book ?: getBook(prefix=prefix))
91+
?.let { dataRepository.getNoteAtPath(it.name,
92+
getStringExtra("${prefix}NOTE_PATH") ?: "") }
93+
94+
fun Intent.getBook(prefix: String = "") =
95+
dataRepository.getBook(getLongExtra("${prefix}BOOK_ID", -1))
96+
?: dataRepository.getBook(getStringExtra("${prefix}BOOK_NAME") ?: "")
97+
98+
fun Intent.getSavedSearch() =
99+
dataRepository.getSavedSearch(getLongExtra("SAVED_SEARCH_ID", -1))
100+
?: dataRepository.getSavedSearches()
101+
.find { it.name == getStringExtra("SAVED_SEARCH_NAME") }
102+
103+
fun Intent.getNewSavedSearch(allowBlank: Boolean = false): SavedSearch? {
104+
val name = getStringExtra("SAVED_SEARCH_NEW_NAME")
105+
val query = getStringExtra("SAVED_SEARCH_NEW_QUERY")
106+
if (!allowBlank && (name.isNullOrBlank() || query.isNullOrBlank())) return null
107+
return SavedSearch(0, name ?: "", query ?: "", 0)
108+
}
109+
110+
private fun JsonObject.getString(name: String) = this[name]?.let {
111+
if (it.isJsonPrimitive && it.asJsonPrimitive.isString)
112+
it.asJsonPrimitive.asString
113+
else null
114+
}
115+
116+
private val JsonElement.asMap: Map<String, String>?
117+
get() = if (this.isJsonObject) {
118+
this.asJsonObject
119+
.entrySet()
120+
.map {
121+
if (it.value.isJsonPrimitive)
122+
it.key to it.value.asJsonPrimitive.asString
123+
else return null
124+
}
125+
.toMap()
126+
} else null
27127

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

app/src/main/java/com/orgzly/android/external/actionhandlers/GetOrgInfo.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package com.orgzly.android.external.actionhandlers
22

3-
import android.content.Context
43
import android.content.Intent
54
import com.orgzly.android.external.types.*
65

@@ -23,12 +22,8 @@ class GetOrgInfo : ExternalAccessActionHandler() {
2322
.map(SavedSearch::from).toTypedArray()
2423
)
2524

26-
private fun getNote(intent: Intent): Response {
27-
val book = getBook(intent) ?: return Response(false, "Couldn't find specified book")
28-
val path = intent.getStringExtra("PATH")
29-
?: return Response(false, "Invalid arguments!")
30-
return dataRepository.getNoteAtPath(book.name, path)
25+
private fun getNote(intent: Intent) =
26+
intent.getNote()
3127
?.let { Response(true, Note.from(it)) }
3228
?: Response(false, "Couldn't find note at specified path!")
33-
}
3429
}

app/src/main/java/com/orgzly/android/external/actionhandlers/ManageWidgets.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ class ManageWidgets : ExternalAccessActionHandler() {
2626
private fun setWidget(intent: Intent, context: Context): Response {
2727
val widgetId = intent.getIntExtra("WIDGET_ID", -1)
2828
if (widgetId < 0) return Response(false, "invalid widget ID")
29-
val savedSearchId = intent.getLongExtra("SAVED_SEARCH_ID", -1)
30-
if (savedSearchId < 0) return Response(false, "invalid saved search ID")
29+
val savedSearch = intent.getSavedSearch()
30+
?: return Response(false, "invalid saved search ID")
3131

3232
context.sendBroadcast(Intent(context, ListWidgetProvider::class.java).apply {
3333
action = AppIntent.ACTION_SET_LIST_WIDGET_SELECTION
3434
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
35-
putExtra(AppIntent.EXTRA_SAVED_SEARCH_ID, savedSearchId)
35+
putExtra(AppIntent.EXTRA_SAVED_SEARCH_ID, savedSearch.id)
3636
})
3737

38-
return Response(true, null)
38+
return Response()
3939
}
4040
}

0 commit comments

Comments
 (0)