Recently Written · git

ink-update

Ink Update: tells you when Rebind, Ink Recents or Ink Dim has a new version. F-Droid, then Google Play, then GitHub.

git clone https://github.com/equwal/ink-update

Log | Files | Refs


app/src/main/java/dev/equwal/inkupdate/Net.kt (2551 bytes)

1 // SPDX-License-Identifier: GPL-3.0-or-later
2 // Copyright (c) 2026 equwal
3 package dev.equwal.inkupdate
4 
5 import java.io.ByteArrayOutputStream
6 import java.io.InputStream
7 import java.net.HttpURLConnection
8 import java.net.URL
9 
10 /**
11  * One HTTP GET, with java.net and nothing else.
12  *
13  * The app sends no identifier and no cookie. It sends the package name or the
14  * repository name in the path, and a User-Agent that names the app. Call this
15  * from a background thread only.
16  */
17 object Net {
18 
19     const val AGENT = "InkUpdate/" + BuildConfig.VERSION_NAME +
20         " (+https://github.com/equwal/ink-update)"
21 
22     private const val CONNECT_MS = 10_000
23     private const val READ_MS = 15_000
24 
25     /** The most bytes to read from one answer. An answer is a few kilobytes. */
26     private const val MAX_BYTES = 512 * 1024
27 
28     /**
29      * @param code the HTTP status, or 0 when the request did not finish.
30      * @param body the text of the answer, or null.
31      */
32     data class Answer(val code: Int, val body: String?)
33 
34     fun get(url: String, accept: String? = null): Answer {
35         var c: HttpURLConnection? = null
36         return try {
37             c = (URL(url).openConnection() as HttpURLConnection).apply {
38                 requestMethod = "GET"
39                 connectTimeout = CONNECT_MS
40                 readTimeout = READ_MS
41                 instanceFollowRedirects = true
42                 setRequestProperty("User-Agent", AGENT)
43                 if (accept != null) setRequestProperty("Accept", accept)
44             }
45             val code = c.responseCode
46             // F-Droid answers 404 with a body that says NOT_FOUND, so the error
47             // stream matters as much as the input stream.
48             val stream: InputStream? = if (code in 200..299) c.inputStream else c.errorStream
49             Answer(code, stream?.let { read(it) })
50         } catch (e: Exception) {
51             Answer(0, null)
52         } finally {
53             c?.disconnect()
54         }
55     }
56 
57     // The bytes go into one buffer and are made into text once. A character of
58     // more than one byte can lie across two reads.
59     private fun read(stream: InputStream): String? = try {
60         stream.use { s ->
61             val out = ByteArrayOutputStream()
62             val buf = ByteArray(8 * 1024)
63             while (out.size() < MAX_BYTES) {
64                 val n = s.read(buf)
65                 if (n <= 0) break
66                 out.write(buf, 0, n)
67             }
68             String(out.toByteArray(), Charsets.UTF_8)
69         }
70     } catch (e: Exception) {
71         null
72     }
73 }