Recently Written · git

sbm-android

sbm bookmarks for Android: fuzzy search, share to add, sync with bm and the browser add-on

git clone https://github.com/equwal/sbm-android

Log | Files | Refs


app/src/main/java/com/equwal/sbm/Store.kt (2813 bytes)

1 package com.equwal.sbm
2 
3 import android.content.Context
4 import android.content.Intent
5 import android.net.Uri
6 import java.io.File
7 import java.io.FileNotFoundException
8 import java.io.IOException
9 
10 /**
11  * The bookmark file. It is the file that the user chose, or else, after a
12  * sign-in to sync, a file of the app. The app keeps only the address of the
13  * file and reads the file again each time, so a file that Syncthing or git
14  * changes is always current.
15  */
16 object Store {
17     private const val PREFS = "sbm"
18     private const val KEY = "file"
19 
20     /** The file that the user chose, or null. */
21     fun uri(ctx: Context): Uri? =
22         ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getString(KEY, null)?.let(Uri::parse)
23 
24     /** The file to use: the chosen file, else the file of the app when sync is on. */
25     fun file(ctx: Context): Uri? =
26         uri(ctx) ?: if (Sync.signedIn(ctx)) Uri.fromFile(File(ctx.filesDir, "bookmarks")) else null
27 
28     fun choose(ctx: Context, uri: Uri) {
29         ctx.contentResolver.takePersistableUriPermission(
30             uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
31         ctx.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit().putString(KEY, uri.toString()).apply()
32     }
33 
34     fun read(ctx: Context): String {
35         val uri = file(ctx) ?: return ""
36         return try {
37             ctx.contentResolver.openInputStream(uri)?.use { it.readBytes().toString(Charsets.UTF_8) } ?: ""
38         } catch (e: FileNotFoundException) {
39             if (uri.scheme == "file") "" else throw e // the file of the app comes with the first sync
40         }
41     }
42 
43     /** Write the whole file. */
44     fun write(ctx: Context, text: String) {
45         val uri = file(ctx) ?: throw IOException("no bookmark file")
46         ctx.contentResolver.openOutputStream(uri, "wt")?.use { it.write(text.toByteArray(Charsets.UTF_8)) }
47             ?: throw IOException("cannot write the bookmark file")
48     }
49 
50     /**
51      * Add [b] at the end of the file. When the file already has the same page,
52      * add nothing and return that bookmark.
53      */
54     fun add(ctx: Context, b: Bookmark): Bookmark? {
55         val uri = file(ctx) ?: throw IOException("no bookmark file")
56         val text = read(ctx)
57         Tsv.find(Tsv.parse(text), b.url)?.let { return it }
58         val extra = Tsv.appendix(text, Tsv.format(b))
59         val appended = try {
60             ctx.contentResolver.openOutputStream(uri, "wa")?.use { it.write(extra.toByteArray(Charsets.UTF_8)) } != null
61         } catch (e: Exception) {
62             false
63         }
64         // Some document providers cannot append, and some write over the file
65         // instead. Then write the whole file.
66         if (!appended || read(ctx) != text + extra) write(ctx, text + extra)
67         return null
68     }
69 }