Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5180f61d5 | ||
|
|
540fffdcc9 | ||
|
|
5357c60eaa | ||
|
|
c59f29121f | ||
|
|
bd38cab377 | ||
|
|
0058431590 |
@@ -36,8 +36,8 @@ public class TelegramStickersPack extends CustomStickersPack {
|
|||||||
public HashMap<String, List<Integer>> emojis = new HashMap<>();
|
public HashMap<String, List<Integer>> emojis = new HashMap<>();
|
||||||
|
|
||||||
public TelegramStickersPack(String id) {
|
public TelegramStickersPack(String id) {
|
||||||
this.id=this.title=id;
|
this.id = this.title = id;
|
||||||
this.hash=id.hashCode();
|
this.hash = id.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject encodeEmojis() throws JSONException {
|
public JSONObject encodeEmojis() throws JSONException {
|
||||||
|
|||||||
@@ -26,19 +26,13 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
|||||||
public class TelegramStickersService {
|
public class TelegramStickersService {
|
||||||
private static final String TAG = "TGStickersService";
|
private static final String TAG = "TGStickersService";
|
||||||
private static TelegramStickersService instance;
|
private static TelegramStickersService instance;
|
||||||
|
|
||||||
private final Context c;
|
|
||||||
|
|
||||||
private final ArrayList<TelegramStickersPack> packs;
|
private final ArrayList<TelegramStickersPack> packs;
|
||||||
private final ArrayList<TelegramStickersPack> activePacks;
|
private final ArrayList<TelegramStickersPack> activePacks;
|
||||||
private final ArrayList<TelegramStickersPack> inactivePacks;
|
private final ArrayList<TelegramStickersPack> inactivePacks;
|
||||||
private final ArrayList<StickersEventsListener> listeners;
|
private final ArrayList<StickersEventsListener> listeners;
|
||||||
private final HashSet<String> currentlyDownloading;
|
private final HashSet<String> currentlyDownloading;
|
||||||
|
|
||||||
private final Handler uiThreadHandler;
|
private final Handler uiThreadHandler;
|
||||||
|
|
||||||
private final TelegramStickersGrabber grabber;
|
private final TelegramStickersGrabber grabber;
|
||||||
|
|
||||||
private final TelegramStickersDbHelper dbHelper;
|
private final TelegramStickersDbHelper dbHelper;
|
||||||
private final ArrayList<Runnable> queuedTasks;
|
private final ArrayList<Runnable> queuedTasks;
|
||||||
private final ThreadPoolExecutor executor;
|
private final ThreadPoolExecutor executor;
|
||||||
@@ -48,16 +42,14 @@ public class TelegramStickersService {
|
|||||||
private TelegramStickersService(Context context) {
|
private TelegramStickersService(Context context) {
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
this.c = context.getApplicationContext();
|
|
||||||
|
|
||||||
listeners = new ArrayList<>();
|
listeners = new ArrayList<>();
|
||||||
currentlyDownloading = new HashSet<>();
|
currentlyDownloading = new HashSet<>();
|
||||||
uiThreadHandler = new Handler(Looper.getMainLooper());
|
uiThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
grabber = new TelegramStickersGrabber(null);
|
grabber = new TelegramStickersGrabber(null);
|
||||||
dbHelper = new TelegramStickersDbHelper(c);
|
dbHelper = new TelegramStickersDbHelper(context);
|
||||||
queuedTasks = new ArrayList<>();
|
queuedTasks = new ArrayList<>();
|
||||||
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
|
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
|
||||||
notificationsHelper = new NotificationsHelper(c);
|
notificationsHelper = new NotificationsHelper(context);
|
||||||
|
|
||||||
packs = new ArrayList<>();
|
packs = new ArrayList<>();
|
||||||
activePacks = new ArrayList<>();
|
activePacks = new ArrayList<>();
|
||||||
@@ -70,11 +62,11 @@ public class TelegramStickersService {
|
|||||||
return instance == null ? new TelegramStickersService(c) : instance;
|
return instance == null ? new TelegramStickersService(c) : instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePacks(final boolean notify) {
|
private void updatePacks(boolean notify) {
|
||||||
packs.clear();
|
packs.clear();
|
||||||
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
|
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPackLoaded(final TelegramStickersPack pack) {
|
public void onPackLoaded(TelegramStickersPack pack) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
packs.add(pack);
|
packs.add(pack);
|
||||||
|
|
||||||
@@ -103,14 +95,6 @@ public class TelegramStickersService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addStickersEventsListener(StickersEventsListener listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeStickersEventsListener(StickersEventsListener listener) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyPackAdded(TelegramStickersPack pack, int atIndex) {
|
private void notifyPackAdded(TelegramStickersPack pack, int atIndex) {
|
||||||
getGlobalContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
|
getGlobalContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
|
||||||
if (listeners.isEmpty())
|
if (listeners.isEmpty())
|
||||||
@@ -188,20 +172,16 @@ public class TelegramStickersService {
|
|||||||
return inactivePacks;
|
return inactivePacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDoneLoading() {
|
public void requestPackDownload(String id, File packFolder) {
|
||||||
return ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean requestPackDownload(final String id, File packFolder) {
|
|
||||||
if (currentlyDownloading.contains(id)) {
|
if (currentlyDownloading.contains(id)) {
|
||||||
Log.e(TAG, String.format("Got request to download pack %s which is already downloading", id));
|
Log.e(TAG, String.format("Got request to download pack %s which is already downloading", id));
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
final File folder = packFolder;
|
File folder = packFolder;
|
||||||
queuedTasks.add(() -> requestPackDownload(id, folder));
|
queuedTasks.add(() -> requestPackDownload(id, folder));
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TelegramStickersPack pack = new TelegramStickersPack(id);
|
TelegramStickersPack pack = new TelegramStickersPack(id);
|
||||||
@@ -221,85 +201,86 @@ public class TelegramStickersService {
|
|||||||
notifyPackAdded(pack, packs.size() - 1);
|
notifyPackAdded(pack, packs.size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
final TelegramStickersPack newPack = pack;
|
TelegramStickersPack newPack = pack;
|
||||||
final boolean isUpdate = update;
|
boolean isUpdate = update;
|
||||||
|
|
||||||
currentlyDownloading.add(id);
|
currentlyDownloading.add(id);
|
||||||
notificationsHelper.packStartedDownloading(newPack);
|
notificationsHelper.packStartedDownloading(newPack);
|
||||||
|
|
||||||
|
// try-catch block to handle possible timeout exception
|
||||||
grabber.enableProxy();
|
try {
|
||||||
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
|
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
|
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
|
||||||
|
runOnUiThread(() -> {
|
||||||
runOnUiThread(() -> {
|
|
||||||
newPack.state = TelegramStickersPack.DOWNLOADED;
|
|
||||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
|
||||||
currentlyDownloading.remove(newPack.id);
|
|
||||||
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;
|
newPack.state = TelegramStickersPack.DOWNLOADED;
|
||||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||||
}
|
currentlyDownloading.remove(newPack.id);
|
||||||
|
syncPack(newPack);
|
||||||
|
|
||||||
notifyPackDownloadError(newPack, e);
|
if (isUpdate) {
|
||||||
currentlyDownloading.remove(newPack.id);
|
if (newPack.enabled)
|
||||||
});
|
notifyActivePacksListChanged();
|
||||||
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
|
} else {
|
||||||
}
|
activePacks.add(newPack);
|
||||||
|
notifyActivePacksListChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGotPackInfo(final TelegramStickersPackInfo packInfo) {
|
public void onPackDownloadError(Exception e) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
newPack.title = packInfo.title;
|
Log.e(TAG, "Error while downloading pack " + newPack.id);
|
||||||
newPack.stickersCount = packInfo.stickersCount;
|
e.printStackTrace();
|
||||||
newPack.version = packInfo.version;
|
|
||||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
|
||||||
});
|
|
||||||
|
|
||||||
notificationsHelper.packDownloadUpdated(newPack, 0);
|
if (!isUpdate) {
|
||||||
}
|
int index = packs.indexOf(newPack);
|
||||||
|
packs.remove(index);
|
||||||
|
notifyPackRemoved(newPack, index);
|
||||||
|
} else {
|
||||||
|
newPack.state = TelegramStickersPack.DOWNLOADED;
|
||||||
|
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
notifyPackDownloadError(newPack, e);
|
||||||
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
|
currentlyDownloading.remove(newPack.id);
|
||||||
stickerIndex--;
|
});
|
||||||
|
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
|
||||||
|
}
|
||||||
|
|
||||||
List<Integer> list = newPack.emojis.get(boundEmoji);
|
@Override
|
||||||
if (list == null) list = new ArrayList<>();
|
public void onGotPackInfo(TelegramStickersPackInfo packInfo) {
|
||||||
list.add(stickerIndex);
|
runOnUiThread(() -> {
|
||||||
newPack.emojis.put(boundEmoji, list);
|
newPack.title = packInfo.title;
|
||||||
|
newPack.stickersCount = packInfo.stickersCount;
|
||||||
|
newPack.version = packInfo.version;
|
||||||
|
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||||
|
});
|
||||||
|
|
||||||
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
|
notificationsHelper.packDownloadUpdated(newPack, 0);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
@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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
// handle other possible exceptions
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPackEnabled(final TelegramStickersPack pack, final boolean enabled, final boolean notify) {
|
public void setPackEnabled(TelegramStickersPack pack, boolean enabled, boolean notify) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
|
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
|
||||||
return;
|
return;
|
||||||
@@ -325,7 +306,7 @@ public class TelegramStickersService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePack(final TelegramStickersPack pack) {
|
public void deletePack(TelegramStickersPack pack) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
queuedTasks.add(() -> deletePack(pack));
|
queuedTasks.add(() -> deletePack(pack));
|
||||||
return;
|
return;
|
||||||
@@ -334,7 +315,6 @@ public class TelegramStickersService {
|
|||||||
boolean d = dbHelper.deletePack(pack);
|
boolean d = dbHelper.deletePack(pack);
|
||||||
Log.d(TAG, "Delete from DB=" + d);
|
Log.d(TAG, "Delete from DB=" + d);
|
||||||
|
|
||||||
|
|
||||||
int index = packs.indexOf(pack);
|
int index = packs.indexOf(pack);
|
||||||
packs.remove(index);
|
packs.remove(index);
|
||||||
notifyPackRemoved(pack, index);
|
notifyPackRemoved(pack, index);
|
||||||
@@ -355,7 +335,7 @@ public class TelegramStickersService {
|
|||||||
uiThreadHandler.post(r);
|
uiThreadHandler.post(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncPack(final TelegramStickersPack pack) {
|
private void syncPack(TelegramStickersPack pack) {
|
||||||
if (!ready) {
|
if (!ready) {
|
||||||
queuedTasks.add(() -> syncPack(pack));
|
queuedTasks.add(() -> syncPack(pack));
|
||||||
return;
|
return;
|
||||||
@@ -397,6 +377,4 @@ public class TelegramStickersService {
|
|||||||
|
|
||||||
void onInactivePacksListChanged();
|
void onInactivePacksListChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,23 +7,20 @@ import android.os.Looper;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.aefyr.tsg.g2.stickersgrabber.util.Flag;
|
import com.aefyr.tsg.g2.stickersgrabber.util.Flag;
|
||||||
import com.aefyr.tsg.g2.stickersgrabber.util.GoalCounter;
|
import com.aefyr.tsg.g2.stickersgrabber.util.GoalCounter;
|
||||||
|
import okhttp3.*;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.lite.net.*;
|
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.PasswordAuthentication;
|
|
||||||
import java.net.Proxy;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -31,14 +28,19 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
*/
|
*/
|
||||||
public class TelegramStickersGrabber {
|
public class TelegramStickersGrabber {
|
||||||
private static final String TAG = "TSG";
|
private static final String TAG = "TSG";
|
||||||
public static String REAL_TG_IP = null;
|
private static final OkHttpClient sClient = VtOkHttpClient.getInstance();
|
||||||
public static String PROXY_IP = null;
|
private static final int MAX_RETRIES = 2;
|
||||||
public static int PROXY_PORT = -1;
|
private static final AtomicInteger retryCount = new AtomicInteger();
|
||||||
public static String PROXY_USER = null;
|
private static final int STICKER_QUALITY = 100;
|
||||||
public static String PROXY_PASS = null;
|
private static final String STICKER_FILE_NAME_FORMAT = "%03d.png";
|
||||||
public static boolean PROXY_SOCKS = true;
|
private static final String STICKERS_KEY = "stickers";
|
||||||
public static boolean HAS_PASS = false;
|
private static final String FILE_ID_KEY = "file_id";
|
||||||
public static boolean USE_PROXY = false;
|
private static final String EMOJI_KEY = "emoji";
|
||||||
|
private static final String IS_ANIMATED_KEY = "is_animated";
|
||||||
|
private static final String IS_VIDEO_KEY = "is_video";
|
||||||
|
private static final String THUMB_KEY = "thumb";
|
||||||
|
private static final String NAME_KEY = "name";
|
||||||
|
private static final String TITLE_KEY = "title";
|
||||||
private static String BOT_API_BASE_URL;
|
private static String BOT_API_BASE_URL;
|
||||||
private static String GET_STICKER_SET_URL;
|
private static String GET_STICKER_SET_URL;
|
||||||
private static String GET_FILE_URL;
|
private static String GET_FILE_URL;
|
||||||
@@ -50,12 +52,10 @@ public class TelegramStickersGrabber {
|
|||||||
|
|
||||||
private final MessageDigest sha256;
|
private final MessageDigest sha256;
|
||||||
private final Handler uiThreadHandler;
|
private final Handler uiThreadHandler;
|
||||||
private String botApiKey = "";
|
private String botApiKey;
|
||||||
private NetClient httpClient;
|
|
||||||
|
|
||||||
public TelegramStickersGrabber(String botApiKey) {
|
public TelegramStickersGrabber(String botApiKey) {
|
||||||
this.botApiKey = botApiKey;
|
this.botApiKey = botApiKey;
|
||||||
httpClient = new NetClient.Builder().connectTimeout(5, TimeUnit.SECONDS).build();
|
|
||||||
uiThreadHandler = new Handler(Looper.getMainLooper());
|
uiThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
try {
|
try {
|
||||||
sha256 = MessageDigest.getInstance("SHA-256");
|
sha256 = MessageDigest.getInstance("SHA-256");
|
||||||
@@ -69,81 +69,12 @@ public class TelegramStickersGrabber {
|
|||||||
public static void updateURLs() {
|
public static void updateURLs() {
|
||||||
BOT_API_BASE_FILE_URL = "https://api.telegram.org/file/bot%s/%s";
|
BOT_API_BASE_FILE_URL = "https://api.telegram.org/file/bot%s/%s";
|
||||||
|
|
||||||
|
BOT_API_BASE_URL = "https://api.telegram.org/bot%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_STICKER_SET_URL = BOT_API_BASE_URL + "getStickerSet?name=%s";
|
||||||
GET_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
|
GET_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isTelegramBlocked() {
|
public void grabPack(String id, File packFolder, String installedVersion, PackDownloadListener listener) {
|
||||||
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 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);
|
getPackInfo(id, packFolder, installedVersion, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,264 +82,247 @@ public class TelegramStickersGrabber {
|
|||||||
botApiKey = key;
|
botApiKey = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkKey(final KeyCheckListener listener) {
|
public void checkKey(KeyCheckListener listener) {
|
||||||
Log.d(TAG, "Checking key: " + botApiKey);
|
Log.d(TAG, "Checking key: " + botApiKey);
|
||||||
NetRequest request = new NetRequest.Builder().get().url(String.format(BOT_API_BASE_URL + "getMe", botApiKey)).build();
|
// Create a request object with the URL and bot API key
|
||||||
httpClient.newCall(request).enqueue(new NetCallback() {
|
Request request = new Request.a()
|
||||||
|
.b(String.format(BOT_API_BASE_URL + "getMe", botApiKey))
|
||||||
|
.a();
|
||||||
|
// Create a new call object and enqueue it to run asynchronously
|
||||||
|
sClient.a(request).a(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(NetCall call, IOException e) {
|
public void a(Call call, IOException e) {
|
||||||
e.printStackTrace();
|
// Handle the failure case
|
||||||
|
Log.d(TAG, e.getMessage());
|
||||||
runOnUiThread(listener::onNetError);
|
runOnUiThread(listener::onNetError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(NetCall call, final NetResponse response) throws IOException {
|
public void a(Call call, Response response) {
|
||||||
runOnUiThread(() -> listener.onKeyChecked(response.code() == 200));
|
Log.d(TAG, response.toString());
|
||||||
|
runOnUiThread(() -> listener.onKeyChecked(response.h()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getPackInfo(final String packName, final File packFolder, final String installedVersion, final PackDownloadListener listener) {
|
private void getPack(StickerSet set, File packFolder, TelegramStickersPackInfo packInfo, PackDownloadListener listener) {
|
||||||
NetRequest packInfoRequest = new NetRequest.Builder().get().url(String.format(GET_STICKER_SET_URL, botApiKey, packName)).build();
|
GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
|
||||||
httpClient.newCall(packInfoRequest).enqueue(new NetCallback() {
|
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
|
||||||
|
listener.onPackDownloaded(packInfo, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Flag deathFlag = new Flag();
|
||||||
|
|
||||||
|
for (int i = 0; i < set.stickers.size(); i++) {
|
||||||
|
Sticker sticker = set.stickers.get(i);
|
||||||
|
String getFileUrl = String.format(GET_FILE_URL, botApiKey, sticker.fileId);
|
||||||
|
Request fileInfoRequest = new Request.a()
|
||||||
|
.b(getFileUrl)
|
||||||
|
.a();
|
||||||
|
|
||||||
|
int stickerIndex = i + 1;
|
||||||
|
|
||||||
|
sClient.a(fileInfoRequest).a(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void a(Call call, IOException e) {
|
||||||
|
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileInfoRequest, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(Call call, Response response) {
|
||||||
|
if (deathFlag.up())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!response.h()) {
|
||||||
|
retryRequest(call, fileInfoRequest, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ResponseBody responseBody = response.a()) {
|
||||||
|
JSONObject fileInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
|
||||||
|
String filePath = fileInfoResponse.getString("file_path");
|
||||||
|
|
||||||
|
if (botApiKey == null) botApiKey = "";
|
||||||
|
|
||||||
|
String fileDownloadUrl = String.format(BOT_API_BASE_FILE_URL, botApiKey, filePath);
|
||||||
|
Request fileDownloadRequest = new Request.a()
|
||||||
|
.b(fileDownloadUrl)
|
||||||
|
.a();
|
||||||
|
|
||||||
|
sClient.a(fileDownloadRequest).a(new Callback() {
|
||||||
|
@Override
|
||||||
|
public void a(Call call, IOException e) {
|
||||||
|
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileDownloadRequest, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a(Call call, Response response) {
|
||||||
|
if (deathFlag.up())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!response.h()) {
|
||||||
|
retryRequest(call, fileDownloadRequest, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (ResponseBody responseBody = response.a()) {
|
||||||
|
if (responseBody == null) {
|
||||||
|
handleFailure(deathFlag, listener, new TSGException("Response body for a sticker is null :/"), downloadedStickers, call, fileDownloadRequest, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream stickerInputStream = responseBody.a();
|
||||||
|
Bitmap stickerImage = BitmapFactory.decodeStream(stickerInputStream);
|
||||||
|
|
||||||
|
if (stickerImage == null) {
|
||||||
|
handleFailure(deathFlag, listener, new TSGException("Unable to decode sticker image"), downloadedStickers, call, fileDownloadRequest, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File stickerFile = new File(packFolder, String.format(STICKER_FILE_NAME_FORMAT, stickerIndex));
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveStickerImage(stickerImage, stickerFile);
|
||||||
|
} catch (TSGException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener.onStickerDownloaded(set.name, stickerFile, sticker.emoji, stickerIndex,
|
||||||
|
downloadedStickers.value() + 1, set.stickers.size());
|
||||||
|
downloadedStickers.increase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleFailure(deathFlag, listener, e, downloadedStickers, call, fileInfoRequest, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleFailure(Flag deathFlag, PackDownloadListener listener, Exception e, GoalCounter downloadedStickers, Call call, Request request, Callback callback) {
|
||||||
|
if ((e instanceof IOException || e instanceof TSGException) && retryCount.get() > MAX_RETRIES) {
|
||||||
|
Log.e(TAG, "Unable to download sticker: " + e.getMessage());
|
||||||
|
downloadedStickers.increase();
|
||||||
|
return;
|
||||||
|
} else if (retryCount.get() < MAX_RETRIES) {
|
||||||
|
retryRequest(call, request, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deathFlag.up()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
deathFlag.raise();
|
||||||
|
listener.onPackDownloadError(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void retryRequest(Call call, Request request, Callback callback) {
|
||||||
|
retryCount.incrementAndGet();
|
||||||
|
|
||||||
|
if (retryCount.get() > MAX_RETRIES) {
|
||||||
|
Log.e(TAG, "retryRequest: max retries");
|
||||||
|
callback.a(call, new IOException());
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "retryRequest");
|
||||||
|
sClient.a(request).a(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveStickerImage(Bitmap image, File file) throws TSGException {
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||||
|
image.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new TSGException("Unable to save sticker image");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getPackInfo(String packName, File packFolder, String installedVersion, PackDownloadListener listener) {
|
||||||
|
String getStickerSetUrl = String.format(GET_STICKER_SET_URL, botApiKey, packName);
|
||||||
|
Request packInfoRequest = new Request.a()
|
||||||
|
.b(getStickerSetUrl)
|
||||||
|
.a();
|
||||||
|
sClient.a(packInfoRequest).a(new Callback() {
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(NetCall call, IOException e) {
|
public void a(Call call, IOException e) {
|
||||||
|
Log.d(TAG, e.getMessage());
|
||||||
listener.onPackDownloadError(e);
|
listener.onPackDownloadError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResponse(NetCall call, NetResponse response) {
|
public void a(Call call, Response response) throws IOException {
|
||||||
if (!response.isSuccessful()) {
|
Log.d(TAG, response.toString());
|
||||||
if (response.getData() != null)
|
|
||||||
listener.onPackDownloadError(new TSGException(response.getDataString() + "\nURL: " + call.request().url()));
|
|
||||||
else
|
|
||||||
listener.onPackDownloadError(new TSGException("Unknown Exception, no response body"));
|
|
||||||
|
|
||||||
|
if (!response.h()) {
|
||||||
|
String errorMessage = response.a() != null ? response.a().g() + "\nURL: " + call.m0().g() : "Unknown Exception, no response body";
|
||||||
|
listener.onPackDownloadError(new TSGException(errorMessage));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StickerSet set;
|
try (ResponseBody responseBody = response.a()) {
|
||||||
try {
|
JSONObject packInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
|
||||||
JSONObject jPackInfo = new JSONObject(response.getDataString()).getJSONObject("result");
|
String packVersion = new String(sha256.digest(packInfoResponse.toString().getBytes(StandardCharsets.UTF_8)));
|
||||||
String packVersion = new String(sha256.digest(jPackInfo.toString().getBytes(StandardCharsets.UTF_8)));
|
|
||||||
|
|
||||||
if (packVersion.equals(installedVersion)) {
|
if (packVersion.equals(installedVersion)) {
|
||||||
listener.onPackDownloaded(null, false);
|
listener.onPackDownloaded(null, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONArray jStickers = jPackInfo.getJSONArray("stickers");
|
JSONArray stickersArray = packInfoResponse.getJSONArray(STICKERS_KEY);
|
||||||
|
|
||||||
if (jStickers.length() == 0) {
|
if (stickersArray.length() == 0) {
|
||||||
listener.onPackDownloadError(new TSGException("No stickers in pack"));
|
listener.onPackDownloadError(new TSGException("No stickers in pack"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Sticker> stickers = new ArrayList<>(jStickers.length());
|
ArrayList<Sticker> stickers = parseStickers(stickersArray);
|
||||||
|
|
||||||
Log.d(TAG, String.format("Parsing stickers in pack %s", packName));
|
StickerSet set = new StickerSet(packInfoResponse.getString(NAME_KEY), packInfoResponse.getString(TITLE_KEY), packVersion, stickers);
|
||||||
|
|
||||||
for (int i = 0; i < jStickers.length(); i++) {
|
if (!packFolder.exists() && !packFolder.mkdirs()) {
|
||||||
JSONObject jSticker = jStickers.getJSONObject(i);
|
// Handle the folder creation failure case
|
||||||
if (jSticker.optBoolean("is_animated") || jSticker.optBoolean("is_video")) {
|
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
|
||||||
if (jSticker.has("thumb")) {
|
return;
|
||||||
stickers.add(new Sticker(jSticker.getJSONObject("thumb").getString("file_id"), jSticker.getString("emoji")));
|
|
||||||
} else {
|
|
||||||
listener.onPackDownloadError(new TSGException("Animated and video stickerpacks without thumbs are not supported!"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stickers.add(new Sticker(jSticker.getString("file_id"), jSticker.getString("emoji")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set = new StickerSet(jPackInfo.getString("name"), jPackInfo.getString("title"), packVersion, stickers);
|
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);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
listener.onPackDownloadError(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) {
|
private ArrayList<Sticker> parseStickers(JSONArray stickersArray) throws JSONException, TSGException {
|
||||||
final GoalCounter downloadedStickers = new GoalCounter(set.stickers.size(), () -> {
|
ArrayList<Sticker> stickers = new ArrayList<>(stickersArray.length());
|
||||||
Log.d(TAG, String.format("Pack %s has been downloaded to %s", set.id, packFolder.getAbsolutePath()));
|
|
||||||
|
|
||||||
listener.onPackDownloaded(packInfo, true);
|
for (int i = 0; i < stickersArray.length(); i++) {
|
||||||
});
|
JSONObject stickerObject = stickersArray.getJSONObject(i);
|
||||||
final Flag deathFlag = new Flag();
|
boolean isAnimated = stickerObject.optBoolean(IS_ANIMATED_KEY);
|
||||||
|
boolean isVideo = stickerObject.optBoolean(IS_VIDEO_KEY);
|
||||||
|
String fileId = stickerObject.getString(FILE_ID_KEY);
|
||||||
|
String emoji = stickerObject.getString(EMOJI_KEY);
|
||||||
|
|
||||||
AtomicInteger retryed = new AtomicInteger();
|
if (isAnimated || isVideo) {
|
||||||
for (int i = 0; i < set.stickers.size(); i++) {
|
if (stickerObject.has(THUMB_KEY)) {
|
||||||
final Sticker sticker = set.stickers.get(i);
|
fileId = stickerObject.getJSONObject(THUMB_KEY).getString(FILE_ID_KEY);
|
||||||
NetRequest fileInfoRequest = new NetRequest.Builder().get().url(String.format(GET_FILE_URL, botApiKey, sticker.fileId)).build();
|
} else {
|
||||||
|
throw new TSGException("Animated and video stickerpacks without thumbs are not supported!");
|
||||||
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
|
stickers.add(new Sticker(fileId, emoji));
|
||||||
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) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return stickers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void runOnUiThread(Runnable r) {
|
private void runOnUiThread(Runnable r) {
|
||||||
@@ -431,15 +345,13 @@ public class TelegramStickersGrabber {
|
|||||||
void onNetError();
|
void onNetError();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TSGException extends Exception {
|
public static class TSGException extends Exception {
|
||||||
private static final long serialVersionUID = 7866915509988422944L;
|
|
||||||
|
|
||||||
private TSGException(String message) {
|
private TSGException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class StickerSet {
|
private static class StickerSet {
|
||||||
String id;
|
String id;
|
||||||
String name;
|
String name;
|
||||||
String version;
|
String version;
|
||||||
@@ -453,7 +365,7 @@ public class TelegramStickersGrabber {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Sticker {
|
private static class Sticker {
|
||||||
String emoji;
|
String emoji;
|
||||||
String fileId;
|
String fileId;
|
||||||
|
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ public class Preferences {
|
|||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
public static boolean checkupdates() {
|
public static boolean checkupdates() {
|
||||||
return !getBoolValue("isRoamingState", false) && isValidSignature() && BuildConfig.BUILD_TYPE.equals("release");
|
return !getBoolValue("isRoamingState", false) && isValidSignature() && BuildConfig.BUILD_TYPE.equals("release") && getBoolValue("autoupdates", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isNewBuild() {
|
public static boolean isNewBuild() {
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ public class ProfileHook {
|
|||||||
|
|
||||||
if (UsersList.hasDescription(userID)) {
|
if (UsersList.hasDescription(userID)) {
|
||||||
try {
|
try {
|
||||||
extendedUserProfile.z2 = Handler.getDescription(userID);
|
String description = Handler.getDescription(userID);
|
||||||
|
if (!TextUtils.isEmpty(description)) {
|
||||||
|
extendedUserProfile.z2 = description;
|
||||||
|
}
|
||||||
} catch (NullPointerException e) {
|
} catch (NullPointerException e) {
|
||||||
Log.e(TAG, "null \"profiles\" node\n" + e);
|
Log.e(TAG, "null \"profiles\" node\n" + e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package ru.vtosters.lite.foaf;
|
package ru.vtosters.lite.foaf;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||||
import com.vk.core.network.Network;
|
|
||||||
import com.vtosters.lite.R;
|
import com.vtosters.lite.R;
|
||||||
import okhttp3.Headers;
|
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -15,13 +12,9 @@ import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
|||||||
import ru.vtosters.lite.net.Request;
|
import ru.vtosters.lite.net.Request;
|
||||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ package ru.vtosters.lite.net;
|
|||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
public final class NetCall {
|
public final class NetCall {
|
||||||
|
// Constants for the request methods, content types, and encoding
|
||||||
|
private static final String GET = "GET";
|
||||||
|
private static final String POST = "POST";
|
||||||
|
private static final String FORM = "application/x-www-form-urlencoded";
|
||||||
|
private static final String UTF_8 = "UTF-8";
|
||||||
|
|
||||||
private final NetRequest req;
|
private final NetRequest req;
|
||||||
private final NetClient cl;
|
private final NetClient cl;
|
||||||
|
|
||||||
@@ -22,13 +25,17 @@ public final class NetCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String constructArgs(Map<String, String> args) throws UnsupportedEncodingException {
|
private static String constructArgs(Map<String, String> args) throws UnsupportedEncodingException {
|
||||||
|
// Use a TreeMap to sort the arguments by key
|
||||||
args = new TreeMap<>(args);
|
args = new TreeMap<>(args);
|
||||||
String str = "";
|
// Use a StringBuilder to append the arguments
|
||||||
for (Map.Entry<String, String> en : args.entrySet()) {
|
StringBuilder sb = new StringBuilder();
|
||||||
if (!str.isEmpty()) str += "&";
|
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||||
str += URLEncoder.encode(en.getKey(), "UTF-8") + "=" + URLEncoder.encode(en.getValue(), "UTF-8");
|
if (sb.length() > 0) sb.append("&");
|
||||||
|
sb.append(URLEncoder.encode(entry.getKey(), UTF_8))
|
||||||
|
.append("=")
|
||||||
|
.append(URLEncoder.encode(entry.getValue(), UTF_8));
|
||||||
}
|
}
|
||||||
return str;
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetRequest request() {
|
public NetRequest request() {
|
||||||
@@ -38,38 +45,52 @@ public final class NetCall {
|
|||||||
public NetResponse execute() throws IOException {
|
public NetResponse execute() throws IOException {
|
||||||
Proxy proxy = cl.getProxy();
|
Proxy proxy = cl.getProxy();
|
||||||
PasswordAuthentication auth = cl.getAuthenticator();
|
PasswordAuthentication auth = cl.getAuthenticator();
|
||||||
HttpURLConnection con = (HttpURLConnection) new URL(req.url()).openConnection(proxy != null ? proxy : Proxy.NO_PROXY);
|
HttpURLConnection connection = (HttpURLConnection) new URL(req.url()).openConnection(proxy != null ? proxy : Proxy.NO_PROXY);
|
||||||
if (proxy != null && auth != null) {
|
if (proxy != null && auth != null) {
|
||||||
|
// Set the proxy authentication header
|
||||||
String authStr = "Basic " + Base64.encodeToString((auth.getUserName() + new String(auth.getPassword())).getBytes(), Base64.NO_WRAP);
|
String authStr = "Basic " + Base64.encodeToString((auth.getUserName() + new String(auth.getPassword())).getBytes(), Base64.NO_WRAP);
|
||||||
con.setRequestProperty("Proxy-Connection", "Keep-Alive");
|
connection.setRequestProperty("Proxy-Connection", "Keep-Alive");
|
||||||
con.setRequestProperty("Proxy-Authorization", authStr);
|
connection.setRequestProperty("Proxy-Authorization", authStr);
|
||||||
}
|
}
|
||||||
con.setConnectTimeout((int) cl.getTimeout());
|
connection.setConnectTimeout((int) cl.getTimeout());
|
||||||
con.setRequestMethod(req.getRequestMethod());
|
connection.setRequestMethod(req.getRequestMethod());
|
||||||
if (req.getRequestMethod().equals("POST")) {
|
if (req.getRequestMethod().equals(POST)) {
|
||||||
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
// Set the content type for POST requests
|
||||||
|
connection.setRequestProperty("Content-Type", FORM);
|
||||||
}
|
}
|
||||||
Map<String, String> params = req.getRequestParams();
|
Map<String, String> params = req.getRequestParams();
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
String strArgs = constructArgs(params);
|
// Write the request parameters to the output stream
|
||||||
con.setRequestProperty("Content-Length", String.valueOf(strArgs.getBytes(StandardCharsets.UTF_8).length));
|
writeParams(connection, params);
|
||||||
con.setDoOutput(true);
|
}
|
||||||
con.getOutputStream().write(strArgs.getBytes(StandardCharsets.UTF_8));
|
connection.connect();
|
||||||
con.getOutputStream().close();
|
InputStream input;
|
||||||
|
if (connection.getResponseCode() == 200) input = connection.getInputStream();
|
||||||
|
else input = connection.getErrorStream();
|
||||||
|
// Use try-with-resources to close the streams automatically
|
||||||
|
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
|
||||||
|
byte[] buffer = new byte[1024 * 1024];
|
||||||
|
int count;
|
||||||
|
while ((count = input.read(buffer)) != -1) output.write(buffer, 0, count);
|
||||||
|
input.close();
|
||||||
|
NetResponse response = new NetResponse(output.toByteArray());
|
||||||
|
response.setCode(connection.getResponseCode());
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeParams(HttpURLConnection connection, Map<String, String> params) throws IOException {
|
||||||
|
// Get the encoded arguments string
|
||||||
|
String args = constructArgs(params);
|
||||||
|
// Set the content length header
|
||||||
|
connection.setRequestProperty("Content-Length", String.valueOf(args.getBytes(StandardCharsets.UTF_8).length));
|
||||||
|
// Enable output mode
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
// Write the arguments to the output stream
|
||||||
|
try (OutputStream output = connection.getOutputStream()) {
|
||||||
|
output.write(args.getBytes(StandardCharsets.UTF_8));
|
||||||
|
output.flush();
|
||||||
}
|
}
|
||||||
con.connect();
|
|
||||||
InputStream in;
|
|
||||||
if (con.getResponseCode() == 200) in = con.getInputStream();
|
|
||||||
else in = con.getErrorStream();
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
|
||||||
byte[] buffer = new byte[1024 * 1024];
|
|
||||||
int c;
|
|
||||||
while ((c = in.read(buffer)) != -1) bos.write(buffer, 0, c);
|
|
||||||
in.close();
|
|
||||||
bos.close();
|
|
||||||
NetResponse r = new NetResponse(bos.toByteArray());
|
|
||||||
r.setCode(con.getResponseCode());
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enqueue(NetCallback cb) {
|
public void enqueue(NetCallback cb) {
|
||||||
@@ -82,12 +103,16 @@ public final class NetCall {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
cb.onFailure(NetCall.this, e);
|
cb.onFailure(NetCall.this, e);
|
||||||
}
|
}
|
||||||
|
// try {
|
||||||
|
//// interrupt();
|
||||||
try {
|
try {
|
||||||
interrupt();
|
|
||||||
join();
|
join();
|
||||||
} catch (Exception e) {
|
} catch (InterruptedException e) {
|
||||||
Log.e("NetCall", "Failed to interrupt and join thread", e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// Log.e("NetCall", "Failed to interrupt and join thread", e);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ public final class NetResponse {
|
|||||||
private final byte[] data;
|
private final byte[] data;
|
||||||
private int code;
|
private int code;
|
||||||
|
|
||||||
NetResponse(byte[] data) {
|
public NetResponse(byte[] data) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCode(int code) {
|
public void setCode(int code) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class Request {
|
|||||||
httpURLConnection.setRequestProperty("Content-Type", mIsVTostersRequest ? "Content-Type" : "application/x-www-form-urlencoded");
|
httpURLConnection.setRequestProperty("Content-Type", mIsVTostersRequest ? "Content-Type" : "application/x-www-form-urlencoded");
|
||||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
|
||||||
|
|
||||||
StringBuffer stringBuffer = new StringBuffer();
|
StringBuilder stringBuffer = new StringBuilder();
|
||||||
while (true) {
|
while (true) {
|
||||||
String readLine = bufferedReader.readLine();
|
String readLine = bufferedReader.readLine();
|
||||||
if (readLine != null) {
|
if (readLine != null) {
|
||||||
|
|||||||
@@ -7,22 +7,7 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
|||||||
|
|
||||||
public class TGPref {
|
public class TGPref {
|
||||||
private static final SharedPreferences Prefs = PreferenceManager.getDefaultSharedPreferences(getGlobalContext());
|
private static final SharedPreferences Prefs = PreferenceManager.getDefaultSharedPreferences(getGlobalContext());
|
||||||
|
|
||||||
private static final String CNCT_METHOD = "VTGS:CM";
|
|
||||||
private static final String TG_BOTKEY = "VTGS:BKey";
|
private static final String TG_BOTKEY = "VTGS:BKey";
|
||||||
private static final String TG_PROXYIP = "VTGS:PIP";
|
|
||||||
private static final String TG_PROXYPORT = "VTGS:PPT";
|
|
||||||
private static final String TG_PROXYPASS = "VTGS:PPS";
|
|
||||||
private static final String TG_PROXYUSER = "VTGS:PUS";
|
|
||||||
private static final String TG_PROXYUSEPASS = "VTGS:PUsePassword";
|
|
||||||
|
|
||||||
public static int getTGConnectMethod() {
|
|
||||||
return Integer.parseInt(Prefs.getString(CNCT_METHOD, "0"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTGConnectMethod(int method) {
|
|
||||||
Prefs.edit().putString(CNCT_METHOD, String.valueOf(method)).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTGBotKey() {
|
public static String getTGBotKey() {
|
||||||
return Prefs.getString(TG_BOTKEY, null);
|
return Prefs.getString(TG_BOTKEY, null);
|
||||||
@@ -31,40 +16,4 @@ public class TGPref {
|
|||||||
public static void setTGBotKey(String key) {
|
public static void setTGBotKey(String key) {
|
||||||
Prefs.edit().putString(TG_BOTKEY, key).apply();
|
Prefs.edit().putString(TG_BOTKEY, key).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getTGProxyIP() {
|
|
||||||
return Prefs.getString(TG_PROXYIP, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTGProxyIP(String ip) {
|
|
||||||
Prefs.edit().putString(TG_PROXYIP, ip).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getTGProxyPort() {
|
|
||||||
return Prefs.getInt(TG_PROXYPORT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTGProxyPort(int port) {
|
|
||||||
Prefs.edit().putInt(TG_PROXYPORT, port).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTGProxyPass() {
|
|
||||||
return Prefs.getString(TG_PROXYPASS, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getTGProxyUser() {
|
|
||||||
return Prefs.getString(TG_PROXYUSER, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTGProxyUserPass(String user, String pass) {
|
|
||||||
Prefs.edit().putString(TG_PROXYUSER, user).putString(TG_PROXYPASS, pass).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isTGProxyPassEnabled() {
|
|
||||||
return Prefs.getBoolean(TG_PROXYUSEPASS, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setTGProxyPassEnabled(boolean en) {
|
|
||||||
Prefs.edit().putBoolean(TG_PROXYUSEPASS, en).apply();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,9 @@ package ru.vtosters.lite.tgs;
|
|||||||
|
|
||||||
import com.aefyr.tsg.g2.TelegramStickersPack;
|
import com.aefyr.tsg.g2.TelegramStickersPack;
|
||||||
import com.vk.dto.stickers.StickerStockItem;
|
import com.vk.dto.stickers.StickerStockItem;
|
||||||
import com.vtosters.lite.R;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
|
||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
@@ -15,14 +13,6 @@ public class TGRoot {
|
|||||||
public static final int X = 976820000;
|
public static final int X = 976820000;
|
||||||
public final static Stack<Integer> pendingStickers = new Stack<>();
|
public final static Stack<Integer> pendingStickers = new Stack<>();
|
||||||
|
|
||||||
public static String getSummary() {
|
|
||||||
return switch (TGPref.getTGConnectMethod()) {
|
|
||||||
case 0 -> AndroidUtils.getString(R.string.ctypedirect);
|
|
||||||
case 2 -> AndroidUtils.getString(R.string.ctypesocks);
|
|
||||||
default -> AndroidUtils.getString(R.string.ctypedisabled);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StickerStockItem toStickerPack(TelegramStickersPack p) throws JSONException {
|
public static StickerStockItem toStickerPack(TelegramStickersPack p) throws JSONException {
|
||||||
JSONArray arr = new JSONArray();
|
JSONArray arr = new JSONArray();
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
getPreferenceScreen(),
|
getPreferenceScreen(),
|
||||||
"",
|
"",
|
||||||
requireContext().getString(R.string.vtssfs),
|
requireContext().getString(R.string.vtssfs),
|
||||||
getSSFSsumm(),
|
"",
|
||||||
R.drawable.ic_link_circle_outline_28,
|
R.drawable.ic_link_circle_outline_28,
|
||||||
preference -> {
|
preference -> {
|
||||||
VKUIwrapper.setLink(Utils.getVKUILink());
|
VKUIwrapper.setLink(Utils.getVKUILink());
|
||||||
@@ -689,6 +689,19 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
PreferenceFragmentUtils.addMaterialSwitchPreference(
|
||||||
|
getPreferenceScreen(),
|
||||||
|
"autoupdates",
|
||||||
|
"Автоматическое обновление",
|
||||||
|
"Получать уведомление о выходе новой версии приложения",
|
||||||
|
R.drawable.ic_history_outline_28,
|
||||||
|
true,
|
||||||
|
(preference, o) -> {
|
||||||
|
Preferences.getPreferences().edit().putBoolean("autoupdates", (boolean) o).apply();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,21 +28,16 @@ import ru.vtosters.lite.ui.adapters.StickerPackAdapter;
|
|||||||
import ru.vtosters.lite.ui.components.StickerTouchHelperCallback;
|
import ru.vtosters.lite.ui.components.StickerTouchHelperCallback;
|
||||||
import ru.vtosters.lite.ui.fragments.BaseToolbarFragment;
|
import ru.vtosters.lite.ui.fragments.BaseToolbarFragment;
|
||||||
import ru.vtosters.lite.utils.AndroidUtils;
|
import ru.vtosters.lite.utils.AndroidUtils;
|
||||||
import ru.vtosters.lite.utils.NavigatorUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
public class StickersFragment extends BaseToolbarFragment {
|
public class StickersFragment extends BaseToolbarFragment {
|
||||||
public final static String ACTION_RELOAD = "com.vtosters.lite.action.RELOAD_TGS_LIST";
|
public static String ACTION_RELOAD = "com.vtosters.lite.action.RELOAD_TGS_LIST";
|
||||||
|
|
||||||
final static int TYPE_DIRECT = 0;
|
|
||||||
final static int TYPE_SOCKS = 2;
|
|
||||||
|
|
||||||
TelegramStickersGrabber mGrabber;
|
TelegramStickersGrabber mGrabber;
|
||||||
StickerPackAdapter mAdapter;
|
StickerPackAdapter mAdapter;
|
||||||
boolean movePending = false;
|
boolean movePending = false;
|
||||||
int from, to;
|
int from, to;
|
||||||
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -59,13 +54,28 @@ public class StickersFragment extends BaseToolbarFragment {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static void resetToken(Context ctx) {
|
||||||
|
if (TGPref.getTGBotKey() == null) AndroidUtils.sendToast("Токен отсутствует");
|
||||||
|
new VkAlertDialog.Builder(ctx)
|
||||||
|
.setTitle("Сбросить токен бота?")
|
||||||
|
.setNegativeButton(R.string.cancel, (dialog, which) -> {
|
||||||
|
dialog.cancel();
|
||||||
|
})
|
||||||
|
.setPositiveButton(R.string.ok, (dialog, which) -> {
|
||||||
|
TGPref.setTGBotKey(null);
|
||||||
|
AndroidUtils.sendToast("Токен бота сброшен");
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreateMenu(Menu menu) {
|
protected void onCreateMenu(Menu menu) {
|
||||||
menu.add(0, 0, 0, "")
|
menu.add(0, 0, 0, "")
|
||||||
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_add_outline_28)))
|
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_add_outline_28)))
|
||||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
menu.add(0, 1, 0, "")
|
menu.add(0, 1, 0, "")
|
||||||
.setIcon(R.drawable.ic_settings_outline_28)
|
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_refresh_outline_28)))
|
||||||
|
.setTitle("Сбросить токен бота")
|
||||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
super.onCreateMenu(menu);
|
super.onCreateMenu(menu);
|
||||||
}
|
}
|
||||||
@@ -74,16 +84,11 @@ public class StickersFragment extends BaseToolbarFragment {
|
|||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case 0 -> addTgsPack();
|
case 0 -> addTgsPack();
|
||||||
case 1 -> openMenu(null);
|
case 1 -> resetToken(requireContext());
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openMenu(String toast) {
|
|
||||||
if (toast != null) Toast.makeText(requireContext(), toast, Toast.LENGTH_SHORT).show();
|
|
||||||
NavigatorUtils.switchFragment(requireContext(), StickersPreferencesFragment.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -96,14 +101,14 @@ public class StickersFragment extends BaseToolbarFragment {
|
|||||||
public View onCreateContent(@NonNull LayoutInflater inflater, @Nullable Bundle bundle) {
|
public View onCreateContent(@NonNull LayoutInflater inflater, @Nullable Bundle bundle) {
|
||||||
setTitle(R.string.vtltgs);
|
setTitle(R.string.vtltgs);
|
||||||
|
|
||||||
final var container = new FrameLayout(requireContext());
|
var container = new FrameLayout(requireContext());
|
||||||
final var recycler = new RecyclerView(requireContext());
|
var recycler = new RecyclerView(requireContext());
|
||||||
|
|
||||||
mAdapter = new StickerPackAdapter();
|
mAdapter = new StickerPackAdapter();
|
||||||
recycler.setAdapter(mAdapter);
|
recycler.setAdapter(mAdapter);
|
||||||
|
|
||||||
final var callback = new StickerTouchHelperCallback(mAdapter);
|
var callback = new StickerTouchHelperCallback(mAdapter);
|
||||||
final var touchHelper = new ItemTouchHelper(callback);
|
var touchHelper = new ItemTouchHelper(callback);
|
||||||
touchHelper.attachToRecyclerView(recycler);
|
touchHelper.attachToRecyclerView(recycler);
|
||||||
|
|
||||||
recycler.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
|
recycler.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
|
||||||
@@ -124,80 +129,50 @@ public class StickersFragment extends BaseToolbarFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addTgsPack() {
|
private void addTgsPack() {
|
||||||
final var method = TGPref.getTGConnectMethod();
|
initGrabber();
|
||||||
if (method != -1) {
|
Runnable lambda = () -> {
|
||||||
initGrabber();
|
var container = new LinearLayout(requireContext());
|
||||||
switch (method) {
|
|
||||||
case TYPE_SOCKS -> {
|
var input = new EditText(requireContext());
|
||||||
if (TGPref.getTGProxyIP() == null) openMenu(requireContext().getString(R.string.stickersproxy1));
|
var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
||||||
}
|
inputLP.setMargins(
|
||||||
case TYPE_DIRECT -> {
|
AndroidUtils.dp2px(24.f),
|
||||||
initGrabber();
|
0,
|
||||||
final Runnable lambda = () ->
|
AndroidUtils.dp2px(24.f),
|
||||||
|
0);
|
||||||
|
container.addView(input, inputLP);
|
||||||
|
|
||||||
|
new VkAlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.stickershelp1)
|
||||||
|
// .setMessage(R.string.stickershelp2)
|
||||||
|
.setView(container)
|
||||||
|
.setPositiveButton(android.R.string.ok, (dialog, which) ->
|
||||||
{
|
{
|
||||||
final var container = new LinearLayout(requireContext());
|
var packId = input.getText().toString();
|
||||||
|
if (packId.startsWith("https://"))
|
||||||
|
packId = packId.substring(packId.lastIndexOf('/') + 1);
|
||||||
|
TelegramStickersService.getInstance(requireContext())
|
||||||
|
.requestPackDownload(
|
||||||
|
packId,
|
||||||
|
new File(requireContext().getFilesDir(), "VT-Stickers/" + packId));
|
||||||
|
})
|
||||||
|
.setNeutralButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
|
||||||
|
.show();
|
||||||
|
};
|
||||||
|
|
||||||
final var input = new EditText(requireContext());
|
if (TGPref.getTGBotKey() == null) enterBotKey(() -> checkApiKey(lambda));
|
||||||
final var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
else checkApiKey(lambda);
|
||||||
inputLP.setMargins(
|
|
||||||
AndroidUtils.dp2px(24.f),
|
|
||||||
0,
|
|
||||||
AndroidUtils.dp2px(24.f),
|
|
||||||
0);
|
|
||||||
container.addView(input, inputLP);
|
|
||||||
|
|
||||||
new VkAlertDialog.Builder(requireContext())
|
|
||||||
.setTitle(R.string.stickershelp1)
|
|
||||||
// .setMessage(R.string.stickershelp2)
|
|
||||||
.setView(container)
|
|
||||||
.setPositiveButton(android.R.string.ok, (dialog, which) ->
|
|
||||||
{
|
|
||||||
var packId = input.getText().toString();
|
|
||||||
if (packId.startsWith("https://"))
|
|
||||||
packId = packId.substring(packId.lastIndexOf('/') + 1);
|
|
||||||
TelegramStickersService.getInstance(requireContext())
|
|
||||||
.requestPackDownload(
|
|
||||||
packId,
|
|
||||||
new File(requireContext().getFilesDir(), "VT-Stickers/" + packId));
|
|
||||||
})
|
|
||||||
.setNeutralButton(android.R.string.cancel, (dialog, which) -> dialog.dismiss())
|
|
||||||
.show();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (TGPref.getTGBotKey() == null) enterBotKey(() -> checkApiKey(lambda));
|
|
||||||
else checkApiKey(lambda);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else openMenu(requireContext().getString(R.string.stickersconnection));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initGrabber() {
|
private void initGrabber() {
|
||||||
switch (TGPref.getTGConnectMethod()) {
|
|
||||||
case TYPE_DIRECT -> {
|
|
||||||
TelegramStickersGrabber.USE_PROXY = false;
|
|
||||||
mGrabber.resetProxy();
|
|
||||||
}
|
|
||||||
case TYPE_SOCKS -> {
|
|
||||||
if (TGPref.getTGProxyIP() == null) break;
|
|
||||||
TelegramStickersGrabber.USE_PROXY = true;
|
|
||||||
TelegramStickersGrabber.PROXY_IP = TGPref.getTGProxyIP();
|
|
||||||
TelegramStickersGrabber.PROXY_PORT = TGPref.getTGProxyPort();
|
|
||||||
if (TGPref.isTGProxyPassEnabled()) {
|
|
||||||
TelegramStickersGrabber.HAS_PASS = true;
|
|
||||||
TelegramStickersGrabber.PROXY_USER = TGPref.getTGProxyUser();
|
|
||||||
TelegramStickersGrabber.PROXY_PASS = TGPref.getTGProxyPass();
|
|
||||||
}
|
|
||||||
mGrabber.enableProxy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TelegramStickersGrabber.updateURLs();
|
TelegramStickersGrabber.updateURLs();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enterBotKey(Runnable callback) {
|
private void enterBotKey(Runnable callback) {
|
||||||
final var container = new LinearLayout(requireContext());
|
var container = new LinearLayout(requireContext());
|
||||||
|
|
||||||
final var input = new EditText(requireContext());
|
var input = new EditText(requireContext());
|
||||||
final var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
||||||
inputLP.setMargins(
|
inputLP.setMargins(
|
||||||
AndroidUtils.dp2px(24.f),
|
AndroidUtils.dp2px(24.f),
|
||||||
0,
|
0,
|
||||||
@@ -222,7 +197,7 @@ public class StickersFragment extends BaseToolbarFragment {
|
|||||||
.setMessage(requireContext().getString(R.string.stickersapi9) +
|
.setMessage(requireContext().getString(R.string.stickersapi9) +
|
||||||
requireContext().getString(R.string.stickersapi10))
|
requireContext().getString(R.string.stickersapi10))
|
||||||
.setNeutralButton(AndroidUtils.getString("open_bot"), (dl, i) -> {
|
.setNeutralButton(AndroidUtils.getString("open_bot"), (dl, i) -> {
|
||||||
final var intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/botfather"))
|
var intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/botfather"))
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
requireActivity().startActivity(intent);
|
requireActivity().startActivity(intent);
|
||||||
})
|
})
|
||||||
@@ -235,7 +210,7 @@ public class StickersFragment extends BaseToolbarFragment {
|
|||||||
private void checkApiKey(Runnable callback) {
|
private void checkApiKey(Runnable callback) {
|
||||||
mGrabber.setBotApiKey(TGPref.getTGBotKey());
|
mGrabber.setBotApiKey(TGPref.getTGBotKey());
|
||||||
|
|
||||||
final var dialog = new ProgressDialog(requireContext());
|
var dialog = new ProgressDialog(requireContext());
|
||||||
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||||
dialog.setMessage(requireContext().getString(R.string.stickersapi1));
|
dialog.setMessage(requireContext().getString(R.string.stickersapi1));
|
||||||
dialog.show();
|
dialog.show();
|
||||||
|
|||||||
-87
@@ -1,87 +0,0 @@
|
|||||||
package ru.vtosters.lite.ui.fragments.tgstickers;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import com.aefyr.tsg.g2.stickersgrabber.TelegramStickersGrabber;
|
|
||||||
import com.vk.core.util.ToastUtils;
|
|
||||||
import com.vtosters.lite.R;
|
|
||||||
import ru.vtosters.lite.tgs.TGPref;
|
|
||||||
import ru.vtosters.lite.tgs.TGRoot;
|
|
||||||
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
|
|
||||||
import ru.vtosters.lite.ui.fragments.TrackedMaterialPreferenceToolbarFragment;
|
|
||||||
|
|
||||||
public class StickersPreferencesFragment extends TrackedMaterialPreferenceToolbarFragment {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle bundle) {
|
|
||||||
super.onCreate(bundle);
|
|
||||||
|
|
||||||
int vtosterXml = R.xml.empty;
|
|
||||||
addPreferencesFromResource(vtosterXml);
|
|
||||||
|
|
||||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.sprefsstickers));
|
|
||||||
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", R.string.sprefsdelkey, 0, 0, preference -> {
|
|
||||||
TGPref.setTGBotKey(null);
|
|
||||||
ToastUtils.a(requireContext().getString(R.string.sprefsdelkey2));
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.sprefsnetwork));
|
|
||||||
PreferenceFragmentUtils.addListPreference(getPreferenceScreen(), "VTGS:CM", "0", requireContext().getString(R.string.sprefsctype), new CharSequence[]{
|
|
||||||
requireContext().getString(R.string.ctypedirect), requireContext().getString(R.string.ctypesocks)
|
|
||||||
}, new String[]{
|
|
||||||
"0", "2"
|
|
||||||
});
|
|
||||||
|
|
||||||
findPreference("VTGS:CM").setTitle(TGRoot.getSummary());
|
|
||||||
|
|
||||||
findPreference("VTGS:CM").setOnPreferenceChangeListener((preference, o) -> {
|
|
||||||
String newData = (String) o;
|
|
||||||
TGPref.setTGConnectMethod(Integer.parseInt(newData));
|
|
||||||
TelegramStickersGrabber.updateURLs();
|
|
||||||
preference.setTitle(TGRoot.getSummary());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), R.string.ssocks);
|
|
||||||
|
|
||||||
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_host", R.string.ssockshost, (preference, o) -> {
|
|
||||||
TGPref.setTGProxyIP(o);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_port", R.string.ssocksport, (preference, o) -> {
|
|
||||||
try {
|
|
||||||
TGPref.setTGProxyPort(Integer.parseInt(o));
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
ToastUtils.a(requireContext().getString(R.string.ssockswport));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "tg_proxy_auth", requireContext().getString(R.string.ssocksauth), "", 0, false, (preference, o) -> {
|
|
||||||
boolean value = (boolean) o;
|
|
||||||
|
|
||||||
findPreference("tg_proxy_login").setEnabled(value);
|
|
||||||
findPreference("tg_proxy_pass").setEnabled(value);
|
|
||||||
|
|
||||||
TGPref.setTGProxyPassEnabled(value);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_login", R.string.ssockslogin, (preference, o) -> {
|
|
||||||
TGPref.setTGProxyUserPass(o, TGPref.getTGProxyPass());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
PreferenceFragmentUtils.addEditTextPreference(getPreferenceScreen(), "tg_proxy_pass", R.string.ssockspass, (preference, o) -> {
|
|
||||||
TGPref.setTGProxyUserPass(TGPref.getTGProxyUser(), o);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
findPreference("tg_proxy_login").setEnabled(TGPref.isTGProxyPassEnabled());
|
|
||||||
findPreference("tg_proxy_pass").setEnabled(TGPref.isTGProxyPassEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int T4() {
|
|
||||||
return R.string.vtltgs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ public class LifecycleUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Activity getCurrentActivity() {
|
public static Activity getCurrentActivity() {
|
||||||
return activities.size() > 0 ? activities.get(activities.size() - 1) : new MainActivity();
|
return !activities.isEmpty() ? activities.get(activities.size() - 1) : new MainActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void restartApplication() {
|
public static void restartApplication() {
|
||||||
|
|||||||
@@ -6097,7 +6097,7 @@
|
|||||||
<string name="BGStickerssum">Для ўключэння/адключэння вам неабходна ачысціць кэш прыкладання</string>
|
<string name="BGStickerssum">Для ўключэння/адключэння вам неабходна ачысціць кэш прыкладання</string>
|
||||||
<string name="addinf">дадатковая інфармацыя</string>
|
<string name="addinf">дадатковая інфармацыя</string>
|
||||||
<string name="copyid">Скапіяваць ID</string>
|
<string name="copyid">Скапіяваць ID</string>
|
||||||
<string name="vtssfs">VT Функцыі</string>
|
<string name="vtssfs">Серверныя функцыі</string>
|
||||||
<string name="vticon">Іконка дадатку</string>
|
<string name="vticon">Іконка дадатку</string>
|
||||||
<string name="vticonbtn">Выкарыстоўваць арыгінальную іконку</string>
|
<string name="vticonbtn">Выкарыстоўваць арыгінальную іконку</string>
|
||||||
<string name="vticonsummary">Выкарыстанне арыгінальнай назвы і іконкі дадатку як у афіцыйнага кліента VK</string>
|
<string name="vticonsummary">Выкарыстанне арыгінальнай назвы і іконкі дадатку як у афіцыйнага кліента VK</string>
|
||||||
|
|||||||
@@ -5446,7 +5446,7 @@
|
|||||||
<string name="BGStickerssum">Для включения/отключения вам необходимо очистить кеш приложения</string>
|
<string name="BGStickerssum">Для включения/отключения вам необходимо очистить кеш приложения</string>
|
||||||
<string name="addinf">Дополнительная информация</string>
|
<string name="addinf">Дополнительная информация</string>
|
||||||
<string name="copyid">Скопировать айди</string>
|
<string name="copyid">Скопировать айди</string>
|
||||||
<string name="vtssfs">VT Функции</string>
|
<string name="vtssfs">Серверные функции</string>
|
||||||
<string name="vticon">Иконка приложения</string>
|
<string name="vticon">Иконка приложения</string>
|
||||||
<string name="vticonbtn">Использовать оригинальную иконку</string>
|
<string name="vticonbtn">Использовать оригинальную иконку</string>
|
||||||
<string name="vticonsummary">Использование оригинального названия и иконки приложения как у официального клиента ВК</string>
|
<string name="vticonsummary">Использование оригинального названия и иконки приложения как у официального клиента ВК</string>
|
||||||
|
|||||||
@@ -5373,7 +5373,7 @@
|
|||||||
<string name="BGStickerssum">Для увімкнення/вимкнення вам необхідно очистити кеш додатка</string>
|
<string name="BGStickerssum">Для увімкнення/вимкнення вам необхідно очистити кеш додатка</string>
|
||||||
<string name="addinf">додаткова інформація</string>
|
<string name="addinf">додаткова інформація</string>
|
||||||
<string name="copyid">Копіювати ID</string>
|
<string name="copyid">Копіювати ID</string>
|
||||||
<string name="vtssfs">VT Функції</string>
|
<string name="vtssfs">Серверні функції</string>
|
||||||
<string name="vticon">Іконка додатку</string>
|
<string name="vticon">Іконка додатку</string>
|
||||||
<string name="vticonbtn">Використовувати оригінальну іконку</string>
|
<string name="vticonbtn">Використовувати оригінальну іконку</string>
|
||||||
<string name="vticonsummary">Використання оригінальної назви та іконки додатку як у офіційного клієнта ВК</string>
|
<string name="vticonsummary">Використання оригінальної назви та іконки додатку як у офіційного клієнта ВК</string>
|
||||||
|
|||||||
@@ -5580,7 +5580,7 @@ Does not affect vk.cc links"</string>
|
|||||||
<string name="BGStickerssum">You need to clear the application cache after toggling this option</string>
|
<string name="BGStickerssum">You need to clear the application cache after toggling this option</string>
|
||||||
<string name="addinf">Additional information</string>
|
<string name="addinf">Additional information</string>
|
||||||
<string name="copyid">Copy ID</string>
|
<string name="copyid">Copy ID</string>
|
||||||
<string name="vtssfs">VT Features</string>
|
<string name="vtssfs">Server features</string>
|
||||||
<string name="vticon">App Icon</string>
|
<string name="vticon">App Icon</string>
|
||||||
<string name="vticonbtn">Use original icon</string>
|
<string name="vticonbtn">Use original icon</string>
|
||||||
<string name="vticonsummary">The original name and icon of VK</string>
|
<string name="vticonsummary">The original name and icon of VK</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user