~singpolyma/cheogram-android

ae4e3a0c883b620542af0c72169bb1b488def8cf — Stephen Paul Weber 3 months ago 78e6102
Option to download default stickers and save their cid and url in database
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>