222 lines
7.7 KiB
Java
222 lines
7.7 KiB
Java
package ru.vtosters.lite.themes;
|
|
|
|
import android.app.Application;
|
|
import android.content.Context;
|
|
import android.graphics.Color;
|
|
import android.util.Log;
|
|
import com.google.devrel.gmscore.tools.apk.arsc.BinaryResourceFile;
|
|
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.*;
|
|
import java.util.zip.CRC32;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipFile;
|
|
import java.util.zip.ZipOutputStream;
|
|
|
|
public class ThemesManager {
|
|
|
|
private final static int[] ACCENT_COLORS = {
|
|
R.color.vk_sky_300,
|
|
R.color.sky_300,
|
|
R.color.viewer_retry_button_text_color,
|
|
R.color.azure_300,
|
|
R.color.picker_camera_button,
|
|
R.color.music_check_button_bg_pressed,
|
|
R.color.notification_color,
|
|
R.color.music_text_counter,
|
|
R.color.music_follow_button_bg_pressed,
|
|
R.color.music_blue_button_normal,
|
|
R.color.music_check_button_bg_normal,
|
|
R.color.vk_terms_link,
|
|
R.color.vk_blue_200,
|
|
R.color.accent_blue,
|
|
R.color.azure_A400,
|
|
R.color.vk_azure_A400,
|
|
R.color.blue,
|
|
R.color.blue_200,
|
|
R.color.vk_blue_300,
|
|
R.color.vk_blue_400,
|
|
R.color.blue_300,
|
|
R.color.blue_400,
|
|
R.color.cool_blue,
|
|
R.color.cornflower_blue,
|
|
R.color.cornflower_blue_two,
|
|
R.color.dot_unread,
|
|
R.color.fave_promo_btn_pressed,
|
|
R.color.header_blue,
|
|
R.color.colorAccent,
|
|
R.color.live_emoji_butt_hide,
|
|
R.color.music_action_button_blue,
|
|
R.color.name,
|
|
R.color.picker_blue,
|
|
R.color.picker_blue_pressed,
|
|
R.color.picker_tab_bg_selected,
|
|
R.color.picker_tab_text_selected,
|
|
R.color.sharing_blue_btn_normal,
|
|
R.color.sharing_blue_btn_pressed,
|
|
R.color.vk_sky_300,
|
|
R.color.vk_blue_A400,
|
|
R.color.blue_A400,
|
|
R.color.sky_300,
|
|
R.color.text_blue,
|
|
R.color.tip_background,
|
|
R.color.tw__blue_default,
|
|
R.color.tw__blue_pressed,
|
|
R.color.vkim_msg_sending_ic,
|
|
R.color.vkim_playing_drawable_rect,
|
|
R.color.vk_white_blue32,
|
|
R.color.date_picker_selector,
|
|
R.color.post_suggest_blue,
|
|
R.color.list_dialog_blue,
|
|
R.color.music_tab_bg_normal,
|
|
R.color.toolbar_blue_background,
|
|
R.color.toolbar_blue_statusBarColorBack,
|
|
R.color.blue_200_muted,
|
|
R.color.vk_blue_200_muted,
|
|
R.color.white_blue32,
|
|
R.color.vk_white_blue32,
|
|
R.color.header_blue_opacity16,
|
|
R.color.music_check_button_bg_checked,
|
|
R.color.attachpicker_item_background,
|
|
R.color.music_placeholder_artist_bg,
|
|
R.color.azure_100_muted,
|
|
R.color.vk_azure_100_muted,
|
|
R.color.muted_blue,
|
|
R.color.blue_600,
|
|
R.color.light_blue,
|
|
R.color.vk_blue_600,
|
|
R.color.muted_blue_opacity40,
|
|
R.color.muted_blue_old,
|
|
R.color.darker_blue,
|
|
R.color.header_blue_opacity40
|
|
};
|
|
// modify it if "doNotCompress" in apktool.yml has been edited
|
|
private final static String[] DO_NOT_COMPRESS = {
|
|
".png",
|
|
".jpg",
|
|
".mp3",
|
|
"res/raw/keep_cronet_api.xml"
|
|
};
|
|
|
|
private static String baseApkPath;
|
|
|
|
private static boolean validated = false;
|
|
|
|
// Directories
|
|
private static File mainDir;
|
|
private static File colorSchemesDir;
|
|
|
|
// Modified archive with resources.arsc, other resources and assets
|
|
private static File modApk;
|
|
|
|
public static void init(Application app) {
|
|
try {
|
|
initPaths(app);
|
|
validateModApk();
|
|
|
|
if (!Preferences.isNewBuild() && !ThemesUtils.isMonetTheme() && canApplyCustomAccent() && ThemesUtils.useNewColorEngine()) {
|
|
ResourcesLoader.init(app);
|
|
ResourcesLoader.load(app, modApk.getAbsolutePath(), false);
|
|
}
|
|
} catch (Exception e) {
|
|
Log.e("ThemesManager", e.getMessage());
|
|
}
|
|
}
|
|
|
|
// validate modded apk before loading
|
|
private static void validateModApk()
|
|
throws IOException {
|
|
final var apk = new ZipFile(modApk);
|
|
apk.close();
|
|
|
|
validated = true;
|
|
}
|
|
|
|
private static void initPaths(Context context) {
|
|
baseApkPath = context.getApplicationInfo().publicSourceDir;
|
|
|
|
mainDir = new File(context.getFilesDir(), "VTLThemes");
|
|
if (!mainDir.exists()) mainDir.mkdir();
|
|
|
|
colorSchemesDir = new File(mainDir, "color_schemes");
|
|
if (!colorSchemesDir.exists()) colorSchemesDir.mkdir();
|
|
|
|
modApk = new File(mainDir, "mod.apk");
|
|
}
|
|
|
|
public static boolean canApplyCustomAccent() {
|
|
return validated && modApk.exists() && ThemesUtils.getReservedAccent() != Color.TRANSPARENT;
|
|
}
|
|
|
|
public static void generateModApk(int accentColor)
|
|
throws Throwable {
|
|
try (
|
|
final var apk = new ZipFile(baseApkPath)
|
|
) {
|
|
final var arscEntry = apk.getEntry("resources.arsc");
|
|
final var arscBis = new BufferedInputStream(apk.getInputStream(arscEntry));
|
|
final var arsc = BinaryResourceFile.fromInputStream(arscBis);
|
|
if (!ArscEditor.changeColors(arsc, ACCENT_COLORS, accentColor))
|
|
throw new IllegalStateException("Not all colors have been changed");
|
|
|
|
try (
|
|
final var zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(modApk)))
|
|
) {
|
|
final var arscBuff = arsc.toByteArray();
|
|
final var newArscEntry = new ZipEntry("resources.arsc");
|
|
|
|
final var crc = new CRC32();
|
|
crc.update(arscBuff);
|
|
|
|
newArscEntry.setSize(arscBuff.length);
|
|
newArscEntry.setMethod(ZipEntry.STORED);
|
|
newArscEntry.setCrc(crc.getValue());
|
|
|
|
zos.putNextEntry(newArscEntry);
|
|
zos.write(arscBuff);
|
|
zos.closeEntry();
|
|
|
|
final var entries = apk.entries();
|
|
while (entries.hasMoreElements()) {
|
|
final var entry = entries.nextElement();
|
|
final var name = entry.getName();
|
|
|
|
if (!name.startsWith("res/") && !name.startsWith("assets/") || name.equals("resources.arsc"))
|
|
continue;
|
|
|
|
var doNotCompress = false;
|
|
for (var suffix : DO_NOT_COMPRESS) {
|
|
if (name.endsWith(suffix)) {
|
|
doNotCompress = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
final var entryBis = new BufferedInputStream(apk.getInputStream(entry));
|
|
final var newEntry = new ZipEntry(name);
|
|
|
|
if (doNotCompress) {
|
|
newEntry.setMethod(ZipEntry.STORED);
|
|
newEntry.setSize(entry.getSize());
|
|
newEntry.setCrc(entry.getCrc());
|
|
} else newEntry.setMethod(ZipEntry.DEFLATED);
|
|
|
|
zos.putNextEntry(newEntry);
|
|
IOUtils.copy(entryBis, zos);
|
|
entryBis.close();
|
|
zos.closeEntry();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void deleteModification() {
|
|
modApk.delete();
|
|
}
|
|
}
|