chiark / gitweb /
keys.scala, etc.: Make merging public keys have a progress bar.
[tripe-android] / progress.scala
1 /* -*-scala-*-
2  *
3  * Reporting progress for long-running jobs
4  *
5  * (c) 2018 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the Trivial IP Encryption (TrIPE) Android app.
11  *
12  * TrIPE is free software: you can redistribute it and/or modify it under
13  * the terms of the GNU General Public License as published by the Free
14  * Software Foundation; either version 3 of the License, or (at your
15  * option) any later version.
16  *
17  * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20  * for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with TrIPE.  If not, see <https://www.gnu.org/licenses/>.
24  */
25
26 package uk.org.distorted.tripe; package object progress {
27
28 /*----- Imports -----------------------------------------------------------*/
29
30 import scala.collection.mutable.{Publisher, Subscriber};
31
32 import java.lang.System.currentTimeMillis;
33
34 /*----- Progress displays -------------------------------------------------*/
35
36 trait Model {
37   protected val t0 = currentTimeMillis;
38
39   def what: String;
40   def max: Long;
41
42   def eta(cur: Long): Double = {
43     /* Report the estimated time remaining in seconds, or -1 if no idea.
44      *
45      * The model here is very stupid.  Weird jobs should override this and
46      * do something more sensible.
47      */
48
49     val max = this.max;
50     val delta = currentTimeMillis - t0
51     if (max < 0 || cur <= 0) -1 else delta*(max - cur)/cur.toDouble
52   }
53
54   protected def fmt1(n: Long): String = n.toString;
55
56   def format(cur: Long): String = {
57     val max = this.max;
58     val fc = fmt1(cur);
59     if (max >= 0) { val fm = fmt1(max); s"%${fm.length}s/%s".format(fc, fm) }
60     else if (cur > 0) fc
61     else ""
62   }
63 }
64
65 class SimpleModel(val what: String, val max: Long) extends Model;
66
67 class DetailedModel(what: String, max: Long) extends SimpleModel(what, max) {
68   var detail: String = null;
69   override def format(cur: Long): String = {
70     val sb = new StringBuilder;
71     sb ++= super.format(cur);
72     if (detail != null) { sb += ' '; sb ++= detail; }
73     sb.result
74   }
75 }
76
77 private val UDATA = Seq("kB", "MB", "GB", "TB", "PB", "EB");
78
79 trait DataModel extends Model {
80   override def fmt1(n: Long): String = {
81     val (x, u) = ((n.toDouble, "B ") /: UDATA) { (xu, n) => (xu, n) match {
82       case ((x, u), name) if x >= 1024.0 => (x/1024.0, name)
83       case (xu, _) => xu
84     } }
85     f"$x%6.1f$u%s"
86   }
87 }
88
89 trait BaseReporter {
90   def done();
91   def failed(e: Exception);
92 }
93
94 trait JobReporter extends BaseReporter {
95   def step(cur: Long);
96   def change(model: Model, cur: Long);
97 }
98
99 trait OperationReporter extends BaseReporter {
100   def step(detail: String);
101 }
102
103 def withReporter[T, P <: BaseReporter](rep: P, body: P => T): T = {
104   val ret = try { body(rep) }
105   catch { case e: Exception => rep.failed(e); throw e; }
106   rep.done();
107   ret
108 }
109
110 trait Eyecandy {
111   def note(msg: String);
112   def clear();
113   def commit();
114   def record(msg: String) { note(msg); commit(); }
115   def done();
116   def cancelled() { failed("cancelled"); }
117   def failed(msg: String);
118
119   def beginJob(model: Model): JobReporter
120     // = new JobReporter(model);
121
122   def beginOperation(what: String): OperationReporter
123     // = new OperationReporter(what);
124
125   def job[T](model: Model)(body: JobReporter => T): T =
126     withReporter(beginJob(model), body);
127
128   def operation[T](what: String)(body: OperationReporter => T): T =
129     withReporter(beginOperation(what), body);
130 }
131
132 /*----- That's all, folks -------------------------------------------------*/
133
134 }