diff --git a/Ladybird/Android/build.gradle.kts b/Ladybird/Android/build.gradle.kts index 76e7de66c2..19b2eb7c70 100644 --- a/Ladybird/Android/build.gradle.kts +++ b/Ladybird/Android/build.gradle.kts @@ -72,6 +72,7 @@ dependencies { implementation("androidx.appcompat:appcompat:1.6.1") implementation("com.google.android.material:material:1.9.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") diff --git a/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp b/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp index e454768b2c..c26f8dd83c 100644 --- a/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp +++ b/Ladybird/Android/src/main/cpp/WebViewImplementationNative.cpp @@ -4,31 +4,78 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include +#include #include #include -using namespace WebView; - namespace { + +Gfx::BitmapFormat to_gfx_bitmap_format(i32 f) +{ + switch (f) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + return Gfx::BitmapFormat::BGRA8888; + default: + VERIFY_NOT_REACHED(); + } +} + class WebViewImplementationNative : public WebView::ViewImplementation { public: - virtual Gfx::IntRect viewport_rect() const override { return {}; } - virtual Gfx::IntPoint to_content_position(Gfx::IntPoint) const override { return {}; } - virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint) const override { return {}; } + virtual Gfx::IntRect viewport_rect() const override { return m_viewport_rect; } + virtual Gfx::IntPoint to_content_position(Gfx::IntPoint p) const override { return p; } + virtual Gfx::IntPoint to_widget_position(Gfx::IntPoint p) const override { return p; } virtual void update_zoom() override { } + void paint_into_bitmap(void* android_bitmap_raw, AndroidBitmapInfo const& info) + { + // Software bitmaps only for now! + VERIFY((info.flags & ANDROID_BITMAP_FLAGS_IS_HARDWARE) == 0); + + auto android_bitmap = MUST(Gfx::Bitmap::create_wrapper(to_gfx_bitmap_format(info.format), { info.width, info.height }, 1, info.stride, android_bitmap_raw)); + Gfx::Painter painter(android_bitmap); + if (auto* bitmap = m_client_state.has_usable_bitmap ? m_client_state.front_bitmap.bitmap.ptr() : m_backup_bitmap.ptr()) + painter.blit({ 0, 0 }, *bitmap, bitmap->rect()); + else + painter.clear_rect(painter.clip_rect(), Gfx::Color::Magenta); + + // Convert our internal BGRA into RGBA. This will be slowwwwwww + // FIXME: Don't do a color format swap here. + for (auto y = 0; y < android_bitmap->height(); ++y) { + auto* scanline = android_bitmap->scanline(y); + for (auto x = 0; x < android_bitmap->width(); ++x) { + auto current_pixel = scanline[x]; + u32 alpha = (current_pixel & 0xFF000000U) >> 24; + u32 red = (current_pixel & 0x00FF0000U) >> 16; + u32 green = (current_pixel & 0x0000FF00U) >> 8; + u32 blue = (current_pixel & 0x000000FFU); + scanline[x] = (alpha << 24U) | (blue << 16U) | (green << 8U) | red; + } + } + } + + void set_viewport_geometry(int w, int h) + { + m_viewport_rect = { { 0, 0 }, { w, h } }; + } + static jclass global_class_reference; static jfieldID instance_pointer_field; + +private: + Gfx::IntRect m_viewport_rect; }; jclass WebViewImplementationNative::global_class_reference; jfieldID WebViewImplementationNative::instance_pointer_field; } extern "C" JNIEXPORT void JNICALL -Java_org_serenityos_ladybird_WebViewImplementationNative_00024Companion_nativeClassInit(JNIEnv* env, jobject /* thiz */) +Java_org_serenityos_ladybird_WebViewImplementation_00024Companion_nativeClassInit(JNIEnv* env, jobject /* thiz */) { - auto local_class = env->FindClass("org/serenityos/ladybird/WebViewImplementationNative"); + auto local_class = env->FindClass("org/serenityos/ladybird/WebViewImplementation"); if (!local_class) TODO(); WebViewImplementationNative::global_class_reference = reinterpret_cast(env->NewGlobalRef(local_class)); @@ -40,7 +87,7 @@ Java_org_serenityos_ladybird_WebViewImplementationNative_00024Companion_nativeCl } extern "C" JNIEXPORT jlong JNICALL -Java_org_serenityos_ladybird_WebViewImplementationNative_nativeObjectInit(JNIEnv*, jobject /* thiz */) +Java_org_serenityos_ladybird_WebViewImplementation_nativeObjectInit(JNIEnv*, jobject /* thiz */) { auto instance = reinterpret_cast(new WebViewImplementationNative); __android_log_print(ANDROID_LOG_DEBUG, "Ladybird", "New WebViewImplementationNative at %p", reinterpret_cast(instance)); @@ -48,8 +95,30 @@ Java_org_serenityos_ladybird_WebViewImplementationNative_nativeObjectInit(JNIEnv } extern "C" JNIEXPORT void JNICALL -Java_org_serenityos_ladybird_WebViewImplementationNative_nativeObjectDispose(JNIEnv*, jobject /* thiz */, jlong instance) +Java_org_serenityos_ladybird_WebViewImplementation_nativeObjectDispose(JNIEnv*, jobject /* thiz */, jlong instance) { delete reinterpret_cast(instance); __android_log_print(ANDROID_LOG_DEBUG, "Ladybird", "Destroyed WebViewImplementationNative at %p", reinterpret_cast(instance)); } + +extern "C" JNIEXPORT void JNICALL +Java_org_serenityos_ladybird_WebViewImplementation_nativeDrawIntoBitmap(JNIEnv* env, jobject /* thiz */, jlong instance, jobject bitmap) +{ + auto* impl = reinterpret_cast(instance); + + AndroidBitmapInfo bitmap_info = {}; + void* pixels = nullptr; + AndroidBitmap_getInfo(env, bitmap, &bitmap_info); + AndroidBitmap_lockPixels(env, bitmap, &pixels); + if (pixels) + impl->paint_into_bitmap(pixels, bitmap_info); + + AndroidBitmap_unlockPixels(env, bitmap); +} + +extern "C" JNIEXPORT void JNICALL +Java_org_serenityos_ladybird_WebViewImplementation_nativeSetViewportGeometry(JNIEnv*, jobject /* thiz */, jlong instance, jint w, jint h) +{ + auto* impl = reinterpret_cast(instance); + impl->set_viewport_geometry(w, h); +} diff --git a/Ladybird/Android/src/main/java/org/serenityos/ladybird/LadybirdActivity.kt b/Ladybird/Android/src/main/java/org/serenityos/ladybird/LadybirdActivity.kt index f3e640ad6d..75ca1e00d2 100644 --- a/Ladybird/Android/src/main/java/org/serenityos/ladybird/LadybirdActivity.kt +++ b/Ladybird/Android/src/main/java/org/serenityos/ladybird/LadybirdActivity.kt @@ -8,9 +8,8 @@ package org.serenityos.ladybird import androidx.appcompat.app.AppCompatActivity import android.os.Bundle -import android.widget.TextView +import android.util.AttributeSet import org.serenityos.ladybird.databinding.ActivityMainBinding -import org.serenityos.ladybird.TransferAssets class LadybirdActivity : AppCompatActivity() { @@ -20,11 +19,13 @@ class LadybirdActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) resourceDir = TransferAssets.transferAssets(this) initNativeCode(resourceDir) - view = WebViewImplementationNative() + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.toolbar) + view = binding.webView } override fun onDestroy() { @@ -32,7 +33,7 @@ class LadybirdActivity : AppCompatActivity() { super.onDestroy() } - private lateinit var view: WebViewImplementationNative + private lateinit var view: WebView /** * A native method that is implemented by the 'ladybird' native library, diff --git a/Ladybird/Android/src/main/java/org/serenityos/ladybird/TransferAssets.java b/Ladybird/Android/src/main/java/org/serenityos/ladybird/TransferAssets.java index 975e8a4fa5..b034188621 100644 --- a/Ladybird/Android/src/main/java/org/serenityos/ladybird/TransferAssets.java +++ b/Ladybird/Android/src/main/java/org/serenityos/ladybird/TransferAssets.java @@ -20,7 +20,7 @@ import java.lang.String; public class TransferAssets { /** - @return new ladybird resource root + * @return new ladybird resource root */ static public String transferAssets(Context context) { Log.d("Ladybird", "Hello from java"); diff --git a/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebView.kt b/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebView.kt new file mode 100644 index 0000000000..f60bff6979 --- /dev/null +++ b/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebView.kt @@ -0,0 +1,31 @@ +package org.serenityos.ladybird + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.util.AttributeSet +import android.view.View + +// FIXME: This should (eventually) implement NestedScrollingChild3 and ScrollingView +class WebView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) { + private val viewImpl = WebViewImplementation() + private lateinit var contentBitmap: Bitmap + + fun dispose() { + viewImpl.dispose() + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + contentBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888) + // FIXME: Account for scroll offset when view supports scrolling + viewImpl.setViewportGeometry(w, h) + } + + override fun onDraw(canvas: Canvas?) { + super.onDraw(canvas) + + viewImpl.drawIntoBitmap(contentBitmap); + canvas?.drawBitmap(contentBitmap, 0f, 0f, null) + } +} diff --git a/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebViewImplementationNative.kt b/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebViewImplementation.kt similarity index 65% rename from Ladybird/Android/src/main/java/org/serenityos/ladybird/WebViewImplementationNative.kt rename to Ladybird/Android/src/main/java/org/serenityos/ladybird/WebViewImplementation.kt index 8852b56eab..b3561027b5 100644 --- a/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebViewImplementationNative.kt +++ b/Ladybird/Android/src/main/java/org/serenityos/ladybird/WebViewImplementation.kt @@ -6,19 +6,20 @@ package org.serenityos.ladybird +import android.graphics.Bitmap import android.util.Log /** * Wrapper around WebView::ViewImplementation for use by Kotlin */ -class WebViewImplementationNative { +class WebViewImplementation { // Instance Pointer to native object, very unsafe :) private var nativeInstance = nativeObjectInit() init { Log.d( "Ladybird", - "New WebViewImplementationNative (Kotlin) with nativeInstance ${this.nativeInstance}" + "New WebViewImplementation (Kotlin) with nativeInstance ${this.nativeInstance}" ) } @@ -27,9 +28,20 @@ class WebViewImplementationNative { nativeInstance = 0 } + fun drawIntoBitmap(bitmap: Bitmap) { + nativeDrawIntoBitmap(nativeInstance, bitmap) + } + + fun setViewportGeometry(w: Int, h: Int) { + nativeSetViewportGeometry(nativeInstance, w, h) + } + private external fun nativeObjectInit(): Long private external fun nativeObjectDispose(instance: Long) + private external fun nativeDrawIntoBitmap(instance: Long, bitmap: Bitmap) + private external fun nativeSetViewportGeometry(instance: Long, w: Int, h: Int) + companion object { /* * We use a static class initializer to allow the native code to cache some diff --git a/Ladybird/Android/src/main/res/layout/activity_main.xml b/Ladybird/Android/src/main/res/layout/activity_main.xml index 6043061dbd..c3a0cec9b1 100644 --- a/Ladybird/Android/src/main/res/layout/activity_main.xml +++ b/Ladybird/Android/src/main/res/layout/activity_main.xml @@ -1,19 +1,34 @@ - + android:fitsSystemWindows="true" + tools:context=".LadybirdActivity"> - + + + + - + + + + + diff --git a/Ladybird/Android/src/main/res/values/themes.xml b/Ladybird/Android/src/main/res/values/themes.xml index cbb2295eba..5413fb17ef 100644 --- a/Ladybird/Android/src/main/res/values/themes.xml +++ b/Ladybird/Android/src/main/res/values/themes.xml @@ -12,5 +12,7 @@ ?attr/colorPrimaryVariant + false + true diff --git a/Ladybird/CMakeLists.txt b/Ladybird/CMakeLists.txt index 508b7ce4a6..a007f56c40 100644 --- a/Ladybird/CMakeLists.txt +++ b/Ladybird/CMakeLists.txt @@ -150,7 +150,7 @@ elseif(ANDROID) Android/src/main/cpp/LadybirdActivity.cpp Android/src/main/cpp/WebViewImplementationNative.cpp ) - target_link_libraries(ladybird PRIVATE log) + target_link_libraries(ladybird PRIVATE log jnigraphics) else() # TODO: Check for other GUI frameworks here when we move them in-tree # For now, we can export a static library of common files for chromes to link to