1package com.cheogram.android;
2
3import android.app.Notification;
4import android.app.NotificationManager;
5import android.app.Service;
6import android.content.Context;
7import android.content.SharedPreferences;
8import android.content.Intent;
9import android.database.Cursor;
10import android.media.MediaScannerConnection;
11import android.net.Uri;
12import android.os.Environment;
13import android.os.IBinder;
14import android.provider.DocumentsContract;
15import android.preference.PreferenceManager;
16import android.provider.MediaStore;
17import android.util.Log;
18
19import androidx.core.app.NotificationCompat;
20
21import com.google.common.io.ByteStreams;
22
23import io.ipfs.cid.Cid;
24
25import java.io.File;
26import java.io.FileOutputStream;
27import java.io.OutputStream;
28import java.io.OutputStreamWriter;
29import java.util.concurrent.atomic.AtomicBoolean;
30
31import org.json.JSONArray;
32import org.json.JSONObject;
33
34import okhttp3.OkHttpClient;
35import okhttp3.Request;
36import okhttp3.Response;
37
38import eu.siacs.conversations.Config;
39import eu.siacs.conversations.R;
40import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
41import eu.siacs.conversations.entities.Account;
42import eu.siacs.conversations.entities.Conversation;
43import eu.siacs.conversations.entities.Message;
44import eu.siacs.conversations.persistance.DatabaseBackend;
45import eu.siacs.conversations.persistance.FileBackend;
46import eu.siacs.conversations.utils.Compatibility;
47import eu.siacs.conversations.utils.FileUtils;
48import eu.siacs.conversations.utils.MimeUtils;
49
50public class DownloadDefaultStickers extends Service {
51
52 private static final int NOTIFICATION_ID = 20;
53 private static final AtomicBoolean RUNNING = new AtomicBoolean(false);
54 private DatabaseBackend mDatabaseBackend;
55 private NotificationManager notificationManager;
56 private File mStickerDir;
57 private OkHttpClient http = new OkHttpClient();
58
59 @Override
60 public void onCreate() {
61 mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
62 notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
63 mStickerDir = stickerDir();
64 }
65
66 @Override
67 public int onStartCommand(Intent intent, int flags, int startId) {
68 if (RUNNING.compareAndSet(false, true)) {
69 new Thread(() -> {
70 try {
71 download();
72 } catch (final Exception e) {
73 Log.d(Config.LOGTAG, "unable to download stickers", e);
74 }
75 stopForeground(true);
76 RUNNING.set(false);
77 stopSelf();
78 }).start();
79 return START_STICKY;
80 } else {
81 Log.d(Config.LOGTAG, "DownloadDefaultStickers. ignoring start command because already running");
82 }
83 return START_NOT_STICKY;
84 }
85
86 private void oneSticker(JSONObject sticker) throws Exception {
87 Response r = http.newCall(new Request.Builder().url(sticker.getString("url")).build()).execute();
88 File file = new File(mStickerDir.getAbsolutePath() + "/" + sticker.getString("pack") + "/" + sticker.getString("name") + "." + MimeUtils.guessExtensionFromMimeType(r.headers().get("content-type")));
89 file.getParentFile().mkdirs();
90 OutputStream os = new FileOutputStream(file);
91 ByteStreams.copy(r.body().byteStream(), os);
92 os.close();
93
94 JSONArray cids = sticker.getJSONArray("cids");
95 for (int i = 0; i < cids.length(); i++) {
96 Cid cid = Cid.decode(cids.getString(i));
97 mDatabaseBackend.saveCid(cid, file, sticker.getString("url"));
98 }
99
100 MediaScannerConnection.scanFile(
101 getBaseContext(),
102 new String[] { file.getAbsolutePath() },
103 null,
104 new MediaScannerConnection.MediaScannerConnectionClient() {
105 @Override
106 public void onMediaScannerConnected() {}
107
108 @Override
109 public void onScanCompleted(String path, Uri uri) {}
110 }
111 );
112
113 try {
114 File copyright = new File(mStickerDir.getAbsolutePath() + "/" + sticker.getString("pack") + "/copyright.txt");
115 OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(copyright, true), "utf-8");
116 w.write(sticker.getString("pack"));
117 w.write('/');
118 w.write(sticker.getString("name"));
119 w.write(": ");
120 w.write(sticker.getString("copyright"));
121 w.write('\n');
122 w.close();
123 } catch (final Exception e) { }
124 }
125
126 private void download() throws Exception {
127 NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
128 mBuilder.setContentTitle("Downloading Default Stickers")
129 .setSmallIcon(R.drawable.ic_archive_white_24dp)
130 .setProgress(1, 0, false);
131 startForeground(NOTIFICATION_ID, mBuilder.build());
132
133 Response r = http.newCall(new Request.Builder().url("https://stickers.cheogram.com/index.json").build()).execute();
134 JSONArray stickers = new JSONArray(r.body().string());
135
136 final Progress progress = new Progress(mBuilder, 1, 0);
137 for (int i = 0; i < stickers.length(); i++) {
138 oneSticker(stickers.getJSONObject(i));
139
140 final int percentage = i * 100 / stickers.length();
141 notificationManager.notify(NOTIFICATION_ID, progress.build(percentage));
142 }
143 }
144
145 private File stickerDir() {
146 SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
147 final String dir = p.getString("sticker_directory", "Stickers");
148 if (dir.startsWith("content://")) {
149 Uri uri = Uri.parse(dir);
150 uri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri));
151 return new File(FileUtils.getPath(getBaseContext(), uri));
152 } else {
153 return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/" + dir);
154 }
155 }
156
157 @Override
158 public IBinder onBind(Intent intent) {
159 return null;
160 }
161
162 private static class Progress {
163 private final NotificationCompat.Builder builder;
164 private final int max;
165 private final int count;
166
167 private Progress(NotificationCompat.Builder builder, int max, int count) {
168 this.builder = builder;
169 this.max = max;
170 this.count = count;
171 }
172
173 private Notification build(int percentage) {
174 builder.setProgress(max * 100, count * 100 + percentage, false);
175 return builder.build();
176 }
177 }
178}