1 /*
2  * Copyright 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.appfunctions.schema.notes
18 
19 import android.net.Uri
20 import androidx.annotation.RestrictTo
21 import androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP
22 import androidx.appfunctions.AppFunctionContext
23 import androidx.appfunctions.AppFunctionOpenable
24 import androidx.appfunctions.AppFunctionSchemaDefinition
25 import androidx.appfunctions.schema.types.SetField
26 import java.time.LocalDateTime
27 
28 // TODO(b/407951385): Expose schema version.
29 /**
30  * The category name of Notes related app functions.
31  *
32  * Example of apps that can support this category of schema include note taking apps.
33  *
34  * The category is used to search app functions related to notes, using
35  * [androidx.appfunctions.AppFunctionSearchSpec.schemaCategory].
36  */
37 public const val APP_FUNCTION_SCHEMA_CATEGORY_NOTES: String = "notes"
38 
39 /** Finds [AppFunctionNote]s with the given search criteria specified in [Parameters]. */
40 @AppFunctionSchemaDefinition(
41     name = "findNotes",
42     version = FindNotesAppFunction.SCHEMA_VERSION,
43     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
44 )
45 public interface FindNotesAppFunction<
46     Parameters : FindNotesAppFunction.Parameters,
47     Response : FindNotesAppFunction.Response
48 > {
49     /**
50      * Finds notes with the given search criteria.
51      *
52      * The implementing app should throw an appropriate [androidx.appfunctions.AppFunctionException]
53      * in exceptional cases.
54      *
55      * @param appFunctionContext The AppFunction execution context.
56      * @param parameters The parameters for finding notes.
57      * @return The response including the list of notes that match the parameters, or an empty list
58      *   if no match is found.
59      */
findNotesnull60     public suspend fun findNotes(
61         appFunctionContext: AppFunctionContext,
62         parameters: Parameters,
63     ): Response
64 
65     /** The parameters for finding notes. */
66     public interface Parameters {
67         /**
68          * The search query to be processed. A null value means to query all notes with the
69          * remaining parameters.
70          *
71          * This parameter is analogous to the caller typing a query into a search box. The app
72          * providing the app function has full control over how the query is interpreted and
73          * processed; for example by name matching or semantic analysis.
74          */
75         public val query: String?
76             get() = null
77 
78         /**
79          * The date/time on or after which a note must have been modified to be included in the
80          * results.
81          *
82          * Acts as a lower bound for filtering notes based on their modification timestamp. A `null`
83          * value means no lower bound is applied.
84          *
85          * If `modifiedAfter` is equal to `modifiedBefore`, an empty list will be returned.
86          *
87          * [androidx.appfunctions.AppFunctionInvalidArgumentException] should be thrown if
88          * `modifiedAfter` is specified and is after `modifiedBefore`.
89          */
90         public val modifiedAfter: LocalDateTime?
91             get() = null
92 
93         /**
94          * The date/time before which a note must have been modified to be included in the results.
95          *
96          * Acts as a strict upper bound for filtering notes based on their modification timestamp. A
97          * `null` value means no upper bound is applied.
98          *
99          * If `modifiedAfter` is equal to `modifiedBefore`, an empty list will be returned.
100          *
101          * [androidx.appfunctions.AppFunctionInvalidArgumentException] should be thrown if
102          * `modifiedAfter` is specified and is after `modifiedBefore`.
103          */
104         public val modifiedBefore: LocalDateTime?
105             get() = null
106     }
107 
108     /** The response including the list of notes that match the parameters. */
109     public interface Response {
110         /** The list of notes that match the parameters, or an empty list if no match is found. */
111         public val notes: List<AppFunctionNote>
112     }
113 
114     public companion object {
115         /** Current schema version. */
116         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
117     }
118 }
119 
120 /** Creates an [AppFunctionNote] with the given parameters. */
121 @AppFunctionSchemaDefinition(
122     name = "createNote",
123     version = CreateNoteAppFunction.SCHEMA_VERSION,
124     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
125 )
126 public interface CreateNoteAppFunction<
127     Parameters : CreateNoteAppFunction.Parameters,
128     Response : CreateNoteAppFunction.Response
129 > {
130     /**
131      * Creates an [AppFunctionNote] with the given parameters.
132      *
133      * The implementing app should throw an appropriate subclass of
134      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
135      *
136      * @param appFunctionContext The AppFunction execution context.
137      * @param parameters The parameters for creating a note.
138      * @return The response including the created note.
139      */
createNotenull140     public suspend fun createNote(
141         appFunctionContext: AppFunctionContext,
142         parameters: Parameters,
143     ): Response
144 
145     /** The parameters for creating a note. */
146     public interface Parameters {
147         /** The title of the note. */
148         public val title: String
149         /** The text content of the note. */
150         public val content: String?
151             get() = null
152 
153         /** The attachments of the note. */
154         public val attachments: List<AppFunctionNote.Attachment>
155             get() = emptyList()
156 
157         /**
158          * The ID of the group the note is in, if any else `null`.
159          *
160          * [androidx.appfunctions.AppFunctionElementNotFoundException] should be thrown when a group
161          * with the specified groupId doesn't exist.
162          */
163         public val groupId: String?
164             get() = null
165 
166         /**
167          * An optional UUID for this note provided by the caller. If provided, the caller can use
168          * this UUID as well as the returned [AppFunctionNote.id] to reference this specific note in
169          * subsequent requests, such as a request to update the note that was just created.
170          *
171          * To support [externalUuid], the application should maintain a mapping between the
172          * [externalUuid] and the internal id of this note. This allows the application to retrieve
173          * the correct note when the caller references it using the provided `externalUuid` in
174          * subsequent requests.
175          *
176          * If the `externalUuid` is not provided by the caller in the creation request, the
177          * application should expect subsequent requests from the caller to reference the note using
178          * the application generated [AppFunctionNote.id].
179          */
180         public val externalUuid: String?
181             get() = null
182     }
183 
184     /** The response including the created note. */
185     public interface Response {
186         /** The created note. */
187         public val createdNote: AppFunctionNote
188     }
189 
190     public companion object {
191         /** Current schema version. */
192         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
193     }
194 }
195 
196 /** Updates an existing [AppFunctionNote]. */
197 @AppFunctionSchemaDefinition(
198     name = "updateNote",
199     version = UpdateNoteAppFunction.SCHEMA_VERSION,
200     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
201 )
202 public interface UpdateNoteAppFunction<
203     Parameters : UpdateNoteAppFunction.Parameters,
204     Response : UpdateNoteAppFunction.Response
205 > {
206     /**
207      * Updates an existing [AppFunctionNote] with the given parameters.
208      *
209      * For each field in [Parameters], if the corresponding [SetField] is not null, the note's field
210      * will be updated. The value within the [SetField] will be used to update the original value.
211      * Fields with a null [SetField] will not be updated.
212      *
213      * The implementing app should throw an appropriate subclass of
214      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
215      *
216      * @param appFunctionContext The AppFunction execution context.
217      * @param parameters The parameters defining the note to update and the new values.
218      * @return The response including the updated note.
219      */
updateNotenull220     public suspend fun updateNote(
221         appFunctionContext: AppFunctionContext,
222         parameters: Parameters,
223     ): Response
224 
225     /** The parameters for updating a note. */
226     public interface Parameters {
227         /**
228          * The ID of the note to update. It can be the ID generated by the application
229          * ([AppFunctionNote.id]) or an external UUID
230          * ([CreateNoteAppFunction.Parameters.externalUuid]) provided by the caller during note
231          * creation.
232          */
233         public val noteId: String
234 
235         /** The new title for the note, if it should be updated. */
236         public val title: SetField<String>?
237             get() = null
238 
239         /** The new content for the note, if it should be updated. */
240         public val content: SetField<String?>?
241             get() = null
242 
243         /** The new attachments for the note, if it should be updated. */
244         public val attachments: SetField<List<AppFunctionNote.Attachment>>?
245             get() = null
246     }
247 
248     /** The response including the updated note. */
249     public interface Response {
250         /** The updated note. */
251         public val updatedNote: AppFunctionNote
252     }
253 
254     public companion object {
255         /** Current schema version. */
256         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
257     }
258 }
259 
260 /** Deletes the notes with the given search criteria, defined by {@code parameters}. */
261 @AppFunctionSchemaDefinition(
262     name = "deleteNotes",
263     version = DeleteNotesAppFunction.SCHEMA_VERSION,
264     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
265 )
266 public interface DeleteNotesAppFunction<
267     Parameters : DeleteNotesAppFunction.Parameters,
268     Response : DeleteNotesAppFunction.Response
269 > {
270     /**
271      * Deletes the notes with the given search criteria and returns the IDs of the notes that were
272      * successfully deleted.
273      *
274      * The implementing app should throw an appropriate subclass of
275      * [androidx.appfunctions.AppFunctionException] in exceptional cases, except for failures in
276      * deleting individual noteId(s), which can be ignored when returning the
277      * [DeleteNotesAppFunction.Response].
278      *
279      * @param appFunctionContext The AppFunction execution context.
280      * @param parameters The parameters defining the criteria of notes to delete.
281      * @return A list of successfully deleted note IDs.
282      */
deleteNotesnull283     public suspend fun deleteNotes(
284         appFunctionContext: AppFunctionContext,
285         parameters: Parameters,
286     ): Response
287 
288     /** The parameters defining the criteria of notes to delete. */
289     public interface Parameters {
290         /** The IDs of the notes to delete. */
291         public val noteIds: List<String>
292     }
293 
294     /** The response of delete notes request. */
295     public interface Response {
296         /** A list of successfully deleted note IDs. */
297         public val noteIds: List<String>
298     }
299 
300     public companion object {
301         /** Current schema version. */
302         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
303     }
304 }
305 
306 /** Gets [AppFunctionNote]s with the given IDs. */
307 @AppFunctionSchemaDefinition(
308     name = "getNotes",
309     version = GetNotesAppFunction.SCHEMA_VERSION,
310     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
311 )
312 public interface GetNotesAppFunction<
313     Parameters : GetNotesAppFunction.Parameters,
314     Response : GetNotesAppFunction.Response
315 > {
316     /**
317      * Gets the notes with the given IDs. Returns only the notes found for the provided IDs. Does
318      * not throw if some IDs are not found.
319      *
320      * The implementing app should throw an appropriate subclass of
321      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
322      *
323      * @param appFunctionContext The AppFunction execution context.
324      * @param parameters The parameters defining which notes to get.
325      * @return The response including the list of notes that match the given IDs.
326      */
getNotesnull327     public suspend fun getNotes(
328         appFunctionContext: AppFunctionContext,
329         parameters: Parameters,
330     ): Response
331 
332     /** The parameters defining the IDs of the notes to get. */
333     public interface Parameters {
334         /** The IDs of the notes to get. */
335         public val noteIds: List<String>
336     }
337 
338     /** The response including the list of notes that match the given IDs. */
339     public interface Response {
340         /** The list of notes that match the given IDs. */
341         public val notes: List<AppFunctionNote>
342     }
343 
344     public companion object {
345         /** Current schema version. */
346         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
347     }
348 }
349 
350 /**
351  * Shows the note with the given parameters.
352  *
353  * @param Parameters The parameters of the note to show.
354  * @param Response The response including the [AppFunctionOpenable] to show the note.
355  */
356 @AppFunctionSchemaDefinition(
357     name = "showNote",
358     version = ShowNoteAppFunction.SCHEMA_VERSION,
359     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
360 )
361 public interface ShowNoteAppFunction<
362     Parameters : ShowNoteAppFunction.Parameters,
363     Response : ShowNoteAppFunction.Response
364 > {
365     /**
366      * Shows the note with the given parameters.
367      *
368      * The implementing app should throw an appropriate subclass of
369      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
370      *
371      * @param appFunctionContext The AppFunction execution context.
372      * @param parameters The params of the note to show.
373      * @return The response including the intent to show the note.
374      */
showNotenull375     public suspend fun showNote(
376         appFunctionContext: AppFunctionContext,
377         parameters: Parameters
378     ): Response
379 
380     /** The parameters for [showNote]. */
381     public interface Parameters {
382         /**
383          * The [AppFunctionNote.id] of the note to show.
384          *
385          * [androidx.appfunctions.AppFunctionElementNotFoundException] should be thrown when a note
386          * with the specified noteId doesn't exist.
387          */
388         public val noteId: String
389     }
390 
391     /** The [AppFunctionOpenable] response for [showNote]. */
392     public interface Response : AppFunctionOpenable
393 
394     public companion object {
395         /** Current schema version. */
396         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
397     }
398 }
399 
400 /** Finds notes groups with the given search criteria. */
401 @AppFunctionSchemaDefinition(
402     name = "findFolders",
403     version = FindNotesGroupsAppFunction.SCHEMA_VERSION,
404     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
405 )
406 public interface FindNotesGroupsAppFunction<
407     Parameters : FindNotesGroupsAppFunction.Parameters,
408     Response : FindNotesGroupsAppFunction.Response
409 > {
410     /**
411      * Finds notes groups with the given search criteria.
412      *
413      * The implementing app should throw an appropriate subclass of
414      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
415      *
416      * @param appFunctionContext The AppFunction execution context.
417      * @param parameters The parameters for finding groups.
418      * @return The response including the list of groups that match the parameters, or an empty list
419      *   if no match is found.
420      */
findNotesGroupsnull421     public suspend fun findNotesGroups(
422         appFunctionContext: AppFunctionContext,
423         parameters: Parameters,
424     ): Response
425 
426     /** The parameters for finding groups. */
427     public interface Parameters {
428         /**
429          * The query to search for groups. A null value means to query all groups.
430          *
431          * This parameter is analogous to the caller typing a query into a search box. The app
432          * providing the app function has full control over how the query is interpreted and
433          * processed; for example by name matching or semantic analysis.
434          */
435         public val query: String?
436             get() = null
437     }
438 
439     /** The response including the list of groups that match the parameters. */
440     public interface Response {
441         /** The list of groups that match the parameters, or an empty list if no match is found. */
442         public val groups: List<AppFunctionNotesGroup>
443     }
444 
445     public companion object {
446 
447         /** Current schema version. */
448         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
449     }
450 }
451 
452 /** Creates a group with the given parameters. */
453 @AppFunctionSchemaDefinition(
454     name = "createFolder",
455     version = CreateNotesGroupAppFunction.SCHEMA_VERSION,
456     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
457 )
458 public interface CreateNotesGroupAppFunction<
459     Parameters : CreateNotesGroupAppFunction.Parameters,
460     Response : CreateNotesGroupAppFunction.Response
461 > {
462     /**
463      * Creates a notes group with the given parameters.
464      *
465      * The implementing app should throw an appropriate subclass of
466      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
467      *
468      * @param appFunctionContext The AppFunction execution context.
469      * @param parameters The parameters for creating a group.
470      * @return The created group.
471      */
createNotesGroupnull472     public suspend fun createNotesGroup(
473         appFunctionContext: AppFunctionContext,
474         parameters: Parameters,
475     ): Response
476 
477     /** The parameters for creating a group. */
478     public interface Parameters {
479         /** The label of the group. */
480         public val label: String
481     }
482 
483     /** The response including the created group. */
484     public interface Response {
485         /** The created group. */
486         public val createdGroup: AppFunctionNotesGroup
487     }
488 
489     public companion object {
490 
491         /** Current schema version. */
492         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
493     }
494 }
495 
496 /** Deletes groups with the given parameters. */
497 @AppFunctionSchemaDefinition(
498     name = "deleteFolders",
499     version = DeleteNotesGroupsAppFunction.SCHEMA_VERSION,
500     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
501 )
502 public interface DeleteNotesGroupsAppFunction<
503     Parameters : DeleteNotesGroupsAppFunction.Parameters,
504     Response : DeleteNotesGroupsAppFunction.Response
505 > {
506     /**
507      * Deletes notes groups with the given parameters and returns the IDs of the groups that were
508      * successfully deleted.
509      *
510      * The implementing app should throw an appropriate subclass of
511      * [androidx.appfunctions.AppFunctionException] in exceptional cases, except for failures in
512      * deleting individual groupId(s), which can be ignored when returning the
513      * [DeleteNotesGroupsAppFunction.Response].
514      *
515      * @param appFunctionContext The AppFunction execution context.
516      * @param parameters The parameters of the groups to delete.
517      * @return The response including the list of successfully deleted group IDs.
518      */
deleteNotesGroupsnull519     public suspend fun deleteNotesGroups(
520         appFunctionContext: AppFunctionContext,
521         parameters: Parameters,
522     ): Response
523 
524     /** The parameters of the groups to delete. */
525     public interface Parameters {
526         /** The [AppFunctionNotesGroup.id] of the groups to delete. */
527         public val groupIds: List<String>
528     }
529 
530     /** The response including the list of successfully deleted group IDs. */
531     public interface Response {
532         /** The IDs of the deleted groups. */
533         public val groupIds: List<String>
534     }
535 
536     public companion object {
537 
538         /** Current schema version. */
539         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
540     }
541 }
542 
543 /** Updates an existing [AppFunctionNotesGroup]. */
544 @AppFunctionSchemaDefinition(
545     name = "updateFolder",
546     version = UpdateNotesGroupAppFunction.SCHEMA_VERSION,
547     category = APP_FUNCTION_SCHEMA_CATEGORY_NOTES
548 )
549 public interface UpdateNotesGroupAppFunction<
550     Parameters : UpdateNotesGroupAppFunction.Parameters,
551     Response : UpdateNotesGroupAppFunction.Response
552 > {
553     /**
554      * Updates an existing [AppFunctionNotesGroup] with the given parameters.
555      *
556      * For each field in [Parameters], if the corresponding [SetField] is not null, the note group's
557      * field will be updated. The value within the [SetField] will be used to update the original
558      * value. Fields with a null [SetField] will not be updated.
559      *
560      * The implementing app should throw an appropriate subclass of
561      * [androidx.appfunctions.AppFunctionException] in exceptional cases.
562      *
563      * @param appFunctionContext The AppFunction execution context.
564      * @param parameters The parameters defining the notes group to update and the new values.
565      * @return The response including the updated notes group.
566      */
updateNotesGroupnull567     public suspend fun updateNotesGroup(
568         appFunctionContext: AppFunctionContext,
569         parameters: Parameters,
570     ): Response
571 
572     /** The parameters for updating a notes group. */
573     public interface Parameters {
574         /**
575          * The ID of the notes group to update.
576          *
577          * [androidx.appfunctions.AppFunctionElementNotFoundException] should be thrown when a group
578          * with the specified notesGroupId doesn't exist.
579          */
580         public val notesGroupId: String
581 
582         /** The new label for the group, if it should be updated. */
583         public val label: SetField<String>?
584             get() = null
585     }
586 
587     /** The response including the updated notes group. */
588     public interface Response {
589         /** The updated notes group. */
590         public val updatedNotesGroup: AppFunctionNotesGroup
591     }
592 
593     public companion object {
594         /** Current schema version. */
595         @RestrictTo(LIBRARY_GROUP) internal const val SCHEMA_VERSION: Int = 2
596     }
597 }
598 
599 /** A note entity. */
600 public interface AppFunctionNote {
601     /** The ID of the note. */
602     public val id: String
603 
604     /** The title of the note. */
605     public val title: String
606 
607     /** The content of the note. */
608     public val content: String?
609         get() = null
610 
611     /** The attachments of the note. */
612     public val attachments: List<Attachment>
613         get() = emptyList()
614 
615     /** An attached file. */
616     public interface Attachment {
617         /**
618          * The URI of the attached file.
619          *
620          * When providing an [Uri] to another app, that app must be granted URI permission using
621          * [android.content.Context.grantUriPermission] to the receiving app.
622          *
623          * The providing app should also consider revoking the URI permission by using
624          * [android.content.Context.revokeUriPermission] after a certain time period.
625          */
626         public val uri: Uri
627 
628         /** The display name of the attached file. */
629         public val displayName: String
630 
631         /** The MIME type of the attached file. Format defined in RFC 6838. */
632         public val mimeType: String?
633             get() = null
634     }
635 
636     /** The ID of the group the note is in, if any else `null`. */
637     public val groupId: String?
638         get() = null
639 }
640 
641 /** Represents a group used to organize notes. */
642 public interface AppFunctionNotesGroup {
643     /** The ID of the group. */
644     public val id: String
645     /** The label of the group. */
646     public val label: String
647 }
648