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 }