ref: 2b948f4e23bba5facf231aae48cb1185adacd702
app/src/main/java/ml/adamsprogs/bimba/TimetableDownloader.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
package ml.adamsprogs.bimba import android.app.IntentService import android.content.Context import android.content.Intent import android.support.v4.app.NotificationCompat import java.net.HttpURLConnection import java.net.URL import org.tukaani.xz.XZInputStream import java.io.* import java.security.MessageDigest import kotlin.experimental.and import android.util.Log import android.app.NotificationManager class TimetableDownloader : IntentService("TimetableDownloader") { lateinit var notificationManager: NotificationManager var size: Int = 0 override fun onHandleIntent(intent: Intent?) { if (intent != null) { notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val prefs = this.getSharedPreferences("ml.adamsprogs.bimba.prefs", Context.MODE_PRIVATE)!! if (!isNetworkAvailable(this)) { sendResult("no connectivity") return } val metadataUrl = URL("https://adamsprogs.ml/w/_media/programmes/bimba/timetable.db.meta") var httpCon = metadataUrl.openConnection() as HttpURLConnection if (httpCon.responseCode != HttpURLConnection.HTTP_OK) throw Exception("Failed to connect") Log.i("Downloader", "Got metadata") val reader = BufferedReader(InputStreamReader(httpCon.inputStream)) val lastModified = reader.readLine() val checksum = reader.readLine() size = Integer.parseInt(reader.readLine()) / 1024 val dbVersion = reader.readLine() if (Integer.parseInt(dbVersion.split(".")[0]) > 1) { //todo version to const sendResult("version mismatch") return } val dbFilename = reader.readLine() val currentLastModified = prefs.getString("timetableLastModified", "19791012") if (lastModified <= currentLastModified && !intent.getBooleanExtra("force", false)) { sendResult("up-to-date") return } Log.i("Downloader", "timetable is newer ($lastModified > $currentLastModified)") notify(0) val xzDbUrl = URL("https://adamsprogs.ml/w/_media/programmes/bimba/$dbFilename") httpCon = xzDbUrl.openConnection() as HttpURLConnection if (httpCon.responseCode != HttpURLConnection.HTTP_OK) throw Exception("Failed to connect") Log.i("Downloader", "connected to db") val xzIn = XZInputStream(httpCon.inputStream) val file = File(this.filesDir, "new_timetable.db") if (copyInputStreamToFile(xzIn, file, checksum)) { Log.i("Downloader", "downloaded") val oldFile = File(this.filesDir, "timetable.db") oldFile.delete() file.renameTo(oldFile) val prefsEditor = prefs.edit() prefsEditor.putString("timetableLastModified", lastModified) prefsEditor.apply() sendResult("downloaded") } else { Log.i("Downloader", "downloaded but is wrong") sendResult("validity failed") } cancelNotification() } } private fun sendResult(result: String) { val broadcastIntent = Intent() broadcastIntent.action = "ml.adamsprogs.bimba.timetableDownloaded" broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT) broadcastIntent.putExtra("result", result) sendBroadcast(broadcastIntent) } private fun notify(progress: Int) { val builder = NotificationCompat.Builder(this) .setSmallIcon(R.drawable.ic_download) .setContentTitle(getString(R.string.timetable_downloading)) .setContentText("$progress KiB/$size KiB") .setCategory(NotificationCompat.CATEGORY_PROGRESS) .setOngoing(true) .setProgress(size, progress, false) notificationManager.notify(42, builder.build()) } private fun cancelNotification() { notificationManager.cancel(42) } private fun copyInputStreamToFile(ins: InputStream, file: File, checksum: String): Boolean { val md = MessageDigest.getInstance("SHA-512") var hex = "" try { val out = FileOutputStream(file) val buf = ByteArray(5 * 1024) var lenSum = 0.0f var len = 42 while (len > 0) { len = ins.read(buf) if (len <= 0) break md.update(buf, 0, len) out.write(buf, 0, len) lenSum += len.toFloat() / 1024.0f notify(lenSum.toInt()) } out.close() } catch (e: Exception) { e.printStackTrace() } finally { ins.close() val digest = md.digest() for (i in 0..digest.size - 1) { hex += Integer.toString((digest[i] and 0xff.toByte()) + 0x100, 16).padStart(3, '0').substring(1) } Log.i("Downloader", "checksum is $checksum, and hex is $hex") return checksum == hex } } } |