Compare commits

..
Author SHA1 Message Date
gdlbo 44fa23020e fixed double pin 2023-09-25 18:19:05 +03:00
gdlbo 4cdcf9aee0 Update R.java 2023-09-25 16:37:31 +03:00
gdlbo 63c5262444 conflicts fixes 2023-09-25 15:45:33 +03:00
gdlbo 9074e80478 Merge branch 'main' into feat/app_pin 2023-09-25 15:26:21 +03:00
gdlbo 13fab86b83 Merge branch 'main' into feat/app_pin 2023-03-25 22:08:20 +03:00
k1zn 5ada5d74d2 /ru/ add time localization 2023-03-12 19:25:06 +05:00
k1zn aa1802c371 /ru/ localization 2023-03-12 19:17:19 +05:00
aliveoutside 0cf1c6ecba Merge remote-tracking branch 'origin/feat/app_pin' into feat/app_pin 2023-03-12 13:38:48 +03:00
gdlbo f051420ff9 upgraded pin-code settings 2023-03-07 19:56:53 +03:00
gdlbo 3902a40e30 Update R.java 2023-03-07 19:16:38 +03:00
aliveoutside 0c97f15088 pincode code cleanup 2023-03-05 17:46:39 +03:00
aliveoutside 4cf112921b open keyboard automatically if fingerprint prompt was dismissed 2023-03-05 16:49:58 +03:00
aliveoutside 6e31b26af0 remove pincode time setting and set the default timer to 24 hrs 2023-03-05 16:15:32 +03:00
gdlbo 6f22b5dd03 pin number limits 2023-03-03 20:22:05 +03:00
gdlbo 9ee1bd0f79 Update UnlockActivity.java 2023-03-03 16:39:06 +03:00
gdlbo 56b11bc43c fixed size of pin 2023-03-03 16:22:44 +03:00
gdlbo a476d490a6 added something
idk lol lmao dead
2023-03-02 19:15:12 +03:00
gdlbo 74b50fb9c2 reset pin 2023-03-02 13:08:47 +03:00
gdlbo ddaedd0ba0 Improved screen locking system + improved lock display 2023-03-02 12:53:46 +03:00
gdlbo 729342085a compiling fix 2023-03-02 12:07:56 +03:00
gdlbo bb20178dc9 Merge branch 'main' into feat/app_pin 2023-03-02 11:59:41 +03:00
aliveoutside 306cee1fd4 Merge branch 'main' into feat/app_pin 2023-02-20 21:10:07 +03:00
aliveoutside 02e8732e9f init PIN Lock 2023-02-20 21:02:28 +03:00
106 changed files with 6452 additions and 1325 deletions
+2 -2
View File
@@ -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'
+2 -1
View File
@@ -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;
+87 -35
View File
@@ -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();
@@ -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;
}
}
+9
View File
@@ -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
View File
@@ -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>
+20
View File
@@ -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>
+3 -34
View File
@@ -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>
+34 -33
View File
@@ -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>
+3 -33
View File
@@ -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>
+4
View File
@@ -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>
+3
View File
@@ -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>
+11
View File
@@ -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>
+16
View File
@@ -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>
+4
View File
@@ -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>
+5
View File
@@ -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>
+36 -34
View File
@@ -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>
+43
View File
@@ -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>
+1 -1
View File
@@ -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"
-5
View File
@@ -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"
-5
View File
@@ -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