This commit is contained in:
aliveoutside
2022-03-24 19:37:43 +03:00
commit aa972fa5b3
37643 changed files with 6680801 additions and 0 deletions
@@ -0,0 +1,37 @@
package com.aefyr.tsg.g2;
/**
* Created by Aefyr on 19.05.2018.
*/
public abstract class CustomStickersEventsListener implements TelegramStickersService.StickersEventsListener {
@Override
public void onPackAdded(TelegramStickersPack pack, int atIndex) {
}
@Override
public void onPackChanged(TelegramStickersPack pack, int atIndex) {
}
@Override
public void onPackRemoved(TelegramStickersPack pack, int atIndex) {
}
@Override
public void onPackDownloadError(TelegramStickersPack pack, Exception error) {
}
@Override
public void onActivePacksListChanged() {
}
@Override
public void onInactivePacksListChanged() {
}
}
+23
View File
@@ -0,0 +1,23 @@
package com.aefyr.tsg.g2;
import android.graphics.Bitmap;
import java.io.File;
/**
* Created by Aefyr on 20.05.2018.
*/
public abstract class CustomStickersPack {
public abstract File folder();
public abstract int stickersCount();
public abstract String name();
public abstract Bitmap getIconBitmap();
public abstract Bitmap getStickerBitmap(int index);
public abstract File getStickerFile(int index);
}
+116
View File
@@ -0,0 +1,116 @@
package com.aefyr.tsg.g2;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicLong;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
/**
* Created by Aefyr on 20.05.2018.
*/
public class NotificationsHelper {
private static final int NOTIFICATION_ID_BASE = 3921337;
private static final String NOTIFICATION_CHANNEL_ID = "tgss_chan_v5";
private HashMap<TelegramStickersPack, Integer> downloadingPacksNotificationsIds;
private Context c;
private NotificationManagerCompat manager;
private int currentNotificationN = 0;
private static final int MAX_UPDATE_NOTIFICATIONS_PER_SECOND = 3;
private static final int MIN_TIME_BETWEEN_UPDATE_NOTIFICATIONS = 1000 / MAX_UPDATE_NOTIFICATIONS_PER_SECOND;
private AtomicLong lastNotificationTime = new AtomicLong(0);
public NotificationsHelper(Context c) {
this.c = c;
manager = NotificationManagerCompat.from(c);
downloadingPacksNotificationsIds = new HashMap<>();
if (oreo()) {
NotificationChannel notificationChan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "Сервис стикеров Telegram в VTosters", NotificationManager.IMPORTANCE_HIGH);
((NotificationManager) c.getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(notificationChan);
}
}
public void packStartedDownloading(TelegramStickersPack pack) {
int notificationId = NOTIFICATION_ID_BASE + currentNotificationN++;
downloadingPacksNotificationsIds.put(pack, notificationId);
Notification.Builder builder = commonBuilder();
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + ": загрузка" : pack.title + ": обновление");
builder.setContentText("Получение информации");
builder.setOngoing(true);
manager.notify(notificationId, builder.build());
}
public void packDownloadUpdated(TelegramStickersPack pack, int downloadProgress) {
if (System.currentTimeMillis() - lastNotificationTime.get() < MIN_TIME_BETWEEN_UPDATE_NOTIFICATIONS)
return;
lastNotificationTime.set(System.currentTimeMillis());
Notification.Builder builder = commonBuilder();
builder.setContentTitle(pack.state == TelegramStickersPack.DOWNLOADING ? pack.title + ": загрузка" : pack.title + ": обновление");
builder.setContentText(downloadProgress + "/" + pack.stickersCount);
builder.setProgress(pack.stickersCount, downloadProgress, false);
builder.setOngoing(true);
builder.setOnlyAlertOnce(true);
if (downloadingPacksNotificationsIds.containsKey(pack))
manager.notify(downloadingPacksNotificationsIds.get(pack), builder.build());
}
public void packDoneDownloading(TelegramStickersPack pack, boolean success, boolean wasUpdate, @Nullable Exception e) {
Notification.Builder builder = commonBuilder();
builder.setProgress(0, 0, false);
builder.setOngoing(false);
builder.setContentTitle("Сервис стикеров Telegram в VTosters");
String text;
if (success)
text = wasUpdate ? pack.title + " успешно обновлен" : pack.title + " успешно загружен";
else
text = wasUpdate ? pack.title + ": ошибка при обновлении" : pack.title + ": ошибка при загрузке";
if (e instanceof TelegramStickersGrabber.TSGException) {
text = pack.title + ": " + e.getMessage();
}
builder.setContentText(String.format(text, pack.title));
manager.notify(downloadingPacksNotificationsIds.remove(pack), builder.build());
}
public void removePackNotification(TelegramStickersPack pack) {
manager.cancel(downloadingPacksNotificationsIds.remove(pack));
}
private Notification.Builder commonBuilder() {
var builder = new Notification.Builder(c).setPriority(Notification.PRIORITY_MAX).setSmallIcon(com.vtosters.lite.R.drawable.icon_vk_104);
if (oreo()) {
builder.setChannelId(NOTIFICATION_CHANNEL_ID);
}
return builder;
}
private boolean oreo() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
}
+37
View File
@@ -0,0 +1,37 @@
package com.aefyr.tsg.g2;
import android.os.AsyncTask;
import android.util.Log;
import java.io.File;
/**
* Created by Aefyr on 20.05.2018.
*/
class PackDeletionTask extends AsyncTask<Void, Void, Boolean> {
private static final String TAG = "TGSSAnnihilator";
private TelegramStickersPack pack;
PackDeletionTask(TelegramStickersPack pack) {
this.pack = pack;
}
@Override
protected Boolean doInBackground(Void... voids) {
boolean success = true;
for (File f : pack.folder.listFiles()) {
success = success && f.delete();
}
success = success && pack.folder.delete();
return success;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
Log.d(TAG, String.format("Attempted deleting pack %s, success?%s", pack.id, aBoolean));
}
}
@@ -0,0 +1,145 @@
package com.aefyr.tsg.g2;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
/**
* Created by Aefyr on 18.05.2018.
*/
public class TelegramStickersPack extends CustomStickersPack {
public static final int UNKNOWN = -1;
public static final int DOWNLOADED = 0;
public static final int DOWNLOADING = 1;
public static final int UPDATING = 2;
public String id;
public String title;
public int state = UNKNOWN;
public boolean enabled = true;
public File folder;
public int stickersCount = -1;
public String version;
public int index = -1;
public HashMap<String, List<Integer>> emojis = new HashMap<>();
public TelegramStickersPack(String id) {
this.id = id.toLowerCase();
title = id;
}
public JSONObject encodeEmojis() throws JSONException {
JSONObject obj = new JSONObject();
for (HashMap.Entry<String, List<Integer>> en : emojis.entrySet()) {
JSONArray arr = new JSONArray();
for (int i : en.getValue()) arr.put(i);
obj.put(en.getKey(), arr);
}
return obj;
}
public void loadEmojis(JSONObject obj) throws JSONException {
Iterator<String> it = obj.keys();
while (it.hasNext()) {
String key = it.next();
//emojis.put(key, obj.getInt(key));
List<Integer> list = emojis.get(key);
if (list == null) list = new ArrayList<>();
JSONArray arr = obj.getJSONArray(key);
for (int i = 0; i < arr.length(); i++)
list.add(arr.getInt(i));
emojis.put(key, list);
}
}
public Bitmap getIcon(int targetMaxSideSize) {
if (state != DOWNLOADED && state != UPDATING)
throw new IllegalStateException("Unable to get an icon of a pack that hasn't been downloaded yet");
File icon = new File(folder, "001.png");
return getScaledBitmap(icon, targetMaxSideSize);
}
private Bitmap getScaledBitmap(File bitmapFile, int targetMaxSideSize) {
BitmapFactory.Options op = new BitmapFactory.Options();
op.inJustDecodeBounds = true;
op.inPreferredConfig = Bitmap.Config.ARGB_8888;
BitmapFactory.decodeFile(bitmapFile.getAbsolutePath(), op);
int oW = op.outWidth;
int oH = op.outHeight;
int maxSideSize = op.outWidth > op.outHeight ? op.outWidth : op.outHeight;
if (maxSideSize > targetMaxSideSize) {
op.inScaled = true;
op.inDensity = maxSideSize;
op.inTargetDensity = targetMaxSideSize;
}
op.inJustDecodeBounds = false;
Bitmap loadedBitmap = BitmapFactory.decodeFile(bitmapFile.getAbsolutePath(), op);
Log.d("AYAYA", String.format("maxside=%d, from %dx%d to %dx%d", targetMaxSideSize, oW, oH, loadedBitmap.getWidth(), loadedBitmap.getHeight()));
return loadedBitmap;
}
@Override
public boolean equals(Object obj) {
return obj instanceof TelegramStickersPack && ((TelegramStickersPack) obj).id.toLowerCase().equals(id.toLowerCase());
}
@Override
public int hashCode() {
return id.toLowerCase().hashCode();
}
@Override
public File folder() {
return folder;
}
@Override
public int stickersCount() {
return stickersCount;
}
@Override
public String name() {
return title;
}
@Override
public Bitmap getIconBitmap() {
return getIcon(128);
}
@Override
public Bitmap getStickerBitmap(int index) {
return BitmapFactory.decodeFile(new File(folder, String.format("%03d.png", index + 1)).getAbsolutePath());
}
@Override
public File getStickerFile(int index) {
return new File(folder, String.format("%03d.png", index + 1));
}
public Bitmap getStickerBitmap(int index, int maxSize) {
return getScaledBitmap(new File(folder, String.format("%03d.png", index + 1)), maxSize);
}
}
@@ -0,0 +1,402 @@
package com.aefyr.tsg.g2;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import com.aefyr.tsg.g2.sql.TelegramStickersDbHelper;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersPackInfo;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import ru.vtosters.lite.utils.Helper;
import ru.vtosters.lite.ui.fragments.tgstickers.StickersFragment;
/**
* Created by Aefyr on 19.05.2018.
*/
public class TelegramStickersService {
private static final String TAG = "TGStickersService";
private static TelegramStickersService instance;
private Context c;
private ArrayList<TelegramStickersPack> packs;
private ArrayList<TelegramStickersPack> activePacks;
private ArrayList<TelegramStickersPack> inactivePacks;
private ArrayList<StickersEventsListener> listeners;
private HashSet<String> currentlyDownloading;
private Handler uiThreadHandler;
private TelegramStickersGrabber grabber;
private TelegramStickersDbHelper dbHelper;
private boolean ready = false;
private ArrayList<Runnable> queuedTasks;
private ThreadPoolExecutor executor;
private NotificationsHelper notificationsHelper;
public static TelegramStickersService getInstance(Context c) {
return instance == null ? new TelegramStickersService(c) : instance;
}
private TelegramStickersService(Context context) {
instance = this;
this.c = context.getApplicationContext();
listeners = new ArrayList<>();
currentlyDownloading = new HashSet<>();
uiThreadHandler = new Handler(Looper.getMainLooper());
grabber = new TelegramStickersGrabber(null);
dbHelper = new TelegramStickersDbHelper(c);
queuedTasks = new ArrayList<>();
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
notificationsHelper = new NotificationsHelper(c);
packs = new ArrayList<>();
activePacks = new ArrayList<>();
inactivePacks = new ArrayList<>();
updatePacks(true);
}
private void updatePacks(final boolean notify) {
packs.clear();
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
@Override
public void onPackLoaded(final TelegramStickersPack pack) {
runOnUiThread(() -> {
packs.add(pack);
if (pack.enabled)
activePacks.add(pack);
else
inactivePacks.add(pack);
if (notify) notifyPackAdded(pack, packs.size() - 1);
});
}
@Override
public void onAllPacksLoaded(ArrayList<TelegramStickersPack> packs) {
ready = true;
if (notify) {
notifyActivePacksListChanged();
notifyInactivePacksListChanged();
}
Log.d(TAG, String.format("Packs list loaded, running %d queued tasks...", queuedTasks.size()));
while (!queuedTasks.isEmpty())
queuedTasks.remove(0).run();
}
});
}
interface StickersEventsListener {
void onPackAdded(TelegramStickersPack pack, int atIndex);
void onPackRemoved(TelegramStickersPack pack, int atIndex);
void onPackChanged(TelegramStickersPack pack, int atIndex);
void onPackDownloadError(TelegramStickersPack pack, Exception error);
void onActivePacksListChanged();
void onInactivePacksListChanged();
}
public void addStickersEventsListener(StickersEventsListener listener) {
listeners.add(listener);
}
public void removeStickersEventsListener(StickersEventsListener listener) {
listeners.remove(listener);
}
private void notifyPackAdded(TelegramStickersPack pack, int atIndex) {
Helper.GetContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
return;
for (StickersEventsListener listener : listeners) {
listener.onPackAdded(pack, atIndex);
}
}
private void notifyPackChanged(TelegramStickersPack pack, int atIndex) {
Helper.GetContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
return;
for (StickersEventsListener listener : listeners) {
listener.onPackChanged(pack, atIndex);
}
}
private void notifyPackRemoved(TelegramStickersPack pack, int atIndex) {
Helper.GetContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
return;
for (StickersEventsListener listener : listeners) {
listener.onPackRemoved(pack, atIndex);
}
}
private void notifyPackDownloadError(TelegramStickersPack pack, Exception error) {
Helper.GetContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
return;
for (StickersEventsListener listener : listeners) {
listener.onPackDownloadError(pack, error);
}
}
private void notifyActivePacksListChanged() {
Helper.GetContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
return;
for (StickersEventsListener listener : listeners) {
listener.onActivePacksListChanged();
}
}
private void notifyInactivePacksListChanged() {
Helper.GetContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
if (listeners.isEmpty())
return;
for (StickersEventsListener listener : listeners) {
listener.onInactivePacksListChanged();
}
}
public void setBotKey(String botKey) {
grabber.setBotApiKey(botKey);
}
public ArrayList<TelegramStickersPack> getPacksListReference() {
return packs;
}
public ArrayList<TelegramStickersPack> getActivePacksListReference() {
return activePacks;
}
public ArrayList<TelegramStickersPack> getInactivePacksListReference() {
return inactivePacks;
}
public boolean isDoneLoading() {
return ready;
}
public boolean requestPackDownload(final String id, File packFolder) {
if (currentlyDownloading.contains(id)) {
Log.e(TAG, String.format("Got request to download pack %s which is already downloading", id));
return false;
}
if (!ready) {
final File folder = packFolder;
queuedTasks.add(() -> requestPackDownload(id, folder));
return true;
}
TelegramStickersPack pack = new TelegramStickersPack(id);
boolean update = false;
int index = packs.indexOf(pack);
if (index != -1) {
pack = packs.get(index);
pack.state = TelegramStickersPack.UPDATING;
update = true;
packFolder = pack.folder;
notifyPackChanged(pack, index);
} else {
pack.state = TelegramStickersPack.DOWNLOADING;
pack.folder = packFolder;
packs.add(pack);
notifyPackAdded(pack, packs.size() - 1);
}
final TelegramStickersPack newPack = pack;
final boolean isUpdate = update;
currentlyDownloading.add(id.toLowerCase());
notificationsHelper.packStartedDownloading(newPack);
grabber.enableProxy();
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
@Override
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
runOnUiThread(() -> {
newPack.state = TelegramStickersPack.DOWNLOADED;
notifyPackChanged(newPack, packs.indexOf(newPack));
currentlyDownloading.remove(newPack.id.toLowerCase());
syncPack(newPack);
if (isUpdate) {
if (newPack.enabled)
notifyActivePacksListChanged();
} else {
activePacks.add(newPack);
notifyActivePacksListChanged();
}
});
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
}
@Override
public void onPackDownloadError(final Exception e) {
runOnUiThread(() -> {
Log.e(TAG, "Error while downloading pack " + newPack.id);
e.printStackTrace();
if (!isUpdate) {
int index = packs.indexOf(newPack);
packs.remove(index);
notifyPackRemoved(newPack, index);
} else {
newPack.state = TelegramStickersPack.DOWNLOADED;
notifyPackChanged(newPack, packs.indexOf(newPack));
}
notifyPackDownloadError(newPack, e);
currentlyDownloading.remove(newPack.id);
});
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
}
@Override
public void onGotPackInfo(final TelegramStickersPackInfo packInfo) {
runOnUiThread(() -> {
newPack.title = packInfo.title;
newPack.stickersCount = packInfo.stickersCount;
newPack.version = packInfo.version;
notifyPackChanged(newPack, packs.indexOf(newPack));
});
notificationsHelper.packDownloadUpdated(newPack, 0);
}
@Override
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
stickerIndex--;
List<Integer> list = newPack.emojis.get(boundEmoji);
if (list == null) list = new ArrayList<>();
list.add(stickerIndex);
newPack.emojis.put(boundEmoji, list);
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
}
});
return true;
}
public void setPackEnabled(final TelegramStickersPack pack, final boolean enabled, final boolean notify) {
if (!ready) {
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
return;
}
if (pack.enabled == enabled)
return;
pack.enabled = enabled;
syncPack(pack);
if (notify) notifyPackChanged(pack, packs.indexOf(pack));
if (pack.enabled) {
inactivePacks.remove(pack);
activePacks.add(pack);
} else {
activePacks.remove(pack);
inactivePacks.add(pack);
}
if (notify) {
notifyActivePacksListChanged();
notifyInactivePacksListChanged();
}
}
public void deletePack(final TelegramStickersPack pack) {
if (!ready) {
queuedTasks.add(() -> deletePack(pack));
return;
}
boolean d = dbHelper.deletePack(pack);
Log.d(TAG, "Delete from DB=" + d);
int index = packs.indexOf(pack);
packs.remove(index);
notifyPackRemoved(pack, index);
if (pack.enabled) {
activePacks.remove(pack);
notifyActivePacksListChanged();
} else {
inactivePacks.remove(pack);
notifyInactivePacksListChanged();
}
if (d)
new PackDeletionTask(pack).executeOnExecutor(executor);
}
private void runOnUiThread(Runnable r) {
uiThreadHandler.post(r);
}
private void syncPack(final TelegramStickersPack pack) {
if (!ready) {
queuedTasks.add(() -> syncPack(pack));
return;
}
Log.d(TAG, "Sync=" + dbHelper.syncPack(pack));
}
public void swap(TelegramStickersPack p1, TelegramStickersPack p2) {
int i1 = p1.index;
int i2 = p2.index;
p1.index = i2;
p2.index = i1;
syncPack(p1);
syncPack(p2);
if (getActivePacksListReference().contains(p1) && getActivePacksListReference().contains(p2) || getInactivePacksListReference().contains(p1) && getInactivePacksListReference().contains(p2)) {
int li1 = getActivePacksListReference().indexOf(p1);
int li2 = getActivePacksListReference().indexOf(p2);
getActivePacksListReference().set(li2, p1);
getActivePacksListReference().set(li1, p2);
}
int li1 = getPacksListReference().indexOf(p1);
int li2 = getPacksListReference().indexOf(p2);
getPacksListReference().set(li2, p1);
getPacksListReference().set(li1, p2);
}
}
@@ -0,0 +1,31 @@
package com.aefyr.tsg.g2.sql;
import android.provider.BaseColumns;
/**
* Created by Aefyr on 19.05.2018.
*/
public final class TelegramStickersContract {
private TelegramStickersContract() {
}
public static class StickersTableEntry implements BaseColumns {
public static final String TABLE_NAME = "packs";
public static final String COLUMN_NAME_ID = "id";
public static final String COLUMN_NAME_INDEX = "pack_index";
public static final String COLUMN_NAME_NAME = "name";
public static final String COLUMN_NAME_FOLDER = "folder";
public static final String COLUMN_NAME_STICKERS_COUNT = "stickers_count";
public static final String COLUMN_NAME_ENABLED = "enabled";
public static final String COLUMN_NAME_VERSION = "version";
public static final String COLUMN_NAME_EMOJIS = "emojis";
}
public static final String SQL_CREATE_ENTRIES = String.format("CREATE TABLE %s (%s INTEGER PRIMARY KEY, %s INTEGER AUTO_INCREMENT, %s TEXT, %s TEXT, %s TEXT, %s INTEGER, %s INTEGER, %s TEXT, %s TEXT)",
StickersTableEntry.TABLE_NAME, StickersTableEntry._ID, StickersTableEntry.COLUMN_NAME_INDEX, StickersTableEntry.COLUMN_NAME_ID, StickersTableEntry.COLUMN_NAME_NAME,
StickersTableEntry.COLUMN_NAME_FOLDER, StickersTableEntry.COLUMN_NAME_STICKERS_COUNT, StickersTableEntry.COLUMN_NAME_ENABLED, StickersTableEntry.COLUMN_NAME_VERSION, StickersTableEntry.COLUMN_NAME_EMOJIS);
}
@@ -0,0 +1,168 @@
package com.aefyr.tsg.g2.sql;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorIndexOutOfBoundsException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.AsyncTask;
import android.util.Log;
import com.aefyr.tsg.g2.TelegramStickersPack;
import java.io.File;
import java.util.ArrayList;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Created by Aefyr on 19.05.2018.
*/
public class TelegramStickersDbHelper extends SQLiteOpenHelper {
private static final int DB_VERSION = 2;
private static final String DB_NAME = "TelegramStickers.db";
private SQLiteDatabase writableDb;
private SQLiteDatabase readableDb;
public interface PacksLoadingListener {
void onPackLoaded(TelegramStickersPack pack);
void onAllPacksLoaded(ArrayList<TelegramStickersPack> packs);
}
public TelegramStickersDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(TelegramStickersContract.SQL_CREATE_ENTRIES);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1 && newVersion == 2) {
db.execSQL(String.format("ALTER TABLE %s ADD COLUMN %s TEXT", TelegramStickersContract.StickersTableEntry.TABLE_NAME, TelegramStickersContract.StickersTableEntry.COLUMN_NAME_EMOJIS));
}
}
public void loadDatabases() {
writableDb = getWritableDatabase();
readableDb = getReadableDatabase();
}
public boolean syncPack(TelegramStickersPack pack) {
ContentValues sqlPack = new ContentValues();
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_ID, pack.id);
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_NAME, pack.title);
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_FOLDER, pack.folder.getAbsolutePath());
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_STICKERS_COUNT, pack.stickersCount);
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_ENABLED, pack.enabled ? 1 : 0);
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_VERSION, pack.version);
try {
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_EMOJIS, pack.encodeEmojis().toString());
} catch (JSONException e) {
e.printStackTrace();
}
if (pack.index == -1) pack.index = getNewIndex();
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_INDEX, pack.index);
int s = writableDb.updateWithOnConflict(TelegramStickersContract.StickersTableEntry.TABLE_NAME, sqlPack, TelegramStickersContract.StickersTableEntry.COLUMN_NAME_ID + "='" + pack.id + "'", null, SQLiteDatabase.CONFLICT_IGNORE);
if (s == 0) {
sqlPack.put(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_INDEX, getNewIndex());
Log.d("DB", "INSERTED!");
return writableDb.insert(TelegramStickersContract.StickersTableEntry.TABLE_NAME, null, sqlPack) != -1;
}
Log.d("DB", "UPDATED!");
return true;
}
private int getNewIndex() {
Cursor c = readableDb.rawQuery("SELECT MAX(" + TelegramStickersContract.StickersTableEntry.COLUMN_NAME_INDEX + ") FROM " + TelegramStickersContract.StickersTableEntry.TABLE_NAME, new String[0]);
c.moveToLast();
int max;
try {
max = c.getInt(0);
c.close();
} catch (CursorIndexOutOfBoundsException e) {
e.printStackTrace();
max = 0;
}
Log.d("sova", "Found last index: " + max);
return max + 1;
}
public boolean deletePack(TelegramStickersPack pack) {
return writableDb.delete(TelegramStickersContract.StickersTableEntry.TABLE_NAME, TelegramStickersContract.StickersTableEntry.COLUMN_NAME_ID + "='" + pack.id + "'", null) == 1;
}
public void getAllPacks(PacksLoadingListener listener) {
new PacksLoadingTask(listener).execute();
}
private class PacksLoadingTask extends AsyncTask<Void, Void, ArrayList<TelegramStickersPack>> {
PacksLoadingListener listener;
PacksLoadingTask(PacksLoadingListener listener) {
this.listener = listener;
}
@Override
protected ArrayList<TelegramStickersPack> doInBackground(Void... voids) {
if (readableDb == null)
loadDatabases();
String sortOrder = TelegramStickersContract.StickersTableEntry.COLUMN_NAME_INDEX + " ASC";
Cursor cursor = readableDb.query(TelegramStickersContract.StickersTableEntry.TABLE_NAME, null, null, null, null, null, sortOrder);
Log.d("DB", String.format("LOADING %d PACKS", cursor.getCount()));
ArrayList<TelegramStickersPack> packs = new ArrayList<>(cursor.getCount() + 8);
while (cursor.moveToNext()) {
Log.d("DB", "PACK LOADED");
String id = cursor.getString(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_ID));
String name = cursor.getString(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_NAME));
String folder = cursor.getString(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_FOLDER));
int stickersCount = cursor.getInt(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_STICKERS_COUNT));
boolean enabled = cursor.getInt(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_ENABLED)) == 1;
String version = cursor.getString(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_VERSION));
int index = cursor.getInt(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_INDEX));
String emojisEncoded = cursor.getString(cursor.getColumnIndexOrThrow(TelegramStickersContract.StickersTableEntry.COLUMN_NAME_EMOJIS));
TelegramStickersPack pack = new TelegramStickersPack(id);
pack.state = TelegramStickersPack.DOWNLOADED;
pack.title = name;
pack.folder = new File(folder);
pack.stickersCount = stickersCount;
pack.enabled = enabled;
pack.version = version;
pack.index = index;
try {
if (emojisEncoded != null) pack.loadEmojis(new JSONObject(emojisEncoded));
} catch (JSONException e) {
e.printStackTrace();
}
packs.add(pack);
listener.onPackLoaded(pack);
}
cursor.close();
return packs;
}
@Override
protected void onPostExecute(ArrayList<TelegramStickersPack> packs) {
listener.onAllPacksLoaded(packs);
}
}
}
@@ -0,0 +1,481 @@
package com.aefyr.tsg.g2.stickersgrabber;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.aefyr.tsg.g2.stickersgrabber.util.Flag;
import com.aefyr.tsg.g2.stickersgrabber.util.GoalCounter;
import ru.f0x1d.net.NetCall;
import ru.f0x1d.net.NetCallback;
import ru.f0x1d.net.NetClient;
import ru.f0x1d.net.NetRequest;
import ru.f0x1d.net.NetResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Aefyr on 11.05.2018.
*/
public class TelegramStickersGrabber {
private static final String TAG = "TSG";
private static String BOT_API_BASE_URL;
private static String GET_STICKER_SET_URL;
private static String GET_FILE_URL;
private static String BOT_API_BASE_FILE_URL;
public static String REAL_TG_IP = null;
public static String PROXY_IP = null;
public static int PROXY_PORT = -1;
public static String PROXY_USER = null;
public static String PROXY_PASS = null;
public static boolean PROXY_SOCKS = true;
public static boolean HAS_PASS = false;
public static boolean USE_PROXY = false;
static {
updateURLs();
}
public static void updateURLs() {
BOT_API_BASE_FILE_URL = "https://api.telegram.org/file/bot%s/%s";
BOT_API_BASE_URL = REAL_TG_IP != null ? "https://" + REAL_TG_IP + "/bot%s/" : "https://api.telegram.org/bot%s/";
GET_STICKER_SET_URL = BOT_API_BASE_URL + "getStickerSet?name=%s";
GET_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
}
private String botApiKey = "";
private NetClient httpClient;
private final MessageDigest sha256;
private final Handler uiThreadHandler;
public static boolean isTelegramBlocked() {
NetClient client = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
try {
if (PROXY_IP != null) {
Socket socket = new Socket(PROXY_IP, PROXY_PORT);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socket.getRemoteSocketAddress());
NetClient.Builder b = new NetClient.Builder().proxy(proxy);
if (PROXY_USER != null) {
b = b.proxyAuthenticator(new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray()));
}
client = b.build();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
NetRequest req = new NetRequest.Builder().url(REAL_TG_IP != null ? "https://" + REAL_TG_IP + "/test/" : "https://api.telegram.org/test/").build();
try {
NetResponse res = client.newCall(req).execute();
String s = res.getDataString();
if (!s.equals("{\"ok\":false,\"error_code\":404,\"description\":\"Not Found\"}"))
return true;
} catch (Exception e) {
return true;
}
return false;
}
public interface PackDownloadListener {
void onPackDownloaded(TelegramStickersPackInfo pack, boolean newVersionFound);
void onPackDownloadError(Exception e);
void onGotPackInfo(TelegramStickersPackInfo packInfo);
void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickerCount);
}
public interface KeyCheckListener {
void onKeyChecked(boolean ok);
void onNetError();
}
public class TSGException extends Exception {
private static final long serialVersionUID = 7866915509988422944L;
private TSGException(String message) {
super(message);
}
}
public TelegramStickersGrabber(String botApiKey) {
this.botApiKey = botApiKey;
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
uiThreadHandler = new Handler(Looper.getMainLooper());
try {
sha256 = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
Log.wtf(TAG, "No support for SHA-256");
e.printStackTrace();
throw new RuntimeException(e);
}
}
public void resetProxy() {
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
}
public void enableProxy() {
Thread kostil = new Thread() {
@Override
public void run() {
try {
if (PROXY_IP != null) {
Socket socket = new Socket(PROXY_IP, PROXY_PORT);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, socket.getRemoteSocketAddress());
NetClient.Builder b = httpClient.newBuilder().proxy(proxy);
if (PROXY_USER != null) {
httpClient = b.proxyAuthenticator(new PasswordAuthentication(PROXY_USER, PROXY_PASS.toCharArray())).build();
}
httpClient = b.build();
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
kostil.start();
try {
kostil.join();
Log.d(TAG, "Proxy set");
} catch (InterruptedException e) {
Log.e(TAG, "Unable to set proxy:");
e.printStackTrace();
}
}
public void grabPack(String id, File packFolder, String installedVersion, final PackDownloadListener listener) {
getPackInfo(id, packFolder, installedVersion, listener);
}
public void setBotApiKey(String key) {
botApiKey = key;
}
public void checkKey(final KeyCheckListener listener) {
Log.d(TAG, "Checking key: " + botApiKey);
NetRequest request = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_URL + "getMe", botApiKey)).build();
httpClient.newCall(request).enqueue(new NetCallback() {
@Override
public void onFailure(NetCall call, IOException e) {
e.printStackTrace();
runOnUiThread(listener::onNetError);
}
@Override
public void onResponse(NetCall call, final NetResponse response) throws IOException {
runOnUiThread(() -> listener.onKeyChecked(response.code() == 200));
}
});
}
private class StickerSet {
String id;
String name;
String version;
ArrayList<Sticker> stickers;
StickerSet(String id, String name, String version, ArrayList<Sticker> stickers) {
this.name = name;
this.stickers = stickers;
this.id = id;
this.version = version;
}
}
private class Sticker {
String emoji;
String fileId;
Sticker(String fileId, String emoji) {
this.fileId = fileId;
this.emoji = emoji;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Sticker && ((Sticker) obj).fileId.equals(fileId);
}
@Override
public int hashCode() {
return (17 * emoji.hashCode()) << 32 + fileId.hashCode();
}
}
private void getPackInfo(final String packName, final File packFolder, final String installedVersion, final PackDownloadListener listener) {
NetRequest packInfoRequest = new NetRequest.Builder().get().url(String.format(GET_STICKER_SET_URL, botApiKey, packName)).build();
httpClient.newCall(packInfoRequest).enqueue(new NetCallback() {
@Override
public void onFailure(NetCall call, IOException e) {
listener.onPackDownloadError(e);
}
@Override
public void onResponse(NetCall call, NetResponse response) throws IOException {
if (!response.isSuccessful()) {
if (response.getData() != null)
listener.onPackDownloadError(new TSGException(response.getDataString() + "\nURL: " + call.request().url()));
else
listener.onPackDownloadError(new TSGException("Unknown Exception, no response body"));
return;
}
StickerSet set;
try {
JSONObject jPackInfo = new JSONObject(response.getDataString()).getJSONObject("result");
String packVersion = new String(sha256.digest(jPackInfo.toString().getBytes(StandardCharsets.UTF_8)));
if (packVersion.equals(installedVersion)) {
listener.onPackDownloaded(null, false);
return;
}
JSONArray jStickers = jPackInfo.getJSONArray("stickers");
if (jStickers.length() == 0) {
listener.onPackDownloadError(new TSGException("No stickers in pack"));
return;
}
if (jPackInfo.optBoolean("is_animated")) {
listener.onPackDownloadError(new TSGException("Animated stickerpacks are not supported!"));
return;
}
ArrayList<Sticker> stickers = new ArrayList<>(jStickers.length());
Log.d(TAG, String.format("Parsing stickers in pack %s", packName));
for (int i = 0; i < jStickers.length(); i++) {
JSONObject jSticker = jStickers.getJSONObject(i);
stickers.add(new Sticker(jSticker.getString("file_id"), jSticker.getString("emoji")));
}
set = new StickerSet(jPackInfo.getString("name"), jPackInfo.getString("title"), packVersion, stickers);
} catch (Exception e) {
listener.onPackDownloadError(e);
return;
}
if (!packFolder.exists() && !packFolder.mkdirs()) {
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
return;
}
Log.d(TAG, String.format("Got info for pack %s, now downloading stickers to %s", packName, packFolder.getAbsolutePath()));
TelegramStickersPackInfo packInfo = new TelegramStickersPackInfo(packName, set.name, set.stickers.size(), set.version);
listener.onGotPackInfo(packInfo);
getPack(set, packFolder, packInfo, listener);
}
});
}
private void getPack(final StickerSet set, final File packFolder, final TelegramStickersPackInfo packInfo, final PackDownloadListener listener) {
final GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
listener.onPackDownloaded(packInfo, true);
});
final Flag deathFlag = new Flag();
AtomicInteger retryed = new AtomicInteger();
for (int i = 0; i < set.stickers.size(); i++) {
final Sticker sticker = set.stickers.get(i);
NetRequest fileInfoRequest = new NetRequest.Builder().get().url(String.format(GET_FILE_URL, botApiKey, sticker.fileId)).build();
final int stickerIndex = i + 1;
AtomicBoolean sync = new AtomicBoolean();
httpClient.newCall(fileInfoRequest).enqueue(new NetCallback() {
@Override
public void onFailure(NetCall call, IOException e) {
if (deathFlag.up())
return;
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
}
@Override
public void onResponse(NetCall call, NetResponse response) throws IOException {
if (deathFlag.up())
return;
if (response.code() != 200) {
retryed.incrementAndGet();
if (retryed.get() > 5)
onFailure(call, new IOException());
else httpClient.newCall(fileInfoRequest)
.enqueue(this);
return;
}
String filePath;
try {
JSONObject jFileInfo = new JSONObject(response.getDataString()).getJSONObject("result");
filePath = jFileInfo.getString("file_path");
} catch (Exception e) {
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
if (botApiKey == null) botApiKey = "";
NetRequest fileDownloadRequest = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_FILE_URL, botApiKey, filePath)).build();
httpClient.newCall(fileDownloadRequest).enqueue(new NetCallback() {
@Override
public void onFailure(NetCall call, IOException e) {
if (deathFlag.up())
return;
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
}
@Override
public void onResponse(NetCall call, NetResponse response) throws IOException {
if (deathFlag.up())
return;
if (response.code() != 200) {
retryed.incrementAndGet();
if (retryed.get() > 5)
onFailure(call, new IOException());
else httpClient.newCall(fileDownloadRequest)
.enqueue(this);
return;
}
if (response.getData() == null) {
deathFlag.raise();
listener.onPackDownloadError(new TSGException("Response body for a sticker is null :/"));
sync.set(true);
return;
}
InputStream stickerInputStream = response.getDataStream();
Bitmap stickerImage = BitmapFactory.decodeStream(stickerInputStream);
try {
stickerInputStream.close();
} catch (IOException e) {
e.printStackTrace();
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
if (stickerImage == null) {
deathFlag.raise();
listener.onPackDownloadError(new TSGException("Unable to decode sticker image"));
sync.set(true);
return;
}
File stickerFile = new File(packFolder, String.format("%03d.png", stickerIndex));
FileOutputStream fileOutputStream;
try {
fileOutputStream = new FileOutputStream(stickerFile);
} catch (Exception e) {
e.printStackTrace();
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
if (!stickerImage.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)) {
stickerImage.recycle();
deathFlag.raise();
listener.onPackDownloadError(new TSGException("Unable to compress sticker to a png file at path: " + stickerFile.getAbsolutePath()));
sync.set(true);
return;
}
stickerImage.recycle();
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
deathFlag.raise();
listener.onPackDownloadError(e);
sync.set(true);
return;
}
listener.onStickerDownloaded(set.name, stickerFile, sticker.emoji, stickerIndex, downloadedStickers.value() + 1, set.stickers.size());
downloadedStickers.increase();
sync.set(true);
}
});
}
});
while (!sync.get())
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
}
}
private void runOnUiThread(Runnable r) {
uiThreadHandler.post(r);
}
}
@@ -0,0 +1,18 @@
package com.aefyr.tsg.g2.stickersgrabber;
/**
* Created by Aefyr on 22.05.2018.
*/
public class TelegramStickersPackInfo {
public String id;
public String title;
public String version;
public int stickersCount;
TelegramStickersPackInfo(String id, String title, int stickersCount, String version) {
this.id = id;
this.title = title;
this.version = version;
this.stickersCount = stickersCount;
}
}
@@ -0,0 +1,20 @@
package com.aefyr.tsg.g2.stickersgrabber.util;
/**
* Created by Aefyr on 11.05.2018.
*/
public class Flag {
private boolean up = false;
public synchronized boolean up() {
return up;
}
public synchronized void raise() {
up = true;
}
public synchronized void lower() {
up = false;
}
}
@@ -0,0 +1,39 @@
package com.aefyr.tsg.g2.stickersgrabber.util;
import android.util.Log;
/**
* Created by Aefyr on 11.05.2018.
*/
public class GoalCounter {
public interface OnGoalReachListener {
void onGoalReached();
}
private int goal;
private int i = 0;
private OnGoalReachListener listener;
public GoalCounter(int goal, OnGoalReachListener listener) {
this.goal = goal;
this.listener = listener;
}
public synchronized void increase() {
if (++i == goal)
listener.onGoalReached();
Log.d("COUNTER", "G=" + goal + ", I=" + i);
}
public synchronized void increase(int delta) {
if ((i += delta) == goal)
listener.onGoalReached();
}
public synchronized int value() {
return i;
}
}
File diff suppressed because it is too large Load Diff