diff --git a/app/build.gradle b/app/build.gradle index 5f8f766c6e..de66b4d082 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,16 +7,6 @@ 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() @@ -30,55 +20,32 @@ task cleanVersionFile { projectDir.toPath().resolve("src/main/assets/version.properties").toFile().delete() } +void copyToOut(InputStream source, OutputStream target) throws IOException { + byte[] buf = new byte[8192]; + int length; + while ((length = source.read(buf)) != -1) { + target.write(buf, 0, length); + } + target.close() + source.close() +} + task exportDex { doLast { + File moduleDir = projectDir try { - File moduleDir = projectDir - ZipInputStream inputStream = new ZipInputStream(new FileInputStream(moduleDir.getAbsolutePath() + "/build/outputs/apk/release/app-release-unsigned.apk")) - ZipFile file = new ZipFile(moduleDir.getAbsolutePath() + "/build/outputs/apk/release/app-release-unsigned.apk"); - ZipEntry entry - while ((entry = inputStream.nextEntry) != null) { - if (entry.getName().equals("classes.dex")) { - print('classes.dex found!') - - FileOutputStream output = new FileOutputStream(new File(moduleDir.getAbsoluteFile(), "classes6.dex")) - - byte[] buffer = new byte[1024] - int len = 0 - while ((len = inputStream.read(buffer)) > 0) { - output.write(buffer, 0, len) - } - output.close() - } - if (entry.getName().equals("classes2.dex")) { - print('\nclasses2.dex found!') - - FileOutputStream output = new FileOutputStream(new File(moduleDir.getAbsoluteFile(), "classes7.dex")) - - byte[] buffer = new byte[1024] - int len = 0 - while ((len = inputStream.read(buffer)) > 0) { - output.write(buffer, 0, len) - } - output.close() - } - } - - for (ZipEntry e : Collections.list(file.entries())) { - if (e.getName().endsWith("assets/version.properties")) { - var is = file.getInputStream(e) - FileOutputStream output = new FileOutputStream(new File(moduleDir.getAbsoluteFile(), "version.properties")) - - byte[] buffer = new byte[1024] - int len = 0 - while ((len = is.read(buffer)) > 0) { - output.write(buffer, 0, len) - } - output.close() - - print('\nversion.properties found!') - } - } + ZipFile file = new ZipFile(moduleDir.getAbsolutePath() + "/build/outputs/apk/release/app-release-unsigned.apk") + var out = moduleDir.getAbsoluteFile().toPath() + copyToOut(file.getInputStream(file.getEntry("classes.dex")), out.resolve("classes6.dex").toFile().newOutputStream()) + copyToOut(file.getInputStream(file.getEntry("classes2.dex")), out.resolve("classes7.dex").toFile().newOutputStream()) + copyToOut(file.getInputStream(file.getEntry("assets/version.properties")), out.resolve("version.properties").toFile().newOutputStream()) + + var arm64 = out.resolve("lib/arm64-v8a/libnative_effects.so").toFile() + arm64.getParentFile().mkdirs() + copyToOut(file.getInputStream(file.getEntry("lib/arm64-v8a/libnative_effects.so")), arm64.newOutputStream()) + var arm32 = out.resolve("lib/armeabi-v7a/libnative_effects.so").toFile() + arm32.getParentFile().mkdirs() + copyToOut(file.getInputStream(file.getEntry("lib/armeabi-v7a/libnative_effects.so")), arm32.newOutputStream()) } catch (Exception e) { e.printStackTrace() } @@ -110,7 +77,6 @@ android { targetCompatibility JavaVersion.VERSION_14 } namespace 'ru.vtosters.lite' - ndkVersion '25.2.9519653' } dependencies { @@ -119,12 +85,11 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.aar']) compileOnly fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.google.guava:guava:23.3-android' + implementation 'ru.vtosters.lite:native_effects:5373b23' } task prepareDexForMerge(type: Copy) { dependsOn('exportDex') - // Remove comment for native lib build - dependsOn('cargoBuild') into smaliFolder.toString() from(projectDir) { include 'classes6.dex' @@ -134,19 +99,14 @@ task prepareDexForMerge(type: Copy) { include 'version.properties' 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' + from("$projectDir/lib/") { + into 'lib/' } doLast { new File(projectDir, "classes6.dex").delete() new File(projectDir, "classes7.dex").delete() new File(projectDir, "version.properties").delete() + new File(projectDir, "lib").delete() } } diff --git a/build.gradle b/build.gradle index 61a22649a8..e0e7225b39 100644 --- a/build.gradle +++ b/build.gradle @@ -3,14 +3,9 @@ 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 } @@ -20,6 +15,18 @@ allprojects { repositories { google() mavenCentral() + maven { + name = "GitHubPackages" + // TODO: set vt org + url = uri("https://maven.pkg.github.com/Saoqq/native-effects") + // TODO: try to resolve from ENV first (for CI/manual publish) and only then use hardcoded read-only + // github moment: https://github.com/orgs/community/discussions/26634 + // TODO: set vt org + credentials { + username = "saoqq" + password = new String(Base64.getDecoder().decode("Z2hwXzFCZEpBUFR0anNQOW9lSmpHNTRDaUJpaXFsN2RLYjNUd1NwaQ==")) + } + } } } diff --git a/native-effects/.gitignore b/native-effects/.gitignore deleted file mode 100644 index 96ef6c0b94..0000000000 --- a/native-effects/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/native-effects/Cargo.toml b/native-effects/Cargo.toml deleted file mode 100644 index b4f69397cf..0000000000 --- a/native-effects/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[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" diff --git a/native-effects/src/lib.rs b/native-effects/src/lib.rs deleted file mode 100644 index 340a66ec73..0000000000 --- a/native-effects/src/lib.rs +++ /dev/null @@ -1,365 +0,0 @@ -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 = &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 = &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::(), - p.g | rng.gen::(), - p.b | rng.gen::(), - ) - } - - 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::, Vec>::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::, Vec>::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::, Vec>::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; -} diff --git a/smali/lib/arm64-v8a/libnative_effects.so b/smali/lib/arm64-v8a/libnative_effects.so deleted file mode 100755 index a14eda9a56..0000000000 Binary files a/smali/lib/arm64-v8a/libnative_effects.so and /dev/null differ diff --git a/smali/lib/armeabi-v7a/libnative_effects.so b/smali/lib/armeabi-v7a/libnative_effects.so deleted file mode 100755 index 14efeae115..0000000000 Binary files a/smali/lib/armeabi-v7a/libnative_effects.so and /dev/null differ