Performance improvements and cache invalidation fix
* Combining assets.zip and res.zip for better performance * Fix apk path on cache invalidation
This commit is contained in:
@@ -2,14 +2,23 @@ package ru.vtosters.lite.hooks;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import b.h.g.k.VKProgressDialog;
|
||||
import com.vk.core.dialogs.alert.VkAlertDialog;
|
||||
import com.vtosters.lite.R;
|
||||
import ru.vtosters.lite.concurrent.VTExecutors;
|
||||
import ru.vtosters.lite.downloaders.notifications.NotificationChannels;
|
||||
import ru.vtosters.lite.ssfs.UsersList;
|
||||
import ru.vtosters.lite.themes.ThemesManager;
|
||||
import ru.vtosters.lite.ui.dialogs.DisableBattery;
|
||||
import ru.vtosters.lite.ui.dialogs.InstallGMS;
|
||||
import ru.vtosters.lite.ui.dialogs.OTADialog;
|
||||
import ru.vtosters.lite.ui.dialogs.Start;
|
||||
import ru.vtosters.lite.utils.DeletedMessagesHandler;
|
||||
import ru.vtosters.lite.utils.LifecycleUtils;
|
||||
import ru.vtosters.lite.utils.Preferences;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static ru.vtosters.lite.ui.dialogs.ServerDialog.sendRequest;
|
||||
import static ru.vtosters.lite.utils.CacheUtils.getInstance;
|
||||
@@ -39,5 +48,64 @@ public class MainActivityInjector {
|
||||
}
|
||||
|
||||
// VKIDProtection.alert(activity);
|
||||
|
||||
if(Preferences.isNewBuild() && !ThemesUtils.isMonetTheme() && ThemesManager.hasTmpArchive()) {
|
||||
Preferences.updateBuildNumber();
|
||||
updateBinsAndTmpArchive(activity, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateBinsAndTmpArchive(Activity activity, boolean applyAccent) {
|
||||
final VKProgressDialog dialog = new VKProgressDialog(activity);
|
||||
|
||||
String msg;
|
||||
Runnable task;
|
||||
if(ThemesManager.hasBins() && !applyAccent) {
|
||||
msg = "Инвалидация кэша";
|
||||
task = () -> {
|
||||
try {
|
||||
ThemesManager.deleteBins();
|
||||
ThemesManager.generateBins();
|
||||
activity.runOnUiThread(() -> {
|
||||
dialog.cancel();
|
||||
updateBinsAndTmpArchive(activity, true);
|
||||
});
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
activity.runOnUiThread(() -> {
|
||||
dialog.cancel();
|
||||
showErrorDialog(activity, "Ошибка при инвалидации кэша");
|
||||
});
|
||||
}
|
||||
};
|
||||
} else {
|
||||
msg = "Применение акцента";
|
||||
task = () -> {
|
||||
try {
|
||||
if(ThemesManager.hasTmpArchive()) ThemesManager.deleteTmpArchive();
|
||||
ThemesManager.generateTempResArchive(ThemesUtils.getReservedAccent());
|
||||
activity.runOnUiThread(LifecycleUtils::restartApplication);
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
activity.runOnUiThread(() -> {
|
||||
dialog.cancel();
|
||||
showErrorDialog(activity, "Ошибка при применении акцента");
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
dialog.setMessage(msg);
|
||||
dialog.show();
|
||||
|
||||
VTExecutors.getIoExecutor().execute(task);
|
||||
}
|
||||
|
||||
static void showErrorDialog(Activity activity, String msg) {
|
||||
new VkAlertDialog.Builder(activity)
|
||||
.setTitle("Ошикба")
|
||||
.setMessage(msg)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,10 @@ import com.vtosters.lite.R;
|
||||
import ru.vtosters.lite.themes.loaders.ResourcesLoader;
|
||||
import ru.vtosters.lite.themes.utils.ArscEditor;
|
||||
import ru.vtosters.lite.utils.IOUtils;
|
||||
import ru.vtosters.lite.utils.Preferences;
|
||||
import ru.vtosters.lite.utils.ThemesUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
@@ -95,11 +93,8 @@ public class ThemesManager {
|
||||
R.color.darker_blue,
|
||||
R.color.header_blue_opacity40
|
||||
};
|
||||
private final static String[] NAMES = {
|
||||
"resources.arsc",
|
||||
"res",
|
||||
"assets"
|
||||
};
|
||||
|
||||
private static String baseApkPath;
|
||||
|
||||
// Directories
|
||||
private static File mainDir;
|
||||
@@ -111,10 +106,7 @@ public class ThemesManager {
|
||||
// Temp archive with resources.arsc and res.zip content for loading resources
|
||||
private static File resTmpZipFile = null;
|
||||
|
||||
// Archive with assets folder content for loading resources
|
||||
private static File assetsZipFile = null;
|
||||
|
||||
// Archive with res folder content
|
||||
// Archive with res and assets folders content
|
||||
private static File resZipFile = null;
|
||||
|
||||
// Resources table file
|
||||
@@ -125,9 +117,9 @@ public class ThemesManager {
|
||||
Log.d("ThemesManager", "init paths");
|
||||
initPaths(app);
|
||||
|
||||
if (hasTmpArchive() && !ThemesUtils.isMonetTheme()) {
|
||||
if (!Preferences.isNewBuild() && !ThemesUtils.isMonetTheme() && hasBins() && hasTmpArchive()) {
|
||||
ResourcesLoader.init(app);
|
||||
ResourcesLoader.load(app, resTmpZipFile.getAbsolutePath(), assetsZipFile.getAbsolutePath(), false);
|
||||
ResourcesLoader.load(app, resTmpZipFile.getAbsolutePath(), false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("ThemesManager", String.valueOf(e));
|
||||
@@ -135,62 +127,46 @@ public class ThemesManager {
|
||||
}
|
||||
|
||||
private static void initPaths(Context context) {
|
||||
baseApkPath = context.getApplicationInfo().sourceDir;
|
||||
|
||||
mainDir = new File(context.getFilesDir(), "VTLThemes");
|
||||
if (!mainDir.exists()) mainDir.mkdir();
|
||||
|
||||
binDir = new File(mainDir, "bin");
|
||||
if (!binDir.exists()) mainDir.mkdir();
|
||||
if (!binDir.exists()) binDir.mkdir();
|
||||
|
||||
palettesDir = new File(mainDir, "palettes");
|
||||
if (!palettesDir.exists()) palettesDir.mkdir();
|
||||
|
||||
assetsZipFile = new File(binDir, "assets.zip");
|
||||
resZipFile = new File(binDir, "res.zip");
|
||||
|
||||
resourcesArscFile = new File(binDir, "resources.arsc");
|
||||
resTmpZipFile = new File(binDir, "res.tmp.zip");
|
||||
}
|
||||
|
||||
public static void generateBins(Context context)
|
||||
public static void generateBins()
|
||||
throws IOException {
|
||||
try (final var apk = new ZipFile(context.getApplicationInfo().sourceDir)) {
|
||||
for (var filename : NAMES) {
|
||||
final var bin = new File(binDir, filename);
|
||||
if (!bin.exists())
|
||||
if (filename.contains(".")) extractFile(apk, filename, bin);
|
||||
else extractAndCompressDirectory(apk, filename, new File(binDir, filename + ".zip"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void extractFile(ZipFile apk, String name, File out)
|
||||
throws IOException {
|
||||
final var is = apk.getInputStream(apk.getEntry(name));
|
||||
if (!out.exists()) out.getParentFile().mkdirs();
|
||||
IOUtils.copy(is, out);
|
||||
}
|
||||
|
||||
private static void extractAndCompressDirectory(ZipFile apk, String dirName, File out)
|
||||
throws IOException {
|
||||
final var zos = new ZipOutputStream(new FileOutputStream(out));
|
||||
try (zos) {
|
||||
final var apk = new ZipFile(baseApkPath);
|
||||
final var zos = new ZipOutputStream(new FileOutputStream(resZipFile));
|
||||
try(apk;zos) {
|
||||
final var entries = apk.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
while(entries.hasMoreElements()) {
|
||||
final var entry = entries.nextElement();
|
||||
if (!entry.getName().startsWith(dirName + "/")) continue;
|
||||
|
||||
zos.putNextEntry(new ZipEntry(entry.getName()));
|
||||
|
||||
final var zis = apk.getInputStream(entry);
|
||||
IOUtils.copy(zis, zos);
|
||||
|
||||
zis.close();
|
||||
zos.closeEntry();
|
||||
final var name = entry.getName();
|
||||
if(name.equals("resources.arsc")) {
|
||||
if (!resourcesArscFile.exists()) resourcesArscFile.getParentFile().mkdirs();
|
||||
IOUtils.copy(apk.getInputStream(entry), resourcesArscFile);
|
||||
} else if(name.startsWith("assets/") || name.startsWith("res/")) {
|
||||
zos.putNextEntry(new ZipEntry(name));
|
||||
IOUtils.copy(apk.getInputStream(entry), zos);
|
||||
zos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasBins() {
|
||||
return assetsZipFile.exists() && resZipFile.exists() && resourcesArscFile.exists();
|
||||
return resZipFile.exists() && resourcesArscFile.exists();
|
||||
}
|
||||
|
||||
public static boolean hasTmpArchive() {
|
||||
@@ -227,7 +203,6 @@ public class ThemesManager {
|
||||
}
|
||||
|
||||
public static void deleteBins() {
|
||||
assetsZipFile.delete();
|
||||
resZipFile.delete();
|
||||
resourcesArscFile.delete();
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class ResourcesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(Context context, String resPath, String assetsPath, boolean reinject)
|
||||
public static void load(Context context, String resPath, boolean reinject)
|
||||
throws Exception {
|
||||
final ApplicationInfo appInfo = context.getApplicationInfo();
|
||||
|
||||
@@ -136,8 +136,7 @@ public class ResourcesLoader {
|
||||
|
||||
sAssetManager = AssetManager.class.newInstance();
|
||||
// Create a new AssetManager instance and point it to the resources installed under
|
||||
if (AssetManagerHelper.addAssetPath(sAssetManager, resPath) == 0
|
||||
|| AssetManagerHelper.addAssetPath(sAssetManager, assetsPath) == 0)
|
||||
if (AssetManagerHelper.addAssetPath(sAssetManager, resPath) == 0)
|
||||
throw new IllegalStateException("Could not create new AssetManager");
|
||||
|
||||
// Add SharedLibraries to AssetManager for resolve system resources not found issue
|
||||
@@ -192,10 +191,10 @@ public class ResourcesLoader {
|
||||
}
|
||||
}
|
||||
|
||||
installResourceInsuranceHacks(context, resPath, assetsPath);
|
||||
installResourceInsuranceHacks(context, resPath);
|
||||
}
|
||||
|
||||
private static void installResourceInsuranceHacks(Context context, String patchedResApkPath, String patchedAssetsApkPath) {
|
||||
private static void installResourceInsuranceHacks(Context context, String patchedResApkPath) {
|
||||
try {
|
||||
final Object activityThread = ReflectionUtils.getActivityThread(context, null);
|
||||
final Field mHField = ReflectionUtils.findField(activityThread, "mH");
|
||||
@@ -204,7 +203,7 @@ public class ResourcesLoader {
|
||||
final Handler.Callback originCallback = (Handler.Callback) mCallbackField.get(mH);
|
||||
if (!(originCallback instanceof ResourceInsuranceHandlerCallback)) {
|
||||
final ResourceInsuranceHandlerCallback hackCallback = new ResourceInsuranceHandlerCallback(
|
||||
context, patchedResApkPath, patchedAssetsApkPath, originCallback, mH.getClass());
|
||||
context, patchedResApkPath, originCallback, mH.getClass());
|
||||
mCallbackField.set(mH, hackCallback);
|
||||
} else {
|
||||
Log.w("ResourcesLoader", "installResourceInsuranceHacks: already installed, skip rest logic.");
|
||||
@@ -274,7 +273,6 @@ public class ResourcesLoader {
|
||||
|
||||
private final Context mContext;
|
||||
private final String mPatchResApkPath;
|
||||
private final String mPatchedAssetsApkPath;
|
||||
private final Handler.Callback mOriginalCallback;
|
||||
|
||||
private final int LAUNCH_ACTIVITY;
|
||||
@@ -284,11 +282,10 @@ public class ResourcesLoader {
|
||||
private Method mGetCallbacksMethod = null;
|
||||
private boolean mSkipInterceptExecuteTransaction = false;
|
||||
|
||||
ResourceInsuranceHandlerCallback(Context context, String patchResApkPath, String patchedAssetsApkPath, Handler.Callback original, Class<?> hClazz) {
|
||||
ResourceInsuranceHandlerCallback(Context context, String patchResApkPath, Handler.Callback original, Class<?> hClazz) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
mContext = (appContext != null ? appContext : context);
|
||||
mPatchResApkPath = patchResApkPath;
|
||||
mPatchedAssetsApkPath = patchedAssetsApkPath;
|
||||
mOriginalCallback = original;
|
||||
LAUNCH_ACTIVITY = fetchMessageId(hClazz, "LAUNCH_ACTIVITY", 100);
|
||||
RELAUNCH_ACTIVITY = fetchMessageId(hClazz, "RELAUNCH_ACTIVITY", 126);
|
||||
@@ -365,7 +362,7 @@ public class ResourcesLoader {
|
||||
}
|
||||
if (shouldReInjectPatchedResources) {
|
||||
try {
|
||||
load(mContext, mPatchResApkPath, mPatchedAssetsApkPath, true);
|
||||
load(mContext, mPatchResApkPath, true);
|
||||
} catch (Throwable thr) {
|
||||
thr.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -58,17 +58,8 @@ public class ThemesFragment extends MaterialPreferenceToolbarFragment {
|
||||
|
||||
var invalidateThemeCache = findPreference("invalidate_theme_cache");
|
||||
invalidateThemeCache.setOnPreferenceClickListener(preference -> {
|
||||
mProgressDialog.setMessage("Инвалидация кэша...");
|
||||
mProgressDialog.show();
|
||||
VTExecutors.getIoExecutor().execute(() -> {
|
||||
ThemesManager.deleteBins();
|
||||
try {
|
||||
ThemesManager.generateBins(requireContext());
|
||||
requireActivity().runOnUiThread(this::restart);
|
||||
} catch(IOException e) {
|
||||
requireActivity().runOnUiThread(() -> showErrorDialog("Ошибка при инвалидации кэша"));
|
||||
}
|
||||
});
|
||||
ThemesManager.deleteBins();
|
||||
setAccentColor(ThemesUtils.getReservedAccent(), true);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -193,7 +184,7 @@ public class ThemesFragment extends MaterialPreferenceToolbarFragment {
|
||||
alertDialog.setButton(
|
||||
DialogInterface.BUTTON_POSITIVE,
|
||||
requireContext().getString(R.string.select),
|
||||
(dialog, which) -> setAccentColor(colorPickerView.getColor())
|
||||
(dialog, which) -> setAccentColor(colorPickerView.getColor(), false)
|
||||
);
|
||||
alertDialog.setView(colorPickerView);
|
||||
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
|
||||
@@ -210,38 +201,39 @@ public class ThemesFragment extends MaterialPreferenceToolbarFragment {
|
||||
.setTitle(AndroidUtils.getString("select_palette"))
|
||||
.setItems(titles, (dialog, which) ->
|
||||
PalettesBottomSheetDialog.create(requireActivity(), manager.getPalette(which),
|
||||
(adapter, vtlcolor) -> setAccentColor(vtlcolor.color))
|
||||
(adapter, vtlcolor) -> setAccentColor(vtlcolor.color, false))
|
||||
)
|
||||
.setPositiveButton(R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
void setAccentColor(int color) {
|
||||
void setAccentColor(int color, boolean invalidate) {
|
||||
String message;
|
||||
Runnable task;
|
||||
if(!ThemesManager.hasBins()) {
|
||||
message = "Генерация файлов...";
|
||||
message = invalidate ? "Инвалидация кэша" : "Генерация файлов...";
|
||||
task = () -> {
|
||||
try {
|
||||
ThemesManager.generateBins(requireContext());
|
||||
requireActivity().runOnUiThread(() -> setAccentColor(color));
|
||||
ThemesManager.generateBins();
|
||||
requireActivity().runOnUiThread(() -> setAccentColor(color, invalidate));
|
||||
} catch(IOException e) {
|
||||
Log.e("ThemesFragment", e+"");
|
||||
ThemesManager.deleteBins();
|
||||
requireActivity().runOnUiThread(() -> showErrorDialog("Ошибка при генерации файлов"));
|
||||
requireActivity().runOnUiThread(() -> showErrorDialog("Ошибка при " + (invalidate ? "инвалидации кэша" : "генерации файлов")));
|
||||
}
|
||||
};
|
||||
} else {
|
||||
message = "Применение акцента...";
|
||||
message = invalidate ? "Инвалидация кэша" : "Применение акцента...";
|
||||
task = () -> {
|
||||
try {
|
||||
if(!invalidate) ThemesUtils.reserveAccentColor(color);
|
||||
ThemesManager.generateTempResArchive(color);
|
||||
requireActivity().runOnUiThread(this::restart);
|
||||
} catch(IOException e) {
|
||||
Log.e("ThemesFragment", e+"");
|
||||
ThemesManager.deleteTmpArchive();
|
||||
requireActivity().runOnUiThread(() -> showErrorDialog("Ошибка при применении акцента"));
|
||||
requireActivity().runOnUiThread(() -> showErrorDialog("Ошибка при " + (invalidate ? "инвалидации кэша" : "применении акцента")));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,6 +291,15 @@ public class Preferences {
|
||||
return !getBoolValue("isRoamingState", false) && isValidSignature() && (!dev() || getBoolValue("autoupdates", true));
|
||||
}
|
||||
|
||||
public static boolean isNewBuild() {
|
||||
return !preferences.getString("build_number", About.getBuildNumber())
|
||||
.equals(About.getBuildNumber());
|
||||
}
|
||||
|
||||
public static void updateBuildNumber() {
|
||||
preferences.edit().putString("build_number", About.getBuildNumber()).apply();
|
||||
}
|
||||
|
||||
public static boolean isValidSignature() {
|
||||
try {
|
||||
return validateAppSignature();
|
||||
|
||||
@@ -101,6 +101,18 @@ public class ThemesUtils {
|
||||
return getColorFromAttr(R.attr.accent);
|
||||
} // Color accent
|
||||
|
||||
//region Used for migrating accent color to new version
|
||||
|
||||
public static int getReservedAccent() {
|
||||
return AndroidUtils.getPreferences().getInt("reserved_accent_color", getAccentColor());
|
||||
}
|
||||
|
||||
public static void reserveAccentColor(int accent) {
|
||||
AndroidUtils.getPreferences().edit().putInt("reserved_accent_color", accent).apply();
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
public static int getMutedAccentColor() {
|
||||
return getMutedColor(getAccentColor());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user