M src/cheogram/AndroidManifest.xml => src/cheogram/AndroidManifest.xml +1 -0
@@ 7,6 7,7 @@
<application tools:ignore="GoogleAppIndexingWarning">
<!-- INSERT -->
+ <service android:name="com.cheogram.android.DownloadDefaultStickers" />
<service android:name="com.cheogram.android.ConnectionService"
android:label="Cheogram"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
A src/cheogram/java/com/cheogram/android/DownloadDefaultStickers.java => src/cheogram/java/com/cheogram/android/DownloadDefaultStickers.java +164 -0
@@ 0,0 1,164 @@
+package com.cheogram.android;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.IBinder;
+import android.provider.DocumentsContract;
+import android.preference.PreferenceManager;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.core.app.NotificationCompat;
+
+import com.google.common.io.ByteStreams;
+
+import io.ipfs.cid.Cid;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.R;
+import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.persistance.DatabaseBackend;
+import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.utils.Compatibility;
+import eu.siacs.conversations.utils.FileUtils;
+import eu.siacs.conversations.utils.MimeUtils;
+
+public class DownloadDefaultStickers extends Service {
+
+ private static final int NOTIFICATION_ID = 20;
+ private static final AtomicBoolean RUNNING = new AtomicBoolean(false);
+ private DatabaseBackend mDatabaseBackend;
+ private NotificationManager notificationManager;
+ private File mStickerDir;
+ private OkHttpClient http = new OkHttpClient();
+
+ @Override
+ public void onCreate() {
+ mDatabaseBackend = DatabaseBackend.getInstance(getBaseContext());
+ notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mStickerDir = stickerDir();
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (RUNNING.compareAndSet(false, true)) {
+ new Thread(() -> {
+ try {
+ download();
+ } catch (final Exception e) {
+ Log.d(Config.LOGTAG, "unable to download stickers", e);
+ }
+ stopForeground(true);
+ RUNNING.set(false);
+ stopSelf();
+ }).start();
+ return START_STICKY;
+ } else {
+ Log.d(Config.LOGTAG, "DownloadDefaultStickers. ignoring start command because already running");
+ }
+ return START_NOT_STICKY;
+ }
+
+ private void oneSticker(JSONObject sticker) throws Exception {
+ Response r = http.newCall(new Request.Builder().url(sticker.getString("url")).build()).execute();
+ File file = new File(mStickerDir.getAbsolutePath() + "/" + sticker.getString("pack") + "/" + sticker.getString("name") + "." + MimeUtils.guessExtensionFromMimeType(r.headers().get("content-type")));
+ file.getParentFile().mkdirs();
+ OutputStream os = new FileOutputStream(file);
+ ByteStreams.copy(r.body().byteStream(), os);
+ os.close();
+
+ JSONArray cids = sticker.getJSONArray("cids");
+ for (int i = 0; i < cids.length(); i++) {
+ Cid cid = Cid.decode(cids.getString(i));
+ mDatabaseBackend.saveCid(cid, file, sticker.getString("url"));
+ }
+
+ try {
+ File copyright = new File(mStickerDir.getAbsolutePath() + "/" + sticker.getString("pack") + "/copyright.txt");
+ OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(copyright, true), "utf-8");
+ w.write(sticker.getString("pack"));
+ w.write('/');
+ w.write(sticker.getString("name"));
+ w.write(": ");
+ w.write(sticker.getString("copyright"));
+ w.write('\n');
+ w.close();
+ } catch (final Exception e) { }
+ }
+
+ private void download() throws Exception {
+ NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext(), "backup");
+ mBuilder.setContentTitle("Downloading Default Stickers")
+ .setSmallIcon(R.drawable.ic_archive_white_24dp)
+ .setProgress(1, 0, false);
+ startForeground(NOTIFICATION_ID, mBuilder.build());
+
+ Response r = http.newCall(new Request.Builder().url("https://stickers.cheogram.com/index.json").build()).execute();
+ JSONArray stickers = new JSONArray(r.body().string());
+
+ final Progress progress = new Progress(mBuilder, 1, 0);
+ for (int i = 0; i < stickers.length(); i++) {
+ oneSticker(stickers.getJSONObject(i));
+
+ final int percentage = i * 100 / stickers.length();
+ notificationManager.notify(NOTIFICATION_ID, progress.build(percentage));
+ }
+ }
+
+ private File stickerDir() {
+ SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
+ final String dir = p.getString("sticker_directory", "Stickers");
+ if (dir.startsWith("content://")) {
+ Uri uri = Uri.parse(dir);
+ uri = DocumentsContract.buildDocumentUriUsingTree(uri, DocumentsContract.getTreeDocumentId(uri));
+ return new File(FileUtils.getPath(getBaseContext(), uri));
+ } else {
+ return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + "/" + dir);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ private static class Progress {
+ private final NotificationCompat.Builder builder;
+ private final int max;
+ private final int count;
+
+ private Progress(NotificationCompat.Builder builder, int max, int count) {
+ this.builder = builder;
+ this.max = max;
+ this.count = count;
+ }
+
+ private Notification build(int percentage) {
+ builder.setProgress(max * 100, count * 100 + percentage, false);
+ return builder.build();
+ }
+ }
+}
M src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java => src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +13 -0
@@ 283,6 283,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("PRAGMA cheogram.user_version = 6");
}
+ if(cheogramVersion < 7) {
+ db.execSQL(
+ "ALTER TABLE cheogram.cids " +
+ "ADD COLUMN url TEXT"
+ );
+ db.execSQL("PRAGMA cheogram.user_version = 7");
+ }
+
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ 776,10 784,15 @@ public class DatabaseBackend extends SQLiteOpenHelper {
}
public void saveCid(Cid cid, File file) {
+ saveCid(cid, file, null);
+ }
+
+ public void saveCid(Cid cid, File file, String url) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put("cid", cid.toString());
cv.put("path", file.getAbsolutePath());
+ cv.put("url", url);
db.insertWithOnConflict("cheogram.cids", null, cv, SQLiteDatabase.CONFLICT_REPLACE);
}
M src/main/java/eu/siacs/conversations/services/XmppConnectionService.java => src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +5 -1
@@ 562,10 562,14 @@ public class XmppConnectionService extends Service {
}
public void saveCid(Cid cid, File file) throws BlockedMediaException {
+ saveCid(cid, file, null);
+ }
+
+ public void saveCid(Cid cid, File file, String url) throws BlockedMediaException {
if (this.databaseBackend.isBlockedMedia(cid)) {
throw new BlockedMediaException();
}
- this.databaseBackend.saveCid(cid, file);
+ this.databaseBackend.saveCid(cid, file, url);
}
public void blockMedia(File f) {
M src/main/java/eu/siacs/conversations/ui/SettingsActivity.java => src/main/java/eu/siacs/conversations/ui/SettingsActivity.java +23 -0
@@ 25,6 25,8 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
+import com.cheogram.android.DownloadDefaultStickers;
+
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ 69,6 71,7 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
public static final String PREVENT_SCREENSHOTS = "prevent_screenshots";
public static final int REQUEST_CREATE_BACKUP = 0xbf8701;
+ public static final int REQUEST_DOWNLOAD_STICKERS = 0xbf8702;
private SettingsFragment mSettingsFragment;
@@ 390,6 393,17 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
}
}
+ final Preference downloadDefaultStickers = mSettingsFragment.findPreference("download_default_stickers");
+ if (downloadDefaultStickers != null) {
+ downloadDefaultStickers.setOnPreferenceClickListener(
+ preference -> {
+ if (hasStoragePermission(REQUEST_DOWNLOAD_STICKERS)) {
+ downloadStickers();
+ }
+ return true;
+ });
+ }
+
final Preference clearBlockedMedia = mSettingsFragment.findPreference("clear_blocked_media");
if (clearBlockedMedia != null) {
clearBlockedMedia.setOnPreferenceClickListener((p) -> {
@@ 587,6 601,9 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
if (requestCode == REQUEST_CREATE_BACKUP) {
createBackup();
}
+ if (requestCode == REQUEST_DOWNLOAD_STICKERS) {
+ downloadStickers();
+ }
} else {
Toast.makeText(
this,
@@ 620,6 637,12 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
builder.create().show();
}
+ private void downloadStickers() {
+ Intent intent = new Intent(this, DownloadDefaultStickers.class);
+ ContextCompat.startForegroundService(this, intent);
+ displayToast("Sticker download started");
+ }
+
private void displayToast(final String msg) {
runOnUiThread(() -> Toast.makeText(SettingsActivity.this, msg, Toast.LENGTH_LONG).show());
}
M src/main/res/xml/preferences.xml => src/main/res/xml/preferences.xml +3 -0
@@ 371,6 371,9 @@
android:title="Change Stickers Location"
android:key="sticker_directory" />
<Preference
+ android:title="Update Default Stickers"
+ android:key="download_default_stickers" />
+ <Preference
android:title="Clear Blocked Media"
android:key="clear_blocked_media" />
</PreferenceCategory>