Compare commits
23
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44fa23020e | ||
|
|
4cdcf9aee0 | ||
|
|
63c5262444 | ||
|
|
9074e80478 | ||
|
|
13fab86b83 | ||
|
|
5ada5d74d2 | ||
|
|
aa1802c371 | ||
|
|
0cf1c6ecba | ||
|
|
f051420ff9 | ||
|
|
3902a40e30 | ||
|
|
0c97f15088 | ||
|
|
4cf112921b | ||
|
|
6e31b26af0 | ||
|
|
6f22b5dd03 | ||
|
|
9ee1bd0f79 | ||
|
|
56b11bc43c | ||
|
|
a476d490a6 | ||
|
|
74b50fb9c2 | ||
|
|
ddaedd0ba0 | ||
|
|
729342085a | ||
|
|
bb20178dc9 | ||
|
|
306cee1fd4 | ||
|
|
02e8732e9f |
+2
-2
@@ -91,8 +91,8 @@ task testClasses {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.2'
|
||||
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.2'
|
||||
implementation 'com.microsoft.appcenter:appcenter-analytics:5.0.1'
|
||||
implementation 'com.microsoft.appcenter:appcenter-crashes:5.0.1'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
implementation 'net.sourceforge.streamsupport:streamsupport-minifuture:1.7.4'
|
||||
implementation 'com.google.guava:guava:23.3-android'
|
||||
|
||||
Vendored
+2
-1
@@ -43,4 +43,5 @@
|
||||
|
||||
-keep class com.vtosters.lite.** { *; }
|
||||
-keep class com.vk.** { *; }
|
||||
-keep class bruhcollective.itaysonlab.libvkx.client.** { *; }
|
||||
-keep class bruhcollective.itaysonlab.libvkx.client.** { *; }
|
||||
-keep class com.guardanis.applock.** { *; }
|
||||
@@ -36,8 +36,8 @@ public class TelegramStickersPack extends CustomStickersPack {
|
||||
public HashMap<String, List<Integer>> emojis = new HashMap<>();
|
||||
|
||||
public TelegramStickersPack(String id) {
|
||||
this.id = this.title = id;
|
||||
this.hash = id.hashCode();
|
||||
this.id=this.title=id;
|
||||
this.hash=id.hashCode();
|
||||
}
|
||||
|
||||
public JSONObject encodeEmojis() throws JSONException {
|
||||
|
||||
@@ -26,13 +26,19 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
public class TelegramStickersService {
|
||||
private static final String TAG = "TGStickersService";
|
||||
private static TelegramStickersService instance;
|
||||
|
||||
private final Context c;
|
||||
|
||||
private final ArrayList<TelegramStickersPack> packs;
|
||||
private final ArrayList<TelegramStickersPack> activePacks;
|
||||
private final ArrayList<TelegramStickersPack> inactivePacks;
|
||||
private final ArrayList<StickersEventsListener> listeners;
|
||||
private final HashSet<String> currentlyDownloading;
|
||||
|
||||
private final Handler uiThreadHandler;
|
||||
|
||||
private final TelegramStickersGrabber grabber;
|
||||
|
||||
private final TelegramStickersDbHelper dbHelper;
|
||||
private final ArrayList<Runnable> queuedTasks;
|
||||
private final ThreadPoolExecutor executor;
|
||||
@@ -42,14 +48,16 @@ public class TelegramStickersService {
|
||||
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(context);
|
||||
dbHelper = new TelegramStickersDbHelper(c);
|
||||
queuedTasks = new ArrayList<>();
|
||||
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
|
||||
notificationsHelper = new NotificationsHelper(context);
|
||||
notificationsHelper = new NotificationsHelper(c);
|
||||
|
||||
packs = new ArrayList<>();
|
||||
activePacks = new ArrayList<>();
|
||||
@@ -62,11 +70,11 @@ public class TelegramStickersService {
|
||||
return instance == null ? new TelegramStickersService(c) : instance;
|
||||
}
|
||||
|
||||
private void updatePacks(boolean notify) {
|
||||
private void updatePacks(final boolean notify) {
|
||||
packs.clear();
|
||||
dbHelper.getAllPacks(new TelegramStickersDbHelper.PacksLoadingListener() {
|
||||
@Override
|
||||
public void onPackLoaded(TelegramStickersPack pack) {
|
||||
public void onPackLoaded(final TelegramStickersPack pack) {
|
||||
runOnUiThread(() -> {
|
||||
packs.add(pack);
|
||||
|
||||
@@ -95,6 +103,14 @@ 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) {
|
||||
getGlobalContext().sendBroadcast(new Intent(StickersFragment.ACTION_RELOAD));
|
||||
if (listeners.isEmpty())
|
||||
@@ -172,16 +188,20 @@ public class TelegramStickersService {
|
||||
return inactivePacks;
|
||||
}
|
||||
|
||||
public void requestPackDownload(String id, File packFolder) {
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
File folder = packFolder;
|
||||
final File folder = packFolder;
|
||||
queuedTasks.add(() -> requestPackDownload(id, folder));
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
TelegramStickersPack pack = new TelegramStickersPack(id);
|
||||
@@ -201,86 +221,85 @@ public class TelegramStickersService {
|
||||
notifyPackAdded(pack, packs.size() - 1);
|
||||
}
|
||||
|
||||
TelegramStickersPack newPack = pack;
|
||||
boolean isUpdate = update;
|
||||
final TelegramStickersPack newPack = pack;
|
||||
final boolean isUpdate = update;
|
||||
|
||||
currentlyDownloading.add(id);
|
||||
notificationsHelper.packStartedDownloading(newPack);
|
||||
|
||||
// try-catch block to handle possible timeout exception
|
||||
try {
|
||||
grabber.grabPack(id, packFolder, pack.version, new TelegramStickersGrabber.PackDownloadListener() {
|
||||
@Override
|
||||
public void onPackDownloaded(TelegramStickersPackInfo packInfo, boolean newVersionFound) {
|
||||
runOnUiThread(() -> {
|
||||
|
||||
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);
|
||||
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));
|
||||
currentlyDownloading.remove(newPack.id);
|
||||
syncPack(newPack);
|
||||
}
|
||||
|
||||
if (isUpdate) {
|
||||
if (newPack.enabled)
|
||||
notifyActivePacksListChanged();
|
||||
} else {
|
||||
activePacks.add(newPack);
|
||||
notifyActivePacksListChanged();
|
||||
}
|
||||
});
|
||||
notificationsHelper.packDoneDownloading(newPack, true, isUpdate, null);
|
||||
}
|
||||
notifyPackDownloadError(newPack, e);
|
||||
currentlyDownloading.remove(newPack.id);
|
||||
});
|
||||
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackDownloadError(Exception e) {
|
||||
runOnUiThread(() -> {
|
||||
Log.e(TAG, "Error while downloading pack " + newPack.id);
|
||||
e.printStackTrace();
|
||||
@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));
|
||||
});
|
||||
|
||||
if (!isUpdate) {
|
||||
int index = packs.indexOf(newPack);
|
||||
packs.remove(index);
|
||||
notifyPackRemoved(newPack, index);
|
||||
} else {
|
||||
newPack.state = TelegramStickersPack.DOWNLOADED;
|
||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||
}
|
||||
notificationsHelper.packDownloadUpdated(newPack, 0);
|
||||
}
|
||||
|
||||
notifyPackDownloadError(newPack, e);
|
||||
currentlyDownloading.remove(newPack.id);
|
||||
});
|
||||
notificationsHelper.packDoneDownloading(newPack, false, isUpdate, e);
|
||||
}
|
||||
@Override
|
||||
public void onStickerDownloaded(String pack, File sticker, String boundEmoji, int stickerIndex, int downloadedStickersCount, int totalStickersCount) {
|
||||
stickerIndex--;
|
||||
|
||||
@Override
|
||||
public void onGotPackInfo(TelegramStickersPackInfo packInfo) {
|
||||
runOnUiThread(() -> {
|
||||
newPack.title = packInfo.title;
|
||||
newPack.stickersCount = packInfo.stickersCount;
|
||||
newPack.version = packInfo.version;
|
||||
notifyPackChanged(newPack, packs.indexOf(newPack));
|
||||
});
|
||||
List<Integer> list = newPack.emojis.get(boundEmoji);
|
||||
if (list == null) list = new ArrayList<>();
|
||||
list.add(stickerIndex);
|
||||
newPack.emojis.put(boundEmoji, list);
|
||||
|
||||
notificationsHelper.packDownloadUpdated(newPack, 0);
|
||||
}
|
||||
notificationsHelper.packDownloadUpdated(newPack, downloadedStickersCount);
|
||||
}
|
||||
});
|
||||
|
||||
@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();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setPackEnabled(TelegramStickersPack pack, boolean enabled, boolean notify) {
|
||||
public void setPackEnabled(final TelegramStickersPack pack, final boolean enabled, final boolean notify) {
|
||||
if (!ready) {
|
||||
queuedTasks.add(() -> setPackEnabled(pack, enabled, notify));
|
||||
return;
|
||||
@@ -306,7 +325,7 @@ public class TelegramStickersService {
|
||||
}
|
||||
}
|
||||
|
||||
public void deletePack(TelegramStickersPack pack) {
|
||||
public void deletePack(final TelegramStickersPack pack) {
|
||||
if (!ready) {
|
||||
queuedTasks.add(() -> deletePack(pack));
|
||||
return;
|
||||
@@ -315,6 +334,7 @@ public class TelegramStickersService {
|
||||
boolean d = dbHelper.deletePack(pack);
|
||||
Log.d(TAG, "Delete from DB=" + d);
|
||||
|
||||
|
||||
int index = packs.indexOf(pack);
|
||||
packs.remove(index);
|
||||
notifyPackRemoved(pack, index);
|
||||
@@ -335,7 +355,7 @@ public class TelegramStickersService {
|
||||
uiThreadHandler.post(r);
|
||||
}
|
||||
|
||||
private void syncPack(TelegramStickersPack pack) {
|
||||
private void syncPack(final TelegramStickersPack pack) {
|
||||
if (!ready) {
|
||||
queuedTasks.add(() -> syncPack(pack));
|
||||
return;
|
||||
@@ -377,4 +397,6 @@ public class TelegramStickersService {
|
||||
|
||||
void onInactivePacksListChanged();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,20 +7,23 @@ 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 okhttp3.*;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
import ru.vtosters.lite.net.*;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -28,19 +31,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
*/
|
||||
public class TelegramStickersGrabber {
|
||||
private static final String TAG = "TSG";
|
||||
private static final OkHttpClient sClient = VtOkHttpClient.getInstance();
|
||||
private static final int MAX_RETRIES = 2;
|
||||
private static final AtomicInteger retryCount = new AtomicInteger();
|
||||
private static final int STICKER_QUALITY = 100;
|
||||
private static final String STICKER_FILE_NAME_FORMAT = "%03d.png";
|
||||
private static final String STICKERS_KEY = "stickers";
|
||||
private static final String FILE_ID_KEY = "file_id";
|
||||
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";
|
||||
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;
|
||||
private static String BOT_API_BASE_URL;
|
||||
private static String GET_STICKER_SET_URL;
|
||||
private static String GET_FILE_URL;
|
||||
@@ -52,10 +50,12 @@ public class TelegramStickersGrabber {
|
||||
|
||||
private final MessageDigest sha256;
|
||||
private final Handler uiThreadHandler;
|
||||
private String botApiKey;
|
||||
private String botApiKey = "";
|
||||
private NetClient httpClient;
|
||||
|
||||
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");
|
||||
@@ -69,12 +69,81 @@ public class TelegramStickersGrabber {
|
||||
public static void updateURLs() {
|
||||
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_FILE_URL = BOT_API_BASE_URL + "getFile?file_id=%s";
|
||||
}
|
||||
|
||||
public void grabPack(String id, File packFolder, String installedVersion, PackDownloadListener listener) {
|
||||
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 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);
|
||||
}
|
||||
|
||||
@@ -82,247 +151,264 @@ public class TelegramStickersGrabber {
|
||||
botApiKey = key;
|
||||
}
|
||||
|
||||
public void checkKey(KeyCheckListener listener) {
|
||||
public void checkKey(final KeyCheckListener listener) {
|
||||
Log.d(TAG, "Checking key: " + botApiKey);
|
||||
// Create a request object with the URL and bot API key
|
||||
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() {
|
||||
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 a(Call call, IOException e) {
|
||||
// Handle the failure case
|
||||
Log.d(TAG, e.getMessage());
|
||||
public void onFailure(NetCall call, IOException e) {
|
||||
e.printStackTrace();
|
||||
runOnUiThread(listener::onNetError);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(Call call, Response response) {
|
||||
Log.d(TAG, response.toString());
|
||||
runOnUiThread(() -> listener.onKeyChecked(response.h()));
|
||||
public void onResponse(NetCall call, final NetResponse response) throws IOException {
|
||||
runOnUiThread(() -> listener.onKeyChecked(response.code() == 200));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getPack(StickerSet set, File packFolder, TelegramStickersPackInfo packInfo, PackDownloadListener listener) {
|
||||
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);
|
||||
});
|
||||
|
||||
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() {
|
||||
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 a(Call call, IOException e) {
|
||||
Log.d(TAG, e.getMessage());
|
||||
public void onFailure(NetCall call, IOException e) {
|
||||
listener.onPackDownloadError(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(Call call, Response response) throws IOException {
|
||||
Log.d(TAG, response.toString());
|
||||
public void onResponse(NetCall call, NetResponse response) {
|
||||
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"));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
try (ResponseBody responseBody = response.a()) {
|
||||
JSONObject packInfoResponse = new JSONObject(responseBody.g()).getJSONObject("result");
|
||||
String packVersion = new String(sha256.digest(packInfoResponse.toString().getBytes(StandardCharsets.UTF_8)));
|
||||
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 stickersArray = packInfoResponse.getJSONArray(STICKERS_KEY);
|
||||
JSONArray jStickers = jPackInfo.getJSONArray("stickers");
|
||||
|
||||
if (stickersArray.length() == 0) {
|
||||
if (jStickers.length() == 0) {
|
||||
listener.onPackDownloadError(new TSGException("No stickers in pack"));
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<Sticker> stickers = parseStickers(stickersArray);
|
||||
ArrayList<Sticker> stickers = new ArrayList<>(jStickers.length());
|
||||
|
||||
StickerSet set = new StickerSet(packInfoResponse.getString(NAME_KEY), packInfoResponse.getString(TITLE_KEY), packVersion, stickers);
|
||||
Log.d(TAG, String.format("Parsing stickers in pack %s", packName));
|
||||
|
||||
if (!packFolder.exists() && !packFolder.mkdirs()) {
|
||||
// Handle the folder creation failure case
|
||||
listener.onPackDownloadError(new IOException("Can't create folder for the pack!"));
|
||||
return;
|
||||
for (int i = 0; i < jStickers.length(); i++) {
|
||||
JSONObject jSticker = jStickers.getJSONObject(i);
|
||||
if (jSticker.optBoolean("is_animated") || jSticker.optBoolean("is_video")) {
|
||||
if (jSticker.has("thumb")) {
|
||||
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")));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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 ArrayList<Sticker> parseStickers(JSONArray stickersArray) throws JSONException, TSGException {
|
||||
ArrayList<Sticker> stickers = new ArrayList<>(stickersArray.length());
|
||||
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()));
|
||||
|
||||
for (int i = 0; i < stickersArray.length(); i++) {
|
||||
JSONObject stickerObject = stickersArray.getJSONObject(i);
|
||||
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);
|
||||
listener.onPackDownloaded(packInfo, true);
|
||||
});
|
||||
final Flag deathFlag = new Flag();
|
||||
|
||||
if (isAnimated || isVideo) {
|
||||
if (stickerObject.has(THUMB_KEY)) {
|
||||
fileId = stickerObject.getJSONObject(THUMB_KEY).getString(FILE_ID_KEY);
|
||||
} else {
|
||||
throw new TSGException("Animated and video stickerpacks without thumbs are not supported!");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
stickers.add(new Sticker(fileId, emoji));
|
||||
@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) {
|
||||
}
|
||||
}
|
||||
|
||||
return stickers;
|
||||
}
|
||||
|
||||
private void runOnUiThread(Runnable r) {
|
||||
@@ -345,13 +431,15 @@ public class TelegramStickersGrabber {
|
||||
void onNetError();
|
||||
}
|
||||
|
||||
public static class TSGException extends Exception {
|
||||
public class TSGException extends Exception {
|
||||
private static final long serialVersionUID = 7866915509988422944L;
|
||||
|
||||
private TSGException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static class StickerSet {
|
||||
private class StickerSet {
|
||||
String id;
|
||||
String name;
|
||||
String version;
|
||||
@@ -365,7 +453,7 @@ public class TelegramStickersGrabber {
|
||||
}
|
||||
}
|
||||
|
||||
private static class Sticker {
|
||||
private class Sticker {
|
||||
String emoji;
|
||||
String fileId;
|
||||
|
||||
|
||||
@@ -0,0 +1,342 @@
|
||||
package com.guardanis.applock;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import androidx.core.os.CancellationSignal;
|
||||
|
||||
import com.vtosters.lite.R;
|
||||
|
||||
import com.guardanis.applock.activities.UnlockActivity;
|
||||
import com.guardanis.applock.services.FingerprintLockService;
|
||||
import com.guardanis.applock.services.FingerprintLockService.AuthenticationDelegate;
|
||||
import com.guardanis.applock.services.LockService;
|
||||
import com.guardanis.applock.services.PINLockService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class AppLock {
|
||||
|
||||
public static final int REQUEST_CODE_UNLOCK = 9371;
|
||||
public static final int REQUEST_CODE_LOCK_CREATION = 9372;
|
||||
public static final int REQUEST_CODE_FINGERPRINT_PERMISSION = 9373;
|
||||
public static final int ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE = 1;
|
||||
public static final int ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED = 2;
|
||||
public static final int ERROR_CODE_FINGERPRINTS_EMPTY = 3;
|
||||
public static final int ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED = 4;
|
||||
public static final int ERROR_CODE_SDK_VERSION_MINIMUM = 5;
|
||||
private static final String PREFS = "pin__preferences";
|
||||
private static final String PREF_UNLOCK_FAILURE_TIME = "pin__unlock_failure_time";
|
||||
private static final String PREF_UNLOCK_SUCCESS_TIME = "pin__unlock_success_time";
|
||||
private static AppLock instance;
|
||||
protected Context context;
|
||||
protected HashMap<Class<? extends LockService>, LockService> lockServices = new HashMap<>();
|
||||
protected int unlockAttemptsCount = 1;
|
||||
|
||||
protected AppLock(Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
|
||||
this.lockServices.put(PINLockService.class, new PINLockService());
|
||||
this.lockServices.put(FingerprintLockService.class, new FingerprintLockService());
|
||||
}
|
||||
|
||||
public static AppLock getInstance(Context context) {
|
||||
if (instance == null)
|
||||
instance = new AppLock(context);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the user has enrolled in either PIN or Fingerprint locking
|
||||
*/
|
||||
public static boolean isEnrolled(Context context) {
|
||||
AppLock helper = getInstance(context);
|
||||
|
||||
for (LockService service : helper.lockServices.values()) {
|
||||
if (service.isEnrolled(context))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setAuthenticationRequired(Context context) {
|
||||
AppLock helper = getInstance(context);
|
||||
|
||||
helper.getPreferences()
|
||||
.edit()
|
||||
.putLong(PREF_UNLOCK_SUCCESS_TIME, 0)
|
||||
.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the user is enrolled in locking and the last successful unlock happened more than the default lock duration ago
|
||||
*/
|
||||
public static boolean isUnlockRequired(Context context) {
|
||||
int minutes = Integer.parseInt(getPinCodeTime(context));
|
||||
|
||||
if (minutes < 1) {
|
||||
minutes = 1;
|
||||
}
|
||||
|
||||
return isUnlockRequired(context, TimeUnit.MINUTES.toMillis(minutes));
|
||||
}
|
||||
|
||||
public static String getPinCodeTime(Context context) {
|
||||
return context.getString(AndroidUtils.getIdentifier("applock__activity_lock_reenable_minutes", "integer"));
|
||||
}
|
||||
|
||||
public static String getPinCodeLimits(Context context) {
|
||||
return context.getString(AndroidUtils.getIdentifier("applock__input_pin_item_count", "integer"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the user is enrolled in locking and the last successful unlock happened more than lastSuccessValidMs ago
|
||||
*/
|
||||
public static boolean isUnlockRequired(Context context, long lastSuccessValidMs) {
|
||||
return isEnrolled(context) && lastSuccessValidMs < System.currentTimeMillis() - getUnlockSuccessTime(context);
|
||||
}
|
||||
|
||||
protected static long getUnlockSuccessTime(Context context) {
|
||||
return AppLock.getInstance(context)
|
||||
.getPreferences()
|
||||
.getLong(PREF_UNLOCK_SUCCESS_TIME, 0);
|
||||
}
|
||||
|
||||
public static void onActivityResumed(Activity activity) {
|
||||
if (isUnlockRequired(activity)) {
|
||||
boolean unlockActivityReturnAllowed = activity.getResources()
|
||||
.getBoolean(AndroidUtils.getIdentifier("applock__unlock_activity_return_allowed", "bool"));
|
||||
|
||||
Intent intent = new Intent(activity, UnlockActivity.class)
|
||||
.putExtra(UnlockActivity.INTENT_ALLOW_UNLOCKED_EXIT, unlockActivityReturnAllowed);
|
||||
|
||||
activity.startActivityForResult(intent, REQUEST_CODE_UNLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action-based unlock is required and navigates to the UnlockActivity if true.
|
||||
*
|
||||
* @return true if unlock is required.
|
||||
*/
|
||||
public static boolean unlockIfRequired(Activity activity) {
|
||||
if (isUnlockRequired(activity)) {
|
||||
Intent intent = new Intent(activity, UnlockActivity.class)
|
||||
.putExtra(UnlockActivity.INTENT_ALLOW_UNLOCKED_EXIT, true);
|
||||
|
||||
activity.startActivityForResult(intent, REQUEST_CODE_UNLOCK);
|
||||
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
public void attemptFingerprintUnlock(final UnlockDelegate eventListener) {
|
||||
if (handleFailureBlocking(eventListener))
|
||||
return;
|
||||
|
||||
AuthenticationDelegate delegate = new AuthenticationDelegate() {
|
||||
@Override
|
||||
public void onResolutionRequired(int errorCode) {
|
||||
// eventListener.onResolutionRequired(errorCode);
|
||||
eventListener.requestOpenKeyboard();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(int code, CharSequence message) {
|
||||
eventListener.onAuthenticationHelp(code, String.valueOf(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticating(CancellationSignal cancellationSignal) {
|
||||
// Handled internally
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(@NonNull FingerprintManagerCompat.AuthenticationResult result) {
|
||||
onUnlockSuccessful(eventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed(String message) {
|
||||
Log.w(getClass().getSimpleName(), "Fingerprint authentication failed: " + message);
|
||||
}
|
||||
};
|
||||
|
||||
getLockService(FingerprintLockService.class)
|
||||
.authenticate(context, delegate);
|
||||
}
|
||||
|
||||
public void attemptPINUnlock(String pin, final UnlockDelegate eventListener) {
|
||||
if (handleFailureBlocking(eventListener))
|
||||
return;
|
||||
|
||||
PINLockService.AuthenticationDelegate delegate = new PINLockService.AuthenticationDelegate() {
|
||||
@Override
|
||||
public void onNoPIN() {
|
||||
onUnlockFailed(context.getString(AndroidUtils.getIdentifier("applock__unlock_error_no_matching_pin_found", "string")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPINDoesNotMatch() {
|
||||
onUnlockFailed(context.getString(AndroidUtils.getIdentifier("applock__unlock_error_match_failed", "string")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPINMatches() {
|
||||
onUnlockSuccessful(eventListener);
|
||||
}
|
||||
|
||||
private void onUnlockFailed(String message) {
|
||||
handleUnlockFailure(message, eventListener);
|
||||
}
|
||||
};
|
||||
|
||||
getLockService(PINLockService.class)
|
||||
.authenticate(context, pin, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if failure blocking is enabled
|
||||
*/
|
||||
private boolean handleFailureBlocking(final UnlockDelegate eventListener) {
|
||||
if (isUnlockFailureBlockEnabled()) {
|
||||
unlockAttemptsCount++;
|
||||
|
||||
if (getFailureDelayMs() < System.currentTimeMillis() - getUnlockFailureBlockStart())
|
||||
resetUnlockFailure();
|
||||
else {
|
||||
String message = String.format(
|
||||
context.getString(AndroidUtils.getIdentifier("applock__unlock_error_retry_limit_exceeded", "string")),
|
||||
formatTimeRemaining());
|
||||
|
||||
if (eventListener != null)
|
||||
eventListener.onFailureLimitExceeded(message);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void handleUnlockFailure(String message, UnlockDelegate eventListener) {
|
||||
this.unlockAttemptsCount++;
|
||||
|
||||
if (eventListener != null)
|
||||
eventListener.onFailureLimitExceeded(message);
|
||||
|
||||
if (context.getResources().getInteger(AndroidUtils.getIdentifier("applock__max_retry_count", "integer")) < unlockAttemptsCount)
|
||||
onFailureExceedsLimit();
|
||||
}
|
||||
|
||||
public SharedPreferences getPreferences() {
|
||||
return context.getSharedPreferences(PREFS, 0);
|
||||
}
|
||||
|
||||
protected void onFailureExceedsLimit() {
|
||||
getPreferences()
|
||||
.edit()
|
||||
.putLong(PREF_UNLOCK_FAILURE_TIME, System.currentTimeMillis())
|
||||
.commit();
|
||||
}
|
||||
|
||||
public boolean isUnlockFailureBlockEnabled() {
|
||||
return context.getResources().getInteger(AndroidUtils.getIdentifier("applock__max_retry_count", "integer")) < unlockAttemptsCount
|
||||
|| System.currentTimeMillis() - getUnlockFailureBlockStart() < getFailureDelayMs();
|
||||
}
|
||||
|
||||
protected long getUnlockFailureBlockStart() {
|
||||
return getPreferences()
|
||||
.getLong(PREF_UNLOCK_FAILURE_TIME, 0);
|
||||
}
|
||||
|
||||
protected void onUnlockSuccessful(UnlockDelegate eventListener) {
|
||||
getPreferences()
|
||||
.edit()
|
||||
.putLong(PREF_UNLOCK_SUCCESS_TIME, System.currentTimeMillis())
|
||||
.commit();
|
||||
|
||||
resetUnlockFailure();
|
||||
|
||||
if (eventListener != null)
|
||||
eventListener.onUnlockSuccessful();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the last login time to require the user to re-authenticate with
|
||||
* AppLock the next time they are eligible to unlock.
|
||||
*/
|
||||
public void setAuthenticationRequired() {
|
||||
getPreferences()
|
||||
.edit()
|
||||
.putLong(PREF_UNLOCK_SUCCESS_TIME, 0)
|
||||
.commit();
|
||||
}
|
||||
|
||||
protected void resetUnlockFailure() {
|
||||
unlockAttemptsCount = 1;
|
||||
|
||||
getPreferences()
|
||||
.edit()
|
||||
.putLong(PREF_UNLOCK_FAILURE_TIME, 0)
|
||||
.commit();
|
||||
}
|
||||
|
||||
protected String formatTimeRemaining() {
|
||||
long millis = getFailureDelayMs() - (System.currentTimeMillis() - getUnlockFailureBlockStart());
|
||||
long seconds = TimeUnit.MILLISECONDS.toSeconds(millis) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis));
|
||||
|
||||
if (TimeUnit.MILLISECONDS.toMinutes(millis) < 1)
|
||||
return String.format(AndroidUtils.getString(R.string.n_seconds), seconds);
|
||||
else
|
||||
return String.format(AndroidUtils.getString(R.string.n_mins_secs), TimeUnit.MILLISECONDS.toMinutes(millis), seconds);
|
||||
}
|
||||
|
||||
protected long getFailureDelayMs() {
|
||||
return TimeUnit.MINUTES.toMillis(context.getResources()
|
||||
.getInteger(AndroidUtils.getIdentifier("applock__failure_retry_delay", "integer")));
|
||||
}
|
||||
|
||||
/**
|
||||
* This will remove all PIN and/or Fingerprint enrollment data.
|
||||
* Users will need to re-enroll in AppLock after this call.
|
||||
*/
|
||||
public void invalidateEnrollments() {
|
||||
resetUnlockFailure();
|
||||
setAuthenticationRequired();
|
||||
|
||||
for (LockService service : lockServices.values())
|
||||
service.invalidateEnrollments(context);
|
||||
}
|
||||
|
||||
public void cancelPendingAuthentications() {
|
||||
for (LockService service : lockServices.values())
|
||||
service.cancelPendingAuthentications(context);
|
||||
}
|
||||
|
||||
public <T extends LockService> T getLockService(Class<T> named) {
|
||||
return (T) lockServices.get(named);
|
||||
}
|
||||
|
||||
public interface UnlockDelegate {
|
||||
void onUnlockSuccessful();
|
||||
|
||||
void onResolutionRequired(int errorCode);
|
||||
|
||||
void onAuthenticationHelp(int code, String message);
|
||||
|
||||
void onFailureLimitExceeded(String message);
|
||||
void requestOpenKeyboard();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.guardanis.applock.activities;
|
||||
|
||||
import static ru.vtosters.hooks.other.ThemesUtils.getBackgroundContent;
|
||||
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
|
||||
import static ru.vtosters.hooks.other.ThemesUtils.isDarkTheme;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getIdentifier;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.guardanis.applock.views.LockCreationViewController;
|
||||
|
||||
|
||||
public class LockCreationActivity extends AppCompatActivity implements LockCreationViewController.Delegate {
|
||||
|
||||
protected LockCreationViewController viewController;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstance) {
|
||||
super.onCreate(savedInstance);
|
||||
|
||||
setContentView(getIdentifier("applock__activity_lock_creation", "layout"));
|
||||
findViewById(getIdentifier("applock__activity_lock_creation", "id")).setBackgroundColor(getBackgroundContent());
|
||||
((TextView) findViewById(getIdentifier("pin__description", "id"))).setTextColor(getTextAttr());
|
||||
|
||||
this.viewController = new LockCreationViewController(this, findViewById(getIdentifier("pin__container", "id")));
|
||||
this.viewController.setDelegate(this);
|
||||
this.viewController.setupRootFlow();
|
||||
|
||||
this.getWindow().setStatusBarColor(getBackgroundContent());
|
||||
this.getWindow().setNavigationBarColor(getBackgroundContent());
|
||||
|
||||
if (!isDarkTheme()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
this.getWindow().getInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
|
||||
} else {
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLockCreated() {
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerCaptureChanged(boolean hasCapture) {
|
||||
super.onPointerCaptureChanged(hasCapture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.guardanis.applock.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
|
||||
public class LockableAppCompatActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
|
||||
AppLock.onActivityResumed(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (requestCode == AppLock.REQUEST_CODE_UNLOCK && resultCode == Activity.RESULT_CANCELED)
|
||||
handleUnlockActivityReturnFailure();
|
||||
}
|
||||
|
||||
protected void handleUnlockActivityReturnFailure() {
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.guardanis.applock.activities;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.guardanis.applock.views.UnlockViewController;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
import static ru.vtosters.hooks.other.ThemesUtils.getBackgroundContent;
|
||||
import static ru.vtosters.hooks.other.ThemesUtils.getTextAttr;
|
||||
import static ru.vtosters.hooks.other.ThemesUtils.isDarkTheme;
|
||||
|
||||
public class UnlockActivity extends AppCompatActivity implements UnlockViewController.Delegate {
|
||||
|
||||
public static final String INTENT_ALLOW_UNLOCKED_EXIT = "pin__allow_activity_exit"; // false by default
|
||||
|
||||
protected UnlockViewController viewController;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstance) {
|
||||
super.onCreate(savedInstance);
|
||||
|
||||
setContentView(AndroidUtils.getIdentifier("applock__activity_unlock", "layout"));
|
||||
findViewById(AndroidUtils.getIdentifier("applock__activity_unlock", "id")).setBackgroundColor(getBackgroundContent());
|
||||
((TextView) findViewById(AndroidUtils.getIdentifier("pin__description", "id"))).setTextColor(getTextAttr());
|
||||
|
||||
this.viewController = new UnlockViewController(this, findViewById(AndroidUtils.getIdentifier("pin__container", "id")));
|
||||
this.viewController.setAutoAuthorizationEnabled(false); // Disable auto authorization so fingerprint doesn't crash onResume
|
||||
this.viewController.setDelegate(this);
|
||||
this.viewController.setupRootFlow();
|
||||
this.viewController.setAutoAuthorizationEnabled(true);
|
||||
|
||||
this.getWindow().setStatusBarColor(getBackgroundContent());
|
||||
this.getWindow().setNavigationBarColor(getBackgroundContent());
|
||||
|
||||
if (!isDarkTheme()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
this.getWindow().getInsetsController().setSystemBarsAppearance(WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
|
||||
} else {
|
||||
this.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnlockSuccessful() {
|
||||
setResult(Activity.RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (event.getRepeatCount() == 0) handleBackPressed();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
handleBackPressed();
|
||||
}
|
||||
|
||||
protected void handleBackPressed() {
|
||||
if (!getIntent().getBooleanExtra(INTENT_ALLOW_UNLOCKED_EXIT, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
setResult(Activity.RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPointerCaptureChanged(boolean hasCapture) {
|
||||
super.onPointerCaptureChanged(hasCapture);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.guardanis.applock.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatDialog;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.views.AppLockViewController;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public abstract class AppLockDialogBuilder<ALVC extends AppLockViewController> {
|
||||
|
||||
protected WeakReference<Activity> activity;
|
||||
protected ALVC viewController;
|
||||
|
||||
protected WeakReference<AppCompatDialog> dialog = new WeakReference<AppCompatDialog>(null);
|
||||
protected int layoutResId;
|
||||
|
||||
public AppLockDialogBuilder(Activity activity, int layoutResId) {
|
||||
this.activity = new WeakReference<>(activity);
|
||||
this.layoutResId = layoutResId;
|
||||
}
|
||||
|
||||
public Dialog show() {
|
||||
if (viewController != null)
|
||||
throw new RuntimeException("You can only call show() once per AppLockDialogBuilder instance.");
|
||||
|
||||
final Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return null;
|
||||
|
||||
View parent = activity.getLayoutInflater()
|
||||
.inflate(layoutResId, null, false);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
builder.setView(parent);
|
||||
builder.setOnCancelListener(dialogInterface -> handleCanceled());
|
||||
builder.setOnDismissListener(dialogInterface -> handleDismissed());
|
||||
|
||||
this.viewController = buildViewControllerInstance(parent);
|
||||
this.viewController.setupRootFlow();
|
||||
|
||||
AppCompatDialog dialog = builder.show();
|
||||
|
||||
this.dialog = new WeakReference<>(dialog);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
protected abstract ALVC buildViewControllerInstance(View parent);
|
||||
|
||||
protected void handleCanceled() {
|
||||
dismissDialog();
|
||||
}
|
||||
|
||||
protected void handleDismissed() {
|
||||
this.dialog = new WeakReference<>(null);
|
||||
this.viewController.unregisterReceivers();
|
||||
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.cancelPendingAuthentications();
|
||||
}
|
||||
|
||||
protected void dismissDialog() {
|
||||
try {
|
||||
AppCompatDialog dialog = this.dialog.get();
|
||||
|
||||
if (dialog == null)
|
||||
return;
|
||||
|
||||
// Attempt to prevent stupid fucking uncatchable system error "View not Attached to Window" when dialog dismissed
|
||||
Context context = ((ContextWrapper) dialog.getContext()).getBaseContext();
|
||||
if (context instanceof Activity) {
|
||||
if (!((Activity) context).isFinishing()) {
|
||||
if (!((Activity) context).isDestroyed())
|
||||
dialog.dismiss();
|
||||
}
|
||||
} else
|
||||
dialog.dismiss();
|
||||
} catch (Throwable e) {
|
||||
Log.e("AppLockDialogBuilder", "View not Attached to Window???", e);
|
||||
}
|
||||
|
||||
this.dialog = new WeakReference<>(null);
|
||||
}
|
||||
|
||||
public ALVC getViewController() {
|
||||
return viewController;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.guardanis.applock.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
|
||||
import com.guardanis.applock.views.LockCreationViewController;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class LockCreationDialogBuilder extends AppLockDialogBuilder<LockCreationViewController> implements LockCreationViewController.Delegate {
|
||||
|
||||
protected Runnable lockCreatedCallback;
|
||||
protected Runnable canceledCallback;
|
||||
|
||||
public LockCreationDialogBuilder(Activity activity) {
|
||||
super(activity, AndroidUtils.getIdentifier("applock__lock_creation", "layout"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Runnable to be triggered when a user has successfully enrolled in locking.
|
||||
*/
|
||||
public LockCreationDialogBuilder onLockCreated(Runnable lockCreatedCallback) {
|
||||
this.lockCreatedCallback = lockCreatedCallback;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Runnable to be triggered when a user has canceled enrollment.
|
||||
*/
|
||||
public LockCreationDialogBuilder onCanceled(Runnable canceledCallback) {
|
||||
this.canceledCallback = canceledCallback;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LockCreationViewController buildViewControllerInstance(View parent) {
|
||||
LockCreationViewController controller = new LockCreationViewController(activity.get(), parent);
|
||||
controller.setDelegate(this);
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLockCreated() {
|
||||
dismissDialog();
|
||||
|
||||
if (lockCreatedCallback != null)
|
||||
lockCreatedCallback.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCanceled() {
|
||||
super.handleCanceled();
|
||||
|
||||
if (canceledCallback != null)
|
||||
canceledCallback.run();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.guardanis.applock.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.views.UnlockViewController;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class UnlockDialogBuilder extends AppLockDialogBuilder<UnlockViewController> implements UnlockViewController.Delegate {
|
||||
|
||||
protected Runnable unlockCallback;
|
||||
protected Runnable canceledCallback;
|
||||
|
||||
public UnlockDialogBuilder(Activity activity) {
|
||||
super(activity, AndroidUtils.getIdentifier("applock__unlock", "layout"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Runnable to be triggered when a user has successfully unlocked.
|
||||
*/
|
||||
public UnlockDialogBuilder onUnlocked(Runnable unlockCallback) {
|
||||
this.unlockCallback = unlockCallback;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a Runnable to be triggered when a user has canceled unlocking.
|
||||
*/
|
||||
public UnlockDialogBuilder onCanceled(Runnable canceledCallback) {
|
||||
this.canceledCallback = canceledCallback;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UnlockViewController buildViewControllerInstance(View parent) {
|
||||
UnlockViewController controller = new UnlockViewController(activity.get(), parent);
|
||||
controller.setDelegate(this);
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnlockSuccessful() {
|
||||
dismissDialog();
|
||||
triggerUnlockCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleCanceled() {
|
||||
super.handleCanceled();
|
||||
|
||||
if (canceledCallback != null)
|
||||
canceledCallback.run();
|
||||
}
|
||||
|
||||
protected void triggerUnlockCallback() {
|
||||
if (unlockCallback != null)
|
||||
unlockCallback.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is actually enrolled in locking before opening the unlock dialog.
|
||||
* If the user is enrolled, show() will be called. Otherwise, this function will trigger this
|
||||
* instance's UnlockDialogBuilder.unlockCallback immediately.
|
||||
*
|
||||
* @return Dialog if show() is called
|
||||
*/
|
||||
public Dialog showIfEnrolledOrSuccess() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return null;
|
||||
|
||||
if (!AppLock.isEnrolled(activity)) {
|
||||
triggerUnlockCallback();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is both enrolled in locking and that unlocking is required before
|
||||
* opening the unlock dialog. If the user is enrolled, show() will be called. Otherwise,
|
||||
* this function will trigger this instance's UnlockDialogBuilder.unlockCallback immediately.
|
||||
*
|
||||
* @param longValidDurationMs the milliseconds a user's last unlock is valid for
|
||||
* @return Dialog if show() is called
|
||||
*/
|
||||
public Dialog showIfRequiredOrSuccess(long longValidDurationMs) {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return null;
|
||||
|
||||
if (!AppLock.isUnlockRequired(activity, longValidDurationMs)) {
|
||||
triggerUnlockCallback();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.guardanis.applock.pin;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
import com.guardanis.applock.AppLock;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
interface InputTextListener {
|
||||
void onTextEntered(int count);
|
||||
}
|
||||
|
||||
public class PINInputController implements TextView.OnEditorActionListener, InputTextListener {
|
||||
|
||||
private final WeakReference<PINInputView> inputView;
|
||||
private WeakReference<InputEventListener> eventListener = new WeakReference<>(null);
|
||||
|
||||
/**
|
||||
* If you want to actually receive events after this, make sure you call setInputEventListener
|
||||
*/
|
||||
public PINInputController(PINInputView inputView) {
|
||||
this.inputView = new WeakReference<>(inputView);
|
||||
|
||||
inputView.setOnEditorActionListener(this);
|
||||
inputView.setInputTextListener(this);
|
||||
}
|
||||
|
||||
public PINInputController ensureKeyboardVisible() {
|
||||
final PINInputView inputView = this.inputView.get();
|
||||
|
||||
if (inputView == null)
|
||||
return this;
|
||||
|
||||
inputView.postDelayed(inputView::ensureKeyboardVisible, 300);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public PINInputController setPasswordCharactersEnabled(boolean passwordCharacterEnabled) {
|
||||
this.inputView.get().setPasswordCharactersEnabled(passwordCharacterEnabled);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PINInputController setInputNumbersCount(int inputNumbersCount) {
|
||||
this.inputView.get().setInputViewsCount(inputNumbersCount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PINInputController setInputEventListener(InputEventListener eventListener) {
|
||||
this.eventListener = new WeakReference<>(eventListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
|
||||
PINInputView inputView = this.inputView.get();
|
||||
|
||||
if (inputView == null)
|
||||
return false;
|
||||
|
||||
if (isSoftKeyboardFinishedAction(textView, i, keyEvent)) {
|
||||
InputEventListener eventListener = this.eventListener.get();
|
||||
|
||||
if (eventListener != null)
|
||||
eventListener.onInputEntered(inputView.getText());
|
||||
|
||||
inputView.reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isSoftKeyboardFinishedAction(TextView view, int action, KeyEvent event) {
|
||||
// Some devices return null event on editor actions for Enter Button (thanks, Samsung)
|
||||
if (!(event == null || event.getAction() == KeyEvent.ACTION_DOWN))
|
||||
return false;
|
||||
|
||||
// Enter clicked on Bluetooth Keyboard (action is zero on hardware keyboards)
|
||||
if (action == EditorInfo.IME_ACTION_UNSPECIFIED && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)
|
||||
return true;
|
||||
|
||||
return action == EditorInfo.IME_ACTION_DONE
|
||||
|| action == EditorInfo.IME_ACTION_GO
|
||||
|| action == EditorInfo.IME_ACTION_SEND;
|
||||
}
|
||||
|
||||
public boolean matchesRequiredPINLength(String input) {
|
||||
final PINInputView inputView = this.inputView.get();
|
||||
|
||||
if (inputView == null)
|
||||
return false;
|
||||
|
||||
return inputView.matchesRequiredPINLength(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextEntered(int count) {
|
||||
if (count == Integer.parseInt(AppLock.getPinCodeLimits(AndroidUtils.getGlobalContext()))) {
|
||||
var inputView = this.inputView.get();
|
||||
InputEventListener eventListener = this.eventListener.get();
|
||||
|
||||
if (eventListener != null)
|
||||
eventListener.onInputEntered(inputView.getText());
|
||||
|
||||
inputView.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public interface InputEventListener {
|
||||
void onInputEntered(String input);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.guardanis.applock.pin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import ru.vtosters.hooks.other.ThemesUtils;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class PINInputView extends LinearLayout implements TextWatcher {
|
||||
|
||||
private final WeakHashMap<PINItemView, PINItemAnimator> animators = new WeakHashMap<PINItemView, PINItemAnimator>();
|
||||
private Paint itemTextPaint;
|
||||
private Paint itemBackgroundPaint;
|
||||
private int inputViewsCount = 10;
|
||||
private PINItemView[] pinItemViews;
|
||||
private EditText editText;
|
||||
private String lastText = "";
|
||||
private boolean passwordCharactersEnabled = true;
|
||||
private String passwordCharacter;
|
||||
private InputTextListener listener;
|
||||
|
||||
|
||||
public PINInputView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
init(attrs);
|
||||
}
|
||||
|
||||
private void init(AttributeSet attrs) {
|
||||
setWillNotDraw(false);
|
||||
setupEditText();
|
||||
|
||||
|
||||
itemTextPaint = new Paint();
|
||||
itemTextPaint.setColor(ThemesUtils.getAccentColor());
|
||||
|
||||
itemBackgroundPaint = new Paint();
|
||||
itemBackgroundPaint.setColor(ThemesUtils.getMutedAccentColor());
|
||||
|
||||
|
||||
inputViewsCount = Integer.parseInt(AppLock.getPinCodeLimits(AndroidUtils.getGlobalContext()));
|
||||
|
||||
passwordCharacter = getResources().getString(AndroidUtils.getIdentifier("applock__password_char", "string"));
|
||||
|
||||
}
|
||||
|
||||
protected void setupEditText() {
|
||||
removeAllViews();
|
||||
|
||||
this.editText = inflateFakeEditText();
|
||||
this.editText.addTextChangedListener(this);
|
||||
|
||||
addView(editText);
|
||||
}
|
||||
|
||||
protected EditText inflateFakeEditText() {
|
||||
return (EditText) LayoutInflater.from(getContext())
|
||||
.inflate(AndroidUtils.getIdentifier("applock__fake_edit_text", "layout"), this, false);
|
||||
}
|
||||
|
||||
public PINInputView setInputViewsCount(int inputViewsCount) {
|
||||
this.inputViewsCount = inputViewsCount;
|
||||
|
||||
return reset();
|
||||
}
|
||||
|
||||
public PINInputView reset() {
|
||||
this.lastText = "";
|
||||
this.editText.setText("");
|
||||
|
||||
if (pinItemViews != null)
|
||||
animateLastOut();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
ensureKeyboardVisible();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
public void ensureKeyboardVisible() {
|
||||
editText.requestFocus();
|
||||
|
||||
((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
|
||||
.showSoftInput(editText, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (pinItemViews == null || pinItemViews.length != inputViewsCount)
|
||||
setupItemViews(canvas);
|
||||
|
||||
String text = editText.getText().toString();
|
||||
|
||||
for (int i = 0; i < pinItemViews.length; i++) {
|
||||
String textItem = i < text.length()
|
||||
? (passwordCharactersEnabled ? passwordCharacter : text.substring(i, i + 1))
|
||||
: "";
|
||||
|
||||
pinItemViews[i].draw(canvas, textItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupItemViews(Canvas canvas) {
|
||||
pinItemViews = new PINItemView[inputViewsCount];
|
||||
|
||||
int cellWidth = canvas.getWidth() / inputViewsCount;
|
||||
int largestRadius = Math.min(cellWidth / 2, canvas.getHeight() / 2);
|
||||
int smallestRadius = (int) (largestRadius * Float.parseFloat(getResources().getString(AndroidUtils.getIdentifier("applock__empty_item_min_size_percent", "string"))));
|
||||
int[] minMaxRadius = new int[]{smallestRadius, largestRadius};
|
||||
|
||||
itemTextPaint.setTextSize((int) (largestRadius * .85));
|
||||
|
||||
for (int i = 0; i < pinItemViews.length; i++) {
|
||||
float[] positionInCanvas = getPositionInCanvas(canvas, i, cellWidth);
|
||||
|
||||
pinItemViews[i] = new PINItemView(positionInCanvas, minMaxRadius, itemTextPaint, itemBackgroundPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private float[] getPositionInCanvas(Canvas canvas, int position, int cellWidth) {
|
||||
return new float[]{
|
||||
(cellWidth * position) + (cellWidth / 2),
|
||||
canvas.getHeight() / 2
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if (editable == null || lastText == null)
|
||||
return;
|
||||
|
||||
String text = editable.toString();
|
||||
if (inputViewsCount < text.length()) {
|
||||
editText.setText(lastText);
|
||||
editText.setSelection(editText.getText().length());
|
||||
} else if (text.length() < lastText.length()) {
|
||||
animateLastOut();
|
||||
this.lastText = text;
|
||||
} else if (lastText.length() < text.length()) {
|
||||
animateLastIn();
|
||||
this.lastText = text;
|
||||
}
|
||||
if (listener != null) {
|
||||
listener.onTextEntered(text.length());
|
||||
}
|
||||
}
|
||||
|
||||
public void setInputTextListener(InputTextListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void animateLastOut() {
|
||||
int startingIndex = editText.getText().toString().length(); // One after current length, only happens on backspace
|
||||
|
||||
for (int i = pinItemViews.length - 1; startingIndex <= i; i--)
|
||||
if (!pinItemViews[i].isAnimatedOut())
|
||||
animate(pinItemViews[i], PINItemAnimator.ItemAnimationDirection.OUT);
|
||||
}
|
||||
|
||||
private void animateLastIn() {
|
||||
PINItemView item = pinItemViews[editText.getText().toString().length() - 1];
|
||||
animate(item, PINItemAnimator.ItemAnimationDirection.IN);
|
||||
}
|
||||
|
||||
private void animate(PINItemView view, PINItemAnimator.ItemAnimationDirection direction) {
|
||||
cancelPreviousAnimation(view);
|
||||
view.setAnimationDirection(direction);
|
||||
|
||||
PINItemAnimator animator = new PINItemAnimator(this, view, direction);
|
||||
animators.put(view, animator);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void cancelPreviousAnimation(PINItemView view) {
|
||||
PINItemAnimator animator = animators.get(view);
|
||||
|
||||
try {
|
||||
animator.cancel();
|
||||
animators.put(view, null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return editText.getText().toString();
|
||||
}
|
||||
|
||||
public void setOnEditorActionListener(TextView.OnEditorActionListener actionListener) {
|
||||
editText.setOnEditorActionListener(actionListener);
|
||||
}
|
||||
|
||||
public void setPasswordCharactersEnabled(boolean passwordCharactersEnabled) {
|
||||
this.passwordCharactersEnabled = passwordCharactersEnabled;
|
||||
}
|
||||
|
||||
public void setPasswordCharacter(String passwordCharacter) {
|
||||
this.passwordCharacter = passwordCharacter;
|
||||
}
|
||||
|
||||
public boolean matchesRequiredPINLength(String input) {
|
||||
return input.length() == inputViewsCount;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.guardanis.applock.pin;
|
||||
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class PINItemAnimator extends Thread {
|
||||
|
||||
private static final int ANIMATION_DURATION = 150;
|
||||
private static final int UPDATE_RATE = 25;
|
||||
private final WeakReference<PINInputView> inputView;
|
||||
private final PINItemView itemView;
|
||||
private final ItemAnimationDirection animationDirection;
|
||||
private final Interpolator itemInterpolator = new AccelerateDecelerateInterpolator();
|
||||
private final float minSizePercent;
|
||||
private long startTime;
|
||||
private boolean canceled = false;
|
||||
|
||||
public PINItemAnimator(PINInputView inputView, PINItemView itemView, ItemAnimationDirection animationDirection) {
|
||||
this.inputView = new WeakReference<>(inputView);
|
||||
this.itemView = itemView;
|
||||
this.animationDirection = animationDirection;
|
||||
this.minSizePercent = Float.parseFloat(inputView.getResources().getString(AndroidUtils.getIdentifier("applock__empty_item_min_size_percent", "string")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
if (animationDirection == ItemAnimationDirection.IN)
|
||||
animateIn();
|
||||
else
|
||||
animateOut();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void animateIn() throws Exception {
|
||||
float percent = minSizePercent;
|
||||
|
||||
while (percent < 1 && !canceled) {
|
||||
percent = Math.min(minSizePercent + calculatePercentComplete(), 1);
|
||||
updateView(percent);
|
||||
|
||||
Thread.sleep(UPDATE_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
private void animateOut() throws Exception {
|
||||
float percent = 1 - calculatePercentComplete();
|
||||
|
||||
while (minSizePercent < percent && !canceled) {
|
||||
percent = Math.max(1 - calculatePercentComplete(), minSizePercent);
|
||||
updateView(percent);
|
||||
|
||||
Thread.sleep(UPDATE_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
protected float calculatePercentComplete() {
|
||||
float completed = ((float) (System.currentTimeMillis() - startTime)) / ANIMATION_DURATION;
|
||||
|
||||
return itemInterpolator.getInterpolation(completed);
|
||||
}
|
||||
|
||||
private void updateView(final float percent) {
|
||||
final PINInputView inputView = this.inputView.get();
|
||||
|
||||
if (inputView == null)
|
||||
return;
|
||||
|
||||
inputView.post(() -> {
|
||||
itemView.onAnimationUpdate(percent);
|
||||
|
||||
inputView.invalidate();
|
||||
});
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
this.canceled = true;
|
||||
}
|
||||
|
||||
public enum ItemAnimationDirection {
|
||||
IN, OUT
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.guardanis.applock.pin;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
|
||||
public class PINItemView {
|
||||
|
||||
private final float[] position;
|
||||
private final int intendedRadius;
|
||||
private int currentRadius;
|
||||
private Paint backgroundPaint;
|
||||
|
||||
private float[] textPosition;
|
||||
private Paint textPaint;
|
||||
|
||||
private PINItemAnimator.ItemAnimationDirection animationDirection = PINItemAnimator.ItemAnimationDirection.IN;
|
||||
|
||||
public PINItemView(float[] position, int[] minMaxRadius, Paint baseTextPaint, Paint baseBackgroundPaint) {
|
||||
this.position = position;
|
||||
this.intendedRadius = minMaxRadius[1];
|
||||
this.currentRadius = minMaxRadius[0];
|
||||
|
||||
setupPaints(baseTextPaint, baseBackgroundPaint);
|
||||
}
|
||||
|
||||
private void setupPaints(Paint baseTextPaint, Paint baseBackgroundPaint) {
|
||||
this.textPaint = new Paint();
|
||||
this.textPaint.setTextAlign(Paint.Align.CENTER);
|
||||
this.textPaint.setAntiAlias(true);
|
||||
this.textPaint.setColor(baseTextPaint.getColor());
|
||||
this.textPaint.setTextSize(baseTextPaint.getTextSize());
|
||||
|
||||
this.textPosition = new float[]{
|
||||
position[0],
|
||||
position[1] - ((textPaint.descent() + textPaint.ascent()) / 2)
|
||||
};
|
||||
|
||||
this.backgroundPaint = new Paint();
|
||||
this.backgroundPaint.setTextAlign(Paint.Align.CENTER);
|
||||
this.backgroundPaint.setAntiAlias(true);
|
||||
this.backgroundPaint.setColor(baseBackgroundPaint.getColor());
|
||||
}
|
||||
|
||||
public void draw(Canvas canvas, String textValue) {
|
||||
canvas.drawCircle(position[0], position[1], currentRadius, backgroundPaint);
|
||||
canvas.drawText(textValue, textPosition[0], textPosition[1], textPaint);
|
||||
}
|
||||
|
||||
public void setAnimationDirection(PINItemAnimator.ItemAnimationDirection animationDirection) {
|
||||
this.animationDirection = animationDirection;
|
||||
}
|
||||
|
||||
public void onAnimationUpdate(float percentCompleted) {
|
||||
this.currentRadius = (int) (intendedRadius * percentCompleted);
|
||||
this.textPaint.setAlpha((int) (255 * percentCompleted));
|
||||
}
|
||||
|
||||
public boolean isAnimatedOut() {
|
||||
return animationDirection == PINItemAnimator.ItemAnimationDirection.OUT;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.guardanis.applock.services;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import androidx.core.os.CancellationSignal;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.utils.CipherGenerator;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class FingerprintLockService extends LockService {
|
||||
|
||||
private static final String PREF_ENROLLMENT_ALLOWED = "pin__fingerprint_enrollment_allowed";
|
||||
protected CancellationSignal fingerprintCancellationSignal;
|
||||
|
||||
@Override
|
||||
public boolean isEnrollmentEligible(Context context) {
|
||||
return Build.VERSION_CODES.M <= Build.VERSION.SDK_INT && context.getResources().getBoolean(AndroidUtils.getIdentifier("applock__fingerprint_service_enabled", "bool")) && isHardwarePresent(context);
|
||||
}
|
||||
|
||||
public void enroll(Context context, AuthenticationDelegate delegate) {
|
||||
authenticate(context, false, delegate);
|
||||
}
|
||||
|
||||
public void authenticate(Context context, AuthenticationDelegate delegate) {
|
||||
authenticate(context, true, delegate);
|
||||
}
|
||||
|
||||
protected void authenticate(Context context, boolean localEnrollmentRequired, AuthenticationDelegate delegate) {
|
||||
int errorCode = getRequiredResolutionErrorCode(context, localEnrollmentRequired);
|
||||
|
||||
if (-1 < errorCode) {
|
||||
delegate.onResolutionRequired(errorCode);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Should be handled by getRequiredResolutionErrorCode(Context, boolean)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return;
|
||||
|
||||
attemptFingerprintManagerAuthentication(context, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the resolvable error code or -1 if there are no issues requiring a resolution
|
||||
*/
|
||||
protected int getRequiredResolutionErrorCode(Context context, boolean localEnrollmentRequired) {
|
||||
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
return AppLock.ERROR_CODE_SDK_VERSION_MINIMUM;
|
||||
|
||||
if (localEnrollmentRequired && !isEnrolled(context))
|
||||
return AppLock.ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED;
|
||||
|
||||
if (!isHardwarePresent(context)) return AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE;
|
||||
|
||||
if (!manager.hasEnrolledFingerprints()) return AppLock.ERROR_CODE_FINGERPRINTS_EMPTY;
|
||||
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED)
|
||||
return AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected void attemptFingerprintManagerAuthentication(final Context context, final AuthenticationDelegate delegate) {
|
||||
this.fingerprintCancellationSignal = new CancellationSignal();
|
||||
|
||||
|
||||
var callback = new FingerprintManagerCompat.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(int errMsgId, CharSequence errString) {
|
||||
super.onAuthenticationError(errMsgId, errString);
|
||||
|
||||
delegate.onResolutionRequired(AndroidUtils.getIdentifier("applock__fingerprint_error_unknown", "string"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
|
||||
super.onAuthenticationHelp(helpMsgId, helpString);
|
||||
|
||||
delegate.onAuthenticationHelp(helpMsgId, helpString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
|
||||
super.onAuthenticationSucceeded(result);
|
||||
|
||||
notifyEnrolled(context);
|
||||
|
||||
delegate.onAuthenticationSuccess(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed();
|
||||
}
|
||||
};
|
||||
|
||||
delegate.onAuthenticating(fingerprintCancellationSignal);
|
||||
|
||||
try {
|
||||
Cipher cipher = generateAuthCipher(context, false, 0);
|
||||
FingerprintManagerCompat.CryptoObject cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher);
|
||||
|
||||
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
|
||||
manager.authenticate(cryptoObject, 0, fingerprintCancellationSignal, callback, null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
delegate.onResolutionRequired(AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHardwarePresent(Context context) {
|
||||
return FingerprintManagerCompat.from(context).isHardwareDetected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnrolled(Context context) {
|
||||
return AppLock.getInstance(context).getPreferences().getBoolean(PREF_ENROLLMENT_ALLOWED, false);
|
||||
}
|
||||
|
||||
protected void notifyEnrolled(Context context) {
|
||||
AppLock.getInstance(context).getPreferences().edit().putBoolean(PREF_ENROLLMENT_ALLOWED, true).commit();
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
protected Cipher generateAuthCipher(Context context, boolean forceRegenerate, int attempts) throws Exception {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
throw new RuntimeException("generateAuthCipher() not supported before Api 23");
|
||||
|
||||
return new CipherGenerator().generateAuthCipher(context, forceRegenerate, attempts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateEnrollments(Context context) {
|
||||
AppLock.getInstance(context).getPreferences().edit().putBoolean(PREF_ENROLLMENT_ALLOWED, false).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelPendingAuthentications(Context context) {
|
||||
if (fingerprintCancellationSignal != null) {
|
||||
this.fingerprintCancellationSignal.cancel();
|
||||
this.fingerprintCancellationSignal = null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface AuthenticationDelegate {
|
||||
void onResolutionRequired(int errorCode);
|
||||
|
||||
void onAuthenticationHelp(int code, CharSequence message);
|
||||
|
||||
void onAuthenticating(CancellationSignal cancellationSignal);
|
||||
|
||||
void onAuthenticationSuccess(FingerprintManagerCompat.AuthenticationResult result);
|
||||
|
||||
void onAuthenticationFailed(String message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.guardanis.applock.services;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public abstract class LockService {
|
||||
|
||||
public abstract boolean isEnrolled(Context context);
|
||||
|
||||
public abstract boolean isEnrollmentEligible(Context context);
|
||||
|
||||
public abstract void cancelPendingAuthentications(Context context);
|
||||
|
||||
public abstract void invalidateEnrollments(Context context);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.guardanis.applock.services;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.utils.CryptoUtils;
|
||||
|
||||
public class PINLockService extends LockService {
|
||||
|
||||
private static final String PREF_SAVED_LOCKED_PASSWORD = "pin__saved_locked_password";
|
||||
|
||||
@Override
|
||||
public boolean isEnrollmentEligible(Context context) {
|
||||
// I couldn't find a good reason to allow the disabling of the PIN as an option.
|
||||
return true;
|
||||
}
|
||||
|
||||
public void authenticate(Context context, String pin, AuthenticationDelegate eventListener) {
|
||||
if (!isEnrolled(context)) {
|
||||
eventListener.onNoPIN();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getEnrolledPIN(context).equals(CryptoUtils.encryptSha1(pin))) {
|
||||
eventListener.onPINDoesNotMatch();
|
||||
return;
|
||||
}
|
||||
|
||||
eventListener.onPINMatches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnrolled(Context context) {
|
||||
return AppLock.getInstance(context)
|
||||
.getPreferences()
|
||||
.getString(PREF_SAVED_LOCKED_PASSWORD, null) != null;
|
||||
}
|
||||
|
||||
private String getEnrolledPIN(Context context) {
|
||||
return AppLock.getInstance(context)
|
||||
.getPreferences()
|
||||
.getString(PREF_SAVED_LOCKED_PASSWORD, null);
|
||||
}
|
||||
|
||||
public void enroll(Context context, String pin) {
|
||||
AppLock.getInstance(context)
|
||||
.getPreferences()
|
||||
.edit()
|
||||
.putString(PREF_SAVED_LOCKED_PASSWORD, CryptoUtils.encryptSha1(pin))
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateEnrollments(Context context) {
|
||||
AppLock.getInstance(context)
|
||||
.getPreferences()
|
||||
.edit()
|
||||
.remove(PREF_SAVED_LOCKED_PASSWORD)
|
||||
.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelPendingAuthentications(Context context) {
|
||||
// There are none
|
||||
}
|
||||
|
||||
public interface AuthenticationDelegate {
|
||||
void onNoPIN();
|
||||
|
||||
void onPINDoesNotMatch();
|
||||
|
||||
void onPINMatches();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.guardanis.applock.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||
import android.security.keystore.KeyProperties;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.KeyGenerator;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public class CipherGenerator {
|
||||
|
||||
private static final String KEYSTORE_NAME = "AndroidKeyStore";
|
||||
|
||||
public Cipher generateAuthCipher(Context context, boolean forceRegenerate, int attempts) throws Exception {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||
throw new RuntimeException("generateAuthCipher() not supported before Api 23");
|
||||
|
||||
String alias = context.getString(AndroidUtils.getIdentifier("applock__fingerprint_alias", "string"));
|
||||
|
||||
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
|
||||
keyStore.load(null);
|
||||
|
||||
if (forceRegenerate || !keyStore.containsAlias(alias)) {
|
||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
|
||||
keyGenerator.init(new KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||
.setUserAuthenticationRequired(true)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||
.build());
|
||||
|
||||
keyGenerator.generateKey();
|
||||
}
|
||||
|
||||
String cipherFormat = String.format(
|
||||
"%s/%s/%s",
|
||||
KeyProperties.KEY_ALGORITHM_AES,
|
||||
KeyProperties.BLOCK_MODE_CBC,
|
||||
KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
||||
|
||||
try {
|
||||
Cipher cipher = Cipher.getInstance(cipherFormat);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyStore.getKey(alias, null));
|
||||
|
||||
return cipher;
|
||||
} catch (KeyPermanentlyInvalidatedException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (1 < attempts)
|
||||
return generateAuthCipher(context, true, attempts + 1);
|
||||
} catch (UnrecoverableKeyException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (1 < attempts)
|
||||
return generateAuthCipher(context, true, attempts + 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.guardanis.applock.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class CryptoUtils {
|
||||
|
||||
public static String encryptSha1(String text) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-1");
|
||||
md.update(text.getBytes(StandardCharsets.UTF_8), 0, text.length());
|
||||
|
||||
return convertToHex(md.digest());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String convertToHex(byte[] data) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
for (byte b : data) {
|
||||
int half = (b >>> 4) & 0x0F;
|
||||
int twoHalves = 0;
|
||||
|
||||
do {
|
||||
buf.append(half <= 9
|
||||
? (char) ('0' + half)
|
||||
: (char) ('a' + half - 10));
|
||||
|
||||
half = b & 0x0F;
|
||||
}
|
||||
while (twoHalves++ < 1);
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.guardanis.applock.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class LifeCycleUtils {
|
||||
|
||||
public static AppLockActivityLifeCycleCallbacks attach(Activity activity, AppLockActivityLifeCycleCallbacks.Delegate delegate) {
|
||||
AppLockActivityLifeCycleCallbacks callbacks = new AppLockActivityLifeCycleCallbacks(activity, delegate);
|
||||
|
||||
activity.getApplication()
|
||||
.registerActivityLifecycleCallbacks(callbacks);
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
public static class AppLockActivityLifeCycleCallbacks implements Application.ActivityLifecycleCallbacks {
|
||||
|
||||
protected WeakReference<Activity> openedActivity;
|
||||
protected WeakReference<Delegate> delegate;
|
||||
|
||||
public AppLockActivityLifeCycleCallbacks(Activity activity, Delegate delegate) {
|
||||
this.openedActivity = new WeakReference<>(activity);
|
||||
this.delegate = new WeakReference<>(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
Activity opened = this.openedActivity.get();
|
||||
Delegate delegate = this.delegate.get();
|
||||
|
||||
if (opened == null || delegate == null || opened != activity)
|
||||
return;
|
||||
|
||||
delegate.onActivityResumed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
Activity opened = this.openedActivity.get();
|
||||
Delegate delegate = this.delegate.get();
|
||||
|
||||
if (opened == null || delegate == null || opened != activity)
|
||||
return;
|
||||
|
||||
delegate.onActivityPaused();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
Activity opened = this.openedActivity.get();
|
||||
|
||||
if (opened == null || opened != activity)
|
||||
return;
|
||||
|
||||
opened.getApplication()
|
||||
.unregisterActivityLifecycleCallbacks(this);
|
||||
}
|
||||
|
||||
public interface Delegate {
|
||||
void onActivityResumed();
|
||||
|
||||
void onActivityPaused();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package com.guardanis.applock.views;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.pin.PINInputController;
|
||||
import com.guardanis.applock.pin.PINInputView;
|
||||
import com.guardanis.applock.utils.LifeCycleUtils;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public abstract class AppLockViewController implements LifeCycleUtils.AppLockActivityLifeCycleCallbacks.Delegate {
|
||||
|
||||
protected PINInputController pinInputController;
|
||||
|
||||
protected WeakReference<Activity> activity;
|
||||
protected WeakReference<View> parent;
|
||||
|
||||
protected WeakReference<PINInputView> pinInputView;
|
||||
|
||||
protected WeakReference<TextView> descriptionView;
|
||||
protected WeakReference<View> actionSettings;
|
||||
|
||||
protected boolean autoAuthorizationEnabled = true;
|
||||
|
||||
protected Application.ActivityLifecycleCallbacks activityLifecycleCallbacks;
|
||||
|
||||
public AppLockViewController(Activity activity, View parent) {
|
||||
this.activity = new WeakReference<>(activity);
|
||||
this.parent = new WeakReference<>(parent);
|
||||
this.descriptionView = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__description", "id")));
|
||||
this.actionSettings = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__action_settings", "id")));
|
||||
|
||||
this.pinInputView = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__input_view", "id")));
|
||||
|
||||
int inputViewsCount = Integer.parseInt(AppLock.getPinCodeLimits(activity));
|
||||
|
||||
boolean passwordCharsEnabled = parent.getResources()
|
||||
.getBoolean(AndroidUtils.getIdentifier("applock__item_password_chars_enabled", "bool"));
|
||||
|
||||
this.pinInputController = new PINInputController(pinInputView.get())
|
||||
.setInputNumbersCount(inputViewsCount)
|
||||
.setPasswordCharactersEnabled(passwordCharsEnabled);
|
||||
|
||||
this.activityLifecycleCallbacks = LifeCycleUtils.attach(activity, this);
|
||||
}
|
||||
|
||||
public abstract void setupRootFlow();
|
||||
|
||||
public void setDescription(int descriptionResId) {
|
||||
final TextView descriptionView = this.descriptionView.get();
|
||||
|
||||
if (descriptionView == null)
|
||||
return;
|
||||
|
||||
descriptionView.setText(descriptionResId);
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
final TextView descriptionView = this.descriptionView.get();
|
||||
|
||||
if (descriptionView == null)
|
||||
return;
|
||||
|
||||
descriptionView.setText(description);
|
||||
}
|
||||
|
||||
protected <T extends View> void hide(WeakReference<T> weakView) {
|
||||
final T view = weakView.get();
|
||||
|
||||
if (view == null)
|
||||
return;
|
||||
|
||||
view.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
protected <T extends View> void show(WeakReference<T> weakView) {
|
||||
final T view = weakView.get();
|
||||
|
||||
if (view == null)
|
||||
return;
|
||||
|
||||
view.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public PINInputController getPINInputController() {
|
||||
return pinInputController;
|
||||
}
|
||||
|
||||
public View getParent() {
|
||||
return parent.get();
|
||||
}
|
||||
|
||||
public void setAutoAuthorizationEnabled(boolean autoAuthorizationEnabled) {
|
||||
this.autoAuthorizationEnabled = autoAuthorizationEnabled;
|
||||
}
|
||||
|
||||
public void updateActionSettings(final int errorCode) {
|
||||
View actionSettings = this.actionSettings.get();
|
||||
|
||||
if (actionSettings == null)
|
||||
return;
|
||||
|
||||
if (getSettingsIntent(errorCode) == null) {
|
||||
actionSettings.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
|
||||
actionSettings.setVisibility(View.VISIBLE);
|
||||
actionSettings.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View view) {
|
||||
handleActionSettingsClicked(errorCode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract void handleActionSettingsClicked(int errorCode);
|
||||
|
||||
protected void handleInitialErrorPrompt(int errorCode) {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
if (errorCode == AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED) {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
new String[]{Manifest.permission.USE_FINGERPRINT},
|
||||
AppLock.REQUEST_CODE_FINGERPRINT_PERMISSION);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getDescriptionResIdForError(int errorCode) {
|
||||
return switch (errorCode) {
|
||||
case AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED ->
|
||||
AndroidUtils.getIdentifier("applock__fingerprint_error_permission", "string");
|
||||
case AppLock.ERROR_CODE_FINGERPRINTS_EMPTY ->
|
||||
AndroidUtils.getIdentifier("applock__fingerprint_error_none", "string");
|
||||
case AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE ->
|
||||
AndroidUtils.getIdentifier("applock__fingerprint_error_hardware", "string");
|
||||
case AppLock.ERROR_CODE_FINGERPRINTS_NOT_LOCALLY_ENROLLED ->
|
||||
AndroidUtils.getIdentifier("applock__fingerprint_error_not_enrolled", "string");
|
||||
default -> AndroidUtils.getIdentifier("applock__fingerprint_error_unknown", "string");
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an Intent directed towards the correct system setting for the error, or null if there is none.
|
||||
*/
|
||||
public Intent getSettingsIntent(int errorCode) {
|
||||
return switch (errorCode) {
|
||||
case AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED, AppLock.ERROR_CODE_FINGERPRINTS_EMPTY, AppLock.ERROR_CODE_FINGERPRINTS_MISSING_HARDWARE ->
|
||||
new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public void unregisterReceivers() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
activity.getApplication()
|
||||
.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.guardanis.applock.views;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
|
||||
import androidx.core.os.CancellationSignal;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.pin.PINInputController;
|
||||
import com.guardanis.applock.services.FingerprintLockService;
|
||||
import com.guardanis.applock.services.PINLockService;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class LockCreationViewController extends AppLockViewController
|
||||
implements PINInputController.InputEventListener, FingerprintLockService.AuthenticationDelegate {
|
||||
|
||||
protected DisplayVariant displayVariant = DisplayVariant.NONE;
|
||||
protected WeakReference<Delegate> delegate;
|
||||
protected WeakReference<View> chooserParent;
|
||||
protected String pinFirst;
|
||||
|
||||
public LockCreationViewController(Activity activity, View parent) {
|
||||
super(activity, parent);
|
||||
|
||||
this.chooserParent = new WeakReference<>(parent.findViewById(AndroidUtils.getIdentifier("pin__create_chooser_items", "id")));
|
||||
}
|
||||
|
||||
public LockCreationViewController setDelegate(Delegate delegate) {
|
||||
this.delegate = new WeakReference<>(delegate);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupRootFlow() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
setupPINCreation();
|
||||
}
|
||||
|
||||
protected void setupPINCreation() {
|
||||
this.displayVariant = DisplayVariant.PIN_CREATION;
|
||||
|
||||
hide(chooserParent);
|
||||
hide(actionSettings);
|
||||
|
||||
show(pinInputView);
|
||||
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_create_pin", "string"));
|
||||
|
||||
pinInputController.ensureKeyboardVisible();
|
||||
pinInputController.setInputEventListener(this);
|
||||
}
|
||||
|
||||
protected void setupPINConfirmation() {
|
||||
this.displayVariant = DisplayVariant.PIN_CONFIRMATION;
|
||||
|
||||
hide(chooserParent);
|
||||
hide(actionSettings);
|
||||
|
||||
show(pinInputView);
|
||||
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_confirm", "string"));
|
||||
|
||||
pinInputController.ensureKeyboardVisible();
|
||||
pinInputController.setInputEventListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEntered(String input) {
|
||||
switch (displayVariant) {
|
||||
case PIN_CREATION -> {
|
||||
if (!pinInputController.matchesRequiredPINLength(input)) {
|
||||
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
|
||||
|
||||
return;
|
||||
}
|
||||
this.pinFirst = input;
|
||||
setupPINConfirmation();
|
||||
}
|
||||
case PIN_CONFIRMATION -> {
|
||||
if (!pinInputController.matchesRequiredPINLength(input)) {
|
||||
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
|
||||
|
||||
return;
|
||||
}
|
||||
if (!input.equals(pinFirst)) {
|
||||
this.pinFirst = null;
|
||||
|
||||
setupPINCreation();
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_create_pin_reattempt", "string"));
|
||||
|
||||
return;
|
||||
}
|
||||
createPINLock(input);
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void createPINLock(String input) {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.getLockService(PINLockService.class)
|
||||
.enroll(activity, input);
|
||||
|
||||
var fingerprintLockService = AppLock.getInstance(activity).getLockService(FingerprintLockService.class);
|
||||
if (fingerprintLockService.isEnrollmentEligible(activity)) {
|
||||
setupFingerprintAuthentication();
|
||||
} else {
|
||||
handleLockCreated();
|
||||
}
|
||||
}
|
||||
|
||||
protected void setupFingerprintAuthentication() {
|
||||
this.displayVariant = DisplayVariant.FINGERPRINT_AUTHENTICATION;
|
||||
|
||||
hide(pinInputView);
|
||||
hide(chooserParent);
|
||||
hide(actionSettings);
|
||||
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint", "string"));
|
||||
|
||||
if (autoAuthorizationEnabled)
|
||||
attemptFingerprintAuthentication();
|
||||
}
|
||||
|
||||
protected void attemptFingerprintAuthentication() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.getLockService(FingerprintLockService.class)
|
||||
.enroll(activity, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResolutionRequired(int errorCode) {
|
||||
setDescription(getDescriptionResIdForError(errorCode));
|
||||
updateActionSettings(errorCode);
|
||||
handleInitialErrorPrompt(errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(int code, CharSequence message) {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
String unformattedHelpMessage = activity.getString(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint_help", "string"));
|
||||
String formatted = String.format(unformattedHelpMessage, message);
|
||||
|
||||
setDescription(formatted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticating(CancellationSignal cancellationSignal) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(FingerprintManagerCompat.AuthenticationResult result) {
|
||||
handleLockCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed(String message) {
|
||||
setDescription(message);
|
||||
}
|
||||
|
||||
protected void handleLockCreated() {
|
||||
this.displayVariant = DisplayVariant.NONE;
|
||||
|
||||
Delegate delegate = this.delegate.get();
|
||||
|
||||
if (delegate != null)
|
||||
delegate.onLockCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.cancelPendingAuthentications();
|
||||
|
||||
if (displayVariant == DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint_paused", "string"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null || displayVariant != DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||
return;
|
||||
|
||||
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
|
||||
setDescription(AndroidUtils.getIdentifier("applock__fingerprint_error_permission_multiple", "string"));
|
||||
updateActionSettings(AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint", "string"));
|
||||
hide(actionSettings);
|
||||
|
||||
attemptFingerprintAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleActionSettingsClicked(int errorCode) {
|
||||
Activity activity = this.activity.get();
|
||||
Intent intent = getSettingsIntent(errorCode);
|
||||
|
||||
if (activity == null || intent == null)
|
||||
return;
|
||||
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public enum DisplayVariant {
|
||||
NONE,
|
||||
PIN_CREATION,
|
||||
PIN_CONFIRMATION,
|
||||
FINGERPRINT_AUTHENTICATION
|
||||
}
|
||||
|
||||
public interface Delegate {
|
||||
void onLockCreated();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.guardanis.applock.views;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.pin.PINInputController;
|
||||
import com.guardanis.applock.services.FingerprintLockService;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class UnlockViewController extends AppLockViewController implements AppLock.UnlockDelegate, PINInputController.InputEventListener {
|
||||
|
||||
protected DisplayVariant displayVariant = DisplayVariant.PIN_UNLOCK;
|
||||
protected WeakReference<Delegate> delegate;
|
||||
|
||||
public UnlockViewController(Activity activity, View parent) {
|
||||
super(activity, parent);
|
||||
}
|
||||
|
||||
public UnlockViewController setDelegate(Delegate delegate) {
|
||||
this.delegate = new WeakReference<Delegate>(delegate);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupRootFlow() {
|
||||
View parent = this.parent.get();
|
||||
|
||||
if (parent == null)
|
||||
return;
|
||||
|
||||
setupPINUnlock();
|
||||
}
|
||||
|
||||
protected void setupPINUnlock() {
|
||||
this.displayVariant = DisplayVariant.PIN_UNLOCK;
|
||||
|
||||
hide(actionSettings);
|
||||
show(pinInputView);
|
||||
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_unlock_pin", "string"));
|
||||
|
||||
requestOpenKeyboard();
|
||||
pinInputController.setInputEventListener(this);
|
||||
|
||||
FingerprintLockService fingerprintService = AppLock.getInstance(this.parent.get().getContext())
|
||||
.getLockService(FingerprintLockService.class);
|
||||
|
||||
if (fingerprintService.isEnrolled(parent.get().getContext()))
|
||||
attemptFingerprintAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInputEntered(String input) {
|
||||
if (!pinInputController.matchesRequiredPINLength(input)) {
|
||||
setDescription(AndroidUtils.getIdentifier("applock__unlock_error_insufficient_selection", "string"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
attemptPINUnlock(input);
|
||||
}
|
||||
|
||||
protected void attemptPINUnlock(String input) {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.attemptPINUnlock(input, this);
|
||||
}
|
||||
|
||||
protected void attemptFingerprintAuthentication() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.attemptFingerprintUnlock(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnlockSuccessful() {
|
||||
this.displayVariant = DisplayVariant.NONE;
|
||||
|
||||
Delegate delegate = this.delegate.get();
|
||||
|
||||
if (delegate != null)
|
||||
delegate.onUnlockSuccessful();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResolutionRequired(int errorCode) {
|
||||
setDescription(getDescriptionResIdForError(errorCode));
|
||||
updateActionSettings(errorCode);
|
||||
handleInitialErrorPrompt(errorCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationHelp(int code, String message) {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
String unformattedHelpMessage = activity.getString(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint_help", "string"));
|
||||
String formatted = String.format(unformattedHelpMessage, message);
|
||||
|
||||
setDescription(formatted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailureLimitExceeded(String message) {
|
||||
setDescription(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestOpenKeyboard() {
|
||||
pinInputController.ensureKeyboardVisible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
AppLock.getInstance(activity)
|
||||
.cancelPendingAuthentications();
|
||||
|
||||
if (displayVariant == DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_create_fingerprint_paused", "string"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed() {
|
||||
Activity activity = this.activity.get();
|
||||
|
||||
if (activity == null || displayVariant != DisplayVariant.FINGERPRINT_AUTHENTICATION)
|
||||
return;
|
||||
|
||||
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
|
||||
setDescription(AndroidUtils.getIdentifier("applock__fingerprint_error_permission_multiple", "string"));
|
||||
updateActionSettings(AppLock.ERROR_CODE_FINGERPRINTS_PERMISSION_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
setDescription(AndroidUtils.getIdentifier("applock__description_unlock_fingerprint", "string"));
|
||||
hide(actionSettings);
|
||||
|
||||
attemptFingerprintAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleActionSettingsClicked(int errorCode) {
|
||||
Activity activity = this.activity.get();
|
||||
Intent intent = getSettingsIntent(errorCode);
|
||||
|
||||
if (activity == null || intent == null)
|
||||
return;
|
||||
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
public enum DisplayVariant {
|
||||
NONE,
|
||||
PIN_UNLOCK,
|
||||
FINGERPRINT_AUTHENTICATION
|
||||
}
|
||||
|
||||
public interface Delegate {
|
||||
void onUnlockSuccessful();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package com.vk.api.audio;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.vk.api.base.ApiRequest;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import com.vk.log.L;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.music.Genius;
|
||||
@@ -12,6 +15,9 @@ import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import ru.vtosters.lite.music.Genius;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class AudioGetLyrics extends ApiRequest<AudioGetLyrics.a> {
|
||||
private final MusicTrack musicTrack;
|
||||
|
||||
|
||||
@@ -2140,6 +2140,8 @@ public final class R {
|
||||
public static final int im_ic_msgdl = 0x7f040774; // 2130970484
|
||||
public static final int im_ic_msgreload = 0x7f040775; // 2130970485
|
||||
public static final int im_ic_stats = 0x7f040776; // 2130970486
|
||||
public static final int pinBackgroundColor = 0x7f040777; // 2130970487
|
||||
public static final int pinTextColor = 0x7f040778; // 2130970488
|
||||
}
|
||||
|
||||
public static final class bool {
|
||||
@@ -2160,6 +2162,9 @@ public final class R {
|
||||
public static final int spb_default_reversed = 0x7f05000e; // 2131034126
|
||||
public static final int vk_is_tablet = 0x7f05000f; // 2131034127
|
||||
public static final int com_samsung_android_icon_container_has_icon_container = 0x7f050010; // 2131034128
|
||||
public static final int applock__fingerprint_service_enabled = 0x7f050011; // 2131034129
|
||||
public static final int applock__item_password_chars_enabled = 0x7f050012; // 2131034130
|
||||
public static final int applock__unlock_activity_return_allowed = 0x7f050013; // 2131034131
|
||||
}
|
||||
|
||||
public static final class color {
|
||||
@@ -2968,6 +2973,13 @@ public final class R {
|
||||
public static final int yellow_light = 0x7f060323; // 2131100451
|
||||
public static final int yellow_sunflower = 0x7f060324; // 2131100452
|
||||
public static final int iclauncher = 0x7f060325; // 2131100453
|
||||
public static final int applock__action_settings = 0x7f060327; // 2131100455
|
||||
public static final int applock__activity_background = 0x7f060328; // 2131100456
|
||||
public static final int applock__create_choose_option_bg = 0x7f060329; // 2131100457
|
||||
public static final int applock__create_choose_option_bg_pressed = 0x7f06032a; // 2131100458
|
||||
public static final int applock__description_text = 0x7f06032b; // 2131100459
|
||||
public static final int applock__item_background = 0x7f06032c; // 2131100460
|
||||
public static final int applock__item_text = 0x7f06032d; // 2131100461
|
||||
public static final int abc_color_highlight_material = 0x7f060004; // 2131099652
|
||||
public static final int alpha05 = 0x7f060326; // 2131100454
|
||||
}
|
||||
@@ -3821,6 +3833,21 @@ public final class R {
|
||||
public static final int wrap_content = 0x7f07035b; // 2131166043
|
||||
public static final int year_label_height = 0x7f07035c; // 2131166044
|
||||
public static final int year_label_text_size = 0x7f07035d; // 2131166045
|
||||
public static final int applock__action_settings = 0x7f07035e; // 2131166046
|
||||
public static final int applock__action_settings_margin_top = 0x7f07035f; // 2131166047
|
||||
public static final int applock__action_settings_padding = 0x7f070360; // 2131166048
|
||||
public static final int applock__activity_padding = 0x7f070361; // 2131166049
|
||||
public static final int applock__chooser_image_size = 0x7f070362; // 2131166050
|
||||
public static final int applock__chooser_parent_padding_vertical = 0x7f070363; // 2131166051
|
||||
public static final int applock__chooser_text_padding_top = 0x7f070364; // 2131166052
|
||||
public static final int applock__content_padding_horizontal = 0x7f070365; // 2131166053
|
||||
public static final int applock__content_padding_vertical = 0x7f070366; // 2131166054
|
||||
public static final int applock__description_padding_bottom = 0x7f070367; // 2131166055
|
||||
public static final int applock__description_text = 0x7f070368; // 2131166056
|
||||
public static final int applock__fingerprint_scan_image_margin_top = 0x7f070369; // 2131166057
|
||||
public static final int applock__fingerprint_scan_image_size = 0x7f07036a; // 2131166058
|
||||
public static final int applock__item_margin_top = 0x7f07036b; // 2131166059
|
||||
public static final int applock__item_size = 0x7f07036c; // 2131166060
|
||||
public static final int vkim_article_blocked_bottom_margin = 0x7f070329; // 2131165993
|
||||
public static final int vkim_article_blocked_ic_margin = 0x7f07032a; // 2131165994
|
||||
public static final int vkim_article_blocked_ic_size = 0x7f07032b; // 2131165995
|
||||
@@ -4999,6 +5026,9 @@ public final class R {
|
||||
public static final int custom_thumb_selector = 0x7f080be9; // 2131233769
|
||||
public static final int custom_track_selector = 0x7f080bea; // 2131233770
|
||||
public static final int splash = 0x7f080bec; // 2131233772
|
||||
public static final int applock__chooser_option_bg = 0x7f080bee; // 2131233774
|
||||
public static final int applock__fingerprint = 0x7f080bef; // 2131233775
|
||||
public static final int applock__pin = 0x7f080bf0; // 2131233776
|
||||
public static final int abc_control_background_material = 0x7f080032; // 2131230770
|
||||
public static final int ic_action_close = 0x7f080322; // 2131231522
|
||||
public static final int ic_camera_outline_28_new = 0x7f0803a0; // 2131231648
|
||||
@@ -11062,6 +11092,12 @@ public final class R {
|
||||
public static final int color_preview_border = 0x7f0a1024; // 2131365924
|
||||
public static final int account_backup_view = 0x7f0a1025; // 2131365925
|
||||
public static final int copy_image = 0x7f0a1026; // 2131365926
|
||||
public static final int applock__activity_lock_creation = 0x7f0a1027; // 2131365927
|
||||
public static final int applock__activity_unlock = 0x7f0a1028; // 2131365928
|
||||
public static final int pin__action_settings = 0x7f0a1029; // 2131365929
|
||||
public static final int pin__container = 0x7f0a102a; // 2131365930
|
||||
public static final int pin__description = 0x7f0a102b; // 2131365931
|
||||
public static final int pin__input_view = 0x7f0a102c; // 2131365932
|
||||
}
|
||||
|
||||
public static final class integer {
|
||||
@@ -11108,6 +11144,10 @@ public final class R {
|
||||
public static final int status_bar_notification_info_maxnum = 0x7f0b002b; // 2131427371
|
||||
public static final int vkim_activity_animation_duration = 0x7f0b002c; // 2131427372
|
||||
public static final int vkim_playing_drawable_rect_count = 0x7f0b002d; // 2131427373
|
||||
public static final int applock__activity_lock_reenable_minutes = 0x7f0b002e; // 2131427374
|
||||
public static final int applock__failure_retry_delay = 0x7f0b002f; // 2131427375
|
||||
public static final int applock__input_pin_item_count = 0x7f0b0030; // 2131427376
|
||||
public static final int applock__max_retry_count = 0x7f0b0031; // 2131427377
|
||||
public static final int emoji_size_in_sprite = 0x7f0b0012; // 2131427346
|
||||
public static final int s1dip = 0x7f0b0027; // 2131427367
|
||||
public static final int s2dip = 0x7f0b0028; // 2131427368
|
||||
@@ -12883,6 +12923,11 @@ public final class R {
|
||||
public static final int wppreview = 0x7f0d06df; // 2131560159
|
||||
public static final int write_bar_i = 0x7f0d06e0; // 2131560160
|
||||
public static final int color_picker_item = 0x7f0d06e1; // 2131560161
|
||||
public static final int applock__activity_lock_creation = 0x7f0d06e2; // 2131560162
|
||||
public static final int applock__activity_unlock = 0x7f0d06e3; // 2131560163
|
||||
public static final int applock__fake_edit_text = 0x7f0d06e4; // 2131560164
|
||||
public static final int applock__lock_creation = 0x7f0d06e5; // 2131560165
|
||||
public static final int applock__unlock = 0x7f0d06e6; // 2131560166
|
||||
}
|
||||
|
||||
public static final class menu {
|
||||
@@ -19658,41 +19703,43 @@ public final class R {
|
||||
public static final int vkx_integration_enabled_info = 0x7f12188a; // 2131892362
|
||||
public static final int whitelisted_ad_groups_stories_title = 0x7f12188b; // 2131892363
|
||||
public static final int writebar_ios_title = 0x7f12188c; // 2131892364
|
||||
public static final int cache_size = 0x7f12188d; // 2131892365
|
||||
public static final int cache_size_summ = 0x7f12188e; // 2131892366
|
||||
public static final int clear_all_cache = 0x7f12188f; // 2131892367
|
||||
public static final int invalidate_theme_cache_each_update_title = 0x7f121890; // 2131892368
|
||||
public static final int invertFilters = 0x7f121891; // 2131892369
|
||||
public static final int invertFilters_summ = 0x7f121892; // 2131892370
|
||||
public static final int invertcached_summ = 0x7f121893; // 2131892371
|
||||
public static final int invertcached_title = 0x7f121894; // 2131892372
|
||||
public static final int microg_dialog1 = 0x7f121895; // 2131892373
|
||||
public static final int microg_dialog2 = 0x7f121896; // 2131892374
|
||||
public static final int microg_dialog3 = 0x7f121897; // 2131892375
|
||||
public static final int serverFeaturesDisable = 0x7f121898; // 2131892376
|
||||
public static final int serverFeaturesDisable_summ = 0x7f121899; // 2131892377
|
||||
public static final int sett_autoupdates = 0x7f12189a; // 2131892378
|
||||
public static final int sett_autoupdates_summ = 0x7f12189b; // 2131892379
|
||||
public static final int sett_battery = 0x7f12189c; // 2131892380
|
||||
public static final int sett_battery_summ1 = 0x7f12189d; // 2131892381
|
||||
public static final int sett_battery_summ2 = 0x7f12189e; // 2131892382
|
||||
public static final int sett_chats = 0x7f12189f; // 2131892383
|
||||
public static final int sett_chats_summ = 0x7f1218a0; // 2131892384
|
||||
public static final int sett_community = 0x7f1218a1; // 2131892385
|
||||
public static final int sett_community_summ = 0x7f1218a2; // 2131892386
|
||||
public static final int sett_missing_links = 0x7f1218a3; // 2131892387
|
||||
public static final int sett_missing_links_summ = 0x7f1218a4; // 2131892388
|
||||
public static final int sett_nogms_summ = 0x7f1218a5; // 2131892389
|
||||
public static final int sett_notifs = 0x7f1218a6; // 2131892390
|
||||
public static final int sett_notifs_summ = 0x7f1218a7; // 2131892391
|
||||
public static final int sett_recommendations = 0x7f1218a8; // 2131892392
|
||||
public static final int tgs_token_missing = 0x7f1218aa; // 2131892394
|
||||
public static final int tgs_token_reset = 0x7f1218ab; // 2131892395
|
||||
public static final int tgs_token_reset_dialog = 0x7f1218ac; // 2131892396
|
||||
public static final int tgs_token_reset_menu = 0x7f1218ad; // 2131892397
|
||||
public static final int update_err = 0x7f1218ae; // 2131892398
|
||||
public static final int update_latest = 0x7f1218af; // 2131892399
|
||||
public static final int APKTOOL_DUMMY_18a9 = 0x7f1218a9; // 2131892393
|
||||
public static final int applock__action_settings = 0x7f12188d; // 2131892365
|
||||
public static final int applock__create_chooser_option_fingerprint = 0x7f12188e; // 2131892366
|
||||
public static final int applock__create_chooser_option_pin = 0x7f12188f; // 2131892367
|
||||
public static final int applock__description_chooser = 0x7f121890; // 2131892368
|
||||
public static final int applock__description_confirm = 0x7f121891; // 2131892369
|
||||
public static final int applock__description_create_fingerprint = 0x7f121892; // 2131892370
|
||||
public static final int applock__description_create_fingerprint_paused = 0x7f121893; // 2131892371
|
||||
public static final int applock__description_create_pin = 0x7f121894; // 2131892372
|
||||
public static final int applock__description_create_pin_reattempt = 0x7f121895; // 2131892373
|
||||
public static final int applock__description_unlock_fingerprint = 0x7f121896; // 2131892374
|
||||
public static final int applock__description_unlock_fingerprint_help = 0x7f121897; // 2131892375
|
||||
public static final int applock__description_unlock_pin = 0x7f121898; // 2131892376
|
||||
public static final int applock__empty_item_min_size_percent = 0x7f121899; // 2131892377
|
||||
public static final int applock__fingerprint_alias = 0x7f12189a; // 2131892378
|
||||
public static final int applock__fingerprint_error_hardware = 0x7f12189b; // 2131892379
|
||||
public static final int applock__fingerprint_error_none = 0x7f12189c; // 2131892380
|
||||
public static final int applock__fingerprint_error_not_enrolled = 0x7f12189d; // 2131892381
|
||||
public static final int applock__fingerprint_error_permission = 0x7f12189e; // 2131892382
|
||||
public static final int applock__fingerprint_error_permission_multiple = 0x7f12189f; // 2131892383
|
||||
public static final int applock__fingerprint_error_unknown = 0x7f1218a0; // 2131892384
|
||||
public static final int applock__fingerprint_error_unrecognized = 0x7f1218a1; // 2131892385
|
||||
public static final int applock__password_char = 0x7f1218a2; // 2131892386
|
||||
public static final int applock__toast_unlock_required = 0x7f1218a3; // 2131892387
|
||||
public static final int applock__unlock_error_insufficient_selection = 0x7f1218a4; // 2131892388
|
||||
public static final int applock__unlock_error_match_failed = 0x7f1218a5; // 2131892389
|
||||
public static final int applock__unlock_error_no_matching_pin_found = 0x7f1218a6; // 2131892390
|
||||
public static final int applock__unlock_error_retry_limit_exceeded = 0x7f1218a7; // 2131892391
|
||||
public static final int cache_size = 0x7f1218a8; // 2131892392
|
||||
public static final int cache_size_summ = 0x7f1218a9; // 2131892393
|
||||
public static final int clear_all_cache = 0x7f1218aa; // 2131892394
|
||||
public static final int invalidate_theme_cache_each_update_title = 0x7f1218ab; // 2131892395
|
||||
public static final int invertFilters = 0x7f1218ac; // 2131892396
|
||||
public static final int invertFilters_summ = 0x7f1218ad; // 2131892397
|
||||
public static final int invertcached_summ = 0x7f1218ae; // 2131892398
|
||||
public static final int invertcached_title = 0x7f1218af; // 2131892399
|
||||
public static final int pin_not_set = 0x7f1218b0; // 2131892400
|
||||
public static final int pin_reset = 0x7f1218b1; // 2131892401
|
||||
}
|
||||
|
||||
public static final class style {
|
||||
@@ -20828,6 +20875,11 @@ public final class R {
|
||||
public static final int VkAmoledTheme = 0x7f130472; // 2131952754
|
||||
public static final int VkMilkAmoledStyle = 0x7f130473; // 2131952755
|
||||
public static final int VkMilkAmoledTheme = 0x7f130474; // 2131952756
|
||||
public static final int applock__action_settings = 0x7f130487; // 2131952775
|
||||
public static final int applock__content_parent = 0x7f130488; // 2131952776
|
||||
public static final int applock__description = 0x7f130489; // 2131952777
|
||||
public static final int applock__input_fingerprint_view = 0x7f13048a; // 2131952778
|
||||
public static final int applock__input_pin_view = 0x7f13048b; // 2131952779
|
||||
public static final int Base_V23_Theme_AppCompat = 0x7f13009d; // 2131951773
|
||||
public static final int Base_V23_Theme_AppCompat_Light = 0x7f13009e; // 2131951774
|
||||
public static final int Platform_V25_AppCompat = 0x7f13017f; // 2131951999
|
||||
|
||||
@@ -1,505 +0,0 @@
|
||||
package com.vtosters.lite.api.execute;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.RectF;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import com.vk.api.base.utils.ApiUtils;
|
||||
import com.vk.core.network.Network;
|
||||
import com.vk.core.util.AppContextHolder;
|
||||
import com.vk.core.util.Screen;
|
||||
import com.vk.dto.common.data.VKList;
|
||||
import com.vk.dto.gift.GiftItem;
|
||||
import com.vk.dto.group.Group;
|
||||
import com.vk.dto.newsfeed.entries.ProfilesRecommendations;
|
||||
import com.vk.dto.photo.Photo;
|
||||
import com.vk.dto.user.UserProfile;
|
||||
import com.vk.dto.user.deactivation.UserDeactivation;
|
||||
import com.vtosters.lite.api.ExtendedUserProfile;
|
||||
import com.vtosters.lite.auth.VKAccountManager;
|
||||
import com.vtosters.lite.utils.Utils;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.OnlineFormatterHook;
|
||||
import ru.vtosters.hooks.RenameHook;
|
||||
import ru.vtosters.hooks.ssfs.ProfileHook;
|
||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
import ru.vtosters.lite.proxy.ProxyUtils;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GetFullUserProfile extends GetFullProfile<ExtendedUserProfile> {
|
||||
private static final OkHttpClient mClient = VtOkHttpClient.getInstance();
|
||||
|
||||
public GetFullUserProfile(int i, String str, String str2) {
|
||||
super(i, "execute.getFullProfileNewNew");
|
||||
i = i == 0 ? VKAccountManager.d().D0() : i;
|
||||
b("user_id", i);
|
||||
b("func_v", 23); // 11
|
||||
this.F = i;
|
||||
if (!TextUtils.isEmpty(str2)) {
|
||||
c("source", str2);
|
||||
}
|
||||
b("photo_sizes", 1);
|
||||
b("skip_hidden", 1);
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return;
|
||||
}
|
||||
c("access_keys", str);
|
||||
}
|
||||
|
||||
public JSONArray getCitiesNames(String ids) {
|
||||
var request = new Request.a()
|
||||
.b("https://" + ProxyUtils.getApi() + "/method/" + "database.getCitiesById"
|
||||
+ "?v=5.119"
|
||||
+ "&city_ids="
|
||||
+ ids
|
||||
+ "&access_token="
|
||||
+ AccountManagerUtils.getUserToken())
|
||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||
|
||||
try {
|
||||
return new JSONObject(mClient.a(request).execute().a().g()).getJSONArray("response");
|
||||
} catch (JSONException | IOException e) {
|
||||
Log.d(this.getClass().getSimpleName(), e.toString());
|
||||
}
|
||||
|
||||
return new JSONArray();
|
||||
}
|
||||
|
||||
public JSONArray getCountriesNames(String ids) {
|
||||
var request = new Request.a()
|
||||
.b("https://" + ProxyUtils.getApi() + "/method/" + "database.getCountriesById"
|
||||
+ "?v=5.119"
|
||||
+ "&country_ids="
|
||||
+ ids
|
||||
+ "&access_token="
|
||||
+ AccountManagerUtils.getUserToken())
|
||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||
|
||||
try {
|
||||
return new JSONObject(mClient.a(request).execute().a().g()).getJSONArray("response");
|
||||
} catch (JSONException | IOException e) {
|
||||
Log.d(this.getClass().getSimpleName(), e.toString());
|
||||
}
|
||||
|
||||
return new JSONArray();
|
||||
}
|
||||
|
||||
|
||||
protected ExtendedUserProfile o() {
|
||||
return new ExtendedUserProfile();
|
||||
}
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
public void a(ExtendedUserProfile extendedUserProfile, JSONObject jSONObject) throws JSONException {
|
||||
char c;
|
||||
RenameHook.injectIntoJson(jSONObject);
|
||||
JSONObject onlineHook = OnlineFormatterHook.onlineHook(jSONObject);
|
||||
UserProfile userProfile = extendedUserProfile.a;
|
||||
int i = onlineHook.getInt("id");
|
||||
this.F = i;
|
||||
userProfile.b = i;
|
||||
ProfileHook.fetchInfo(extendedUserProfile, onlineHook);
|
||||
|
||||
extendedUserProfile.a.c = onlineHook.getString("first_name");
|
||||
extendedUserProfile.a.e = onlineHook.getString("last_name");
|
||||
extendedUserProfile.a.d = extendedUserProfile.a.c + " " + extendedUserProfile.a.e;
|
||||
extendedUserProfile.a.f = onlineHook.getString(Screen.a() > 1.0f ? "photo_medium_rec" : "photo_rec");
|
||||
extendedUserProfile.a.h = onlineHook.getInt("friend_status") == 3;
|
||||
extendedUserProfile.j = onlineHook.optString("photo_max", onlineHook.getString("photo_medium_rec"));
|
||||
extendedUserProfile.m = onlineHook.optString("activity", "");
|
||||
extendedUserProfile.a.g = onlineHook.getInt("sex") == 1;
|
||||
extendedUserProfile.a.E = UserProfile.d(jSONObject);
|
||||
|
||||
if (onlineHook.has("crop_photo")) {
|
||||
extendedUserProfile.k = new Photo(onlineHook.getJSONObject("crop_photo").getJSONObject("photo"));
|
||||
JSONObject jSONObject2 = onlineHook.getJSONObject("crop_photo").getJSONObject("rect");
|
||||
JSONObject jSONObject3 = onlineHook.getJSONObject("crop_photo").getJSONObject("crop");
|
||||
double d = jSONObject3.getDouble("x");
|
||||
double d2 = jSONObject3.getDouble("x2");
|
||||
double d3 = jSONObject3.getDouble("y");
|
||||
double d4 = jSONObject3.getDouble("y2");
|
||||
double d5 = jSONObject2.getDouble("x");
|
||||
double d6 = jSONObject2.getDouble("x2");
|
||||
double d7 = jSONObject2.getDouble("y");
|
||||
double d8 = (d2 - d) / 100.0d;
|
||||
double d9 = (d4 - d3) / 100.0d;
|
||||
double d10 = (d / 100.0d) + ((d5 / 100.0d) * d8);
|
||||
double d11 = (d3 / 100.0d) + ((d7 / 100.0d) * d9);
|
||||
extendedUserProfile.l = new RectF((float) d10, (float) d11, (float) (d10 + (((d6 - d5) / 100.0d) * d8)), (float) (d11 + (((jSONObject2.getDouble("y2") - d7) / 100.0d) * d9)));
|
||||
}
|
||||
|
||||
SparseArray<String> sparseArray = new SparseArray<>();
|
||||
SparseArray<String> sparseArray2 = new SparseArray<>();
|
||||
|
||||
if (onlineHook.has("cities") && onlineHook.getJSONArray("cities").length() > 0) {
|
||||
List<String> cityNames = new ArrayList<>();
|
||||
|
||||
JSONArray cities = onlineHook.getJSONArray("cities");
|
||||
|
||||
for (int i21 = 0; i21 < cities.length(); i21++) {
|
||||
cityNames.add(cities.getString(i21));
|
||||
}
|
||||
|
||||
JSONArray citiesNamesJSONArray = getCitiesNames(String.join(", ", cityNames));
|
||||
|
||||
Log.d(this.getClass().getSimpleName(), citiesNamesJSONArray.toString());
|
||||
|
||||
for (int i22 = 0; i22 < citiesNamesJSONArray.length(); i22++) {
|
||||
JSONObject cityJSONObject = citiesNamesJSONArray.getJSONObject(i22);
|
||||
int cityid = cityJSONObject.getInt("id");
|
||||
String cityTitle = cityJSONObject.getString("title");
|
||||
sparseArray.put(cityid, cityTitle);
|
||||
}
|
||||
|
||||
extendedUserProfile.i0 = sparseArray.get(cities.getInt(0));
|
||||
}
|
||||
|
||||
if (onlineHook.has("countries") && onlineHook.getJSONArray("countries").length() > 0) {
|
||||
JSONArray countries = onlineHook.getJSONArray("countries");
|
||||
List<String> countryList = new ArrayList<>();
|
||||
|
||||
for (int i23 = 0; i23 < countries.length(); i23++) {
|
||||
countryList.add(countries.getString(i23));
|
||||
}
|
||||
|
||||
JSONArray countryInfo = getCountriesNames(String.join(", ", countryList));
|
||||
|
||||
Log.d(this.getClass().getSimpleName(), countryInfo.toString());
|
||||
|
||||
for (int i24 = 0; i24 < countryInfo.length(); i24++) {
|
||||
JSONObject country = countryInfo.getJSONObject(i24);
|
||||
int countryid = country.getInt("id");
|
||||
String countryTitle = country.getString("title");
|
||||
sparseArray2.put(countryid, countryTitle);
|
||||
}
|
||||
|
||||
extendedUserProfile.j0 = sparseArray2.get(countries.getInt(0));
|
||||
}
|
||||
|
||||
extendedUserProfile.U = onlineHook.getInt("can_write_private_message") == 1;
|
||||
extendedUserProfile.X = onlineHook.getInt("can_post") == 1;
|
||||
extendedUserProfile.Y = onlineHook.optInt("can_suggest") == 1;
|
||||
extendedUserProfile.V = onlineHook.optInt("can_see_all_posts") == 1;
|
||||
extendedUserProfile.f0 = onlineHook.optInt("can_see_archived_posts") == 1;
|
||||
extendedUserProfile.a0 = "all".equals(onlineHook.optString("wall_default"));
|
||||
extendedUserProfile.W = onlineHook.optBoolean("can_call", false);
|
||||
extendedUserProfile.Z = onlineHook.optInt("blacklisted_by_me") == 1;
|
||||
|
||||
if (onlineHook.optInt("blacklisted") == 1) {
|
||||
extendedUserProfile.Y0 = new ExtendedUserProfile.c();
|
||||
}
|
||||
|
||||
extendedUserProfile.u = onlineHook.optInt("relation");
|
||||
if (onlineHook.has("relation_partner")) {
|
||||
extendedUserProfile.v = onlineHook.getJSONObject("relation_partner").getInt("id");
|
||||
extendedUserProfile.w = onlineHook.getJSONObject("relation_partner").getString("first_name") + " " + onlineHook.getJSONObject("relation_partner").getString("last_name");
|
||||
}
|
||||
|
||||
if (onlineHook.has("bdate")) {
|
||||
String string = onlineHook.getString("bdate");
|
||||
extendedUserProfile.a.I = string;
|
||||
String[] split = string.split("\\.");
|
||||
extendedUserProfile.s = Integer.parseInt(split[0]);
|
||||
extendedUserProfile.t = Integer.parseInt(split[1]);
|
||||
if (split.length > 2) {
|
||||
extendedUserProfile.r = Integer.parseInt(split[2]);
|
||||
}
|
||||
}
|
||||
|
||||
extendedUserProfile.d = onlineHook.optString("first_name_dat", extendedUserProfile.a.c);
|
||||
extendedUserProfile.b = onlineHook.optString("first_name_gen", extendedUserProfile.a.c);
|
||||
extendedUserProfile.c = onlineHook.optString("first_name_ins", extendedUserProfile.a.c);
|
||||
extendedUserProfile.h = onlineHook.optString("first_name_acc", extendedUserProfile.a.c);
|
||||
extendedUserProfile.g = onlineHook.optString("last_name_dat", extendedUserProfile.a.e);
|
||||
extendedUserProfile.e = onlineHook.optString("last_name_gen", extendedUserProfile.a.e);
|
||||
extendedUserProfile.f = onlineHook.optString("last_name_ins", extendedUserProfile.a.e);
|
||||
|
||||
String optString = onlineHook.optString("last_name_acc", extendedUserProfile.a.e);
|
||||
extendedUserProfile.i = optString;
|
||||
|
||||
if (onlineHook.has("mobile_phone") && !onlineHook.getString("mobile_phone").isEmpty()) {
|
||||
extendedUserProfile.l0 = onlineHook.getString("mobile_phone");
|
||||
}
|
||||
|
||||
if (onlineHook.has("home_phone") && !onlineHook.getString("home_phone").isEmpty()) {
|
||||
extendedUserProfile.m0 = onlineHook.getString("home_phone");
|
||||
}
|
||||
|
||||
if (onlineHook.has("skype")) {
|
||||
extendedUserProfile.n0 = onlineHook.getString("skype");
|
||||
}
|
||||
|
||||
if (onlineHook.has("twitter")) {
|
||||
extendedUserProfile.o0 = onlineHook.getString("twitter");
|
||||
}
|
||||
|
||||
if (onlineHook.has("livejournal")) {
|
||||
extendedUserProfile.r0 = onlineHook.getString("livejournal");
|
||||
}
|
||||
|
||||
if (onlineHook.has("facebook") && onlineHook.optLong("facebook", -1L) != -1) {
|
||||
extendedUserProfile.q0 = onlineHook.getLong("facebook");
|
||||
extendedUserProfile.p0 = onlineHook.getString("facebook_name");
|
||||
}
|
||||
|
||||
if (onlineHook.has("instagram")) {
|
||||
extendedUserProfile.u0 = onlineHook.getString("instagram");
|
||||
}
|
||||
|
||||
extendedUserProfile.R0 = onlineHook.getInt("friend_status");
|
||||
JSONArray optJSONArray = onlineHook.optJSONArray("schools");
|
||||
extendedUserProfile.v0 = new ArrayList<>();
|
||||
|
||||
ArrayList arrayList = null;
|
||||
if (optJSONArray != null) {
|
||||
for (int i4 = 0; i4 < optJSONArray.length(); i4++) {
|
||||
ExtendedUserProfile.f fVar = new ExtendedUserProfile.f();
|
||||
JSONObject jSONObject6 = optJSONArray.getJSONObject(i4);
|
||||
fVar.c = sparseArray.get(jSONObject6.optInt(optString));
|
||||
fVar.a = jSONObject6.optString("name", "???");
|
||||
fVar.g = jSONObject6.optInt("year_from");
|
||||
fVar.f = jSONObject6.optInt("year_to");
|
||||
fVar.h = jSONObject6.optInt("year_graduated");
|
||||
fVar.b = jSONObject6.optString("class", null);
|
||||
fVar.d = jSONObject6.optString("speciality", null);
|
||||
fVar.e = jSONObject6.optString("type_str", AppContextHolder.a.getResources().getString(2131889117));
|
||||
extendedUserProfile.v0.add(fVar);
|
||||
}
|
||||
}
|
||||
extendedUserProfile.w0 = new ArrayList<>();
|
||||
|
||||
JSONArray optJSONArray2 = onlineHook.optJSONArray("universities");
|
||||
if (optJSONArray2 != null) {
|
||||
for (int i5 = 0; i5 < optJSONArray2.length(); i5++) {
|
||||
ExtendedUserProfile.g gVar = new ExtendedUserProfile.g();
|
||||
JSONObject jSONObject7 = optJSONArray2.getJSONObject(i5);
|
||||
gVar.a = jSONObject7.getString("name").trim();
|
||||
if (jSONObject7.has("faculty_name")) {
|
||||
gVar.b = jSONObject7.getString("faculty_name").trim();
|
||||
}
|
||||
if (jSONObject7.has("chair_name")) {
|
||||
gVar.c = jSONObject7.getString("chair_name").trim();
|
||||
}
|
||||
gVar.d = jSONObject7.optInt("graduation");
|
||||
extendedUserProfile.w0.add(gVar);
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineHook.has("interests") && onlineHook.getString("interests").length() > 0) {
|
||||
extendedUserProfile.y0 = onlineHook.getString("interests");
|
||||
}
|
||||
|
||||
if (onlineHook.has("movies") && onlineHook.getString("movies").length() > 0) {
|
||||
extendedUserProfile.A0 = onlineHook.getString("movies");
|
||||
}
|
||||
|
||||
if (onlineHook.has("music") && onlineHook.getString("music").length() > 0) {
|
||||
extendedUserProfile.z0 = onlineHook.getString("music");
|
||||
}
|
||||
|
||||
if (onlineHook.has("tv") && onlineHook.getString("tv").length() > 0) {
|
||||
extendedUserProfile.B0 = onlineHook.getString("tv");
|
||||
}
|
||||
|
||||
if (onlineHook.has("books") && onlineHook.getString("books").length() > 0) {
|
||||
extendedUserProfile.C0 = onlineHook.getString("books");
|
||||
}
|
||||
|
||||
if (onlineHook.has("games") && onlineHook.getString("games").length() > 0) {
|
||||
extendedUserProfile.D0 = onlineHook.getString("games");
|
||||
}
|
||||
|
||||
if (onlineHook.has("about") && onlineHook.getString("about").length() > 0) {
|
||||
extendedUserProfile.F0 = onlineHook.getString("about");
|
||||
}
|
||||
|
||||
if (onlineHook.has("quotes") && onlineHook.getString("quotes").length() > 0) {
|
||||
extendedUserProfile.E0 = onlineHook.getString("quotes");
|
||||
}
|
||||
|
||||
if (onlineHook.has("activities") && onlineHook.getString("activities").length() > 0) {
|
||||
extendedUserProfile.x0 = onlineHook.getString("activities");
|
||||
}
|
||||
|
||||
if (onlineHook.has("home_town") && onlineHook.getString("home_town").length() > 0) {
|
||||
extendedUserProfile.g0 = onlineHook.getString("home_town");
|
||||
}
|
||||
|
||||
if (onlineHook.has("site") && onlineHook.getString("site").length() > 0) {
|
||||
extendedUserProfile.M = onlineHook.getString("site");
|
||||
}
|
||||
|
||||
extendedUserProfile.a.Q.b(onlineHook);
|
||||
|
||||
JSONObject optJSONObject = onlineHook.optJSONObject("personal");
|
||||
if (optJSONObject != null) {
|
||||
JSONArray optJSONArray3 = optJSONObject.optJSONArray("langs");
|
||||
|
||||
if (optJSONArray3 != null) {
|
||||
ArrayList arrayList2 = new ArrayList<>();
|
||||
for (int i6 = 0; i6 < optJSONArray3.length(); i6++) {
|
||||
arrayList2.add(optJSONArray3.getString(i6));
|
||||
}
|
||||
extendedUserProfile.G0 = TextUtils.join(", ", arrayList2);
|
||||
}
|
||||
|
||||
extendedUserProfile.J0 = optJSONObject.optInt("political");
|
||||
extendedUserProfile.H0 = optJSONObject.optString("religion");
|
||||
extendedUserProfile.K0 = optJSONObject.optInt("life_main");
|
||||
extendedUserProfile.L0 = optJSONObject.optInt("people_main");
|
||||
extendedUserProfile.I0 = optJSONObject.optString("inspired_by");
|
||||
extendedUserProfile.M0 = optJSONObject.optInt("smoking");
|
||||
extendedUserProfile.N0 = optJSONObject.optInt("alcohol");
|
||||
}
|
||||
|
||||
SparseArray sparseArray3 = new SparseArray();
|
||||
JSONArray optJSONArray4 = onlineHook.optJSONArray("relatives_profiles");
|
||||
if (optJSONArray4 != null) {
|
||||
for (int i7 = 0; i7 < optJSONArray4.length(); i7++) {
|
||||
UserProfile userProfile2 = new UserProfile(optJSONArray4.getJSONObject(i7));
|
||||
sparseArray3.put(userProfile2.b, userProfile2);
|
||||
}
|
||||
}
|
||||
|
||||
JSONArray optJSONArray5 = onlineHook.optJSONArray("relatives");
|
||||
if (optJSONArray5 != null) {
|
||||
ArrayList arrayList3 = null;
|
||||
ArrayList arrayList4 = null;
|
||||
ArrayList arrayList5 = null;
|
||||
ArrayList arrayList6 = null;
|
||||
for (int i8 = 0; i8 < optJSONArray5.length(); i8++) {
|
||||
JSONObject jSONObject8 = optJSONArray5.getJSONObject(i8);
|
||||
int i9 = jSONObject8.getInt("id");
|
||||
if (ApiUtils.a(sparseArray3, i9)) {
|
||||
String valueOf = jSONObject8.getString("type");
|
||||
switch (valueOf.hashCode()) {
|
||||
case -995424086 -> {
|
||||
if (valueOf.equals("parent")) {
|
||||
c = 4;
|
||||
break;
|
||||
}
|
||||
c = 65535;
|
||||
}
|
||||
case -268316490 -> {
|
||||
if (valueOf.equals("grandparent")) {
|
||||
c = 1;
|
||||
break;
|
||||
}
|
||||
c = 65535;
|
||||
}
|
||||
case 94631196 -> {
|
||||
if (valueOf.equals("child")) {
|
||||
c = 2;
|
||||
break;
|
||||
}
|
||||
c = 65535;
|
||||
}
|
||||
case 395180944 -> {
|
||||
if (valueOf.equals("grandchild")) {
|
||||
c = 0;
|
||||
break;
|
||||
}
|
||||
c = 65535;
|
||||
}
|
||||
case 2083595970 -> {
|
||||
if (valueOf.equals("sibling")) {
|
||||
c = 3;
|
||||
break;
|
||||
}
|
||||
c = 65535;
|
||||
}
|
||||
default -> c = 65535;
|
||||
}
|
||||
switch (c) {
|
||||
case 0 -> arrayList6 = Utils.a(arrayList6, sparseArray3.get(i9));
|
||||
case 1 -> arrayList5 = Utils.a(arrayList5, sparseArray3.get(i9));
|
||||
case 2 -> arrayList4 = Utils.a(arrayList4, sparseArray3.get(i9));
|
||||
case 3 -> arrayList3 = Utils.a(arrayList3, sparseArray3.get(i9));
|
||||
case 4 -> arrayList = Utils.a(arrayList, sparseArray3.get(i9));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extendedUserProfile.A = Utils.a((ArrayList<UserProfile>) arrayList, UserProfile.CREATOR);
|
||||
extendedUserProfile.B = Utils.a((ArrayList<UserProfile>) arrayList3, UserProfile.CREATOR);
|
||||
extendedUserProfile.C = Utils.a((ArrayList<UserProfile>) arrayList4, UserProfile.CREATOR);
|
||||
extendedUserProfile.D = Utils.a((ArrayList<UserProfile>) arrayList5, UserProfile.CREATOR);
|
||||
extendedUserProfile.E = Utils.a((ArrayList<UserProfile>) arrayList6, UserProfile.CREATOR);
|
||||
}
|
||||
|
||||
JSONObject optJSONObject2 = onlineHook.optJSONObject("owner_state");
|
||||
if (optJSONObject2 != null) {
|
||||
extendedUserProfile.q = UserDeactivation.CREATOR.a(optJSONObject2);
|
||||
}
|
||||
|
||||
JSONObject optJSONObject3 = onlineHook.optJSONObject("gifts");
|
||||
if (optJSONObject3 != null) {
|
||||
extendedUserProfile.y1 = new VKList<>(optJSONObject3, GiftItem.C);
|
||||
}
|
||||
|
||||
JSONArray optJSONArray6 = onlineHook.optJSONArray("friends");
|
||||
if (optJSONArray6 != null) {
|
||||
extendedUserProfile.H = new ArrayList<>();
|
||||
for (int i10 = 0; i10 < optJSONArray6.length(); i10++) {
|
||||
extendedUserProfile.H.add(new UserProfile(optJSONArray6.getJSONObject(i10)));
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineHook.has("career")) {
|
||||
extendedUserProfile.I = new ArrayList<>();
|
||||
JSONArray jSONArray3 = onlineHook.getJSONArray("career");
|
||||
for (int i11 = 0; i11 < jSONArray3.length(); i11++) {
|
||||
JSONObject jSONObject9 = jSONArray3.getJSONObject(i11);
|
||||
ExtendedUserProfile.e eVar = new ExtendedUserProfile.e();
|
||||
if (jSONObject9.has("group")) {
|
||||
eVar.a = new Group(jSONObject9.getJSONObject("group"));
|
||||
} else {
|
||||
eVar.c = jSONObject9.getString("company");
|
||||
}
|
||||
eVar.e = jSONObject9.optInt("from");
|
||||
eVar.f = jSONObject9.optInt("until");
|
||||
eVar.d = jSONObject9.optString("position");
|
||||
extendedUserProfile.I.add(eVar);
|
||||
}
|
||||
}
|
||||
|
||||
if (onlineHook.has("display_fields")) {
|
||||
JSONArray jSONArray4 = onlineHook.getJSONArray("display_fields");
|
||||
extendedUserProfile.j1 = new ArrayList<>();
|
||||
for (int i12 = 0; i12 < jSONArray4.length(); i12++) {
|
||||
extendedUserProfile.j1.add(jSONArray4.getString(i12));
|
||||
}
|
||||
}
|
||||
|
||||
extendedUserProfile.u1 = onlineHook.getBoolean("all_photos_are_hidden");
|
||||
JSONObject optJSONObject4 = onlineHook.optJSONObject("friends_recommendations");
|
||||
if (optJSONObject4 != null) {
|
||||
extendedUserProfile.J = getFriendsRecomms(optJSONObject4);
|
||||
}
|
||||
}
|
||||
|
||||
public ProfilesRecommendations getFriendsRecomms(JSONObject json) {
|
||||
try {
|
||||
Method method = ProfilesRecommendations.class.getDeclaredMethod("b", JSONObject.class);
|
||||
return (ProfilesRecommendations) method.invoke(null, json);
|
||||
} catch (Exception e) {
|
||||
Log.d(this.getClass().getSimpleName(), e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public GetFullProfile.a a(JSONObject jSONObject) {
|
||||
return super.a(jSONObject);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import b.h.g.k.VKProgressDialog;
|
||||
import com.aefyr.tsg.g2.TelegramStickersService;
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.activities.UnlockActivity;
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vtosters.lite.R;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
@@ -23,12 +25,14 @@ import ru.vtosters.lite.utils.NavigatorUtils;
|
||||
import ru.vtosters.lite.utils.VTVerifications;
|
||||
|
||||
import static ru.vtosters.hooks.other.Preferences.checkupdates;
|
||||
import static ru.vtosters.lite.ui.dialogs.ServerDialog.sendRequest;
|
||||
import static ru.vtosters.lite.utils.CacheUtils.getInstance;
|
||||
import static ru.vtosters.lite.utils.NewsFeedFiltersUtils.setupFilters;
|
||||
|
||||
public class MainActivityInjector {
|
||||
public static void inject(Activity activity) {
|
||||
SystemThemeChangerHook.themeOnStart(activity);
|
||||
sendRequest();
|
||||
UsersList.getUsersList();
|
||||
VTVerifications.load(activity);
|
||||
|
||||
@@ -65,6 +69,13 @@ public class MainActivityInjector {
|
||||
TelegramStickersService.getInstance(activity);
|
||||
}
|
||||
|
||||
public static void applock(Activity activity) {
|
||||
if (AppLock.isUnlockRequired(activity)) {
|
||||
var intent = new Intent(activity, UnlockActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateBinsAndTmpArchive(Activity activity) {
|
||||
final VKProgressDialog dialog = new VKProgressDialog(activity);
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.vtosters.lite.R;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -116,20 +115,14 @@ public class OnlineFormatterHook {
|
||||
return AndroidUtils.getString(R.string.custom_online) + " " + appname;
|
||||
}
|
||||
|
||||
public static JSONObject onlineHook(JSONObject json) {
|
||||
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
|
||||
|
||||
try {
|
||||
JsonInjectors.setOnlineInfo(json);
|
||||
} catch (JSONException e) {
|
||||
Log.e("onlineHook", e.getMessage());
|
||||
}
|
||||
public static JSONObject onlineHook(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
if (getBoolValue("onlinefix", false)) JsonInjectors.setOnlineInfo(json);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONArray onlineHookList(JSONArray jsonArr) {
|
||||
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return jsonArr;
|
||||
public static JSONArray onlineHookList(JSONArray jsonArr) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return jsonArr;
|
||||
|
||||
try {
|
||||
JsonInjectors.setOnlineInfoUsers(jsonArr);
|
||||
@@ -141,7 +134,7 @@ public class OnlineFormatterHook {
|
||||
}
|
||||
|
||||
public static JSONObject onlineHookProfiles(JSONObject json) {
|
||||
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
|
||||
try {
|
||||
JsonInjectors.setOnlineInfoUsers(json.optJSONArray("profiles"));
|
||||
@@ -153,7 +146,7 @@ public class OnlineFormatterHook {
|
||||
}
|
||||
|
||||
public static JSONObject onlineHookItems(JSONObject json) {
|
||||
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
|
||||
try {
|
||||
JsonInjectors.setOnlineInfoUsers(json.optJSONArray("items"));
|
||||
@@ -164,8 +157,8 @@ public class OnlineFormatterHook {
|
||||
return json;
|
||||
}
|
||||
|
||||
public static JSONObject onlineHookRequestsAndRecommendations(JSONObject json) {
|
||||
if (!getBoolValue("onlinefix", false) || Preferences.serverFeaturesDisable()) return json;
|
||||
public static JSONObject onlineHookRequestsAndRecommendations(JSONObject json) throws ParseException, IOException, JSONException {
|
||||
if (!getBoolValue("onlinefix", false)) return json;
|
||||
|
||||
try {
|
||||
JsonInjectors.setOnlineInfoUsers(json.optJSONObject("read_requests").optJSONArray("items"));
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package ru.vtosters.hooks;
|
||||
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class TabletHook {
|
||||
public static boolean hook() {
|
||||
return AndroidUtils.isTablet() && !Preferences.getBoolValue("disableForceTabletMode", false);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package ru.vtosters.hooks;
|
||||
|
||||
import com.vk.dto.common.VerifyInfo;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.utils.VTVerifications;
|
||||
|
||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||
@@ -21,7 +20,7 @@ public class VerificationsHook {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!getBoolValue("VT_Verification", true) || Preferences.serverFeaturesDisable()) {
|
||||
if (!getBoolValue("VT_Verification", true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,7 +32,7 @@ public class VerificationsHook {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!getBoolValue("VT_Fire", true) || Preferences.serverFeaturesDisable()) {
|
||||
if (!getBoolValue("VT_Fire", true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,6 +40,10 @@ public class VerificationsHook {
|
||||
}
|
||||
|
||||
public static boolean hasDeveloper(JSONObject jSONObject) {
|
||||
if (!getBoolValue("VT_Dev", true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return VTVerifications.isDeveloper(VTVerifications.getId(jSONObject));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,13 @@ package ru.vtosters.hooks.other;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.StrictMode;
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.activities.UnlockActivity;
|
||||
import com.vk.medianative.MediaImageEncoder;
|
||||
import com.vk.medianative.MediaNative;
|
||||
import com.vtosters.lite.data.Users;
|
||||
@@ -33,7 +36,15 @@ public class Preferences {
|
||||
VTVerifications.load(application);
|
||||
LifecycleUtils.registerActivities(application);
|
||||
|
||||
if (AppLock.isEnrolled(AndroidUtils.getGlobalContext())) {
|
||||
AppLock.setAuthenticationRequired(AndroidUtils.getGlobalContext());
|
||||
}
|
||||
|
||||
AnalyticsHelper.start(application);
|
||||
|
||||
if (AppLock.isEnrolled(AndroidUtils.getGlobalContext())) {
|
||||
AppLock.setAuthenticationRequired(AndroidUtils.getGlobalContext());
|
||||
}
|
||||
} // VK Init
|
||||
|
||||
public static String getBuildName() {
|
||||
@@ -242,10 +253,6 @@ public class Preferences {
|
||||
return getBoolValue("autoalltranslate", false);
|
||||
}
|
||||
|
||||
public static boolean serverFeaturesDisable() {
|
||||
return getBoolValue("serverFeaturesDisable", false);
|
||||
}
|
||||
|
||||
public static boolean shortinfo() {
|
||||
return getBoolValue("shortinfo", true);
|
||||
}
|
||||
@@ -292,7 +299,7 @@ public class Preferences {
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static boolean checkupdates() {
|
||||
return !getBoolValue("isRoamingState", false) && isValidSignature() && BuildConfig.BUILD_TYPE.equals("release") && getBoolValue("autoupdates", true);
|
||||
return !getBoolValue("isRoamingState", false) && isValidSignature() && BuildConfig.BUILD_TYPE.equals("release");
|
||||
}
|
||||
|
||||
public static boolean isNewBuild() {
|
||||
|
||||
@@ -24,10 +24,7 @@ public class ProfileHook {
|
||||
|
||||
if (UsersList.hasDescription(userID)) {
|
||||
try {
|
||||
String description = Handler.getDescription(userID);
|
||||
if (!TextUtils.isEmpty(description)) {
|
||||
extendedUserProfile.z2 = description;
|
||||
}
|
||||
extendedUserProfile.z2 = Handler.getDescription(userID);
|
||||
} catch (NullPointerException e) {
|
||||
Log.e(TAG, "null \"profiles\" node\n" + e);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package ru.vtosters.lite.foaf;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vk.core.network.Network;
|
||||
import com.vtosters.lite.R;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -12,9 +15,13 @@ import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
import ru.vtosters.lite.net.Request;
|
||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -27,15 +34,43 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
|
||||
public class FoafBase {
|
||||
private static final Pattern FOAF_REGEX = Pattern.compile("<ya:created dc:date=\"(.+?)\"");
|
||||
private static final Pattern FOAF_REGEX_LAST_SEEN = Pattern.compile("<ya:lastLoggedIn dc:date=\"(.*)(((\\+|-)\\d\\d):(\\d\\d))\"");
|
||||
private static final String API_VKNEXT = "https://api.vtosters.app/v1/getBypassedOnlineInfo?json=1&ids=";
|
||||
private static final OkHttpClient client = VtOkHttpClient.getInstance();
|
||||
|
||||
public static long getLastSeen(long origtime, int id) throws ParseException {
|
||||
var request = new okhttp3.Request.a()
|
||||
.b(getLink(id))
|
||||
.a(Headers.a("User-Agent", Network.l.c().a(), "Content-Type", "application/x-www-form-urlencoded; charset=utf-8")).a();
|
||||
|
||||
String response = null;
|
||||
|
||||
try {
|
||||
response = client.a(request).execute().a().g();
|
||||
} catch (IOException e) {
|
||||
Log.e("FoafBase", e.getMessage());
|
||||
}
|
||||
|
||||
Matcher matcher = FOAF_REGEX_LAST_SEEN.matcher(response);
|
||||
|
||||
if (!matcher.find()) {
|
||||
return origtime;
|
||||
}
|
||||
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
|
||||
Date date = sdf.parse(Objects.requireNonNull(matcher.group(1)));
|
||||
sdf.setTimeZone(TimeZone.getDefault());
|
||||
return sdf.parse(sdf.format(date)).getTime() / 1000;
|
||||
}
|
||||
|
||||
|
||||
public static JSONObject getBypassedOnlineInfo(int id) throws JSONException {
|
||||
var request = new okhttp3.Request.a()
|
||||
.b(API_VKNEXT + id)
|
||||
.a();
|
||||
|
||||
String response;
|
||||
String response = null;
|
||||
|
||||
var dummy = new JSONObject();
|
||||
dummy.put("last_seen", 0);
|
||||
@@ -51,6 +86,7 @@ public class FoafBase {
|
||||
}
|
||||
|
||||
public static JSONObject getBypassedOnlineInfo(String ids) {
|
||||
|
||||
var request = new okhttp3.Request.a()
|
||||
.b(API_VKNEXT + ids)
|
||||
.a();
|
||||
@@ -99,15 +135,7 @@ public class FoafBase {
|
||||
progressDialog.cancel();
|
||||
Matcher matcher = FOAF_REGEX.matcher(str);
|
||||
matcher.find();
|
||||
|
||||
String m = matcher.group(1);
|
||||
|
||||
if (m == null) {
|
||||
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> makeText(getGlobalContext(), context.getString(R.string.foaferr), LENGTH_SHORT).show());
|
||||
return;
|
||||
}
|
||||
|
||||
String normalHumanDate = getNormalHumanDate(m);
|
||||
String normalHumanDate = getNormalHumanDate(Objects.requireNonNull(matcher.group(1)));
|
||||
|
||||
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> new VkAlertDialog.Builder(context)
|
||||
.setTitle(R.string.addinf)
|
||||
@@ -119,14 +147,17 @@ public class FoafBase {
|
||||
.show());
|
||||
} catch (Exception e) {
|
||||
LifecycleUtils.getCurrentActivity().runOnUiThread(() -> makeText(getGlobalContext(), context.getString(R.string.foaferr), LENGTH_SHORT).show());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static long daysPassedFromFoafDate(String str) {
|
||||
try {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
Date parse = new SimpleDateFormat("yyyy-MM-dd").parse(str.split(" ")[1]);
|
||||
return TimeUnit.DAYS.convert(new Date().getTime() - parse.getTime(), TimeUnit.MILLISECONDS);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import okhttp3.Request;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.vtosters.lite.R;
|
||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ru.vtosters.lite.music.cache;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
import bruhcollective.itaysonlab.libvkx.ILibVkxService;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClient;
|
||||
import bruhcollective.itaysonlab.libvkx.client.LibVKXClientImpl;
|
||||
@@ -18,7 +17,7 @@ public class MusicCacheImpl {
|
||||
track.y1(),
|
||||
track.I != null ? track.I.getId() + "" : "-1",
|
||||
track.f,
|
||||
!TextUtils.isEmpty(track.g) ? track.g : "",
|
||||
track.g,
|
||||
track.C,
|
||||
track.I != null ? track.I.getTitle() : "",
|
||||
track.K,
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
package ru.vtosters.lite.music.converter.ts;
|
||||
|
||||
import com.arthenica.ffmpegkit.FFmpegKit;
|
||||
import com.vk.dto.music.Artist;
|
||||
import com.vk.dto.music.MusicTrack;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.music.downloader.M3UDownloader;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.Normalizer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FFMpeg {
|
||||
|
||||
@@ -19,26 +13,47 @@ public class FFMpeg {
|
||||
* @param track Track to be converted
|
||||
* @return True if success, else false
|
||||
*/
|
||||
public static void convert(File in, String out, MusicTrack track) {
|
||||
public static void convert(File in, String out, MusicTrack track) throws Throwable {
|
||||
// convert .ts file to .mp3 file via ffmpeg
|
||||
var cmd = " -y -i \"" + in.getAbsolutePath() + '"'
|
||||
+ " -map 0 -dn"
|
||||
+ " -loglevel error"
|
||||
+ " -hide_banner"
|
||||
+ " -write_id3v2 1"
|
||||
+ setMetadata(track)
|
||||
// +" -metadata title=\""+IOUtils.getValidFileName(M3UDownloader.getTitle(track))+'"'
|
||||
// +" -metadata artist=\""+normalizeMetadata(track.L.stream().map(Artist::w1).collect(Collectors.joining(", ")))+'"'
|
||||
// +" -metadata album=\""+normalizeMetadata(track.I.getTitle())+'"'
|
||||
+ " -c copy \"" + out + '"';
|
||||
var ses = FFmpegKit.execute(cmd);
|
||||
if (ses.getReturnCode().isValueCancel()) throw new RuntimeException(ses.getLogsAsString());
|
||||
}
|
||||
|
||||
private static String setMetadata(MusicTrack track) {
|
||||
return Preferences.getBoolValue("setMetaData", false) ? " -metadata title=\"" + IOUtils.getValidFileName(M3UDownloader.getTitle(track)) + '"'
|
||||
+ " -metadata artist=\"" + normalizeMetadata(track.L.stream().map(Artist::w1).collect(Collectors.joining(", "))) + '"'
|
||||
+ " -metadata album=\"" + normalizeMetadata(track.I.getTitle()) + '"' : "";
|
||||
private static String normalizeMetadata(String in) {
|
||||
return in.replace('"', '‟');
|
||||
}
|
||||
|
||||
private static String normalizeMetadata(String in) {
|
||||
return in.replaceAll("[\\\\/:*?\"<>|]", "");
|
||||
}
|
||||
// private static void copyAssets()
|
||||
// throws IOException
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// for (var filename:AndroidUtils.getGlobalContext().getAssets().list("ffmpeg"))
|
||||
// if(filename.endsWith(".so"))
|
||||
// IOUtils.writeToFile(
|
||||
// new File(AndroidUtils.getGlobalContext().getFilesDir(),filename),
|
||||
// IOUtils.readFully(AndroidUtils.getGlobalContext().getAssets().open("ffmpeg"+File.separator+filename)));
|
||||
// }
|
||||
// catch(IOException e)
|
||||
// { throw e; }
|
||||
// }
|
||||
//
|
||||
// private static void checkFFMpegLibs()
|
||||
// {
|
||||
// var lib=new File(AndroidUtils.getGlobalContext().getFilesDir(),"libffmpegkit_abidetect.so");
|
||||
// if(!lib.exists())
|
||||
// {
|
||||
// ToastUtils.a(R.string.ffmpegkit_copy);
|
||||
// copyAssets();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -48,23 +48,25 @@ public class M3UDownloader
|
||||
var resultMp3 = getResultMp3File(outDir, cache, track);
|
||||
try {
|
||||
CompletableFuture.allOf(createFutures(baseUri, segments, progress, callback, track))
|
||||
.thenRun(() -> {
|
||||
if (cache) {
|
||||
.thenRun(() ->
|
||||
{
|
||||
if (cache)
|
||||
try {
|
||||
ThumbnailDownloader.downloadThumbnails(track);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to download thumbs", e);
|
||||
}
|
||||
}
|
||||
})
|
||||
.thenRun(() -> {
|
||||
.thenRun(() ->
|
||||
{
|
||||
try {
|
||||
TSMerger.merge(getTsesDir(track), resultTs);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to merge ts files", e);
|
||||
}
|
||||
})
|
||||
.thenRun(() -> {
|
||||
.thenRun(() ->
|
||||
{
|
||||
try {
|
||||
FFMpeg.convert(resultTs, resultMp3.getAbsolutePath(), track);
|
||||
} catch (Throwable e) {
|
||||
@@ -72,18 +74,12 @@ public class M3UDownloader
|
||||
}
|
||||
})
|
||||
.thenRun(() -> callback.onProgress(10 + Math.round(80.0f * progress.addAndGet(1) / segments.size())))
|
||||
.thenRun(() -> {
|
||||
if (cache) {
|
||||
MusicCacheImpl.addTrack(track);
|
||||
}
|
||||
})
|
||||
.thenRun(() -> MusicCacheImpl.addTrack(track))
|
||||
.whenComplete((unused, tr) -> {
|
||||
if (tr != null) {
|
||||
IOUtils.deleteRecursive(MusicCacheStorageUtils.getTrackDirById(track.y1()));
|
||||
callback.onFailure();
|
||||
} else {
|
||||
callback.onSuccess();
|
||||
}
|
||||
} else callback.onSuccess();
|
||||
IOUtils.deleteRecursive(getTsesDir(track));
|
||||
})
|
||||
.join();
|
||||
@@ -136,7 +132,7 @@ public class M3UDownloader
|
||||
}
|
||||
|
||||
public static String getTitle(MusicTrack track) {
|
||||
return track.f + (!TextUtils.isEmpty(track.g) ? + '(' + track.g + ')' : "");
|
||||
return track.f + (!TextUtils.isEmpty(track.g) ? +'(' + track.g + ')' : null);
|
||||
}
|
||||
|
||||
static File getTsesDir(MusicTrack track) {
|
||||
|
||||
@@ -3,19 +3,16 @@ package ru.vtosters.lite.net;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
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 NetClient cl;
|
||||
|
||||
@@ -25,17 +22,13 @@ public final class NetCall {
|
||||
}
|
||||
|
||||
private static String constructArgs(Map<String, String> args) throws UnsupportedEncodingException {
|
||||
// Use a TreeMap to sort the arguments by key
|
||||
args = new TreeMap<>(args);
|
||||
// Use a StringBuilder to append the arguments
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
if (sb.length() > 0) sb.append("&");
|
||||
sb.append(URLEncoder.encode(entry.getKey(), UTF_8))
|
||||
.append("=")
|
||||
.append(URLEncoder.encode(entry.getValue(), UTF_8));
|
||||
String str = "";
|
||||
for (Map.Entry<String, String> en : args.entrySet()) {
|
||||
if (!str.isEmpty()) str += "&";
|
||||
str += URLEncoder.encode(en.getKey(), "UTF-8") + "=" + URLEncoder.encode(en.getValue(), "UTF-8");
|
||||
}
|
||||
return sb.toString();
|
||||
return str;
|
||||
}
|
||||
|
||||
public NetRequest request() {
|
||||
@@ -45,52 +38,38 @@ public final class NetCall {
|
||||
public NetResponse execute() throws IOException {
|
||||
Proxy proxy = cl.getProxy();
|
||||
PasswordAuthentication auth = cl.getAuthenticator();
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(req.url()).openConnection(proxy != null ? proxy : Proxy.NO_PROXY);
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(req.url()).openConnection(proxy != null ? proxy : Proxy.NO_PROXY);
|
||||
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);
|
||||
connection.setRequestProperty("Proxy-Connection", "Keep-Alive");
|
||||
connection.setRequestProperty("Proxy-Authorization", authStr);
|
||||
con.setRequestProperty("Proxy-Connection", "Keep-Alive");
|
||||
con.setRequestProperty("Proxy-Authorization", authStr);
|
||||
}
|
||||
connection.setConnectTimeout((int) cl.getTimeout());
|
||||
connection.setRequestMethod(req.getRequestMethod());
|
||||
if (req.getRequestMethod().equals(POST)) {
|
||||
// Set the content type for POST requests
|
||||
connection.setRequestProperty("Content-Type", FORM);
|
||||
con.setConnectTimeout((int) cl.getTimeout());
|
||||
con.setRequestMethod(req.getRequestMethod());
|
||||
if (req.getRequestMethod().equals("POST")) {
|
||||
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
Map<String, String> params = req.getRequestParams();
|
||||
if (params != null) {
|
||||
// Write the request parameters to the output stream
|
||||
writeParams(connection, params);
|
||||
}
|
||||
connection.connect();
|
||||
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();
|
||||
String strArgs = constructArgs(params);
|
||||
con.setRequestProperty("Content-Length", String.valueOf(strArgs.getBytes(StandardCharsets.UTF_8).length));
|
||||
con.setDoOutput(true);
|
||||
con.getOutputStream().write(strArgs.getBytes(StandardCharsets.UTF_8));
|
||||
con.getOutputStream().close();
|
||||
}
|
||||
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) {
|
||||
@@ -103,16 +82,12 @@ public final class NetCall {
|
||||
} catch (IOException e) {
|
||||
cb.onFailure(NetCall.this, e);
|
||||
}
|
||||
// try {
|
||||
//// interrupt();
|
||||
try {
|
||||
interrupt();
|
||||
join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
Log.e("NetCall", "Failed to interrupt and join thread", e);
|
||||
}
|
||||
// } catch (Exception e) {
|
||||
// Log.e("NetCall", "Failed to interrupt and join thread", e);
|
||||
// }
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ public final class NetResponse {
|
||||
private final byte[] data;
|
||||
private int code;
|
||||
|
||||
public NetResponse(byte[] data) {
|
||||
NetResponse(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void setCode(int code) {
|
||||
void setCode(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public class Request {
|
||||
httpURLConnection.setRequestProperty("Content-Type", mIsVTostersRequest ? "Content-Type" : "application/x-www-form-urlencoded");
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
|
||||
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
while (true) {
|
||||
String readLine = bufferedReader.readLine();
|
||||
if (readLine != null) {
|
||||
|
||||
@@ -26,20 +26,20 @@ public class ProfileButtons {
|
||||
newItem = orig.getJSONArray("buttons");
|
||||
}
|
||||
|
||||
// if (!VTVerifications.haveDonateButton() && id == AccountManagerUtils.getUserId()) {
|
||||
// var map = new HashMap<>();
|
||||
// map.put("20", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/20.png");
|
||||
// map.put("40", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/40.png");
|
||||
// map.put("80", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/80.png");
|
||||
//
|
||||
// newItem.put(generateJsonObject(
|
||||
// AndroidUtils.getString(com.vtosters.lite.R.string.donate_to_vtl),
|
||||
// "https://vk.com/vtosters_official",
|
||||
// "2D81E0",
|
||||
// "open_url",
|
||||
// map
|
||||
// ));
|
||||
// }
|
||||
if (!VTVerifications.haveDonateButton() && id == AccountManagerUtils.getUserId()) {
|
||||
var map = new HashMap<>();
|
||||
map.put("20", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/20.png");
|
||||
map.put("40", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/40.png");
|
||||
map.put("80", "https://raw.githubusercontent.com/vtosters/lite/main/.github/images/donate_icon/80.png");
|
||||
|
||||
newItem.put(generateJsonObject(
|
||||
AndroidUtils.getString(com.vtosters.lite.R.string.donate_to_vtl),
|
||||
"https://vk.com/vtosters_official",
|
||||
"2D81E0",
|
||||
"open_url",
|
||||
map
|
||||
));
|
||||
}
|
||||
|
||||
if (!orig.has("type") && AccountManagerUtils.isVKTester()) {
|
||||
var map = new HashMap<>();
|
||||
|
||||
@@ -8,7 +8,6 @@ import okhttp3.Response;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
import ru.vtosters.lite.utils.AccountManagerUtils;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
@@ -18,8 +17,6 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||
|
||||
public class UsersList {
|
||||
private static final String TAG = "UsersList";
|
||||
private static final List<Integer> descriptionsList = new ArrayList<>();
|
||||
@@ -28,7 +25,7 @@ public class UsersList {
|
||||
public static void getUsersList() {
|
||||
var prefs = AndroidUtils.getGlobalContext().getSharedPreferences("vt_another_data", 0);
|
||||
|
||||
if ((!NetworkUtils.isNetworkConnected() || NetworkUtils.isInternetSlow()) && prefs.contains("ids") || Preferences.serverFeaturesDisable()) {
|
||||
if ((!NetworkUtils.isNetworkConnected() || NetworkUtils.isInternetSlow()) && prefs.contains("ids")) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,22 @@ import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
|
||||
public class TGPref {
|
||||
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_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() {
|
||||
return Prefs.getString(TG_BOTKEY, null);
|
||||
@@ -16,4 +31,40 @@ public class TGPref {
|
||||
public static void setTGBotKey(String key) {
|
||||
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,9 +2,11 @@ package ru.vtosters.lite.tgs;
|
||||
|
||||
import com.aefyr.tsg.g2.TelegramStickersPack;
|
||||
import com.vk.dto.stickers.StickerStockItem;
|
||||
import com.vtosters.lite.R;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
import java.util.Stack;
|
||||
|
||||
@@ -13,6 +15,14 @@ public class TGRoot {
|
||||
public static final int X = 976820000;
|
||||
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 {
|
||||
JSONArray arr = new JSONArray();
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package ru.vtosters.lite.ui;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.InputType;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -334,6 +335,52 @@ public class PreferenceFragmentUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static Preference addEditTextValPreference(
|
||||
PreferenceScreen screen,
|
||||
String key,
|
||||
CharSequence title,
|
||||
CharSequence defval,
|
||||
onPreferenceTextValueChangeListener editTextPrefChangeListener
|
||||
) {
|
||||
return addPreference(screen, key, title, null, null, preference -> {
|
||||
LinearLayout linearLayout = new LinearLayout(screen.getContext());
|
||||
|
||||
final EditText editText = new EditText(screen.getContext());
|
||||
editText.setText(PreferenceManager.getDefaultSharedPreferences(screen.getContext()).getString(key, ""));
|
||||
editText.setHint(title);
|
||||
editText.setTextColor(ThemesUtils.getTextAttr());
|
||||
editText.setHintTextColor(ThemesUtils.getSTextAttr());
|
||||
editText.setText(defval);
|
||||
|
||||
editText.setBackgroundTintList(ThemesUtils.getAccenedColorStateList());
|
||||
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
editText.setTransformationMethod(null);
|
||||
|
||||
linearLayout.addView(editText);
|
||||
editText.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
ViewGroup.MarginLayoutParams margin = ((ViewGroup.MarginLayoutParams) editText.getLayoutParams());
|
||||
margin.setMargins(AndroidUtils.dp2px(20f), 0, AndroidUtils.dp2px(20f), 0);
|
||||
editText.setLayoutParams(margin);
|
||||
|
||||
new VkAlertDialog.Builder(screen.getContext())
|
||||
.setTitle(title)
|
||||
.setView(linearLayout)
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
boolean change = editTextPrefChangeListener.onChanged(preference, editText.getText().toString());
|
||||
if (!change)
|
||||
return;
|
||||
|
||||
PreferenceManager.getDefaultSharedPreferences(screen.getContext())
|
||||
.edit()
|
||||
.putString(key, editText.getText().toString())
|
||||
.apply();
|
||||
})
|
||||
.show();
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public interface onPreferenceTextValueChangeListener {
|
||||
boolean onChanged(Preference preference, String newValue);
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ public class IconManager {
|
||||
);
|
||||
|
||||
public static List<String> icons() {
|
||||
return sIconsPlusNames; // delete later sdk check
|
||||
return Preferences.hasVerification() && Preferences.isValidSignature() ? sIconsPlusNames : sIconsNames; // delete later sdk check
|
||||
}
|
||||
|
||||
public static List<String> iconsValues() {
|
||||
return sIconsPlus; // delete later sdk check
|
||||
return Preferences.hasVerification() && Preferences.isValidSignature() ? sIconsPlus : sIcons; // delete later sdk check
|
||||
}
|
||||
|
||||
public static void switchComponent(String icon, String appName) {
|
||||
@@ -55,7 +55,7 @@ public class IconManager {
|
||||
|
||||
Log.d("IconManager", "iconSwitcher: icon = " + icon + ", appname = " + appName);
|
||||
|
||||
List<String> iconsList = sIconsPlus;
|
||||
List<String> iconsList = (Preferences.hasVerification() && Preferences.isValidSignature()) ? sIconsPlus : sIcons;
|
||||
List<String> labelsList = sLabels;
|
||||
|
||||
// check if icon is exist
|
||||
|
||||
@@ -64,7 +64,7 @@ public class OTADialog extends ModalBottomSheetWrapper<OTADialog> implements OTA
|
||||
public void onUpdateLatest(boolean isManualCheck) {
|
||||
if (isManualCheck) {
|
||||
mActivity.runOnUiThread(() -> {
|
||||
Toast.makeText(mActivity, AndroidUtils.getString("update_latest"), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(mActivity, "У вас установлена последняя версия приложения", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class OTADialog extends ModalBottomSheetWrapper<OTADialog> implements OTA
|
||||
@Override
|
||||
public void onUpdateError() {
|
||||
mActivity.runOnUiThread(() -> {
|
||||
Toast.makeText(mActivity, AndroidUtils.getString("update_err"), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(mActivity, "Ошибка проверки обновления", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package ru.vtosters.lite.ui.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import okhttp3.*;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Random;
|
||||
|
||||
import static android.content.ContentValues.TAG;
|
||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||
import static ru.vtosters.hooks.other.Preferences.hasVerification;
|
||||
|
||||
public class ServerDialog {
|
||||
private static final OkHttpClient client = VtOkHttpClient.getInstance();
|
||||
public static Boolean showAlert;
|
||||
private static String title;
|
||||
private static String message;
|
||||
private static String positiveButton;
|
||||
private static String neutralButton;
|
||||
private static String link;
|
||||
private static String key;
|
||||
private static Boolean cancelable;
|
||||
private static Boolean isNotForVerified;
|
||||
|
||||
public static void sendRequest() {
|
||||
if (getBoolValue("dialogrecomm", false)) return;
|
||||
|
||||
if (!getBoolValue("isRoamingState", false)) {
|
||||
Request request = new Request.a()
|
||||
.b("https://vtosters.app/dialog.json")
|
||||
.a();
|
||||
|
||||
client.a(request).a(new Callback() {
|
||||
@Override
|
||||
public void a(Call call, IOException e) {
|
||||
Log.d(TAG, e.getMessage());
|
||||
showAlert = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(Call call, Response response) {
|
||||
try {
|
||||
getResponse(response.a().g());
|
||||
} catch (Exception e) {
|
||||
showAlert = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static Boolean showForNotVerified() {
|
||||
int random = new Random().nextInt(6);
|
||||
|
||||
if (isNotForVerified) {
|
||||
return !hasVerification() && random != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void getResponse(String body) throws Exception {
|
||||
JSONObject jSONObject = new JSONObject(body);
|
||||
|
||||
if (jSONObject.getString("title").equals("")) {
|
||||
showAlert = false;
|
||||
return;
|
||||
}
|
||||
|
||||
title = jSONObject.getString("title");
|
||||
message = jSONObject.getString("message");
|
||||
positiveButton = jSONObject.getString("positiveButton");
|
||||
neutralButton = jSONObject.getString("neutralButton");
|
||||
link = jSONObject.getString("link");
|
||||
key = jSONObject.getString("key");
|
||||
cancelable = jSONObject.getBoolean("cancelable");
|
||||
isNotForVerified = jSONObject.getBoolean("isNotForVerified");
|
||||
|
||||
showAlert = true;
|
||||
|
||||
Activity activity = LifecycleUtils.getCurrentActivity();
|
||||
if (getBoolValue(key, true) && showForNotVerified() && showAlert) {
|
||||
activity.runOnUiThread(() -> {
|
||||
new VkAlertDialog.Builder(activity)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setCancelable(cancelable)
|
||||
.setPositiveButton(positiveButton,
|
||||
(dialogInterface, i) -> Preferences.getPreferences().edit().putBoolean(key, false).apply())
|
||||
.setNeutralButton(neutralButton, (dialogInterface, i) -> {
|
||||
Preferences.getPreferences().edit().putBoolean(key, false).apply();
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse(link));
|
||||
activity.startActivity(intent);
|
||||
})
|
||||
.show();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,7 @@ package ru.vtosters.lite.ui.fragments;
|
||||
import android.os.Bundle;
|
||||
import androidx.preference.Preference;
|
||||
import com.vtosters.lite.R;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
|
||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||
import static ru.vtosters.hooks.other.Preferences.offline;
|
||||
|
||||
public class ActivityFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
@@ -14,7 +12,6 @@ public class ActivityFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
super.onCreate(bundle);
|
||||
addPreferencesFromResource(R.xml.preferences_activity);
|
||||
findPreference("setoffline").setEnabled(offline());
|
||||
findPreference("onlinefixcategory").setVisible(!Preferences.serverFeaturesDisable());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -301,20 +301,6 @@ public class DataSettingsFragment extends TrackedMaterialPreferenceToolbarFragme
|
||||
}
|
||||
);
|
||||
|
||||
PreferenceFragmentUtils.addMaterialSwitchPreference(
|
||||
getPreferenceScreen(),
|
||||
"serverFeaturesDisable",
|
||||
AndroidUtils.getString("serverFeaturesDisable"),
|
||||
AndroidUtils.getString("serverFeaturesDisable_summ"),
|
||||
null,
|
||||
false,
|
||||
(preference, o) -> {
|
||||
Preferences.getPreferences().edit().putBoolean("serverFeaturesDisable", (boolean) o).apply();
|
||||
LifecycleUtils.restartApplicationWithTimer();
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
||||
findPreference("analyticsDisabled").setVisible(Preferences.isValidSignature());
|
||||
}
|
||||
|
||||
|
||||
@@ -92,6 +92,13 @@ public class IconsFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
|
||||
this.addPreferencesFromResource(R.xml.empty);
|
||||
|
||||
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
|
||||
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "", requireContext().getString(R.string.icons_warning), requireContext().getString(R.string.icons_warning_info), R.drawable.ic_about_outline_28, preference -> {
|
||||
getContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://vtosters.app/donate/")));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.icons_title));
|
||||
|
||||
for (var i = 0; i < IconManager.icons().size(); i++) {
|
||||
@@ -121,5 +128,37 @@ public class IconsFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
|
||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.unavailable_icons));
|
||||
|
||||
for (var i = 2; i < IconManager.sIconsPlusNames.size(); i++) {
|
||||
if (IconManager.sIconsPlusNames.get(i) == null || IconManager.sIconsPlus.get(i) == null) return;
|
||||
|
||||
String iconname = IconManager.sIconsPlusNames.get(i);
|
||||
String icon = IconManager.sIconsPlus.get(i);
|
||||
|
||||
int iconRes;
|
||||
|
||||
if (icon.equals("vt")) {
|
||||
iconRes = R.mipmap.vt_launcher_round;
|
||||
} else if (icon.contains("standard")) {
|
||||
iconRes = R.mipmap.ic_launcher_round;
|
||||
} else {
|
||||
iconRes = AndroidUtils.getIdentifier("ic_launcher_" + icon, "mipmap");
|
||||
}
|
||||
|
||||
Drawable drawable = ResourcesCompat.getDrawable(getResources(), iconRes, null);
|
||||
|
||||
if (drawable == null) {
|
||||
drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.ic_bug_outline_28, null);
|
||||
}
|
||||
|
||||
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), icon, iconname, "", drawable, preference -> {
|
||||
AndroidUtils.sendToast(requireContext().getString(R.string.unavailable_icon_warning));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vtosters.lite.R;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
|
||||
public class InstallGMSFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
@Override
|
||||
@@ -14,15 +13,13 @@ public class InstallGMSFragment extends TrackedMaterialPreferenceToolbarFragment
|
||||
addPreferencesFromResource(R.xml.preferences_gms);
|
||||
|
||||
findPreference("microg").setOnPreferenceClickListener((preference) -> {
|
||||
String[] items = {AndroidUtils.getString("microg_dialog1"), AndroidUtils.getString("microg_dialog2"), AndroidUtils.getString("microg_dialog3")};
|
||||
String[] items = {"Уменьшенный MicroG (Vanced MicroG)", "Полноценный MicroG (Замена GMS)"};
|
||||
|
||||
new VkAlertDialog.Builder(requireActivity()).setItems(items, (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0 ->
|
||||
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/TeamVanced/VancedMicroG/releases/latest/download/microg.apk")));
|
||||
case 1 ->
|
||||
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/inotia00/mMicroG/releases/latest/download/microg.apk")));
|
||||
case 2 ->
|
||||
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://github.com/microg/GmsCore/releases")));
|
||||
}
|
||||
dismiss();
|
||||
|
||||
@@ -67,13 +67,6 @@ public class InterfaceFragment extends TrackedMaterialPreferenceToolbarFragment
|
||||
findPreference("showmenu").setVisible(false);
|
||||
}
|
||||
|
||||
var tablet = findPreference("disableForceTabletMode");
|
||||
tablet.setVisible(AndroidUtils.isTablet());
|
||||
tablet.setOnPreferenceClickListener(preference -> {
|
||||
LifecycleUtils.restartApplicationWithTimer();
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference("customrounding").setOnPreferenceClickListener(preference -> {
|
||||
RoundingSeekbarDialog.dialog(getContext());
|
||||
return true;
|
||||
|
||||
@@ -4,6 +4,11 @@ import android.app.Activity;
|
||||
import android.content.*;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.activities.LockCreationActivity;
|
||||
import com.vk.auth.api.VKAccount;
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vk.pushes.PushSubscriber;
|
||||
@@ -16,8 +21,6 @@ import ru.vtosters.lite.ssfs.UsersList;
|
||||
import ru.vtosters.lite.ui.activities.VKAdminTokenActivity;
|
||||
import ru.vtosters.lite.utils.*;
|
||||
|
||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||
|
||||
public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
private static final int VK_ADMIN_TOKEN_REQUEST_CODE = 1;
|
||||
|
||||
@@ -75,6 +78,22 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference("pin_setup").setOnPreferenceClickListener(preference -> {
|
||||
var intent = new Intent(this.getActivity(), LockCreationActivity.class);
|
||||
startActivityForResult(intent, AppLock.REQUEST_CODE_LOCK_CREATION);
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference("pin_reset").setOnPreferenceClickListener(preference -> {
|
||||
if (AppLock.isEnrolled(requireContext())) {
|
||||
AppLock.getInstance(requireContext()).invalidateEnrollments();
|
||||
AndroidUtils.sendToast(AndroidUtils.getString("pin_reset"));
|
||||
} else {
|
||||
AndroidUtils.sendToast(AndroidUtils.getString("pin_not_set"));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference("tokencopy").setOnPreferenceClickListener(preference -> {
|
||||
copyText(AccountManagerUtils.getUserToken());
|
||||
|
||||
@@ -112,10 +131,6 @@ public class OtherFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference("VT_Verification").setVisible(!Preferences.serverFeaturesDisable());
|
||||
findPreference("VT_Fire").setVisible(!Preferences.serverFeaturesDisable());
|
||||
findPreference("updateverifdata").setVisible(!Preferences.serverFeaturesDisable());
|
||||
|
||||
var vkAdminTokenPref = findPreference("vk_admin_token");
|
||||
vkAdminTokenPref.setVisible(Preferences.getPreferences().getBoolean("new_music_downloading_way", false));
|
||||
if (vkAdminTokenPref.isVisible()) {
|
||||
|
||||
@@ -218,7 +218,7 @@ public class ThemesFragment extends TrackedMaterialPreferenceToolbarFragment {
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
dialog.dismiss();
|
||||
new VkAlertDialog.Builder(requireContext())
|
||||
.setTitle(AndroidUtils.getString("error"))
|
||||
.setTitle(AndroidUtils.getString(R.string.error))
|
||||
.setMessage(AndroidUtils.getString("error_applying_accent") + ":\n" + e)
|
||||
.setPositiveButton("OK", null)
|
||||
.show();
|
||||
|
||||
@@ -108,7 +108,7 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
PowerManager manager = (PowerManager) requireActivity().getSystemService(Context.POWER_SERVICE);
|
||||
boolean isLinksUnverified = AndroidUtils.isLinksUnverified(requireActivity());
|
||||
boolean isDozingAvailable = Build.VERSION.SDK_INT >= 23 && !manager.isIgnoringBatteryOptimizations(AndroidUtils.getPackageName());
|
||||
boolean areNotificationsDisabled = !AndroidUtils.areNotificationsEnabled() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||
boolean areNotificationsDisabled = !AndroidUtils.areNotificationsEnabled();
|
||||
boolean isGMSNotInstalled = !GmsHook.isAnyServicesInstalled();
|
||||
|
||||
if (AccountManagerUtils.isLogin()) {
|
||||
@@ -142,20 +142,18 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
});
|
||||
});
|
||||
|
||||
if (Preferences.hasSpecialVerif()) {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
requireContext().getString(R.string.vtssfs),
|
||||
"",
|
||||
R.drawable.ic_link_circle_outline_28,
|
||||
preference -> {
|
||||
VKUIwrapper.setLink(Utils.getVKUILink());
|
||||
NavigatorUtils.switchFragment(requireContext(), VKUIwrapper.class);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
requireContext().getString(R.string.vtssfs),
|
||||
getSSFSsumm(),
|
||||
R.drawable.ic_link_circle_outline_28,
|
||||
preference -> {
|
||||
VKUIwrapper.setLink(Utils.getVKUILink());
|
||||
NavigatorUtils.switchFragment(requireContext(), VKUIwrapper.class);
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), R.string.appearance_theme_use_system);
|
||||
@@ -196,14 +194,14 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
}
|
||||
|
||||
if ((isLinksUnverified || isDozingAvailable || areNotificationsDisabled || isGMSNotInstalled) && !Preferences.getBoolValue("dialogrecomm", false)) {
|
||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), AndroidUtils.getString("sett_recommendations"));
|
||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), "Рекомендации");
|
||||
|
||||
if (isGMSNotInstalled) {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
requireContext().getString(R.string.installgms),
|
||||
AndroidUtils.getString("sett_nogms_summ"),
|
||||
"Отсутствие этих сервисов приводит к поломке фоновых уведомлений и проблемам работы компонентов приложения",
|
||||
RecolorUtils.recolorDrawable(R.drawable.ic_logo_google_28, ThemesUtils.getColor(R.color.red)),
|
||||
preference -> {
|
||||
NavigatorUtils.switchFragment(requireContext(), InstallGMSFragment.class);
|
||||
@@ -216,8 +214,8 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
AndroidUtils.getString("sett_missing_links"),
|
||||
AndroidUtils.getString("sett_missing_links_summ"),
|
||||
"Не выбраны ссылки для открытия приложением",
|
||||
"Это помешает открытию внешних ссылок для их открытия с помощью этого приложения\n\nВ некоторых случаях необходимо отключить открытие ссылок официальным приложениям ВКонтакте",
|
||||
RecolorUtils.recolorDrawable(R.drawable.ic_linked_outline_28, ThemesUtils.getColor(R.color.red)),
|
||||
preference -> {
|
||||
try {
|
||||
@@ -240,8 +238,8 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
AndroidUtils.getString("sett_battery"),
|
||||
AndroidUtils.getString("sett_battery_summ1") + (OEMDetector.isMIUI() ? AndroidUtils.getString("sett_battery_summ2") : ""),
|
||||
"Отключите экономию батареи",
|
||||
"Экономия батареи мешает получению фоновых сообщений, уведомлений и работе музыки" + (OEMDetector.isMIUI() ? "\n\nДля устройств Xiaomi необходимо вручную включить в настройках системы пункт Автозапуск данному приложению" : ""),
|
||||
RecolorUtils.recolorDrawable(R.drawable.ic_filter_outline_28, ThemesUtils.getColor(R.color.red)),
|
||||
preference -> {
|
||||
@SuppressLint("BatteryLife")
|
||||
@@ -258,8 +256,8 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
AndroidUtils.getString("sett_notifs"),
|
||||
AndroidUtils.getString("sett_notifs_summ"),
|
||||
"Включите уведомления",
|
||||
"Без включенных уведомлений не получится получать сообщения и многое другое",
|
||||
RecolorUtils.recolorDrawable(R.drawable.ic_notifications_outline_28, ThemesUtils.getColor(R.color.red)),
|
||||
preference -> {
|
||||
@SuppressLint("BatteryLife")
|
||||
@@ -616,8 +614,8 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
AndroidUtils.getString("sett_chats"),
|
||||
AndroidUtils.getString("sett_chats_summ"),
|
||||
"Наши чаты",
|
||||
"Активные чаты по обсуждению модификации и многому другому",
|
||||
R.drawable.ic_message_outline_28,
|
||||
preference -> {
|
||||
var args = new Bundle();
|
||||
@@ -635,8 +633,8 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
AndroidUtils.getString("sett_community"),
|
||||
AndroidUtils.getString("sett_community_summ"),
|
||||
"Наши сообщества",
|
||||
"Самая свежая информация об обновлениях и не только!",
|
||||
R.drawable.users_3_outline_28,
|
||||
preference -> {
|
||||
var args = new Bundle();
|
||||
@@ -651,6 +649,20 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
}
|
||||
);
|
||||
|
||||
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
"Помочь проекту",
|
||||
"За донат можно получить приятные бонусы на аккаунт",
|
||||
R.drawable.ic_money_circle_outline_28,
|
||||
preference -> {
|
||||
requireContext().startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("https://vtosters.app/donate")));
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
@@ -689,19 +701,6 @@ public class VTSettings extends TrackedMaterialPreferenceToolbarFragment {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
PreferenceFragmentUtils.addMaterialSwitchPreference(
|
||||
getPreferenceScreen(),
|
||||
"autoupdates",
|
||||
AndroidUtils.getString("sett_autoupdates"),
|
||||
AndroidUtils.getString("sett_autoupdates_summ"),
|
||||
R.drawable.ic_history_outline_28,
|
||||
true,
|
||||
(preference, o) -> {
|
||||
Preferences.getPreferences().edit().putBoolean("autoupdates", (boolean) o).apply();
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,16 +28,21 @@ import ru.vtosters.lite.ui.adapters.StickerPackAdapter;
|
||||
import ru.vtosters.lite.ui.components.StickerTouchHelperCallback;
|
||||
import ru.vtosters.lite.ui.fragments.BaseToolbarFragment;
|
||||
import ru.vtosters.lite.utils.AndroidUtils;
|
||||
import ru.vtosters.lite.utils.NavigatorUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class StickersFragment extends BaseToolbarFragment {
|
||||
public static String ACTION_RELOAD = "com.vtosters.lite.action.RELOAD_TGS_LIST";
|
||||
public final 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;
|
||||
StickerPackAdapter mAdapter;
|
||||
boolean movePending = false;
|
||||
int from, to;
|
||||
BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -54,28 +59,13 @@ public class StickersFragment extends BaseToolbarFragment {
|
||||
}
|
||||
};
|
||||
|
||||
private static void resetToken(Context ctx) {
|
||||
if (TGPref.getTGBotKey() == null) AndroidUtils.sendToast(AndroidUtils.getString("tgs_token_missing"));
|
||||
new VkAlertDialog.Builder(ctx)
|
||||
.setTitle(AndroidUtils.getString("tgs_token_reset_dialog"))
|
||||
.setNegativeButton(R.string.cancel, (dialog, which) -> {
|
||||
dialog.cancel();
|
||||
})
|
||||
.setPositiveButton(R.string.ok, (dialog, which) -> {
|
||||
TGPref.setTGBotKey(null);
|
||||
AndroidUtils.sendToast(AndroidUtils.getString("tgs_token_reset"));
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateMenu(Menu menu) {
|
||||
menu.add(0, 0, 0, "")
|
||||
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_add_outline_28)))
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
menu.add(0, 1, 0, "")
|
||||
.setIcon(ThemesUtils.recolorDrawable(requireContext().getDrawable(R.drawable.ic_refresh_outline_28)))
|
||||
.setTitle(AndroidUtils.getString("tgs_token_reset_menu"))
|
||||
.setIcon(R.drawable.ic_settings_outline_28)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
super.onCreateMenu(menu);
|
||||
}
|
||||
@@ -84,11 +74,16 @@ public class StickersFragment extends BaseToolbarFragment {
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case 0 -> addTgsPack();
|
||||
case 1 -> resetToken(requireContext());
|
||||
case 1 -> openMenu(null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void openMenu(String toast) {
|
||||
if (toast != null) Toast.makeText(requireContext(), toast, Toast.LENGTH_SHORT).show();
|
||||
NavigatorUtils.switchFragment(requireContext(), StickersPreferencesFragment.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -101,14 +96,14 @@ public class StickersFragment extends BaseToolbarFragment {
|
||||
public View onCreateContent(@NonNull LayoutInflater inflater, @Nullable Bundle bundle) {
|
||||
setTitle(R.string.vtltgs);
|
||||
|
||||
var container = new FrameLayout(requireContext());
|
||||
var recycler = new RecyclerView(requireContext());
|
||||
final var container = new FrameLayout(requireContext());
|
||||
final var recycler = new RecyclerView(requireContext());
|
||||
|
||||
mAdapter = new StickerPackAdapter();
|
||||
recycler.setAdapter(mAdapter);
|
||||
|
||||
var callback = new StickerTouchHelperCallback(mAdapter);
|
||||
var touchHelper = new ItemTouchHelper(callback);
|
||||
final var callback = new StickerTouchHelperCallback(mAdapter);
|
||||
final var touchHelper = new ItemTouchHelper(callback);
|
||||
touchHelper.attachToRecyclerView(recycler);
|
||||
|
||||
recycler.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
|
||||
@@ -129,50 +124,80 @@ public class StickersFragment extends BaseToolbarFragment {
|
||||
}
|
||||
|
||||
private void addTgsPack() {
|
||||
initGrabber();
|
||||
Runnable lambda = () -> {
|
||||
var container = new LinearLayout(requireContext());
|
||||
|
||||
var input = new EditText(requireContext());
|
||||
var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
||||
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) ->
|
||||
final var method = TGPref.getTGConnectMethod();
|
||||
if (method != -1) {
|
||||
initGrabber();
|
||||
switch (method) {
|
||||
case TYPE_SOCKS -> {
|
||||
if (TGPref.getTGProxyIP() == null) openMenu(requireContext().getString(R.string.stickersproxy1));
|
||||
}
|
||||
case TYPE_DIRECT -> {
|
||||
initGrabber();
|
||||
final Runnable lambda = () ->
|
||||
{
|
||||
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 container = new LinearLayout(requireContext());
|
||||
|
||||
if (TGPref.getTGBotKey() == null) enterBotKey(() -> checkApiKey(lambda));
|
||||
else checkApiKey(lambda);
|
||||
final var input = new EditText(requireContext());
|
||||
final var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
|
||||
private void enterBotKey(Runnable callback) {
|
||||
var container = new LinearLayout(requireContext());
|
||||
final var container = new LinearLayout(requireContext());
|
||||
|
||||
var input = new EditText(requireContext());
|
||||
var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
||||
final var input = new EditText(requireContext());
|
||||
final var inputLP = new ViewGroup.MarginLayoutParams(-1, -2);
|
||||
inputLP.setMargins(
|
||||
AndroidUtils.dp2px(24.f),
|
||||
0,
|
||||
@@ -197,7 +222,7 @@ public class StickersFragment extends BaseToolbarFragment {
|
||||
.setMessage(requireContext().getString(R.string.stickersapi9) +
|
||||
requireContext().getString(R.string.stickersapi10))
|
||||
.setNeutralButton(AndroidUtils.getString("open_bot"), (dl, i) -> {
|
||||
var intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/botfather"))
|
||||
final var intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/botfather"))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
requireActivity().startActivity(intent);
|
||||
})
|
||||
@@ -210,7 +235,7 @@ public class StickersFragment extends BaseToolbarFragment {
|
||||
private void checkApiKey(Runnable callback) {
|
||||
mGrabber.setBotApiKey(TGPref.getTGBotKey());
|
||||
|
||||
var dialog = new ProgressDialog(requireContext());
|
||||
final var dialog = new ProgressDialog(requireContext());
|
||||
dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
dialog.setMessage(requireContext().getString(R.string.stickersapi1));
|
||||
dialog.show();
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ import java.nio.ByteBuffer;
|
||||
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
|
||||
|
||||
enum ImageEffects {
|
||||
Blur(AndroidUtils.getArray(R.array.filter_types),
|
||||
Blur(true,
|
||||
AndroidUtils.getArray(R.array.filter_types),
|
||||
new String[]{"disabled", "low", "med", "high"},
|
||||
getGlobalContext().getString(R.string.filter_blur)
|
||||
) {
|
||||
@@ -39,7 +40,8 @@ enum ImageEffects {
|
||||
};
|
||||
}
|
||||
},
|
||||
Dim(AndroidUtils.getArray(R.array.filter_dim_types),
|
||||
Dim(true,
|
||||
AndroidUtils.getArray(R.array.filter_dim_types),
|
||||
new String[]{"disabled", "dim_black", "dim_white"},
|
||||
getGlobalContext().getString(R.string.filter_dim)) {
|
||||
@Override
|
||||
@@ -64,7 +66,8 @@ enum ImageEffects {
|
||||
};
|
||||
}
|
||||
},
|
||||
Mosaic(AndroidUtils.getArray(R.array.filter_types),
|
||||
Mosaic(true,
|
||||
AndroidUtils.getArray(R.array.filter_types),
|
||||
new String[]{"disabled", "low", "med", "high"},
|
||||
getGlobalContext().getString(R.string.filter_mosaic)) {
|
||||
@Override
|
||||
@@ -91,7 +94,7 @@ enum ImageEffects {
|
||||
};
|
||||
}
|
||||
},
|
||||
Monochrome(null, null,
|
||||
Monochrome(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_monochrome)) {
|
||||
@Override
|
||||
public void applyEffect(ByteBuffer bitmap, int height, int width) {
|
||||
@@ -100,7 +103,7 @@ enum ImageEffects {
|
||||
}
|
||||
}
|
||||
},
|
||||
Invert(null, null,
|
||||
Invert(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_invert_colors)) {
|
||||
@Override
|
||||
public void applyEffect(ByteBuffer bitmap, int height, int width) {
|
||||
@@ -109,7 +112,7 @@ enum ImageEffects {
|
||||
}
|
||||
}
|
||||
},
|
||||
Sepia(null, null,
|
||||
Sepia(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_sepia)) {
|
||||
@Override
|
||||
public void applyEffect(ByteBuffer bitmap, int height, int width) {
|
||||
@@ -118,7 +121,7 @@ enum ImageEffects {
|
||||
}
|
||||
}
|
||||
},
|
||||
Emboss(null, null,
|
||||
Emboss(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_emboss)) {
|
||||
@Override
|
||||
public String getSummary() {
|
||||
@@ -132,7 +135,7 @@ enum ImageEffects {
|
||||
}
|
||||
}
|
||||
},
|
||||
Engrave(null, null,
|
||||
Engrave(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_engrave)) {
|
||||
@Override
|
||||
public String getSummary() {
|
||||
@@ -146,7 +149,7 @@ enum ImageEffects {
|
||||
}
|
||||
}
|
||||
},
|
||||
Flea(null, null,
|
||||
Flea(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_flea)) {
|
||||
@Override
|
||||
public void applyEffect(ByteBuffer bitmap, int height, int width) {
|
||||
@@ -155,7 +158,7 @@ enum ImageEffects {
|
||||
}
|
||||
}
|
||||
},
|
||||
Snow(null, null,
|
||||
Snow(false, null, null,
|
||||
getGlobalContext().getString(R.string.filter_snow)) {
|
||||
@Override
|
||||
public void applyEffect(ByteBuffer bitmap, int height, int width) {
|
||||
@@ -165,13 +168,15 @@ enum ImageEffects {
|
||||
}
|
||||
};
|
||||
|
||||
private final boolean free;
|
||||
private final String title;
|
||||
@Nullable
|
||||
private final String[] entryValues;
|
||||
@Nullable
|
||||
private final String[] entries;
|
||||
|
||||
ImageEffects(@Nullable String[] entries, @Nullable String[] entryValues, String title) {
|
||||
ImageEffects(boolean free, @Nullable String[] entries, @Nullable String[] entryValues, String title) {
|
||||
this.free = free;
|
||||
this.entries = entries;
|
||||
this.entryValues = entryValues;
|
||||
this.title = title;
|
||||
@@ -202,6 +207,10 @@ enum ImageEffects {
|
||||
return entryValues != null && entryValues.length > 0;
|
||||
}
|
||||
|
||||
public boolean isFree() {
|
||||
return free;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String[] getEntries() {
|
||||
return entries;
|
||||
|
||||
@@ -80,7 +80,24 @@ public class WallpaperMenuFragment extends MaterialPreferenceToolbarFragment {
|
||||
|
||||
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.vtlfilters));
|
||||
|
||||
if (!Preferences.hasVerification() && !Preferences.getBoolValue("dialogrecomm", false)) {
|
||||
PreferenceFragmentUtils.addPreference(
|
||||
getPreferenceScreen(),
|
||||
"",
|
||||
requireContext().getString(R.string.filters_warning),
|
||||
requireContext().getString(R.string.icons_warning_info),
|
||||
0,
|
||||
preference -> {
|
||||
requireContext().startActivity(
|
||||
new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse("https://vtosters.app/donate/"))
|
||||
);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
Arrays.stream(ImageEffects.values())
|
||||
.filter(imageEffects -> imageEffects.isFree() || Preferences.hasVerification())
|
||||
.forEach(it -> {
|
||||
if (it.isList()) {
|
||||
var pref = PreferenceFragmentUtils.addListPreference(
|
||||
|
||||
@@ -12,14 +12,13 @@ import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import com.vk.core.util.Screen;
|
||||
import com.vk.core.util.ToastUtils;
|
||||
import com.vtosters.lite.general.fragments.WebViewFragment;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -33,12 +32,6 @@ import java.util.Random;
|
||||
|
||||
public class AndroidUtils {
|
||||
private static final String ALLOWED_CHARACTERS = "0123456789qwertyuiopasdfghjklzxcvbnm";
|
||||
public enum ScreenSize {
|
||||
small,
|
||||
normal,
|
||||
large,
|
||||
xlarge
|
||||
}
|
||||
|
||||
public static boolean isDebuggable() {
|
||||
return 0 != (AndroidUtils.getGlobalContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE);
|
||||
@@ -51,8 +44,7 @@ public class AndroidUtils {
|
||||
}
|
||||
|
||||
public static boolean isTablet() {
|
||||
String string = getGlobalContext().getResources().getString(b.h.g.d.screen_size);
|
||||
return TextUtils.equals(string, ScreenSize.large.name()) || TextUtils.equals(string, ScreenSize.xlarge.name());
|
||||
return Screen.l(getGlobalContext());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -7,6 +7,10 @@ import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import com.guardanis.applock.AppLock;
|
||||
import com.guardanis.applock.activities.UnlockActivity;
|
||||
import com.vk.core.fragments.FragmentImpl;
|
||||
import com.vk.navigation.Navigator;
|
||||
import com.vtosters.lite.MainActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -42,6 +46,11 @@ public class LifecycleUtils {
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
public void onActivityCreated(Activity activity, Bundle bundle) {
|
||||
activities.add(activity);
|
||||
|
||||
if (AppLock.isUnlockRequired(activity) && activity.getClass() != MainActivity.class) {
|
||||
var intent = new Intent(activity, UnlockActivity.class);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override // android.app.Application.ActivityLifecycleCallbacks
|
||||
|
||||
@@ -3,7 +3,6 @@ package ru.vtosters.lite.utils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
|
||||
import static ru.vtosters.lite.foaf.FoafBase.getBypassedOnlineInfo;
|
||||
|
||||
@@ -14,7 +13,7 @@ public class OnlineBypass {
|
||||
return json;
|
||||
}
|
||||
var onlineinfo = json.optJSONObject("online_info");
|
||||
if (onlineinfo != null && !onlineinfo.optBoolean("visible") && !Preferences.serverFeaturesDisable()) {
|
||||
if (onlineinfo != null && !onlineinfo.optBoolean("visible")) {
|
||||
var bypassed = getBypassedOnlineInfo(id);
|
||||
if (bypassed.optInt("last_seen", 0) != 0) {
|
||||
json.remove("online_info");
|
||||
@@ -38,7 +37,7 @@ public class OnlineBypass {
|
||||
}
|
||||
|
||||
public static JSONArray setOnlineInfoUsers(JSONArray profiles) throws JSONException {
|
||||
if (profiles == null || profiles.length() == 0 || Preferences.serverFeaturesDisable()) return profiles;
|
||||
if (profiles == null || profiles.length() == 0) return profiles;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
var curVkId = AccountManagerUtils.getUserId();
|
||||
for (int i = 0; i < profiles.length(); i++) {
|
||||
@@ -52,7 +51,7 @@ public class OnlineBypass {
|
||||
sb.append(",");
|
||||
}
|
||||
var ids = sb.toString();
|
||||
if (!ids.isEmpty()) {
|
||||
if (ids.length() > 0) {
|
||||
ids = ids.substring(0, ids.length() - 1);
|
||||
} else {
|
||||
return profiles;
|
||||
|
||||
@@ -7,14 +7,15 @@ import okhttp3.*;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import ru.vtosters.hooks.other.Preferences;
|
||||
import ru.vtosters.lite.di.singleton.VtOkHttpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static ru.vtosters.hooks.other.Preferences.getBoolValue;
|
||||
import static ru.vtosters.hooks.other.Preferences.hasVerification;
|
||||
|
||||
public class VTVerifications {
|
||||
public static final List<Integer> sVerifications = new ArrayList<>();
|
||||
@@ -32,7 +33,7 @@ public class VTVerifications {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || getBoolValue("isRoamingState", false)) && prefs.contains("ids") || Preferences.serverFeaturesDisable()) {
|
||||
if ((!NetworkUtils.isNetworkConnected() && NetworkUtils.isInternetSlow() || getBoolValue("isRoamingState", false)) && prefs.contains("ids")) {
|
||||
parseJson(prefs.getString("ids", "[]"));
|
||||
Log.d("VTVerifications", "load from memory. Roaming or Network issues");
|
||||
isLoaded = true;
|
||||
@@ -128,7 +129,7 @@ public class VTVerifications {
|
||||
return -id;
|
||||
}
|
||||
|
||||
// public static boolean haveDonateButton() {
|
||||
// return hasVerification() || new Random().nextInt(6) != 1;
|
||||
// }
|
||||
public static boolean haveDonateButton() {
|
||||
return hasVerification() || new Random().nextInt(6) != 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
|
||||
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
|
||||
@@ -197,6 +198,14 @@
|
||||
android:name="ru.vtosters.lite.ui.activities.VKAdminTokenActivity"
|
||||
android:launchMode="singleTop">
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.guardanis.applock.activities.LockCreationActivity"
|
||||
android:launchMode="singleTop">
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.guardanis.applock.activities.UnlockActivity"
|
||||
android:launchMode="singleTop">
|
||||
</activity>
|
||||
<activity
|
||||
android:name="com.vtosters.lite.MainActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
|
||||
+1
-1
@@ -25,5 +25,5 @@ usesFramework:
|
||||
tag: null
|
||||
version: 2.6.0
|
||||
versionInfo:
|
||||
versionCode: '4839'
|
||||
versionCode: '4838'
|
||||
versionName: 5.56.1
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:drawable="@color/applock__create_choose_option_bg_pressed" android:state_selected="true"/>
|
||||
<item android:drawable="@color/applock__create_choose_option_bg_pressed" android:state_pressed="true"/>
|
||||
<item android:drawable="@color/applock__create_choose_option_bg" />
|
||||
|
||||
</selector>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/applock__activity_lock_creation"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/applock__activity_padding">
|
||||
|
||||
<include
|
||||
android:id="@+id/pin__container"
|
||||
layout="@layout/applock__lock_creation" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/applock__activity_unlock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/applock__activity_padding">
|
||||
|
||||
<include
|
||||
android:id="@+id/pin__container"
|
||||
layout="@layout/applock__unlock" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<EditText
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:cursorVisible="false"
|
||||
android:imeOptions="flagNoExtractUi|actionDone"
|
||||
android:inputType="number"
|
||||
android:textColor="@android:color/transparent"/>
|
||||
@@ -0,0 +1,20 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:applock="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/applock__content_parent"
|
||||
android:descendantFocusability="beforeDescendants"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pin__description"
|
||||
style="@style/applock__description" />
|
||||
|
||||
<com.guardanis.applock.pin.PINInputView
|
||||
android:id="@+id/pin__input_view"
|
||||
style="@style/applock__input_pin_view"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/pin__action_settings"
|
||||
style="@style/applock__action_settings"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,20 @@
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:applock="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/applock__content_parent"
|
||||
android:descendantFocusability="beforeDescendants"
|
||||
android:focusableInTouchMode="true">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pin__description"
|
||||
style="@style/applock__description"/>
|
||||
|
||||
<com.guardanis.applock.pin.PINInputView
|
||||
android:id="@+id/pin__input_view"
|
||||
style="@style/applock__input_pin_view"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/pin__action_settings"
|
||||
style="@style/applock__action_settings"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -5981,7 +5981,6 @@
|
||||
<string name="any_bank_card">Карта любога банка</string>
|
||||
<string name="current_place_number">Нумар бягучага месца</string>
|
||||
<string name="eight_friends">Гуляе 8 Вашых сяброў</string>
|
||||
|
||||
<string name="vtlfeed">Стужка</string>
|
||||
<string name="vtlinterface">Інтэрфейс</string>
|
||||
<string name="vtlmedia">Медыякантэнт</string>
|
||||
@@ -6067,7 +6066,7 @@
|
||||
<string name="gms_download_microg">Спампаваць MicroG</string>
|
||||
<string name="gms_what_to_do_next">Што рабіць далей?</string>
|
||||
<string name="gms_what_to_do_after_microg_title">Што рабіць пасля ўстаноўкі MicroG?</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Увайдзіце ў свой Google рахунак на наладах MicroG\n2. Уключыце рэгістрацыю прылады\n3. Уключыце воблачны абмен паведамленнямі\n4. Пераўсталюеце VTLite\n5. Дазвольце яму прымаць паведамлення</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Увайдзіце ў свой Google акаўнт \n2. Пераўсталюеце прыкладанне VTLite</string>
|
||||
<string name="donate">Дапамагчы праекту</string>
|
||||
<string name="fulltime">dd MMMM yyyy у HH:mm:ss</string>
|
||||
<string name="fulltime2">dd MMMM у HH:mm:ss</string>
|
||||
@@ -6097,7 +6096,7 @@
|
||||
<string name="BGStickerssum">Для ўключэння/адключэння вам неабходна ачысціць кэш прыкладання</string>
|
||||
<string name="addinf">дадатковая інфармацыя</string>
|
||||
<string name="copyid">Скапіяваць ID</string>
|
||||
<string name="vtssfs">Серверныя функцыі</string>
|
||||
<string name="vtssfs">VT Функцыі</string>
|
||||
<string name="vticon">Іконка дадатку</string>
|
||||
<string name="vticonbtn">Выкарыстоўваць арыгінальную іконку</string>
|
||||
<string name="vticonsummary">Выкарыстанне арыгінальнай назвы і іконкі дадатку як у афіцыйнага кліента VK</string>
|
||||
@@ -6311,7 +6310,7 @@
|
||||
<string name="friendsblock_title">Схаваць сяброў</string>
|
||||
<string name="friendsblock_summ">Схаваць блок рэкамендацый сяброў у сябрах</string>
|
||||
<string name="onlineformatter_title">Пашыраны анлайн</string>
|
||||
<string name="onlineformatter_summ">Паказваць з якога прыкладання/кліента чалавек у анлайне\n\nНапрыклад: Онлайн З дапамогай Kate Mobile</string>
|
||||
<string name="onlineformatter_summ">Паказваць з якога прыкладання/кліента чалавек у анлайне</string>
|
||||
<string name="voices_vtl">Галасавыя паведамленні</string>
|
||||
<string name="disable_voice_listening_summ">Функцыя дазваляе не адпраўляць статут праслухоўвання галасавых
|
||||
паведамленняў чалавека вамі
|
||||
@@ -7034,34 +7033,4 @@
|
||||
|
||||
<string name="invertcached_title">Інвертаваць cпіс</string>
|
||||
<string name="invertcached_summ">Інвертаваць cпіс кэшаваных трэкаў</string>
|
||||
|
||||
<string name="sett_recommendations">Рэкамендацыі</string>
|
||||
<string name="sett_nogms_summ">Адсутнасць гэтых сэрвісаў прыводзіць да паломкі фонавых апавяшчэнняў і праблемах працы кампанентаў прыкладання</string>
|
||||
<string name="sett_missing_links">Не абраныя спасылкі для адкрыцця дадаткам</string>
|
||||
<string name="sett_missing_links_summ">Гэта перашкодзіць адкрыццю знешніх спасылак для іх адкрыцця з дапамогай гэтага прыкладання\n\nБ некаторых выпадках неабходна адключыць адкрыццё спасылак афіцыйным прыкладанням ВКонтакте</string>
|
||||
<string name="sett_battery">Адключыце эканомію батарэі</string>
|
||||
<string name="sett_battery_summ1">Эканомія батарэі перашкаджае атрыманню фонавых паведамленняў, апавяшчэнняў і працы музыкі</string>
|
||||
<string name="sett_battery_summ2">\n\nДля прылад Xiaomi неабходна ўручную ўключыць у наладах сістэмы пункт Аўтазапуск дадзеным дадаткам</string>
|
||||
<string name="sett_notifs">Уключыце апавяшчэння</string>
|
||||
<string name="sett_notifs_summ">Без уключаных апавяшчэнняў не атрымаецца атрымліваць паведамленні і многае іншае</string>
|
||||
<string name="sett_chats">Нашы чаты</string>
|
||||
<string name="sett_chats_summ">Актыўныя чаты па абмеркаванні мадыфікацыі і шмат чаму іншаму</string>
|
||||
<string name="sett_community">Наша група</string>
|
||||
<string name="sett_community_summ">Самая свежая інфармацыя аб абнаўленнях і не толькі</string>
|
||||
<string name="sett_autoupdates">Аўтаматычнае абнаўленне</string>
|
||||
<string name="sett_autoupdates_summ">Атрымліваць апавяшчэнне аб выхадзе новай версіі прыкладання</string>
|
||||
|
||||
<string name="update_latest">У вас ўстаноўлена АПОШНЯЯ версія прыкладання</string>
|
||||
<string name="update_err">Памылка праверкі абнаўлення</string>
|
||||
<string name="serverFeaturesDisable">Адключыць усю працу з серверам</string>
|
||||
<string name="serverFeaturesDisable_summ">Адключае верыфікацыі і ўсе дадатковыя функцыі звязаныя з атрыманнем дадзеных з сервера ВТостерс</string>
|
||||
|
||||
<string name="tgs_token_missing">Токен адсутнічае</string>
|
||||
<string name="tgs_token_reset_dialog">Скінуць токен бота?</string>
|
||||
<string name="tgs_token_reset">Токен бота скінуты</string>
|
||||
<string name="tgs_token_reset_menu">Скінуць токен бота</string>
|
||||
|
||||
<string name="microg_dialog1">Паменшаны MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog2">Альтэрнатыўны MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog3">Паўнавартасны MicroG (Замена GMS)</string>
|
||||
</resources>
|
||||
@@ -5420,7 +5420,7 @@
|
||||
<string name="gms_download_microg">Скачать MicroG</string>
|
||||
<string name="gms_what_to_do_next">Что делать дальше?</string>
|
||||
<string name="gms_what_to_do_after_microg_title">Что делать после установки MicroG?</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Войдите в свой Google аккаунт в настройках MicroG \n2. Включите регистрацию устройства \n3. Включите облачный обмен сообщениями \n4. Переустановите VTLite \n5. Разрешите ему принимать уведомления</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Войдите в свой Google аккаунт в MicroG \n2. Переустановите приложение VTLite</string>
|
||||
<string name="donate">Помочь проекту</string>
|
||||
<string name="fulltime">dd MMMM yyyy в HH:mm:ss</string>
|
||||
<string name="fulltime2">dd MMMM в HH:mm:ss</string>
|
||||
@@ -5446,7 +5446,7 @@
|
||||
<string name="BGStickerssum">Для включения/отключения вам необходимо очистить кеш приложения</string>
|
||||
<string name="addinf">Дополнительная информация</string>
|
||||
<string name="copyid">Скопировать айди</string>
|
||||
<string name="vtssfs">Серверные функции</string>
|
||||
<string name="vtssfs">VT Функции</string>
|
||||
<string name="vticon">Иконка приложения</string>
|
||||
<string name="vticonbtn">Использовать оригинальную иконку</string>
|
||||
<string name="vticonsummary">Использование оригинального названия и иконки приложения как у официального клиента ВК</string>
|
||||
@@ -5640,7 +5640,7 @@
|
||||
<string name="friendsblock_title">Скрыть друзей</string>
|
||||
<string name="friendsblock_summ">Скрыть блок рекомендаций друзей в друзьях</string>
|
||||
<string name="onlineformatter_title">Расширенный онлайн</string>
|
||||
<string name="onlineformatter_summ">Показывать с какого приложения/клиента человек в онлайне\n\nНапример: Онлайн с помощью Kate Mobile</string>
|
||||
<string name="onlineformatter_summ">Показывать с какого приложения/клиента человек в онлайне</string>
|
||||
<string name="voices_vtl">Голосовые сообщения</string>
|
||||
<string name="disable_voice_listening_summ">Функция позволяет не отправлять статус прослушивания голосовых сообщений человека вами</string>
|
||||
<string name="listen_v_title">Отключить статус прослушивания</string>
|
||||
@@ -6364,6 +6364,37 @@
|
||||
<string name="device_info_copied">Информация об устройстве скопирована</string>
|
||||
<string name="app_open_by_default_settings">Для нормальной работы приложения необходимо разрешить приложению открытие ссылок по умолчанию</string>
|
||||
|
||||
<string name="applock__description_chooser">Защитить это приложение с помощью PIN-кода или отпечатка пальцев?</string>
|
||||
<string name="applock__description_create_pin">Создайте PIN-код для защиты этого приложения</string>
|
||||
<string name="applock__description_create_pin_reattempt">Неверный PIN-код. Попробуйте еще раз</string>
|
||||
<string name="applock__description_create_fingerprint">Используйте свой отпечаток пальца, чтобы защитить это приложение</string>
|
||||
<string name="applock__description_create_fingerprint_paused">Разблокировка по биометрии отменена. Попробуйте снова</string>
|
||||
<string name="applock__description_confirm">Введите PIN-код для подтверждения</string>
|
||||
<string name="applock__description_unlock_pin">Введите свой PIN-код для разблокировки</string>
|
||||
<string name="applock__description_unlock_fingerprint">Приложите палец к сканеру</string>
|
||||
<string name="applock__description_unlock_fingerprint_help">Проблема при распознавании отпечатка пальца: %1$s</string>
|
||||
<string name="applock__create_chooser_option_pin">PIN</string>
|
||||
<string name="applock__create_chooser_option_fingerprint">Отпечаток пальца</string>
|
||||
<string name="applock__unlock_error_no_matching_pin_found">Не существует сохраненного PIN-кода для разблокировки</string>
|
||||
<string name="applock__unlock_error_match_failed">Неверный PIN-код!</string>
|
||||
<string name="applock__unlock_error_insufficient_selection">Неправильное количество цифр! Пожалуйста, попробуйте еще раз</string>
|
||||
<string name="applock__unlock_error_retry_limit_exceeded">Слишком много неудачных попыток. Попробуйте снова через %1$s</string>
|
||||
<string name="applock__toast_unlock_required">Вы не можете этого сделать</string>
|
||||
<string name="applock__password_char">*</string>
|
||||
<string name="applock__action_settings">Открыть системные настройки</string>
|
||||
<string name="applock__fingerprint_alias">AppLock</string>
|
||||
<string name="applock__fingerprint_error_not_enrolled">Вы не настроили разблокировку по отпечатку пальца</string>
|
||||
<string name="applock__fingerprint_error_unrecognized">Ваш отпечаток пальца не был распознан</string>
|
||||
<string name="applock__fingerprint_error_unknown">Произошло что-то странное</string>
|
||||
<string name="applock__fingerprint_error_none">Отпечатки пальцев не найдены! Добавьте их в системных настройках</string>
|
||||
<string name="applock__fingerprint_error_hardware">Отсутствует сканер отпечатков пальцев</string>
|
||||
<string name="applock__fingerprint_error_permission">Требуется разрешение на использование отпечатков пальцев</string>
|
||||
<string name="applock__fingerprint_error_permission_multiple">Разрешение на работу с биометрией не было получено. Выдайте его приложению в системных настройках</string>
|
||||
<item name="applock__empty_item_min_size_percent" format="float" type="string">0.2</item>
|
||||
|
||||
<string name="pin_not_set">PIN-код не установлен</string>
|
||||
<string name="pin_reset">PIN-код был успешно сброшен</string>
|
||||
|
||||
<string name="failed_to_get_text">Ошибка получения текста</string>
|
||||
<string name="error_no_text">Ошибка получения текста, возможно текст не найден</string>
|
||||
<string name="applying_accent">Применение акцента</string>
|
||||
@@ -6417,34 +6448,4 @@
|
||||
|
||||
<string name="invertcached_title">Инвертировать список</string>
|
||||
<string name="invertcached_summ">Инвертировать список кешированных треков</string>
|
||||
|
||||
<string name="sett_recommendations">Рекомендации</string>
|
||||
<string name="sett_nogms_summ">Отсутствие этих сервисов приводит к поломке фоновых уведомлений и проблемам работы компонентов приложения</string>
|
||||
<string name="sett_missing_links">Не выбраны ссылки для открытия приложением</string>
|
||||
<string name="sett_missing_links_summ">Это помешает открытию внешних ссылок для их открытия с помощью этого приложения\n\nВ некоторых случаях необходимо отключить открытие ссылок официальным приложениям ВКонтакте</string>
|
||||
<string name="sett_battery">Отключите экономию батареи</string>
|
||||
<string name="sett_battery_summ1">Экономия батареи мешает получению фоновых сообщений, уведомлений и работе музыки</string>
|
||||
<string name="sett_battery_summ2">\n\nДля устройств Xiaomi необходимо вручную включить в настройках системы пункт Автозапуск данному приложению</string>
|
||||
<string name="sett_notifs">Включите уведомления</string>
|
||||
<string name="sett_notifs_summ">Без включенных уведомлений не получится получать сообщения и многое другое</string>
|
||||
<string name="sett_chats">Наши чаты</string>
|
||||
<string name="sett_chats_summ">Активные чаты по обсуждению модификации и многому другому</string>
|
||||
<string name="sett_community">Наши сообщества</string>
|
||||
<string name="sett_community_summ">Самая свежая информация об обновлениях и не только</string>
|
||||
<string name="sett_autoupdates">Автоматическое обновление</string>
|
||||
<string name="sett_autoupdates_summ">Получать уведомление о выходе новой версии приложения</string>
|
||||
|
||||
<string name="update_latest">У вас установлена последняя версия приложения</string>
|
||||
<string name="update_err">Ошибка проверки обновления</string>
|
||||
<string name="serverFeaturesDisable">Отключить всю работу с сервером</string>
|
||||
<string name="serverFeaturesDisable_summ">Отключает галочки и все дополнительные функции связанные с получением данных с сервера ВТостерс</string>
|
||||
|
||||
<string name="tgs_token_missing">Токен отсутствует</string>
|
||||
<string name="tgs_token_reset_dialog">Сбросить токен бота?</string>
|
||||
<string name="tgs_token_reset">Токен бота сброшен</string>
|
||||
<string name="tgs_token_reset_menu">Сбросить токен бота</string>
|
||||
|
||||
<string name="microg_dialog1">Уменьшенный MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog2">Альтернативный MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog3">Полноценный MicroG (Замена GMS)</string>
|
||||
</resources>
|
||||
@@ -5347,7 +5347,7 @@
|
||||
<string name="gms_download_microg">Завантажити MicroG</string>
|
||||
<string name="gms_what_to_do_next">Що робити далі?</string>
|
||||
<string name="gms_what_to_do_after_microg_title">Що робити після встановлення MicroG?</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">"1. Увійдіть у свій обліковий запис Google у налаштуваннях MicroG\n2. Увімкніть реєстрацію пристрою\n3. Увімкніть Хмарний обмін повідомленнями\n4. Перевстановіть VTLite\n5. Дозвольте йому приймати повідомлення"</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Увійдіть до свого облікового запису Google \n2. Перевстановіть додаток VTLite"</string>
|
||||
<string name="donate">Допомогти проекту</string>
|
||||
<string name="fulltime">dd MMMM yyyy в HH:mm:ss</string>
|
||||
<string name="fulltime2">dd MMMM в HH:mm:ss</string>
|
||||
@@ -5373,7 +5373,7 @@
|
||||
<string name="BGStickerssum">Для увімкнення/вимкнення вам необхідно очистити кеш додатка</string>
|
||||
<string name="addinf">додаткова інформація</string>
|
||||
<string name="copyid">Копіювати ID</string>
|
||||
<string name="vtssfs">Серверні функції</string>
|
||||
<string name="vtssfs">VT Функції</string>
|
||||
<string name="vticon">Іконка додатку</string>
|
||||
<string name="vticonbtn">Використовувати оригінальну іконку</string>
|
||||
<string name="vticonsummary">Використання оригінальної назви та іконки додатку як у офіційного клієнта ВК</string>
|
||||
@@ -5567,7 +5567,7 @@
|
||||
<string name="friendsblock_title">Приховати друзів</string>
|
||||
<string name="friendsblock_summ">Приховати блок рекомендацій друзів у друзях</string>
|
||||
<string name="onlineformatter_title">Розширений онлайн</string>
|
||||
<string name="onlineformatter_summ">Показувати з якого додатка/клієнта людина в онлайні\n\nНаприклад: Онлайн за допомогою Kate Mobile</string>
|
||||
<string name="onlineformatter_summ">Показувати з якого додатка/клієнта людина в онлайні</string>
|
||||
<string name="voices_vtl">Голосові повідомлення</string>
|
||||
<string name="disable_voice_listening_summ">Функція дозволяє не надсилати статус прослуховування голосових повідомлень вами</string>
|
||||
<string name="listen_v_title">Вимкнути статус прослуховування</string>
|
||||
@@ -6342,34 +6342,4 @@
|
||||
|
||||
<string name="invertcached_title">Інвертувати список</string>
|
||||
<string name="invertcached_summ">Інвертувати список кешованих треків</string>
|
||||
|
||||
<string name="sett_recommendations">Рекомендації</string>
|
||||
<string name="sett_nogms_summ">Відсутність цих сервісів призводить до поломки фонових сповіщень і проблем роботи компонентів застосунку</string>
|
||||
<string name="sett_missing_links">Не вибрані посилання для відкриття додатком</string>
|
||||
<string name="sett_missing_links_summ">Це завадить відкриттю зовнішніх посилань для їхнього відкриття за допомогою цього застосунку\n\пУ деяких випадках необхідно відключити відкриття посилань офіційним застосункам ВКонтакте</string>
|
||||
<string name="sett_battery">Відключіть економію батареї</string>
|
||||
<string name="sett_battery_summ1">Економія батареї заважає отриманню фонових повідомлень, сповіщень та роботі музики</string>
|
||||
<string name="sett_battery_summ2">\n\nДля пристроїв Xiaomi необхідно вручну увімкнути в налаштуваннях системи пункт Автозапуск цього додатка</string>
|
||||
<string name="sett_notifs">Увімкніть сповіщення</string>
|
||||
<string name="sett_notifs_summ">Без увімкнених сповіщень не вдасться отримувати повідомлення та багато іншого</string>
|
||||
<string name="sett_chats">Наші чати</string>
|
||||
<string name="sett_chats_summ">Активні чати з обговорення модифікації та багато чого іншого</string>
|
||||
<string name="sett_community">Наші спільноти</string>
|
||||
<string name="sett_community_summ">Найсвіжіша інформація про оновлення і не тільки</string>
|
||||
<string name="sett_autoupdates">Автоматичне оновлення</string>
|
||||
<string name="sett_autoupdates_summ">Отримувати повідомлення про вихід нової версії застосунку</string>
|
||||
|
||||
<string name="update_latest">Вас встановлена остання версія програми</string>
|
||||
<string name="update_err">Помилка перевірки оновлення</string>
|
||||
<string name="serverFeaturesDisable">Відключити всю роботу з сервером</string>
|
||||
<string name="serverFeaturesDisable_summ">Відключає галочки і всі додаткові функції, пов\'язані з отриманням даних з сервера ВТостерс</string>
|
||||
|
||||
<string name="tgs_token_missing">Токен відсутній</string>
|
||||
<string name="tgs_token_reset_dialog">Відкинути токен бота?</string>
|
||||
<string name="tgs_token_reset">Токен бота скинуто</string>
|
||||
<string name="tgs_token_reset_menu">Скинути токен бота</string>
|
||||
|
||||
<string name="microg_dialog1">Зменшений MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog2">Альтернативний MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog3">Повноцінний MicroG (Заміна GMS)</string>
|
||||
</resources>
|
||||
|
||||
@@ -2511,4 +2511,8 @@
|
||||
<attr name="im_ic_msgreload" format="reference" />
|
||||
<attr name="im_ic_msgdl" format="reference" />
|
||||
<attr name="im_ic_stats" format="reference" />
|
||||
<declare-styleable name="PINInputView">
|
||||
<attr name="pinTextColor" format="reference|color"/>
|
||||
<attr name="pinBackgroundColor" format="reference|color"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -17,4 +17,7 @@
|
||||
<bool name="spb_default_reversed">false</bool>
|
||||
<bool name="vk_is_tablet">false</bool>
|
||||
<bool name="com_samsung_android_icon_container_has_icon_container">true</bool>
|
||||
<bool name="applock__unlock_activity_return_allowed">false</bool>
|
||||
<bool name="applock__item_password_chars_enabled">true</bool>
|
||||
<bool name="applock__fingerprint_service_enabled">true</bool>
|
||||
</resources>
|
||||
|
||||
@@ -686,4 +686,15 @@
|
||||
<color name="yellow_light">#ffffd54f</color>
|
||||
<color name="yellow_sunflower">#fffece00</color>
|
||||
<color name="iclauncher">#fffcfcfc</color>
|
||||
|
||||
<color name="applock__activity_background">#FAFAFA</color>
|
||||
<color name="applock__description_text">#7F8C8D</color>
|
||||
|
||||
<color name="applock__item_background">#3C9ADF</color>
|
||||
<color name="applock__item_text">#FAFAFA</color>
|
||||
|
||||
<color name="applock__create_choose_option_bg">#00FFFFFF</color>
|
||||
<color name="applock__create_choose_option_bg_pressed">#11000000</color>
|
||||
|
||||
<color name="applock__action_settings">#95A5A6</color>
|
||||
</resources>
|
||||
|
||||
@@ -855,4 +855,20 @@
|
||||
<item name="wrap_content" type="dimen">-2</item>
|
||||
<dimen name="year_label_height">64.0dip</dimen>
|
||||
<dimen name="year_label_text_size">22.0dip</dimen>
|
||||
|
||||
<dimen name="applock__activity_padding">24dip</dimen>
|
||||
<dimen name="applock__content_padding_horizontal">24dip</dimen>
|
||||
<dimen name="applock__content_padding_vertical">36dip</dimen>
|
||||
<dimen name="applock__item_margin_top">20dip</dimen>
|
||||
<dimen name="applock__item_size">50dip</dimen>
|
||||
<dimen name="applock__fingerprint_scan_image_margin_top">35dip</dimen>
|
||||
<dimen name="applock__fingerprint_scan_image_size">65dip</dimen>
|
||||
<dimen name="applock__chooser_image_size">50dip</dimen>
|
||||
<dimen name="applock__chooser_parent_padding_vertical">24dip</dimen>
|
||||
<dimen name="applock__chooser_text_padding_top">12dip</dimen>
|
||||
<dimen name="applock__description_text">18dip</dimen>
|
||||
<dimen name="applock__description_padding_bottom">20dip</dimen>
|
||||
<dimen name="applock__action_settings">14dip</dimen>
|
||||
<dimen name="applock__action_settings_padding">12dip</dimen>
|
||||
<dimen name="applock__action_settings_margin_top">24dip</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -3919,6 +3919,10 @@
|
||||
<item name="download_mp3" type="id" />
|
||||
<item name="remove_from_cache_vkx" type="id" />
|
||||
<item name="add_to_cache_vkx" type="id" />
|
||||
<item name="pin__description" type="id" />
|
||||
<item name="pin__action_settings" type="id" />
|
||||
<item name="pin__input_view" type="id" />
|
||||
<item name="pin__container" type="id"/>
|
||||
<item name="account_backup_view" type="id" />
|
||||
<item name="copy_image" type="id" />
|
||||
</resources>
|
||||
|
||||
@@ -43,4 +43,9 @@
|
||||
<integer name="status_bar_notification_info_maxnum">999</integer>
|
||||
<integer name="vkim_activity_animation_duration">200</integer>
|
||||
<integer name="vkim_playing_drawable_rect_count">4</integer>
|
||||
|
||||
<integer name="applock__input_pin_item_count">4</integer>
|
||||
<integer name="applock__max_retry_count">5</integer>
|
||||
<integer name="applock__failure_retry_delay">5</integer>
|
||||
<integer name="applock__activity_lock_reenable_minutes">1440</integer>
|
||||
</resources>
|
||||
|
||||
@@ -5554,7 +5554,7 @@ Filters are not case-sensitive!"</string>
|
||||
<string name="gms_download_microg">Download MicroG</string>
|
||||
<string name="gms_what_to_do_next">What to do next?</string>
|
||||
<string name="gms_what_to_do_after_microg_title">What to do after installing MicroG?</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Log in to your Google account in the MicroG settings\n2. Enable device registration\n3. Enable cloud messaging\n4. Reinstall VTLite\n5. Allow him to receive notifications</string>
|
||||
<string name="gms_what_to_do_after_microg_summ">1. Log in to your Google account \n2. Reinstall VTLite application</string>
|
||||
<string name="donate">Donate</string>
|
||||
<string name="fulltime">dd MMMM yyyy HH:mm:ss</string>
|
||||
<string name="fulltime2">dd MMMM HH:mm:ss</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="addinf">Additional information</string>
|
||||
<string name="copyid">Copy ID</string>
|
||||
<string name="vtssfs">Server features</string>
|
||||
<string name="vtssfs">VT Features</string>
|
||||
<string name="vticon">App Icon</string>
|
||||
<string name="vticonbtn">Use original icon</string>
|
||||
<string name="vticonsummary">The original name and icon of VK</string>
|
||||
@@ -5615,7 +5615,7 @@ Warning! It can cause long downloads of user lists if there are a lot of users i
|
||||
<string name="ctypesocks">Socks</string>
|
||||
<string name="dnr">Do not read</string>
|
||||
<string name="dns">Disable status of viewing</string>
|
||||
<string name="dnw">Do not type</string>
|
||||
<string name="dnw">Do not write</string>
|
||||
<string name="docknumbers">Counters in dockbar</string>
|
||||
<string name="docknumberssum">"Disabling doesn't work in the tablet interface"</string>
|
||||
<string name="dockbar_tab_titles">Show dockbar item titles</string>
|
||||
@@ -5774,7 +5774,7 @@ You need to restart the application to apply proxy settings"</string>
|
||||
<string name="friendsblock_title">Hide friends</string>
|
||||
<string name="friendsblock_summ">Hide the block of recommendations of friends in friends section</string>
|
||||
<string name="onlineformatter_title">Extended online indicator</string>
|
||||
<string name="onlineformatter_summ">Show what app/device a person is online from\n\nFor example: Online using Kate Mobile</string>
|
||||
<string name="onlineformatter_summ">Show what app/device a person is online from</string>
|
||||
<string name="voices_vtl">Voice messages</string>
|
||||
<string name="disable_voice_listening_summ">The function allows you not to send the status of listening to the person\'s voice messages by you</string>
|
||||
<string name="listen_v_title">Disable listening status</string>
|
||||
@@ -5879,7 +5879,7 @@ May look like a fake leaving the online"</string>
|
||||
|
||||
<string name="activity_what_is_it">What is it?</string>
|
||||
<string name="activity_dnr_summary">Function lets you don\'t send read message status</string>
|
||||
<string name="activity_dnw_summary">Function lets you don\'t send type message status</string>
|
||||
<string name="activity_dnw_summary">Function lets you don\'t send write message status</string>
|
||||
<string name="activity_dns_summary">Function lets you don\'t send read stories status</string>
|
||||
|
||||
<string name="activity_dnr_visual">Visually may not work correctly (locally read), but companion will see that it wasn\'t read.</string>
|
||||
@@ -6502,6 +6502,37 @@ May look like a fake leaving the online"</string>
|
||||
<string name="device_info_copied">Device info copied</string>
|
||||
<string name="app_open_by_default_settings">You need to allow the app to open links by default for the app to work properly</string>
|
||||
|
||||
<string name="applock__description_chooser">Secure this application with a PIN or your fingerprints?</string>
|
||||
<string name="applock__description_create_pin">Create a PIN to secure this application</string>
|
||||
<string name="applock__description_create_pin_reattempt">PINs didn\'t match. Try again</string>
|
||||
<string name="applock__description_create_fingerprint">Scan your fingerprint to secure this application</string>
|
||||
<string name="applock__description_create_fingerprint_paused">Fingerprint authentication canceled. Reload this content to restart</string>
|
||||
<string name="applock__description_confirm">Re-enter your PIN to confirm</string>
|
||||
<string name="applock__description_unlock_pin">Enter your PIN to unlock</string>
|
||||
<string name="applock__description_unlock_fingerprint">Scan your fingerprint to unlock</string>
|
||||
<string name="applock__description_unlock_fingerprint_help">We\'re having trouble recognizing your fingerprint: %1$s</string>
|
||||
<string name="applock__create_chooser_option_pin">PIN</string>
|
||||
<string name="applock__create_chooser_option_fingerprint">Fingerprint</string>
|
||||
<string name="applock__unlock_error_no_matching_pin_found">There is no saved PIN to unlock</string>
|
||||
<string name="applock__unlock_error_match_failed">The PINs don\'t match!</string>
|
||||
<string name="applock__unlock_error_insufficient_selection">Wrong number of digits! Please try again</string>
|
||||
<string name="applock__unlock_error_retry_limit_exceeded">You failed too many times. You may try again in %1$s</string>
|
||||
<string name="applock__toast_unlock_required">You can\'t do that</string>
|
||||
<string name="applock__password_char">*</string>
|
||||
<string name="applock__action_settings">Open System Settings</string>
|
||||
<string name="applock__fingerprint_alias">AppLock</string>
|
||||
<string name="applock__fingerprint_error_not_enrolled">You haven\'t setup fingerprint unlocking</string>
|
||||
<string name="applock__fingerprint_error_unrecognized">Your fingerprint wasn\'t recognized</string>
|
||||
<string name="applock__fingerprint_error_unknown">Something weird happened</string>
|
||||
<string name="applock__fingerprint_error_none">No fingerprints found! Enable them in your system settings</string>
|
||||
<string name="applock__fingerprint_error_hardware">Missing fingerprint hardware?</string>
|
||||
<string name="applock__fingerprint_error_permission">Fingerprint permission required</string>
|
||||
<string name="applock__fingerprint_error_permission_multiple">Fingerprint permission denied. Please enable fingerprint support in your system settings</string>
|
||||
<item name="applock__empty_item_min_size_percent" format="float" type="string">0.2</item>
|
||||
|
||||
<string name="pin_not_set">PIN not set</string>
|
||||
<string name="pin_reset">PIN has been successfully reset</string>
|
||||
|
||||
<string name="failed_to_get_text">Failed to get text</string>
|
||||
<string name="error_no_text">Error receiving text, possibly no text found</string>
|
||||
<string name="applying_accent">Applying accent</string>
|
||||
@@ -6555,33 +6586,4 @@ May look like a fake leaving the online"</string>
|
||||
|
||||
<string name="invertcached_title">Invert list</string>
|
||||
<string name="invertcached_summ">Invert cached track list</string>
|
||||
|
||||
<string name="sett_recommendations">Recommendations</string>
|
||||
<string name="sett_nogms_summ">Missing these services causes background notifications to break and application components to have problems working</string>
|
||||
<string name="sett_missing_links">There is no links selected to be opened by the application</string>
|
||||
<string name="sett_missing_links_summ">This will prevent external links from being opened by this app to open them\n\nIn some cases it is necessary to disable the opening of links to official VKontakte applications</string>
|
||||
<string name="sett_battery">Turn off power management</string>
|
||||
<string name="sett_battery_summ1">Battery savings prevents background messages, notifications and music from functioning properly</string>
|
||||
<string name="sett_battery_summ2">\n\nFor Xiaomi devices, you need to manually enable Autorun to this application in the system settings</string>
|
||||
<string name="sett_notifs">Turn on notifications</string>
|
||||
<string name="sett_notifs_summ">Without notifications enabled, you won\'t be able to receive messages and many others things</string>
|
||||
<string name="sett_chats">Our chats</string>
|
||||
<string name="sett_chats_summ">Live chats to discuss modification and more</string>
|
||||
<string name="sett_community">Our communities</string>
|
||||
<string name="sett_community_summ">Latest information about updates and more</string>
|
||||
<string name="sett_autoupdates">Autoupdate</string>
|
||||
<string name="sett_autoupdates_summ">Receive notification when a new version of the application is released</string>
|
||||
<string name="update_latest">You have the latest version of the app installed</string>
|
||||
<string name="update_err">Error checking for updates</string>
|
||||
<string name="serverFeaturesDisable">Disable all server processing</string>
|
||||
<string name="serverFeaturesDisable_summ">Disables verifications and all additional functions related to receiving data from the VTosters server</string>
|
||||
|
||||
<string name="tgs_token_missing">Token is missing</string>
|
||||
<string name="tgs_token_reset_dialog">Reset the bot token?</string>
|
||||
<string name="tgs_token_reset">The bot token has been reset</string>
|
||||
<string name="tgs_token_reset_menu">Reset the bot token</string>
|
||||
|
||||
<string name="microg_dialog1">Reduced MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog2">Alternate MicroG (ReVanced)</string>
|
||||
<string name="microg_dialog3">Full MicroG (GMS Replacement)</string>
|
||||
</resources>
|
||||
|
||||
@@ -10262,4 +10262,47 @@
|
||||
<item name="android:shadowDy">-1.0</item>
|
||||
<item name="android:shadowRadius">0.25</item>
|
||||
</style>
|
||||
|
||||
<style name="applock__content_parent">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:gravity">center_horizontal</item>
|
||||
<item name="android:orientation">vertical</item>
|
||||
<item name="android:paddingLeft">@dimen/applock__content_padding_horizontal</item>
|
||||
<item name="android:paddingTop">@dimen/applock__content_padding_vertical</item>
|
||||
<item name="android:paddingRight">@dimen/applock__content_padding_horizontal</item>
|
||||
<item name="android:paddingBottom">@dimen/applock__content_padding_vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="applock__description">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:text">These is some kind of instructions you should follow.</item>
|
||||
<item name="android:textColor">?text_primary</item>
|
||||
<item name="android:textSize">@dimen/applock__description_text</item>
|
||||
</style>
|
||||
|
||||
<style name="applock__input_pin_view">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">@dimen/applock__item_size</item>
|
||||
<item name="android:layout_marginTop">@dimen/applock__item_margin_top</item>
|
||||
</style>
|
||||
|
||||
<style name="applock__input_fingerprint_view">
|
||||
<item name="android:layout_width">@dimen/applock__fingerprint_scan_image_size</item>
|
||||
<item name="android:layout_height">@dimen/applock__fingerprint_scan_image_size</item>
|
||||
<item name="android:layout_marginTop">@dimen/applock__fingerprint_scan_image_margin_top</item>
|
||||
</style>
|
||||
|
||||
<style name="applock__action_settings">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginTop">@dimen/applock__action_settings_margin_top</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:padding">@dimen/applock__action_settings_padding</item>
|
||||
<item name="android:text">@string/applock__action_settings</item>
|
||||
<item name="android:textColor">?text_primary</item>
|
||||
<item name="android:textSize">@dimen/applock__action_settings</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
android:key="listen_v"
|
||||
android:title="@string/listen_v_title"/>
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory android:title="@string/vkme_online_vtl" android:key="onlinefixcategory">
|
||||
<PreferenceCategory android:title="@string/vkme_online_vtl">
|
||||
<com.vtosters.lite.ui.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="onlinefix"
|
||||
|
||||
@@ -94,11 +94,6 @@
|
||||
android:key="pollresults"
|
||||
android:summary="@string/pollresults_summ"
|
||||
android:title="@string/pollresults_title"/>
|
||||
<com.vtosters.lite.ui.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="disableForceTabletMode"
|
||||
android:summary="Отключает интерфейс для планшетов и возвращает докбар"
|
||||
android:title="Отключить режим планшета"/>
|
||||
<com.vtosters.lite.ui.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="friendsblock"
|
||||
|
||||
@@ -45,11 +45,6 @@
|
||||
android:entryValues="@array/musicdefcatalog"
|
||||
android:key="musicdefcatalog"
|
||||
android:title="@string/musicdefcatalog_title"/>
|
||||
<com.vtosters.lite.ui.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="setMetaData"
|
||||
android:summary="Сохранять id3v2 теги для песен для MP3 (Экспериментально)"
|
||||
android:title="Сохранять метадату песен"/>
|
||||
<Preference
|
||||
android:key="audio_download"
|
||||
android:title="@string/audio_download_title"/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user