DownloadDefaultStickers.java

  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.HashSet;
 30import java.util.concurrent.atomic.AtomicBoolean;
 31
 32import org.json.JSONArray;
 33import org.json.JSONObject;
 34
 35import okhttp3.OkHttpClient;
 36import okhttp3.Request;
 37import okhttp3.Response;
 38
 39import eu.siacs.conversations.Config;
 40import eu.siacs.conversations.R;
 41import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
 42import eu.siacs.conversations.entities.Account;
 43import eu.siacs.conversations.entities.Conversation;
 44import eu.siacs.conversations.entities.Message;
 45import eu.siacs.conversations.http.HttpConnectionManager;
 46import eu.siacs.conversations.persistance.DatabaseBackend;
 47import eu.siacs.conversations.persistance.FileBackend;
 48import eu.siacs.conversations.utils.Compatibility;
 49import eu.siacs.conversations.utils.FileUtils;
 50import eu.siacs.conversations.utils.MimeUtils;
 51
 52public class DownloadDefaultStickers extends Service {
 53
 54	private static final int NOTIFICATION_ID = 20;
 55	private static final AtomicBoolean RUNNING = new AtomicBoolean(false);
 56	private DatabaseBackend mDatabaseBackend;
 57	private NotificationManager notificationManager;
 58	private File mStickerDir;
 59	private OkHttpClient http = null;
 60	private HashSet<Uri> pendingPacks = new HashSet<Uri>();
 61
 62	@Override
 63	public void onCreate() {
 64		mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
 65		notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 66		mStickerDir = stickerDir();
 67	}
 68
 69	@Override
 70	public int onStartCommand(Intent intent, int flags, int startId) {
 71		if (http == null) {
 72			http = HttpConnectionManager.newBuilder(intent.getBooleanExtra("tor", getResources().getBoolean(R.bool.use_tor))).build();
 73		}
 74		synchronized(pendingPacks) {
 75			pendingPacks.add(intent.getData() == null ? Uri.parse("https://stickers.cheogram.com/index.json") : intent.getData());
 76		}
 77		if (RUNNING.compareAndSet(false, true)) {
 78			new Thread(() -> {
 79				try {
 80					download();
 81				} catch (final Exception e) {
 82					Log.d(Config.LOGTAG, "unable to download stickers", e);
 83				}
 84				stopForeground(true);
 85				RUNNING.set(false);
 86				stopSelf();
 87			}).start();
 88			return START_STICKY;
 89		} else {
 90			Log.d(Config.LOGTAG, "DownloadDefaultStickers. ignoring start command because already running");
 91		}
 92		return START_NOT_STICKY;
 93	}
 94
 95	private void oneSticker(JSONObject sticker) throws Exception {
 96		Response r = http.newCall(new Request.Builder().url(sticker.getString("url")).build()).execute();
 97		File file = null;
 98		try {
 99			file = new File(mStickerDir.getAbsolutePath() + "/" + sticker.getString("pack") + "/" + sticker.getString("name") + "." + MimeUtils.guessExtensionFromMimeType(r.headers().get("content-type")));
100			file.getParentFile().mkdirs();
101			OutputStream os = new FileOutputStream(file);
102			ByteStreams.copy(r.body().byteStream(), os);
103			os.close();
104		} catch (final Exception e) {
105			file = null;
106			e.printStackTrace();
107		}
108
109		JSONArray cids = sticker.getJSONArray("cids");
110		for (int i = 0; i < cids.length(); i++) {
111			Cid cid = Cid.decode(cids.getString(i));
112			mDatabaseBackend.saveCid(cid, file, sticker.getString("url"));
113		}
114
115		MediaScannerConnection.scanFile(
116			getBaseContext(),
117			new String[] { file.getAbsolutePath() },
118			null,
119			new MediaScannerConnection.MediaScannerConnectionClient() {
120				@Override
121				public void onMediaScannerConnected() {}
122
123				@Override
124				public void onScanCompleted(String path, Uri uri) {}
125			}
126		);
127
128		try {
129			File copyright = new File(mStickerDir.getAbsolutePath() + "/" + sticker.getString("pack") + "/copyright.txt");
130			OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(copyright, true), "utf-8");
131			w.write(sticker.getString("pack"));
132			w.write('/');
133			w.write(sticker.getString("name"));
134			w.write(": ");
135			w.write(sticker.getString("copyright"));
136			w.write('\n');
137			w.close();
138		} catch (final Exception e) { }
139	}
140
141	private void download() throws Exception {
142		Uri jsonUri;
143		synchronized(pendingPacks) {
144			if (pendingPacks.iterator().hasNext()) {
145				jsonUri = pendingPacks.iterator().next();
146			} else {
147				return;
148			}
149		}
150
151		NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
152		mBuilder.setContentTitle("Downloading Stickers")
153				.setSmallIcon(R.drawable.ic_archive_white_24dp)
154				.setProgress(1, 0, false);
155		startForeground(NOTIFICATION_ID, mBuilder.build());
156
157		Response r = http.newCall(new Request.Builder().url(jsonUri.toString()).build()).execute();
158		JSONArray stickers = new JSONArray(r.body().string());
159
160		final Progress progress = new Progress(mBuilder, 1, 0);
161		for (int i = 0; i < stickers.length(); i++) {
162			try {
163				oneSticker(stickers.getJSONObject(i));
164			} catch (final Exception e) {
165				e.printStackTrace();
166			}
167
168			final int percentage = i * 100 / stickers.length();
169			notificationManager.notify(NOTIFICATION_ID, progress.build(percentage));
170		}
171
172		synchronized(pendingPacks) {
173			pendingPacks.remove(jsonUri);
174		}
175		download();
176	}
177
178	private File stickerDir() {
179		SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
180		final String dir = p.getString("sticker_directory", "Stickers");
181		if (dir.startsWith("content://")) {
182			Uri uri = Uri.parse(dir);
183			uri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri));
184			return new File(FileUtils.getPath(getBaseContext(), uri));
185		} else {
186			return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/" + dir);
187		}
188	}
189
190	@Override
191	public IBinder onBind(Intent intent) {
192		return null;
193	}
194
195	private static class Progress {
196		private final NotificationCompat.Builder builder;
197		private final int max;
198		private final int count;
199
200		private Progress(NotificationCompat.Builder builder, int max, int count) {
201			this.builder = builder;
202			this.max = max;
203			this.count = count;
204		}
205
206		private Notification build(int percentage) {
207			builder.setProgress(max * 100, count * 100 + percentage, false);
208			return builder.build();
209		}
210	}
211}