Auto merge of #21234 - paulrouget:surface, r=MortimerGoro

Android: Introduce ServoSurface

(WIP as it depends on 2 other PRs)

Depends on #21199. Only last commit matters.

r? @MortimerGoro

Please look at ServoSurface.java. The rest is mostly some refactoring to share as much code as possible with ServoView.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/21234)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2018-08-01 12:04:32 -04:00 committed by GitHub
commit 733552d997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 544 additions and 212 deletions

View file

@ -39,6 +39,9 @@ pub trait HostTrait {
/// Will be called from the thread used for the init call.
/// Will be called when the GL buffer has been updated.
fn flush(&self);
/// Will be called before drawing.
/// Time to make the targetted GL context current.
fn make_current(&self);
/// Page starts loading.
/// "Reload button" should be disabled.
/// "Stop button" should be enabled.
@ -355,6 +358,7 @@ impl WindowMethods for ServoCallbacks {
_height: Length<u32, DevicePixel>,
) -> bool {
debug!("WindowMethods::prepare_for_composite");
self.host_callbacks.make_current();
true
}

View file

@ -28,6 +28,7 @@ fn call<F>(f: F) where F: Fn(&mut ServoGlue) -> Result<(), &'static str> {
#[repr(C)]
pub struct CHostCallbacks {
pub flush: extern fn(),
pub make_current: extern fn(),
pub on_load_started: extern fn(),
pub on_load_ended: extern fn(),
pub on_title_changed: extern fn(title: *const c_char),
@ -223,6 +224,11 @@ impl HostTrait for HostCallbacks {
(self.0.flush)();
}
fn make_current(&self) {
debug!("make_current");
(self.0.make_current)();
}
fn on_load_started(&self) {
debug!("on_load_ended");
(self.0.on_load_started)();

View file

@ -36,21 +36,19 @@ where
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_version(env: JNIEnv, _class: JClass) -> jstring {
pub fn Java_com_mozilla_servoview_JNIServo_version(env: JNIEnv, _class: JClass) -> jstring {
let v = api::servo_version();
let output = env.new_string(v).expect("Couldn't create java string");
output.into_inner()
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_init(
pub fn Java_com_mozilla_servoview_JNIServo_init(
env: JNIEnv,
_: JClass,
activity: JObject,
args: JString,
url: JString,
wakeup_obj: JObject,
readfile_obj: JObject,
callbacks_obj: JObject,
width: jint,
height: jint,
@ -80,9 +78,11 @@ pub fn Java_com_mozilla_servoview_NativeServo_init(
Some(env.get_string(url).expect("Couldn't get java string").into())
};
let wakeup = Box::new(WakeupCallback::new(wakeup_obj, &env));
let readfile = Box::new(ReadFileCallback::new(readfile_obj, &env));
let callbacks = Box::new(HostCallbacks::new(callbacks_obj, &env));
let callbacks_ref = env.new_global_ref(callbacks_obj).unwrap();
let wakeup = Box::new(WakeupCallback::new(callbacks_ref.clone(), &env));
let readfile = Box::new(ReadFileCallback::new(callbacks_ref.clone(), &env));
let callbacks = Box::new(HostCallbacks::new(callbacks_ref, &env));
gl_glue::egl::init().and_then(|gl| {
api::init(
@ -100,7 +100,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_init(
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_setBatchMode(
pub fn Java_com_mozilla_servoview_JNIServo_setBatchMode(
env: JNIEnv,
_: JClass,
batch: jboolean,
@ -110,7 +110,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_setBatchMode(
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_resize(
pub fn Java_com_mozilla_servoview_JNIServo_resize(
env: JNIEnv,
_: JClass,
width: jint,
@ -121,38 +121,38 @@ pub fn Java_com_mozilla_servoview_NativeServo_resize(
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_performUpdates(env: JNIEnv, _class: JClass) {
pub fn Java_com_mozilla_servoview_JNIServo_performUpdates(env: JNIEnv, _class: JClass) {
debug!("performUpdates");
call(env, |s| s.perform_updates());
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_loadUri(env: JNIEnv, _class: JClass, url: JString) {
pub fn Java_com_mozilla_servoview_JNIServo_loadUri(env: JNIEnv, _class: JClass, url: JString) {
debug!("loadUri");
let url: String = env.get_string(url).unwrap().into();
call(env, |s| s.load_uri(&url));
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_reload(env: JNIEnv, _class: JClass) {
pub fn Java_com_mozilla_servoview_JNIServo_reload(env: JNIEnv, _class: JClass) {
debug!("reload");
call(env, |s| s.reload());
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_goBack(env: JNIEnv, _class: JClass) {
pub fn Java_com_mozilla_servoview_JNIServo_goBack(env: JNIEnv, _class: JClass) {
debug!("goBack");
call(env, |s| s.go_back());
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_goForward(env: JNIEnv, _class: JClass) {
pub fn Java_com_mozilla_servoview_JNIServo_goForward(env: JNIEnv, _class: JClass) {
debug!("goForward");
call(env, |s| s.go_forward());
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_scrollStart(
pub fn Java_com_mozilla_servoview_JNIServo_scrollStart(
env: JNIEnv,
_: JClass,
dx: jint,
@ -165,7 +165,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_scrollStart(
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_scrollEnd(
pub fn Java_com_mozilla_servoview_JNIServo_scrollEnd(
env: JNIEnv,
_: JClass,
dx: jint,
@ -179,7 +179,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_scrollEnd(
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_scroll(
pub fn Java_com_mozilla_servoview_JNIServo_scroll(
env: JNIEnv,
_: JClass,
dx: jint,
@ -192,7 +192,7 @@ pub fn Java_com_mozilla_servoview_NativeServo_scroll(
}
#[no_mangle]
pub fn Java_com_mozilla_servoview_NativeServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) {
pub fn Java_com_mozilla_servoview_JNIServo_click(env: JNIEnv, _: JClass, x: jint, y: jint) {
debug!("click");
call(env, |s| s.click(x as u32, y as u32));
}
@ -203,12 +203,9 @@ pub struct WakeupCallback {
}
impl WakeupCallback {
pub fn new(jobject: JObject, env: &JNIEnv) -> WakeupCallback {
pub fn new(callback: GlobalRef, env: &JNIEnv) -> WakeupCallback {
let jvm = Arc::new(env.get_java_vm().unwrap());
WakeupCallback {
callback: env.new_global_ref(jobject).unwrap(),
jvm,
}
WakeupCallback { callback, jvm }
}
}
@ -233,9 +230,9 @@ pub struct ReadFileCallback {
}
impl ReadFileCallback {
pub fn new(jobject: JObject, env: &JNIEnv) -> ReadFileCallback {
pub fn new(callback: GlobalRef, env: &JNIEnv) -> ReadFileCallback {
let jvm = env.get_java_vm().unwrap();
let callback = Mutex::new(env.new_global_ref(jobject).unwrap());
let callback = Mutex::new(callback);
ReadFileCallback { callback, jvm }
}
}
@ -260,12 +257,9 @@ impl ReadFileTrait for ReadFileCallback {
}
impl HostCallbacks {
pub fn new(jobject: JObject, env: &JNIEnv) -> HostCallbacks {
pub fn new(callbacks: GlobalRef, env: &JNIEnv) -> HostCallbacks {
let jvm = env.get_java_vm().unwrap();
HostCallbacks {
callbacks: env.new_global_ref(jobject).unwrap(),
jvm,
}
HostCallbacks { callbacks, jvm }
}
}
@ -277,6 +271,13 @@ impl HostTrait for HostCallbacks {
.unwrap();
}
fn make_current(&self) {
debug!("make_current");
let env = self.jvm.get_env().unwrap();
env.call_method(self.callbacks.as_obj(), "makeCurrent", "()V", &[])
.unwrap();
}
fn on_load_started(&self) {
debug!("on_load_started");
let env = self.jvm.get_env().unwrap();

View file

@ -20,10 +20,11 @@ import android.widget.EditText;
import android.widget.ProgressBar;
import com.mozilla.servoview.ServoView;
import com.mozilla.servoview.Servo;
import java.io.File;
public class MainActivity extends Activity implements ServoView.Client {
public class MainActivity extends Activity implements Servo.Client {
private static final String LOGTAG = "MainActivity";
@ -49,10 +50,10 @@ public class MainActivity extends Activity implements ServoView.Client {
mUrlField = findViewById(R.id.urlfield);
mProgressBar = findViewById(R.id.progressbar);
mServoView.setClient(this);
mBackButton.setEnabled(false);
mFwdButton.setEnabled(false);
mServoView.setClient(this);
mServoView.requestFocus();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
@ -66,9 +67,7 @@ public class MainActivity extends Activity implements ServoView.Client {
}
String args = getIntent().getStringExtra("servoargs");
if (args != null) {
mServoView.setServoArgs(args);
}
mServoView.setServoArgs(args);
setupUrlField();
}
@ -105,18 +104,16 @@ public class MainActivity extends Activity implements ServoView.Client {
mServoView.loadUri(Uri.parse(uri));
}
// From activity_main.xml:
public void onReloadClicked(View v) {
mServoView.reload();
}
public void onBackClicked(View v) {
mServoView.goBack();
}
public void onForwardClicked(View v) {
mServoView.goForward();
}
public void onStopClicked(View v) {
mServoView.stop();
}

View file

@ -10,48 +10,65 @@ import android.app.Activity;
/**
* Maps /ports/libsimpleservo API
*/
public class NativeServo {
public native String version();
public native void init(Activity activity,
String args,
String url,
WakeupCallback wakeup,
ReadFileCallback readfile,
ServoCallbacks callbacks,
int width, int height, boolean log);
public native void setBatchMode(boolean mode);
public native void performUpdates();
public native void resize(int width, int height);
public native void reload();
public native void stop();
public native void goBack();
public native void goForward();
public native void loadUri(String uri);
public native void scrollStart(int dx, int dy, int x, int y);
public native void scroll(int dx, int dy, int x, int y);
public native void scrollEnd(int dx, int dy, int x, int y);
public native void click(int x, int y);
NativeServo() {
@SuppressWarnings("JniMissingFunction")
public class JNIServo {
JNIServo() {
System.loadLibrary("c++_shared");
System.loadLibrary("simpleservo");
}
public interface ReadFileCallback {
public native String version();
public native void init(Activity activity,
String args,
String url,
Callbacks callbacks,
int width, int height, boolean log);
public native void setBatchMode(boolean mode);
public native void performUpdates();
public native void resize(int width, int height);
public native void reload();
public native void stop();
public native void goBack();
public native void goForward();
public native void loadUri(String uri);
public native void scrollStart(int dx, int dy, int x, int y);
public native void scroll(int dx, int dy, int x, int y);
public native void scrollEnd(int dx, int dy, int x, int y);
public native void click(int x, int y);
public interface Callbacks {
void wakeup();
void flush();
void makeCurrent();
void onAnimatingChanged(boolean animating);
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
byte[] readfile(String file);
}
public interface WakeupCallback {
void wakeup();
}
public interface ServoCallbacks {
void flush();
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
void onAnimatingChanged(boolean animating);
}
}

View file

@ -0,0 +1,173 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.mozilla.servoview;
import android.app.Activity;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
public class Servo {
private static final String LOGTAG = "Servo";
private AssetManager mAssetMgr;
private JNIServo mJNI = new JNIServo();
private RunCallback mRunCallback;
public Servo(
RunCallback runCallback,
GfxCallbacks gfxcb,
Client client,
Activity activity,
String args, String url,
int width, int height, boolean log) {
mRunCallback = runCallback;
mAssetMgr = activity.getResources().getAssets();
Callbacks cbs = new Callbacks(client, gfxcb);
mRunCallback.inGLThread(() -> {
mJNI.init(activity, args, url, cbs, width, height, log);
});
}
public String version() {
return mJNI.version();
}
public void setBatchMode(boolean mode) {
mRunCallback.inGLThread(() -> mJNI.setBatchMode(mode));
}
public void resize(int width, int height) {
mRunCallback.inGLThread(() -> mJNI.resize(width, height));
}
public void reload() {
mRunCallback.inGLThread(() -> mJNI.reload());
}
public void stop() {
mRunCallback.inGLThread(() -> mJNI.stop());
}
public void goBack() {
mRunCallback.inGLThread(() -> mJNI.goBack());
}
public void goForward() {
mRunCallback.inGLThread(() -> mJNI.goForward());
}
public void loadUri(String uri) {
mRunCallback.inGLThread(() -> mJNI.loadUri(uri));
}
public void scrollStart(int dx, int dy, int x, int y) {
mRunCallback.inGLThread(() -> mJNI.scrollStart(dx, dy, x, y));
}
public void scroll(int dx, int dy, int x, int y) {
mRunCallback.inGLThread(() -> mJNI.scroll(dx, dy, x, y));
}
public void scrollEnd(int dx, int dy, int x, int y) {
mRunCallback.inGLThread(() -> mJNI.scrollEnd(dx, dy, x, y));
}
public void click(int x, int y) {
mRunCallback.inGLThread(() -> mJNI.click(x, y));
}
public interface Client {
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
}
public interface RunCallback {
void inGLThread(Runnable f);
void inUIThread(Runnable f);
}
public interface GfxCallbacks {
void flushGLBuffers();
void animationStateChanged(boolean animating);
void makeCurrent();
}
private class Callbacks implements JNIServo.Callbacks, Client {
private final GfxCallbacks mGfxCb;
Client mClient;
Callbacks(Client client, GfxCallbacks gfxcb) {
mClient = client;
mGfxCb = gfxcb;
}
public void wakeup() {
mRunCallback.inGLThread(() -> mJNI.performUpdates());
}
public void flush() {
mRunCallback.inUIThread(() -> mGfxCb.flushGLBuffers());
}
public void makeCurrent() {
mRunCallback.inUIThread(() -> mGfxCb.makeCurrent());
}
public void onAnimatingChanged(boolean animating) {
mRunCallback.inUIThread(() -> mGfxCb.animationStateChanged(animating));
}
public void onLoadStarted() {
mRunCallback.inUIThread(() -> mClient.onLoadStarted());
}
public void onLoadEnded() {
mRunCallback.inUIThread(() -> mClient.onLoadEnded());
}
public void onTitleChanged(String title) {
mRunCallback.inUIThread(() -> mClient.onTitleChanged(title));
}
public void onUrlChanged(String url) {
mRunCallback.inUIThread(() -> mClient.onUrlChanged(url));
}
public void onHistoryChanged(boolean canGoBack, boolean canGoForward) {
mRunCallback.inUIThread(() -> mClient.onHistoryChanged(canGoBack, canGoForward));
}
public byte[] readfile(String file) {
try {
InputStream stream = mAssetMgr.open(file);
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
stream.close();
return bytes;
} catch (IOException e) {
Log.e(LOGTAG, e.getMessage());
return null;
}
}
}
}

View file

@ -1,32 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package com.mozilla.servoview;
import android.opengl.GLES31;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class ServoGLRenderer implements GLSurfaceView.Renderer {
private final ServoView mView;
ServoGLRenderer(ServoView view) {
mView = view;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
mView.onGLReady();
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES31.glViewport(0, 0, width, height);
mView.onSurfaceResized(width, height);
}
}

View file

@ -0,0 +1,186 @@
package com.mozilla.servoview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.net.Uri;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.mozilla.servoview.Servo.Client;
import com.mozilla.servoview.Servo.GfxCallbacks;
import com.mozilla.servoview.Servo.RunCallback;
import static android.opengl.EGL14.EGL_CONTEXT_CLIENT_VERSION;
import static android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
public class ServoSurface {
private final GLThread mGLThread;
private final Handler mMainLooperHandler;
private Handler mGLLooperHandler;
private Surface mASurface;
private int mWidth;
private int mHeight;
private Servo mServo;
private Client mClient = null;
private String mServoArgs = "";
private Uri mInitialUri = null;
private Activity mActivity;
public ServoSurface(Surface surface, int width, int height) {
mWidth = width;
mHeight = height;
mASurface = surface;
mMainLooperHandler = new Handler(Looper.getMainLooper());
mGLThread = new GLThread();
}
public void setClient(Client client) {
mClient = client;
}
public void setServoArgs(String args) {
mServoArgs = args != null ? args : "";
}
public void setActivity(Activity activity) {
mActivity = activity;
}
public void runLoop() {
mGLThread.start();
}
public void reload() {
mServo.reload();
}
public void goBack() {
mServo.goBack();
}
public void goForward() {
mServo.goForward();
}
public void stop() {
mServo.stop();
}
public void onSurfaceResized(int width, int height) {
mServo.resize(width, height);
}
public void loadUri(Uri uri) {
if (mServo != null) {
mServo.loadUri(uri.toString());
} else {
mInitialUri = uri;
}
}
static class Surface implements GfxCallbacks {
private static final String LOGTAG = "ServoSurface";
private EGLConfig[] mEGLConfigs;
private EGLDisplay mEglDisplay;
private EGLContext mEglContext;
private EGLSurface mEglSurface;
Surface(Surface surface) {
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
int[] version = new int[2];
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
throw new RuntimeException("Error: eglInitialize() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
}
mEGLConfigs = new EGLConfig[1];
int[] configsCount = new int[1];
int[] configSpec = new int[]{
EGL14.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 0,
EGL14.EGL_STENCIL_SIZE, 0,
EGL14.EGL_NONE
};
if ((!EGL14.eglChooseConfig(mEglDisplay, configSpec, 0, mEGLConfigs, 0, 1, configsCount, 0)) || (configsCount[0] == 0)) {
throw new IllegalArgumentException("Error: eglChooseConfig() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
}
if (mEGLConfigs[0] == null) {
throw new RuntimeException("Error: eglConfig() not Initialized");
}
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL14.EGL_NONE};
mEglContext = EGL14.eglCreateContext(mEglDisplay, mEGLConfigs[0], EGL14.EGL_NO_CONTEXT, attrib_list, 0);
int glError = EGL14.eglGetError();
if (glError != EGL14.EGL_SUCCESS) {
throw new RuntimeException("Error: eglCreateContext() Failed " + GLUtils.getEGLErrorString(glError));
}
mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEGLConfigs[0], surface, new int[]{EGL14.EGL_NONE}, 0);
if (mEglSurface == null || mEglSurface == EGL14.EGL_NO_SURFACE) {
glError = EGL14.eglGetError();
if (glError == EGL14.EGL_BAD_NATIVE_WINDOW) {
Log.e(LOGTAG, "Error: createWindowSurface() Returned EGL_BAD_NATIVE_WINDOW.");
return;
}
throw new RuntimeException("Error: createWindowSurface() Failed " + GLUtils.getEGLErrorString(glError));
}
flushGLBuffers();
}
public void makeCurrent() {
if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
throw new RuntimeException("Error: eglMakeCurrent() Failed " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
}
}
public void flushGLBuffers() {
EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
}
public void animationStateChanged(boolean animating) {
// FIXME
}
}
class GLThread extends Thread implements RunCallback {
public void inGLThread(Runnable r) {
mGLLooperHandler.post(r);
}
public void inUIThread(Runnable r) {
mMainLooperHandler.post(r);
}
// FIXME: HandlerLeak
@SuppressLint("HandlerLeak")
public void run() {
Looper.prepare();
Surface surface = new Surface(mASurface);
final boolean showLogs = true;
String uri = mInitialUri == null ? null : mInitialUri.toString();
mServo = new Servo(this, surface, mClient, mActivity, mServoArgs, uri, mWidth, mHeight, showLogs);
mGLLooperHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
Looper.loop();
}
}
}

View file

@ -7,28 +7,44 @@ package com.mozilla.servoview;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.net.Uri;
import android.opengl.GLES31;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Choreographer;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.OverScroller;
import java.io.IOException;
import java.io.InputStream;
public class ServoView extends GLSurfaceView implements GestureDetector.OnGestureListener, Choreographer.FrameCallback {
import com.mozilla.servoview.Servo.Client;
import com.mozilla.servoview.Servo.GfxCallbacks;
import com.mozilla.servoview.Servo.RunCallback;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
public class ServoView extends GLSurfaceView
implements
GestureDetector.OnGestureListener,
Choreographer.FrameCallback,
GfxCallbacks,
RunCallback {
private static final String LOGTAG = "ServoView";
private Activity mActivity;
private NativeServo mServo;
private Servo mServo;
private Client mClient = null;
private Uri mInitialUri = null;
private boolean mAnimating;
private String mServoArgs = "";
private GestureDetector mGestureDetector;
private OverScroller mScroller;
private int mLastX = 0;
private int mCurX = 0;
private int mLastY = 0;
private int mCurY = 0;
private boolean mFlinging;
public ServoView(Context context, AttributeSet attrs) {
super(context, attrs);
@ -40,149 +56,87 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
setEGLConfigChooser(8, 8, 8, 8, 24, 0);
ServoGLRenderer mRenderer = new ServoGLRenderer(this);
setRenderer(mRenderer);
mServo = new NativeServo();
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
initGestures(context);
}
public void setServoArgs(String args) {
mServoArgs = args;
mServoArgs = args != null ? args : "";
}
public void reload() {
queueEvent(() -> mServo.reload());
mServo.reload();
}
public void goBack() {
queueEvent(() -> mServo.goBack());
mServo.goBack();
}
public void goForward() {
queueEvent(() -> mServo.goForward());
mServo.goForward();
}
public void stop() {
queueEvent(() -> mServo.stop());
mServo.stop();
}
public void onSurfaceResized(int width, int height) {
queueEvent(() -> mServo.resize(width, height));
if (mServo != null) {
mServo.resize(width, height);
}
}
public void loadUri(Uri uri) {
if (mServo != null) {
queueEvent(() -> mServo.loadUri(uri.toString()));
mServo.loadUri(uri.toString());
} else {
mInitialUri = uri;
}
}
class WakeupCallback implements NativeServo.WakeupCallback {
public void wakeup() {
queueEvent(() -> mServo.performUpdates());
};
public void flushGLBuffers() {
requestRender();
}
class ReadFileCallback implements NativeServo.ReadFileCallback {
public byte[] readfile(String file) {
try {
AssetManager assetMgr = getContext().getResources().getAssets();
InputStream stream = assetMgr.open(file);
byte[] bytes = new byte[stream.available()];
stream.read(bytes);
stream.close();
return bytes;
} catch (IOException e) {
Log.e(LOGTAG, e.getMessage());
return null;
}
// Scroll and click
public void animationStateChanged(boolean animating) {
if (!mAnimating && animating) {
post(() -> Choreographer.getInstance().postFrameCallback(ServoView.this));
}
mAnimating = animating;
}
class ServoCallbacks implements NativeServo.ServoCallbacks {
public void flush() {
requestRender();
}
public void makeCurrent() {
}
public void onLoadStarted() {
if (mClient != null) {
post(() -> mClient.onLoadStarted());
}
}
public void inGLThread(Runnable f) {
queueEvent(f);
}
public void onLoadEnded() {
if (mClient != null) {
post(() -> mClient.onLoadEnded());
}
}
public void onTitleChanged(final String title) {
if (mClient != null) {
post(() -> mClient.onTitleChanged(title));
}
}
public void onUrlChanged(final String url) {
if (mClient != null) {
post(() -> mClient.onUrlChanged(url));
}
}
public void onHistoryChanged(final boolean canGoBack, final boolean canGoForward) {
if (mClient != null) {
post(() -> mClient.onHistoryChanged(canGoBack, canGoForward));
}
}
public void onAnimatingChanged(final boolean animating) {
if (!mAnimating && animating) {
post(() -> Choreographer.getInstance().postFrameCallback(ServoView.this));
}
mAnimating = animating;
}
public void inUIThread(Runnable f) {
post(f);
}
public void onGLReady() {
final WakeupCallback c1 = new WakeupCallback();
final ReadFileCallback c2 = new ReadFileCallback();
final ServoCallbacks c3 = new ServoCallbacks();
final boolean showLogs = true;
int width = getWidth();
int height = getHeight();
queueEvent(() -> {
inGLThread(() -> {
String uri = mInitialUri == null ? null : mInitialUri.toString();
mServo.init(mActivity, mServoArgs, uri, c1, c2, c3, width, height, showLogs);
mServo = new Servo(this, this, mClient, mActivity, mServoArgs, uri, width, height, showLogs);
});
}
public interface Client {
void onLoadStarted();
void onLoadEnded();
void onTitleChanged(String title);
void onUrlChanged(String url);
void onHistoryChanged(boolean canGoBack, boolean canGoForward);
}
public void setClient(Client client) {
mClient = client;
}
// Scroll and click
private GestureDetector mGestureDetector;
private OverScroller mScroller;
private int mLastX = 0;
private int mCurX = 0;
private int mLastY = 0;
private int mCurY = 0;
private boolean mFlinging;
private void initGestures(Context context) {
mGestureDetector = new GestureDetector(context, this);
mScroller = new OverScroller(context);
}
@Override
public void doFrame(long frameTimeNanos) {
if (mScroller.isFinished() && mFlinging) {
@ -229,7 +183,7 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
mLastX = mCurX;
mCurY = velocityY < 0 ? mPageHeight : 0;
mLastY = mCurY;
mScroller.fling(mCurX, mCurY, (int)velocityX, (int)velocityY, 0, mPageWidth, 0, mPageHeight);
mScroller.fling(mCurX, mCurY, (int) velocityX, (int) velocityY, 0, mPageWidth, 0, mPageHeight);
return true;
}
@ -242,19 +196,19 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
mGestureDetector.onTouchEvent(e);
int action = e.getActionMasked();
switch(action) {
switch (action) {
case (MotionEvent.ACTION_DOWN):
mCurX = (int)e.getX();
mCurX = (int) e.getX();
mLastX = mCurX;
mCurY = (int)e.getY();
mCurY = (int) e.getY();
mLastY = mCurY;
mScroller.forceFinished(true);
queueEvent(() -> mServo.scrollStart(0, 0, mCurX, mCurY));
Choreographer.getInstance().postFrameCallback(this);
return true;
case (MotionEvent.ACTION_MOVE):
mCurX = (int)e.getX();
mCurY = (int)e.getY();
mCurX = (int) e.getX();
mCurY = (int) e.getY();
return true;
case (MotionEvent.ACTION_UP):
case (MotionEvent.ACTION_CANCEL):
@ -269,12 +223,38 @@ public class ServoView extends GLSurfaceView implements GestureDetector.OnGestur
}
public boolean onSingleTapUp(MotionEvent e) {
queueEvent(() -> mServo.click((int)e.getX(), (int)e.getY()));
queueEvent(() -> mServo.click((int) e.getX(), (int) e.getY()));
return false;
}
public void onLongPress(MotionEvent e) { }
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return true; }
public void onShowPress(MotionEvent e) { }
public void onLongPress(MotionEvent e) {
}
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return true;
}
public void onShowPress(MotionEvent e) {
}
static class ServoGLRenderer implements Renderer {
private final ServoView mView;
ServoGLRenderer(ServoView view) {
mView = view;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
mView.onGLReady();
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES31.glViewport(0, 0, width, height);
mView.onSurfaceResized(width, height);
}
}
}