Refactor wp (#944)

* Extract couple filters to separate classes

* Move ImageEffects to separate file

* Refactor effects to use bitmap

* Reformat code, extract last batch of effects to classes

* Shitty intermediate refactor

* Still intermediate sh**, buildable btw

* More refactoring, still looks disgusting, but a bit better)

* Add fixme about order

* Remove unused stuff

* Remove unused code, prevent re-creating of drawable

* Refactor img effects to native

* Add native lib sources

* Implement Dim, Mosaic and Blur in native

* Add pre-built libs

* Get rid of filter abstraction

* Include native build

* Force rust toolchains

* Fix typos

* Comment not used smali

* Simplify wallpapers handling

* Fixes after main merge

* Minor fixes, add prebuilt lib, disable auto re-builds for native

* Fix missed copyscpec for native builds

* Fix entry for dim effect

* Fix resizing to respect current image dimensions

* Wtf? who stole NDK from actions

* Learning GH actions

I don't want to read the docs

* Revert "Learning GH actions"

This reverts commit f6f9a97d10.

* Revert "Wtf? who stole NDK from actions"

This reverts commit 1d0f1b8879.

* Try out latest LTS just why not

---------

Co-authored-by: saoq <wfmfe@ya.ru>
This commit is contained in:
Saoqq
2023-02-21 09:20:35 +03:00
committed by GitHub
co-authored by saoq
parent 0d8c52548f
commit a45e6a7f2a
16 changed files with 1013 additions and 718 deletions
+2
View File
@@ -37,6 +37,8 @@ jobs:
sudo apt install zipalign
chmod +x scripts/apktool.jar
chmod +x scripts/apksigner.jar
rustup target add armv7-linux-androideabi
rustup target add aarch64-linux-android
- name: Build VTLite
run: |
+23 -1
View File
@@ -7,6 +7,16 @@ import java.util.zip.ZipInputStream
plugins {
id 'com.android.application'
id 'net.nemerosa.versioning' version '2.15.1'
id 'org.mozilla.rust-android-gradle.rust-android'
}
cargo {
profile = 'release'
module = '../native-effects'
libname = 'native-effects'
targets = ["arm", "arm64"]
prebuiltToolchains = true
pythonCommand = "python3"
}
def smaliFolder = projectDir.toPath().resolve("../smali").normalize()
@@ -100,6 +110,7 @@ android {
targetCompatibility JavaVersion.VERSION_14
}
namespace 'ru.vtosters.lite'
ndkVersion '25.2.9519653'
}
dependencies {
@@ -112,6 +123,8 @@ dependencies {
task prepareDexForMerge(type: Copy) {
dependsOn('exportDex')
// Remove comment for native lib build
dependsOn('cargoBuild')
into smaliFolder.toString()
from(projectDir) {
include 'classes6.dex'
@@ -119,7 +132,16 @@ task prepareDexForMerge(type: Copy) {
}
from(projectDir) {
include 'version.properties'
into("assets")
into 'assets'
}
// Remove comment for native lib build
from('../native-effects/target/aarch64-linux-android/release'){
include 'libnative_effects.so'
into 'lib/arm64-v8a'
}
from('../native-effects/target/armv7-linux-androideabi/release'){
include 'libnative_effects.so'
into 'lib/armeabi-v7a'
}
doLast {
new File(projectDir, "classes6.dex").delete()
@@ -0,0 +1,245 @@
package ru.vtosters.lite.ui.wallpapers;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.vtosters.lite.R;
import ru.vtosters.lite.utils.AndroidUtils;
import java.nio.ByteBuffer;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
enum ImageEffects {
Blur(true,
AndroidUtils.getArray(R.array.filter_types),
new String[]{"disabled", "low", "med", "high"},
getGlobalContext().getString(R.string.filter_blur)
) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
int radius;
switch (getPreferences().getString(ImageEffects.Blur.toString(), "disabled")) {
case "low":
radius = 8;
break;
case "med":
radius = 14;
break;
case "high":
radius = 20;
break;
default:
return;
}
NativeEffects.gaussian(bitmap, height, width, radius);
}
@Override
public String getSummary() {
switch (getPreferences().getString(ImageEffects.Blur.toString(), "disabled")) {
case "low":
return AndroidUtils.getString(R.string.wallpapers_low);
case "med":
return AndroidUtils.getString(R.string.wallpapers_med);
case "high":
return AndroidUtils.getString(R.string.wallpapers_high);
default:
return AndroidUtils.getString(R.string.wallpapers_disabled);
}
}
},
Dim(true,
AndroidUtils.getArray(R.array.filter_dim_types),
new String[]{"disabled", "dim_black", "dim_white"},
getGlobalContext().getString(R.string.filter_dim)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
var delta = 0;
switch (getPreferences().getString(ImageEffects.Dim.toString(), "disabled")) {
case "dim_black":
delta = -50; break;
case "dim_white":
delta = 50; break;
default:
return;
}
NativeEffects.dim(bitmap, height, width, delta);
}
@Override
public String getSummary() {
switch (getPreferences().getString(ImageEffects.Dim.toString(), "disabled")) {
case "dim_black":
return AndroidUtils.getString(R.string.wallpapers_dim_black);
case "dim_white":
return AndroidUtils.getString(R.string.wallpapers_dim_white);
default:
return AndroidUtils.getString(R.string.wallpapers_disabled);
}
}
},
Mosaic(true,
AndroidUtils.getArray(R.array.filter_types),
new String[]{"disabled", "low", "med", "high"},
getGlobalContext().getString(R.string.filter_mosaic)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
var scale = 0;
switch (getPreferences().getString(ImageEffects.Mosaic.toString(), "disabled")) {
case "high":
scale = 50; break;
case "med":
scale = 25; break;
case "low":
scale = 10; break;
default:
return;
}
NativeEffects.mosaic(bitmap, height, width, scale);
}
@Override
public String getSummary() {
switch (getPreferences().getString(ImageEffects.Mosaic.toString(), "disabled")) {
case "low":
return AndroidUtils.getString(R.string.wallpapers_low);
case "med":
return AndroidUtils.getString(R.string.wallpapers_med);
case "high":
return AndroidUtils.getString(R.string.wallpapers_high);
default:
return AndroidUtils.getString(R.string.wallpapers_disabled);
}
}
},
Monochrome(false, null, null,
getGlobalContext().getString(R.string.filter_monochrome)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()) {
NativeEffects.monochrome(bitmap);
}
}
},
Invert(false, null, null,
getGlobalContext().getString(R.string.filter_invert_colors)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()) {
NativeEffects.invert(bitmap);
}
}
},
Sepia(false, null, null,
getGlobalContext().getString(R.string.filter_sepia)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()) {
NativeEffects.sepia(bitmap);
}
}
},
Emboss(false, null, null,
getGlobalContext().getString(R.string.filter_emboss)) {
@Override
public String getSummary() {
return getGlobalContext().getString(R.string.filter_maybe_lag);
}
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()) {
NativeEffects.emboss(bitmap, height, width);
}
}
},
Engrave(false, null, null,
getGlobalContext().getString(R.string.filter_engrave)) {
@Override
public String getSummary() {
return getGlobalContext().getString(R.string.filter_maybe_lag);
}
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()) {
NativeEffects.engrave(bitmap, height, width);
}
}
},
Flea(false, null, null,
getGlobalContext().getString(R.string.filter_flea)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()) {
NativeEffects.flea(bitmap);
}
}
},
Snow(false, null, null,
getGlobalContext().getString(R.string.filter_snow)) {
@Override
public void applyEffect(ByteBuffer bitmap, int height, int width) {
if (isApplied()){
NativeEffects.snow(bitmap);
}
}
};
private final boolean free;
private final String title;
@Nullable
private final String[] entryValues;
@Nullable
private final String[] entries;
ImageEffects(boolean free, @Nullable String[] entries, @Nullable String[] entryValues, String title) {
this.free = free;
this.entries = entries;
this.entryValues = entryValues;
this.title = title;
}
public String getTitle() {
return title;
}
public Boolean isSwitch() {
return entryValues == null || entryValues.length == 0;
}
public Boolean isList() {
return entryValues != null && entryValues.length > 0;
}
public boolean isFree() {
return free;
}
@Nullable
public String[] getEntries() {
return entries;
}
@Nullable
public String[] getEntryValues() {
return entryValues;
}
public String getSummary() {
return "";
}
public abstract void applyEffect(ByteBuffer bitmap, int height, int width);
protected boolean isApplied() {
return getPreferences().getBoolean(this.toString(), false);
}
@NonNull
@Override
public String toString() {
return "msg_" + super.toString();
}
}
@@ -1,351 +1,37 @@
package ru.vtosters.lite.ui.wallpapers;
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
import static ru.vtosters.lite.utils.AndroidUtils.getResources;
import static ru.vtosters.lite.utils.Preferences.hasVerification;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import com.vk.medianative.MediaNative;
import java.util.Random;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class ImageFilters {
public static final int COLOR_MAX = 0xFF;
public static Drawable getFilteredDrawable(Drawable orig) {
if (orig == null) return null;
if (!(orig instanceof BitmapDrawable)) return orig;
if (!getPreferences().getString("msg_blur_radius", "disabled").equals("disabled")) {
orig = getBlurredWallpaper(orig, getRadius());
Bitmap bitmap = ((BitmapDrawable) orig).getBitmap();
if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) {
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
}
if (!getPreferences().getString("msg_dim", "off").equals("off")) {
orig = getDimmed(orig);
var directBuff = ByteBuffer.allocateDirect(bitmap.getByteCount());
directBuff.order(ByteOrder.nativeOrder());
bitmap.copyPixelsToBuffer(directBuff);
// FIXME: preserve order of filters
// e.g. user turned invert first then mosaic then monochrome
// invocation here happens regardless of that order
for (var type : ImageEffects.values()) {
type.applyEffect(directBuff, bitmap.getHeight(), bitmap.getWidth());
}
if (!getPreferences().getString("msg_mosaic", "disabled").equals("disabled")) {
orig = getMosaic(orig);
}
if (!hasVerification()) {
return orig;
}
if (getPreferences().getBoolean("msg_monochrome", false)) {
orig = getMonochrome(orig);
}
Bitmap bm;
if (orig != null) {
bm = ((BitmapDrawable) orig).getBitmap();
} else {
return null;
}
Bitmap instance = bm.copy(Bitmap.Config.ARGB_8888, true);
if (getPreferences().getBoolean("msg_invert", false)) {
instance = applyInvertEffect(instance);
}
if (getPreferences().getBoolean("msg_sepia", false)) {
instance = applySepiaEffect(instance);
}
if (getPreferences().getBoolean("msg_emboss", false)) {
instance = applyEmbossEffect(instance);
}
if (getPreferences().getBoolean("msg_engrave", false)) {
instance = applyEngraveEffect(instance);
}
if (getPreferences().getBoolean("msg_flea", false)) {
instance = applyFleaEffect(instance);
}
if (getPreferences().getBoolean("msg_snow", false)) {
instance = applySnowEffect(instance);
}
return new BitmapDrawable(getResources(), instance);
}
private static int getRadius() {
String radius = getPreferences().getString("msg_blur_radius", "disabled");
return switch (radius) {
case "low" -> 8;
case "med" -> 14;
default -> 20;
};
}
public static Drawable getBlurredWallpaper(Drawable orig, int radius) {
try {
if (orig == null) return null;
if (!(orig instanceof BitmapDrawable)) return orig; // we need only BitmapDrawable
Bitmap bm = ((BitmapDrawable) orig).getBitmap();
Bitmap instance = bm.copy(Bitmap.Config.ARGB_8888, true);
MediaNative.blurBitmap(instance, radius);
return new BitmapDrawable(getResources(), instance);
} catch (Exception e) {
e.printStackTrace();
return orig;
}
}
public static Drawable getMosaic(Drawable orig) {
int scale = 100;
if (orig == null) return null;
if (!(orig instanceof BitmapDrawable)) return orig;
Bitmap bm = ((BitmapDrawable) orig).getBitmap();
Bitmap instance = bm.copy(Bitmap.Config.ARGB_8888, true);
String radius = getPreferences().getString("msg_mosaic", "disabled");
switch (radius) {
case "high" -> scale = 25;
case "med" -> scale = 50;
case "low" -> scale = 75;
}
Bitmap temp = Bitmap.createScaledBitmap(instance, scale, scale, false);
Bitmap mosaicBitmap = Bitmap.createScaledBitmap(temp, instance.getWidth(), instance.getHeight(), false);
return new BitmapDrawable(getResources(), mosaicBitmap);
}
public static Drawable getDimmed(Drawable orig) {
if (orig == null) return null;
if (!(orig instanceof BitmapDrawable)) return orig;
Bitmap bm = ((BitmapDrawable) orig).getBitmap();
Bitmap instance = bm.copy(Bitmap.Config.ARGB_8888, true);
String radius = getPreferences().getString("msg_dim", "disabled");
switch (radius) {
case "dim_black":
darken(instance);
case "dim_white":
lighten(instance);
}
return new BitmapDrawable(getResources(), instance);
}
public static Drawable getMonochrome(Drawable orig) {
if (orig == null) return null;
if (!(orig instanceof BitmapDrawable)) return orig;
Bitmap bm = ((BitmapDrawable) orig).getBitmap();
Bitmap instance = bm.copy(Bitmap.Config.ARGB_8888, true);
Bitmap bwBitmap = Bitmap.createBitmap(instance.getWidth(), instance.getHeight(), Bitmap.Config.RGB_565);
float[] hsv = new float[3];
for (int col = 0; col < instance.getWidth(); col++) {
for (int row = 0; row < instance.getHeight(); row++) {
Color.colorToHSV(instance.getPixel(col, row), hsv);
if (hsv[2] > 0.5f) {
bwBitmap.setPixel(col, row, 0xffffffff);
} else {
bwBitmap.setPixel(col, row, 0xff000000);
}
}
}
return new BitmapDrawable(getResources(), bwBitmap);
}
private static void darken(Bitmap bm) {
try {
Canvas canvas = new Canvas(bm);
Paint p = new Paint(Color.RED);
ColorFilter filter = new LightingColorFilter(0xFF7F7F7F, 0x00000000); // darken
p.setColorFilter(filter);
canvas.drawBitmap(bm, new Matrix(), p);
} catch (Exception e) {
Log.d("WallpaperD", e.getMessage());
}
}
private static void lighten(Bitmap bm) {
try {
Canvas canvas = new Canvas(bm);
Paint p = new Paint(Color.RED);
ColorFilter filter = new LightingColorFilter(0xFFFFFFFF, 0x00222222); // lighten
p.setColorFilter(filter);
canvas.drawBitmap(bm, new Matrix(), p);
} catch (Exception e) {
Log.d("WallpaperL", e.getMessage());
}
}
public static Bitmap applyInvertEffect(Bitmap src) {
// create new bitmap with the same settings as source bitmap
Bitmap bmOut = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig());
// color info
int A, R, G, B;
int pixelColor;
// image size
int height = src.getHeight();
int width = src.getWidth();
// scan through every pixel
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// get one pixel
pixelColor = src.getPixel(x, y);
// saving alpha channel
A = Color.alpha(pixelColor);
// inverting byte for each R/G/B channel
R = 255 - Color.red(pixelColor);
G = 255 - Color.green(pixelColor);
B = 255 - Color.blue(pixelColor);
// set newly-inverted pixel to output image
bmOut.setPixel(x, y, Color.argb(A, R, G, B));
}
}
// return final bitmap
return bmOut;
}
public static Bitmap applySepiaEffect(Bitmap src) {
// image size
int width = src.getWidth();
int height = src.getHeight();
// create output bitmap
Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
// constant grayscale
final double GS_RED = 0.3;
final double GS_GREEN = 0.59;
final double GS_BLUE = 0.11;
// color information
int A, R, G, B;
int pixel;
// scan through all pixels
for (int x = 0; x < width; ++x) {
for (int y = 0; y < height; ++y) {
// get pixel color
pixel = src.getPixel(x, y);
// get color on each channel
A = Color.alpha(pixel);
R = Color.red(pixel);
G = Color.green(pixel);
B = Color.blue(pixel);
// apply grayscale sample
B = (int) (GS_RED * R + GS_GREEN * G + GS_BLUE * B);
// apply intensity level for sepia tone
R = G = B = (int) (B * 0.9);
// set new pixel color to output bitmap
bmOut.setPixel(x, y, Color.argb(A, R, G, B));
}
}
// return final image
return bmOut;
}
public static Bitmap applyEmbossEffect(Bitmap src) {
double[][] EmbossConfig = new double[][]{
{-1, 0, -1},
{0, 4, 0},
{-1, 0, -1}
};
ConvolutionMatrix convMatrix = new ConvolutionMatrix(3);
convMatrix.applyConfig(EmbossConfig);
convMatrix.Factor = 1;
convMatrix.Offset = 127;
return ConvolutionMatrix.computeConvolution3x3(src, convMatrix);
}
public static Bitmap applyEngraveEffect(Bitmap src) {
ConvolutionMatrix convMatrix = new ConvolutionMatrix(3);
convMatrix.setAll(0);
convMatrix.Matrix[0][0] = -2;
convMatrix.Matrix[1][1] = 2;
convMatrix.Factor = 1;
convMatrix.Offset = 95;
return ConvolutionMatrix.computeConvolution3x3(src, convMatrix);
}
public static Bitmap applyFleaEffect(Bitmap source) {
// get image size
int width = source.getWidth();
int height = source.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
source.getPixels(pixels, 0, width, 0, 0, width, height);
// a random object
Random random = new Random();
int index;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// get current index in 2D-matrix
index = y * width + x;
// get random color
int randColor = Color.rgb(random.nextInt(COLOR_MAX),
random.nextInt(COLOR_MAX), random.nextInt(COLOR_MAX));
// OR
pixels[index] |= randColor;
}
}
// output bitmap
Bitmap bmOut = Bitmap.createBitmap(width, height, source.getConfig());
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}
public static Bitmap applySnowEffect(Bitmap source) {
// get image size
int width = source.getWidth();
int height = source.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
source.getPixels(pixels, 0, width, 0, 0, width, height);
// random object
Random random = new Random();
int R, G, B, index, thresHold;
// iteration through pixels
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
// get current index in 2D-matrix
index = y * width + x;
// get color
R = Color.red(pixels[index]);
G = Color.green(pixels[index]);
B = Color.blue(pixels[index]);
// generate threshold
thresHold = random.nextInt(COLOR_MAX);
if (R > thresHold && G > thresHold && B > thresHold) {
pixels[index] = Color.rgb(COLOR_MAX, COLOR_MAX, COLOR_MAX);
}
}
}
// output bitmap
Bitmap bmOut = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
directBuff.flip();
bitmap.copyPixelsFromBuffer(directBuff);
return new BitmapDrawable(getResources(), bitmap);
}
}
@@ -0,0 +1,22 @@
package ru.vtosters.lite.ui.wallpapers;
import java.nio.ByteBuffer;
public class NativeEffects {
static {
System.loadLibrary("native_effects");
}
public static native String monochrome(ByteBuffer input);
public static native String invert(ByteBuffer input);
public static native String sepia(ByteBuffer input);
public static native String emboss(ByteBuffer input, int height, int width);
public static native String engrave(ByteBuffer input, int height, int width);
public static native String flea(ByteBuffer input);
public static native String snow(ByteBuffer input);
// FIXME: slow when comparing with MediaNative.blurBitmap
public static native String gaussian(ByteBuffer input, int height, int width, float radius);
public static native String dim(ByteBuffer input, int height, int width, int delta);
public static native String mosaic(ByteBuffer input, int height, int width, int scale);
}
@@ -7,17 +7,16 @@ import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.preference.ListPreference;
import com.vtosters.lite.R;
import com.vtosters.lite.general.fragments.MaterialPreferenceToolbarFragment;
import com.vtosters.lite.im.ImEngineProvider;
import ru.vtosters.lite.ui.PreferenceFragmentUtils;
import ru.vtosters.lite.utils.AndroidUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import static ru.vtosters.lite.ui.wallpapers.WallpapersHooks.*;
import static ru.vtosters.lite.utils.AndroidUtils.edit;
@@ -29,7 +28,6 @@ public class WallpaperMenuFragment extends MaterialPreferenceToolbarFragment {
private WallpaperPreferences mWPPreviewPref;
public static void copyStream(InputStream copyFrom, OutputStream copyTo) throws IOException {
byte[] buffer = new byte[1024];
int length;
while ((length = copyFrom.read(buffer)) > 0) {
@@ -47,146 +45,117 @@ public class WallpaperMenuFragment extends MaterialPreferenceToolbarFragment {
addPreferencesFromResource(R.xml.empty);
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.vkim_settings_appearance_chat_preview_title));
PreferenceFragmentUtils.addPreferenceCategory(
getPreferenceScreen(),
requireContext().getString(R.string.vkim_settings_appearance_chat_preview_title)
);
mWPPreviewPref = new WallpaperPreferences(getContext());
mWPPreviewPref.setIcon(null);
mWPPreviewPref.setIconSpaceReserved(false);
getPreferenceScreen().addPreference(mWPPreviewPref);
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "wp_set", requireContext().getString(R.string.wallpaper_select), requireContext().getString(R.string.wallpaper_from_gallery), R.drawable.ic_picture_outline_28, preference -> {
startActivityForResult(new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI), 1488);
removeStickCache();
return true;
});
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"wp_set",
requireContext().getString(R.string.wallpaper_select),
requireContext().getString(R.string.wallpaper_from_gallery),
R.drawable.ic_picture_outline_28,
preference -> {
startActivityForResult(
new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
),
1488
);
removeStickCache();
return true;
});
PreferenceFragmentUtils.addPreference(getPreferenceScreen(), "wp_clear", requireContext().getString(R.string.wallpaper_remove), "", R.drawable.ic_delete_outline_28, preference -> {
removeWallpaper();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
removeStickCache();
return true;
});
PreferenceFragmentUtils.addPreference(
getPreferenceScreen(),
"wp_clear",
requireContext().getString(R.string.wallpaper_remove),
"",
R.drawable.ic_delete_outline_28,
preference -> {
removeWallpaper();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
removeStickCache();
return true;
});
PreferenceFragmentUtils.addPreferenceCategory(getPreferenceScreen(), requireContext().getString(R.string.vtlfilters));
if (!hasVerification() && !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;
});
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;
});
}
PreferenceFragmentUtils.addListPreference(getPreferenceScreen(), "msg_blur_radius", "disabled", requireContext().getString(R.string.filter_blur), AndroidUtils.getArray(R.array.filter_types), new String[]{
"disabled", "low", "med", "high"
});
Arrays.stream(ImageEffects.values())
.filter(imageEffects -> imageEffects.isFree() || hasVerification())
.forEach(it -> {
if (it.isList()) {
var pref = PreferenceFragmentUtils.addListPreference(
getPreferenceScreen(),
it.toString(),
"disabled",
it.getTitle(),
it.getEntries(),
it.getEntryValues()
);
pref.setSummary(it.getSummary());
pref.setOnPreferenceChangeListener((preference, o) -> {
edit().putString(it.toString(), (String) o).apply();
preference.setSummary(it.getSummary());
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
} else if (it.isSwitch()) {
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
it.toString(),
it.getTitle(),
it.getSummary(),
0,
false,
(preference, o) -> {
edit().putBoolean(it.toString(), (Boolean) o).apply();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
}
});
PreferenceFragmentUtils.addListPreference(getPreferenceScreen(), "msg_dim", "off", requireContext().getString(R.string.filter_dim), AndroidUtils.getArray(R.array.filter_dim_types), new String[]{
"off", "dim_black", "dim_white"
});
PreferenceFragmentUtils.addListPreference(getPreferenceScreen(), "msg_mosaic", "disabled", requireContext().getString(R.string.filter_mosaic), AndroidUtils.getArray(R.array.filter_types), new String[]{
"disabled", "low", "med", "high"
});
if (hasVerification()) {
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_monochrome", requireContext().getString(R.string.filter_monochrome), "", 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_monochrome", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_invert", requireContext().getString(R.string.filter_invert_colors), "", 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_invert", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_sepia", requireContext().getString(R.string.filter_sepia), "", 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_sepia", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_emboss", requireContext().getString(R.string.filter_emboss), requireContext().getString(R.string.filter_maybe_lag), 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_emboss", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_engrave", requireContext().getString(R.string.filter_engrave), requireContext().getString(R.string.filter_maybe_lag), 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_engrave", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_flea", requireContext().getString(R.string.filter_flea), "", 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_flea", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "msg_snow", requireContext().getString(R.string.filter_flea), "", 0, false, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("msg_snow", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
}
PreferenceFragmentUtils.addMaterialSwitchPreference(getPreferenceScreen(), "compresswp", requireContext().getString(R.string.compress_wallpaper_title), requireContext().getString(R.string.compress_wallpaper_summ), 0, true, (preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("compresswp", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
ListPreference blur = (ListPreference) findPreference("msg_blur_radius");
ListPreference dim = (ListPreference) findPreference("msg_dim");
ListPreference mosaic = (ListPreference) findPreference("msg_mosaic");
blur.setSummary(getRadiusSummary());
dim.setSummary(getDimmingSummary());
mosaic.setSummary(getMosaicSummary());
blur.setOnPreferenceChangeListener((preference, o) -> {
edit().putString("msg_blur_radius", (String) o).apply();
preference.setSummary(getRadiusSummary());
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
dim.setOnPreferenceChangeListener((preference, o) -> {
edit().putString("msg_dim", (String) o).apply();
preference.setSummary(getDimmingSummary());
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
mosaic.setOnPreferenceChangeListener((preference, o) -> {
edit().putString("msg_mosaic", (String) o).apply();
preference.setSummary(getMosaicSummary());
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
PreferenceFragmentUtils.addMaterialSwitchPreference(
getPreferenceScreen(),
"compresswp",
requireContext().getString(R.string.compress_wallpaper_title),
requireContext().getString(R.string.compress_wallpaper_summ),
0,
true,
(preference, o) -> {
boolean value = (boolean) o;
edit().putBoolean("compresswp", value).commit();
requestUpdateWallpaper();
mWPPreviewPref.redraw();
return true;
});
}
@Override
@@ -1,7 +1,6 @@
package ru.vtosters.lite.ui.wallpapers;
import static ru.vtosters.lite.ui.wallpapers.WallpapersHooks.getFilteredFile;
import static ru.vtosters.lite.ui.wallpapers.WallpapersHooks.getWallpaperFile;
import static ru.vtosters.lite.ui.wallpapers.WallpapersHooks.*;
import static ru.vtosters.lite.utils.AndroidUtils.dp2px;
import android.annotation.SuppressLint;
@@ -59,12 +58,8 @@ public class WallpaperPreferences extends Preference {
}
private void requestBg() {
final Drawable drawable = getFilteredFile();
if (drawable != null && getWallpaperFile() != null)
mChatBackground.setImageDrawable(drawable);
else
mChatBackground.setImageDrawable(null);
final Drawable drawable = getWallpaper();
mChatBackground.setImageDrawable(drawable);
}
@SuppressLint("NotifyDataSetChanged")
@@ -1,78 +1,97 @@
package ru.vtosters.lite.ui.wallpapers;
import static com.vk.im.engine.h.im_bg_chat;
import static ru.vtosters.lite.ui.wallpapers.ImageFilters.getFilteredDrawable;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.getPreferences;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import com.vtosters.lite.R;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import ru.vtosters.lite.utils.AndroidUtils;
import ru.vtosters.lite.utils.ThemesUtils;
import vigo.sdk.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.util.function.Supplier;
import static com.vk.im.engine.h.im_bg_chat;
import static ru.vtosters.lite.ui.wallpapers.ImageFilters.getFilteredDrawable;
import static ru.vtosters.lite.utils.AndroidUtils.getGlobalContext;
import static ru.vtosters.lite.utils.AndroidUtils.sendToast;
import static ru.vtosters.lite.utils.Preferences.getBoolValue;
public class WallpapersHooks {
private static final boolean compress = getBoolValue("compresswp", true);
private static File mWallpaperFile;
// TODO: force compress by default not asking user about that?
private static final Supplier<Boolean> compress = () -> getBoolValue("compresswp", true);
private static final File originalWp = new File(getGlobalContext().getFilesDir(), "wallpaper.jpeg");
private static final File compressedWp = new File(getGlobalContext().getFilesDir(), "compressedwp.jpeg");
// TODO: cache to file?
private static Drawable mWallpaper;
private static boolean mUpdateWallpaperRequested = true;
private static boolean mUpdateWallpaperFileRequested = true;
public static File getWallpaperFile() {
if (mWallpaperFile == null) {
mWallpaperFile = new File(getGlobalContext().getFilesDir(), "wallpaper.webp");
// TODO: respect blur, when there is blur/mosaic effect applied these two can be even less, just HD (1280x720) for e.g.
private static final int MAX_WP_WIDTH = 1080;
private static final int MAX_WP_HEIGHT = 1920;
public static void setBg(View view) {
if (hasWallpapers()) {
((ImageView) view).setImageDrawable(getWallpaper()); // set picture to background
} else {
view.setBackgroundColor(ThemesUtils.getColorFromAttr(im_bg_chat)); // set default bg color
}
return mWallpaperFile;
}
public static Drawable getFilteredFile() {
File file = null;
File wp = mWallpaperFile;
String temp = getGlobalContext().getFilesDir() + File.separator + "filteredwp.webp";
if (wp != null && mUpdateWallpaperFileRequested) {
try {
file = new File(temp);
file.createNewFile();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
((BitmapDrawable) getFilteredDrawable(Drawable.createFromPath(wp.getAbsolutePath()))).getBitmap().compress(Bitmap.CompressFormat.WEBP, (compress ? 80 : 95), bos);
byte[] bitmapdata = bos.toByteArray();
FileOutputStream fos = new FileOutputStream(file);
fos.write(bitmapdata);
fos.flush();
fos.close();
} catch (Exception e) {
Log.d("Wallpapers", e.getMessage());
}
mUpdateWallpaperFileRequested = false;
}
try {
return Drawable.createFromPath(temp);
} catch (Exception e) {
if (file != null) {
return Drawable.createFromPath(file.getAbsolutePath());
} else {
public static Drawable getWallpaper() {
if (mWallpaper == null || mUpdateWallpaperRequested) {
if (!originalWp.exists()) {
return null;
}
Drawable drawable;
if (compress.get()) {
if (!compressedWp.exists() && !prepareCompressed()) {
return null;
}
drawable = Drawable.createFromPath(compressedWp.getAbsolutePath());
} else {
drawable = Drawable.createFromPath(originalWp.getAbsolutePath());
}
if (drawable == null) {
return null;
}
mWallpaper = getFilteredDrawable(drawable);
mUpdateWallpaperRequested = false;
}
return mWallpaper;
}
private static boolean prepareCompressed() {
// TODO remove this check? with "compress" enabled it should be fine
if (!eligibleWallpaperFile(originalWp)) {
return false;
}
try (var f = new FileOutputStream(compressedWp)) {
var bitmap = BitmapFactory.decodeFile(originalWp.getAbsolutePath());
if (bitmap.getHeight() > MAX_WP_HEIGHT || bitmap.getWidth() > MAX_WP_WIDTH) {
bitmap = resize(bitmap, MAX_WP_WIDTH, MAX_WP_HEIGHT);
}
var drawable = new BitmapDrawable(null, bitmap);
// TODO: remove duplication without changes when provided image already fits required params
drawable.getBitmap().compress(Bitmap.CompressFormat.WEBP, 90, f);
} catch (Exception e) {
Log.d("Wallpapers", e.getMessage());
return false;
}
return true;
}
public static File getWallpaperFile() {
return originalWp;
}
private static Boolean eligibleWallpaperFile(File file) {
@@ -85,94 +104,40 @@ public class WallpapersHooks {
}
}
public static void setBg(View view) {
if (hasWallpapers()) {
((ImageView) view).setImageDrawable(getFilteredFile()); // set picture to background
} else {
view.setBackgroundColor(ThemesUtils.getColorFromAttr(im_bg_chat)); // set default bg color
}
}
public static String getWallpaperUrl() {
var file = getWallpaperFile();
if (file.exists() && eligibleWallpaperFile(file)) {
return file.getAbsolutePath();
}
return "default";
}
public static boolean hasWallpapers() {
return getWallpaper() != null;
}
public static Drawable getWallpaper() {
if (mWallpaper == null || mUpdateWallpaperRequested) {
String url = getWallpaperUrl();
if (url.equals("default") || url.isEmpty()) {
return null;
}
Drawable drawable = Drawable.createFromPath(getWallpaperUrl());
if (drawable == null) {
return null;
}
mWallpaper = drawable;
mWallpaper = getFilteredDrawable(mWallpaper);
mUpdateWallpaperRequested = false;
}
return mWallpaper;
}
public static void requestUpdateWallpaper() {
mUpdateWallpaperRequested = true;
mUpdateWallpaperFileRequested = true;
}
public static String getRadiusSummary() {
String radius = getPreferences().getString("msg_blur_radius", "disabled");
return switch (radius) {
case "low" -> AndroidUtils.getString(R.string.wallpapers_low);
case "med" -> AndroidUtils.getString(R.string.wallpapers_med);
case "high" -> AndroidUtils.getString(R.string.wallpapers_high);
default -> AndroidUtils.getString(R.string.wallpapers_disabled);
};
}
public static String getDimmingSummary() {
String radius = getPreferences().getString("msg_dim", "disabled");
return switch (radius) {
case "dim_black" -> AndroidUtils.getString(R.string.wallpapers_dim_black);
case "dim_white" -> AndroidUtils.getString(R.string.wallpapers_dim_white);
default -> AndroidUtils.getString(R.string.wallpapers_disabled);
};
}
public static String getMosaicSummary() {
String radius = getPreferences().getString("msg_mosaic", "disabled");
return switch (radius) {
case "low" -> AndroidUtils.getString(R.string.wallpapers_low);
case "med" -> AndroidUtils.getString(R.string.wallpapers_med);
case "high" -> AndroidUtils.getString(R.string.wallpapers_high);
default -> AndroidUtils.getString(R.string.wallpapers_disabled);
};
}
public static void removeWallpaper() {
try {
new File(getGlobalContext().getFilesDir(), "wallpaper.webp").delete();
new File(getGlobalContext().getFilesDir(), "filteredwp.webp").delete();
mWallpaperFile = null;
mWallpaper = null;
originalWp.delete();
compressedWp.delete();
} catch (Exception e) {
Log.d("Wallpapers", e.getMessage());
}
}
private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) {
if (maxHeight > 0 && maxWidth > 0) {
int width = image.getWidth();
int height = image.getHeight();
float ratioBitmap = (float) width / (float) height;
float ratioMax = (float) maxWidth / (float) maxHeight;
int finalWidth = maxWidth;
int finalHeight = maxHeight;
if (ratioMax > ratioBitmap) {
finalWidth = (int) ((float) maxHeight * ratioBitmap);
} else {
finalHeight = (int) ((float) maxWidth / ratioBitmap);
}
image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
}
return image;
}
}
+4
View File
@@ -3,9 +3,13 @@ buildscript {
repositories {
google()
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'org.mozilla.rust-android-gradle:plugin:0.9.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
+2
View File
@@ -0,0 +1,2 @@
/target
Cargo.lock
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "native-effects"
version = "0.1.0"
edition = "2021"
[profile.release]
opt-level = 3
strip = true
lto = true
codegen-units = 1
[lib]
crate-type = ["cdylib"]
[dependencies]
jni = "0.20.0"
rand = "0.8.5"
image = "0.24.5"
+365
View File
@@ -0,0 +1,365 @@
use jni::objects::JByteBuffer;
use jni::objects::JClass;
use jni::sys::jfloat;
use jni::sys::jint;
use jni::sys::jstring;
use jni::JNIEnv;
use rand::Rng;
use std::cmp::max;
// We assume that input ByteBuffer is always in ARGB_8888 format
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
impl Pixel {
fn set_color(&mut self, r: u8, g: u8, b: u8) {
self.r = r;
self.g = g;
self.b = b;
}
fn invert(&mut self) {
self.r = 255 - self.r;
self.g = 255 - self.g;
self.b = 255 - self.b;
}
}
fn resolve_pixel_slice<'a>(env: &JNIEnv<'a>, bitmap_buffer: &JByteBuffer<'a>) -> &'a mut [Pixel] {
let start = env
.get_direct_buffer_address(*bitmap_buffer)
.expect("Unable to resolve DirectBuffer address");
let capacity = env
.get_direct_buffer_capacity(*bitmap_buffer)
.expect("Unable to resolve DirectBuffer size");
let s = start as *mut Pixel;
unsafe { std::slice::from_raw_parts_mut(s, capacity / 4) }
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_monochrome(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
) -> jstring {
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
pixels.iter_mut().for_each(|p| {
// value (V) from HSV
let value = max(p.r, max(p.g, p.b));
if value > 127 {
p.set_color(255, 255, 255);
} else {
p.set_color(0, 0, 0);
}
});
let output = env
.new_string("Applied Monochrome effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_invert(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
) -> jstring {
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
pixels.iter_mut().for_each(|p| p.invert());
let output = env
.new_string("Applied Invert effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_sepia(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
) -> jstring {
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
pixels.iter_mut().for_each(|p| {
let r = p.r as f32;
let g = p.g as f32;
let b = p.b as f32;
let new_red = 0.393 * r + 0.769 * g + 0.189 * b;
let new_green = 0.349 * r + 0.686 * g + 0.168 * b;
let new_blue = 0.272 * r + 0.534 * g + 0.131 * b;
p.set_color(new_red as u8, new_green as u8, new_blue as u8);
});
let output = env
.new_string("Applied Sepia effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_emboss(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
height: jint,
width: jint,
) -> jstring {
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
let kernel = [-1.0, 0.0, -1.0, 0.0, 4.0, 0.0, -1.0, 0.0, -1.0];
let out: &mut Vec<Pixel> = &mut Vec::new();
for (y, row) in pixels.chunks(width as usize).enumerate() {
for (x, pixel) in row.iter().enumerate() {
if y == 0 || x == 0 || y == (height - 1) as usize || x == (width - 1) as usize {
out.push(*pixel);
continue;
}
let start = y * width as usize + x - 1;
let end = start + 3;
let matrix = [
&pixels[start - width as usize..end - width as usize],
&pixels[start..end],
&pixels[start + width as usize..end + width as usize],
]
.concat();
let mut acc = [64.0, 64.0, 64.0];
for (p, k) in matrix.iter().zip(kernel.iter()) {
acc[0] += p.r as f32 * k;
acc[1] += p.g as f32 * k;
acc[2] += p.b as f32 * k;
}
out.push(Pixel {
r: acc[0] as u8,
g: acc[1] as u8,
b: acc[2] as u8,
a: pixel.a,
});
}
}
pixels.copy_from_slice(&out);
let output = env
.new_string("Applied Emboss effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_engrave(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
height: jint,
width: jint,
) -> jstring {
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
let kernel = [-2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0];
let out: &mut Vec<Pixel> = &mut Vec::new();
for (y, row) in pixels.chunks(width as usize).enumerate() {
for (x, pixel) in row.iter().enumerate() {
if y == 0 || x == 0 || y == (height - 1) as usize || x == (width - 1) as usize {
out.push(*pixel);
continue;
}
let start = y * width as usize + x - 1;
let end = start + 3;
let matrix = [
&pixels[start - width as usize..end - width as usize],
&pixels[start..end],
&pixels[start + width as usize..end + width as usize],
]
.concat();
let mut acc = [95.0, 95.0, 95.0];
for (p, k) in matrix.iter().zip(kernel.iter()) {
acc[0] += p.r as f32 * k;
acc[1] += p.g as f32 * k;
acc[2] += p.b as f32 * k;
}
out.push(Pixel {
r: acc[0] as u8,
g: acc[1] as u8,
b: acc[2] as u8,
a: pixel.a,
});
}
}
pixels.copy_from_slice(&out);
let output = env
.new_string("Applied Engrave effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_flea(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
) -> jstring {
let mut rng = rand::thread_rng();
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
for p in pixels {
p.set_color(
p.r | rng.gen::<u8>(),
p.g | rng.gen::<u8>(),
p.b | rng.gen::<u8>(),
)
}
let output = env
.new_string("Applied Flea effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_snow(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
) -> jstring {
let mut rng = rand::thread_rng();
let pixels = resolve_pixel_slice(&env, &bitmap_buffer);
for p in pixels {
let threshold: u8 = rng.gen();
if p.r > threshold && p.g > threshold && p.b > threshold {
p.set_color(255, 255, 255);
}
}
let output = env
.new_string("Applied Snow effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_gaussian(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
height: jint,
width: jint,
radius: jfloat,
) -> jstring {
let start = env
.get_direct_buffer_address(bitmap_buffer)
.expect("Unable to resolve DirectBuffer address");
let capacity = env
.get_direct_buffer_capacity(bitmap_buffer)
.expect("Unable to resolve DirectBuffer size");
let s = start as *mut u8;
let s = unsafe { std::slice::from_raw_parts_mut(s, capacity) };
let mut img = image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::from_raw(
width as u32,
height as u32,
s.to_vec(),
)
.unwrap();
let img = image::imageops::blur(&mut img, radius);
s.copy_from_slice(img.as_raw());
let output = env
.new_string("Applied Gaussian effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_dim(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
height: jint,
width: jint,
delta: jint,
) -> jstring {
let start = env
.get_direct_buffer_address(bitmap_buffer)
.expect("Unable to resolve DirectBuffer address");
let capacity = env
.get_direct_buffer_capacity(bitmap_buffer)
.expect("Unable to resolve DirectBuffer size");
let s = start as *mut u8;
let s = unsafe { std::slice::from_raw_parts_mut(s, capacity) };
let img = image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::from_raw(
width as u32,
height as u32,
s.to_vec(),
)
.unwrap();
let img = image::imageops::brighten(&img, delta);
s.copy_from_slice(img.as_raw());
let output = env
.new_string("Applied Dim effect!")
.expect("Couldn't create java string!");
return **output;
}
#[no_mangle]
pub extern "C" fn Java_ru_vtosters_lite_ui_wallpapers_NativeEffects_mosaic(
env: JNIEnv,
_: JClass,
bitmap_buffer: JByteBuffer,
height: jint,
width: jint,
scale: jint,
) -> jstring {
let start = env
.get_direct_buffer_address(bitmap_buffer)
.expect("Unable to resolve DirectBuffer address");
let capacity = env
.get_direct_buffer_capacity(bitmap_buffer)
.expect("Unable to resolve DirectBuffer size");
let s = start as *mut u8;
let s = unsafe { std::slice::from_raw_parts_mut(s, capacity) };
let mut img = image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::from_raw(
width as u32,
height as u32,
s.to_vec(),
)
.unwrap();
img = image::imageops::resize(
&img,
(width / scale) as u32,
(height / scale) as u32,
image::imageops::FilterType::Nearest,
);
img = image::imageops::resize(
&img,
width as u32,
height as u32,
image::imageops::FilterType::Nearest,
);
s.copy_from_slice(img.as_raw());
let output = env
.new_string("Applied Mosaic effect!")
.expect("Couldn't create java string!");
return **output;
}
Binary file not shown.
Binary file not shown.
@@ -424,38 +424,38 @@
return v0
.end method
.method public final c()Ljava/lang/String;
.locals 3
# .method public final c()Ljava/lang/String;
# .locals 3
invoke-static {}, Lru/vtosters/lite/ui/wallpapers/WallpapersHooks;->getWallpaperUrl()Ljava/lang/String;
# invoke-static {}, Lru/vtosters/lite/ui/wallpapers/WallpapersHooks;->getWallpaperUrl()Ljava/lang/String;
move-result-object v0
# move-result-object v0
return-object v0
# return-object v0
.line 1
invoke-direct {p0}, Lcom/vk/im/ui/ImUiPrefs;->k()Landroid/content/SharedPreferences;
# .line 1
# invoke-direct {p0}, Lcom/vk/im/ui/ImUiPrefs;->k()Landroid/content/SharedPreferences;
move-result-object v0
# move-result-object v0
const-string v1, "pref_dialog_background_uri"
# const-string v1, "pref_dialog_background_uri"
const-string v2, "default"
# const-string v2, "default"
invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
# invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
move-result-object v0
# move-result-object v0
if-eqz v0, :cond_0
# if-eqz v0, :cond_0
goto :goto_0
# goto :goto_0
:cond_0
const-string v0, ""
# :cond_0
# const-string v0, ""
:goto_0
return-object v0
.end method
# :goto_0
# return-object v0
# .end method
.method public final d()J
.locals 4
@@ -772,180 +772,180 @@
return-void
.line 1
sget-object v0, Lcom/vk/im/ui/ImUiPrefs;->g:Lcom/vk/im/ui/ImUiPrefs;
# .line 1
# sget-object v0, Lcom/vk/im/ui/ImUiPrefs;->g:Lcom/vk/im/ui/ImUiPrefs;
invoke-virtual {v0}, Lcom/vk/im/ui/ImUiPrefs;->c()Ljava/lang/String;
# invoke-virtual {v0}, Lcom/vk/im/ui/ImUiPrefs;->c()Ljava/lang/String;
move-result-object v0
# move-result-object v0
.line 2
invoke-interface {v0}, Ljava/lang/CharSequence;->length()I
# .line 2
# invoke-interface {v0}, Ljava/lang/CharSequence;->length()I
move-result v1
# move-result v1
const/4 v2, 0x1
# const/4 v2, 0x1
const/4 v3, 0x0
# const/4 v3, 0x0
if-nez v1, :cond_0
# if-nez v1, :cond_0
const/4 v1, 0x1
# const/4 v1, 0x1
goto :goto_0
# goto :goto_0
:cond_0
const/4 v1, 0x0
# :cond_0
# const/4 v1, 0x0
:goto_0
const-string v4, "msgListComponent"
# :goto_0
# const-string v4, "msgListComponent"
const-string v5, "dialogBackgroundView"
# const-string v5, "dialogBackgroundView"
const/4 v6, 0x0
# const/4 v6, 0x0
if-nez v1, :cond_6
# if-nez v1, :cond_6
const-string v1, "default"
# const-string v1, "default"
invoke-static {v0, v1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/Object;)Z
# invoke-static {v0, v1}, Lkotlin/jvm/internal/Intrinsics;->a(Ljava/lang/Object;Ljava/lang/Object;)Z
move-result v1
# move-result v1
if-nez v1, :cond_6
# if-nez v1, :cond_6
iget-object v1, p0, Lcom/vk/im/ui/fragments/ChatFragment;->E:Lcom/vk/im/engine/models/dialogs/DialogExt;
# iget-object v1, p0, Lcom/vk/im/ui/fragments/ChatFragment;->E:Lcom/vk/im/engine/models/dialogs/DialogExt;
if-eqz v1, :cond_5
# if-eqz v1, :cond_5
invoke-virtual {v1}, Lcom/vk/im/engine/models/dialogs/DialogExt;->t1()Lcom/vk/im/engine/models/dialogs/Dialog;
# invoke-virtual {v1}, Lcom/vk/im/engine/models/dialogs/DialogExt;->t1()Lcom/vk/im/engine/models/dialogs/Dialog;
move-result-object v1
# move-result-object v1
invoke-virtual {v1}, Lcom/vk/im/engine/models/dialogs/Dialog;->U1()Z
# invoke-virtual {v1}, Lcom/vk/im/engine/models/dialogs/Dialog;->U1()Z
move-result v1
# move-result v1
if-eqz v1, :cond_1
# if-eqz v1, :cond_1
goto :goto_1
# goto :goto_1
.line 3
:cond_1
iget-object v1, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
# .line 3
# :cond_1
# iget-object v1, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
if-eqz v1, :cond_4
# if-eqz v1, :cond_4
new-instance v3, Lcom/vk/im/engine/models/Image;
# new-instance v3, Lcom/vk/im/engine/models/Image;
invoke-direct {v3, v0}, Lcom/vk/im/engine/models/Image;-><init>(Ljava/lang/String;)V
# invoke-direct {v3, v0}, Lcom/vk/im/engine/models/Image;-><init>(Ljava/lang/String;)V
invoke-virtual {v1, v3}, Lcom/vk/im/ui/views/FrescoImageView;->setLocalImage(Lcom/vk/im/engine/models/Image;)V
# invoke-virtual {v1, v3}, Lcom/vk/im/ui/views/FrescoImageView;->setLocalImage(Lcom/vk/im/engine/models/Image;)V
.line 4
iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
# .line 4
# iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
if-eqz v0, :cond_3
# if-eqz v0, :cond_3
invoke-virtual {v0, v6}, Lcom/vk/im/ui/views/FrescoImageView;->setBackgroundImage(Landroid/graphics/drawable/Drawable;)V
# invoke-virtual {v0, v6}, Lcom/vk/im/ui/views/FrescoImageView;->setBackgroundImage(Landroid/graphics/drawable/Drawable;)V
.line 5
iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->d0:Lcom/vk/im/ui/components/msg_list/MsgListComponent;
# .line 5
# iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->d0:Lcom/vk/im/ui/components/msg_list/MsgListComponent;
if-eqz v0, :cond_2
# if-eqz v0, :cond_2
invoke-virtual {v0, v2}, Lcom/vk/im/ui/components/msg_list/MsgListComponent;->b(Z)V
# invoke-virtual {v0, v2}, Lcom/vk/im/ui/components/msg_list/MsgListComponent;->b(Z)V
goto :goto_2
# goto :goto_2
:cond_2
invoke-static {v4}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# :cond_2
# invoke-static {v4}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
.line 6
:cond_3
invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# .line 6
# :cond_3
# invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
.line 7
:cond_4
invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# .line 7
# :cond_4
# invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
:cond_5
const-string v0, "dialogExt"
# :cond_5
# const-string v0, "dialogExt"
.line 8
invoke-static {v0}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# .line 8
# invoke-static {v0}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
.line 9
:cond_6
:goto_1
iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
# .line 9
# :cond_6
# :goto_1
# iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
if-eqz v0, :cond_a
# if-eqz v0, :cond_a
invoke-virtual {v0, v6}, Lcom/vk/im/ui/views/FrescoImageView;->setLocalImage(Lcom/vk/im/engine/models/Image;)V
# invoke-virtual {v0, v6}, Lcom/vk/im/ui/views/FrescoImageView;->setLocalImage(Lcom/vk/im/engine/models/Image;)V
.line 10
iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
# .line 10
# iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->k0:Lcom/vk/im/ui/views/FrescoImageView;
if-eqz v0, :cond_9
# if-eqz v0, :cond_9
new-instance v1, Landroid/graphics/drawable/ColorDrawable;
# new-instance v1, Landroid/graphics/drawable/ColorDrawable;
iget-object v2, p0, Lcom/vk/im/ui/fragments/ChatFragment;->L:Landroid/content/Context;
# iget-object v2, p0, Lcom/vk/im/ui/fragments/ChatFragment;->L:Landroid/content/Context;
if-eqz v2, :cond_8
# if-eqz v2, :cond_8
sget v5, Lcom/vk/im/ui/c;->im_bg_chat:I
# sget v5, Lcom/vk/im/ui/c;->im_bg_chat:I
invoke-static {v2, v5}, Lcom/vk/core/util/ContextExtKt;->h(Landroid/content/Context;I)I
# invoke-static {v2, v5}, Lcom/vk/core/util/ContextExtKt;->h(Landroid/content/Context;I)I
move-result v2
# move-result v2
invoke-direct {v1, v2}, Landroid/graphics/drawable/ColorDrawable;-><init>(I)V
# invoke-direct {v1, v2}, Landroid/graphics/drawable/ColorDrawable;-><init>(I)V
invoke-virtual {v0, v1}, Lcom/vk/im/ui/views/FrescoImageView;->setBackgroundImage(Landroid/graphics/drawable/Drawable;)V
# invoke-virtual {v0, v1}, Lcom/vk/im/ui/views/FrescoImageView;->setBackgroundImage(Landroid/graphics/drawable/Drawable;)V
.line 11
iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->d0:Lcom/vk/im/ui/components/msg_list/MsgListComponent;
# .line 11
# iget-object v0, p0, Lcom/vk/im/ui/fragments/ChatFragment;->d0:Lcom/vk/im/ui/components/msg_list/MsgListComponent;
if-eqz v0, :cond_7
# if-eqz v0, :cond_7
invoke-virtual {v0, v3}, Lcom/vk/im/ui/components/msg_list/MsgListComponent;->b(Z)V
# invoke-virtual {v0, v3}, Lcom/vk/im/ui/components/msg_list/MsgListComponent;->b(Z)V
:goto_2
return-void
# :goto_2
# return-void
:cond_7
invoke-static {v4}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# :cond_7
# invoke-static {v4}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
:cond_8
const-string v0, "themedContext"
# :cond_8
# const-string v0, "themedContext"
.line 12
invoke-static {v0}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# .line 12
# invoke-static {v0}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
:cond_9
invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# :cond_9
# invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
.line 13
:cond_a
invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
# .line 13
# :cond_a
# invoke-static {v5}, Lkotlin/jvm/internal/Intrinsics;->b(Ljava/lang/String;)V
throw v6
# throw v6
.end method
.method private final Y4()V