1
+ package com.zulipmobile
2
+
3
+ import android.util.Base64
4
+ import com.facebook.react.bridge.Promise
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
7
+ import com.facebook.react.bridge.ReactMethod
8
+ import java.io.ByteArrayOutputStream
9
+ import java.io.IOException
10
+ import java.io.UnsupportedEncodingException
11
+ import java.util.HashMap
12
+ import java.util.zip.DataFormatException
13
+ import java.util.zip.Deflater
14
+ import java.util.zip.Inflater
15
+
16
+ // TODO: Write unit tests; see
17
+ // https://github.com/zulip/zulip-mobile/blob/main/docs/howto/testing.md#unit-tests-android.
18
+ internal class TextCompressionModule (reactContext : ReactApplicationContext ? ) :
19
+ ReactContextBaseJavaModule (reactContext) {
20
+ override fun getName (): String {
21
+ return " TextCompressionModule"
22
+ }
23
+
24
+ // TODO: Experiment what value gives the best performance.
25
+ private val bufferSize = 8192
26
+
27
+ private val header = " z|zlib base64|"
28
+ override fun getConstants (): Map <String , Any >? {
29
+ val constants: MutableMap <String , Any > = HashMap ()
30
+ constants[" header" ] = header
31
+ return constants
32
+ }
33
+
34
+ @ReactMethod
35
+ fun compress (input : String , promise : Promise ) {
36
+ try {
37
+ val outputStream = ByteArrayOutputStream ()
38
+ val deflater = Deflater ()
39
+ deflater.setInput(input.toByteArray(charset(" UTF-8" )))
40
+ deflater.finish()
41
+ val buffer = ByteArray (bufferSize)
42
+ while (! deflater.finished()) {
43
+ val byteCount = deflater.deflate(buffer)
44
+ outputStream.write(buffer, 0 , byteCount)
45
+ }
46
+ deflater.end()
47
+ outputStream.close()
48
+ // The RN bridge currently doesn't support sending byte strings, so we
49
+ // have to encode the compressed output as a `String`. To avoid any
50
+ // trouble, we use base64 to keep things inside ASCII.
51
+ //
52
+ // Ultimately our ASCII data seems to end up going to SQLite with size
53
+ // no more than about 1 byte/char (presumably the string gets encoded
54
+ // as UTF-8 and it's exactly 1 byte/char), so this is pretty OK.
55
+ promise.resolve(header + Base64 .encodeToString(outputStream.toByteArray(),
56
+ Base64 .DEFAULT ))
57
+ } catch (e: UnsupportedEncodingException ) {
58
+ promise.reject(" UNSUPPORTED_ENCODING_EXCEPTION" , e)
59
+ } catch (e: IOException ) {
60
+ promise.reject(" IO_EXCEPTION" , e)
61
+ }
62
+ }
63
+
64
+ @ReactMethod
65
+ fun decompress (input : String , promise : Promise ) {
66
+ try {
67
+ val inflater = Inflater ()
68
+ val inputBytes = input.toByteArray(charset(" ISO-8859-1" ))
69
+ inflater.setInput(Base64 .decode(inputBytes,
70
+ header.length,
71
+ inputBytes.size - header.length,
72
+ Base64 .DEFAULT ))
73
+ val outputStream = ByteArrayOutputStream ()
74
+ val buffer = ByteArray (bufferSize)
75
+ while (inflater.remaining != 0 ) {
76
+ val byteCount = inflater.inflate(buffer)
77
+ outputStream.write(buffer, 0 , byteCount)
78
+ }
79
+ inflater.end()
80
+ outputStream.close()
81
+ promise.resolve(outputStream.toString(" UTF-8" ))
82
+ } catch (e: UnsupportedEncodingException ) {
83
+ promise.reject(" UNSUPPORTED_ENCODING_EXCEPTION" , e)
84
+ } catch (e: IOException ) {
85
+ promise.reject(" IO_EXCEPTION" , e)
86
+ } catch (e: DataFormatException ) {
87
+ promise.reject(" DATA_FORMAT_EXCEPTION" , e)
88
+ }
89
+ }
90
+ }
0 commit comments