Build a AAR archive alongside the APK

This commit is contained in:
Paul Rouget 2018-07-18 20:48:38 +08:00
parent 78e90d6358
commit e8af185a3d
16 changed files with 396 additions and 236 deletions

View file

@ -0,0 +1,258 @@
apply plugin: 'com.android.library'
import groovy.io.FileType
import java.util.regex.Matcher
import java.util.regex.Pattern
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
buildDir = rootDir.absolutePath + "/../../../target/gradle/servoview"
defaultConfig {
minSdkVersion 18
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
compileOptions {
incremental false
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
flavorDimensions "default"
productFlavors {
main {
}
googlevr {
minSdkVersion 21
}
oculusvr {
minSdkVersion 21
}
}
splits {
density {
enable false
}
abi {
enable false
}
}
buildTypes {
// Default debug and release build types are used as templates
debug {
jniDebuggable true
}
release {
signingConfig signingConfigs.debug // Change this to sign with a production key
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
// Custom build types
armDebug {
initWith(debug)
ndk {
abiFilters getNDKAbi('arm')
}
}
armRelease {
initWith(release)
ndk {
abiFilters getNDKAbi('arm')
}
}
armv7Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('armv7')
}
}
armv7Release {
initWith(release)
ndk {
abiFilters getNDKAbi('armv7')
}
}
arm64Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('arm64')
}
}
arm64Release {
initWith(release)
ndk {
abiFilters getNDKAbi('arm64')
}
}
x86Debug {
initWith(debug)
ndk {
abiFilters getNDKAbi('x86')
}
}
x86Release {
initWith(release)
ndk {
abiFilters getNDKAbi('x86')
}
}
}
sourceSets {
armDebug {
jniLibs.srcDirs = [getJniLibsPath(true, 'arm')]
}
armRelease {
jniLibs.srcDirs = [getJniLibsPath(false, 'arm')]
}
armv7Debug {
jniLibs.srcDirs = [getJniLibsPath(true, 'armv7')]
}
armv7Release {
jniLibs.srcDirs = [getJniLibsPath(false, 'armv7')]
}
arm64Debug {
jniLibs.srcDirs = [getJniLibsPath(true, 'arm64')]
}
arm64Release {
jniLibs.srcDirs = [getJniLibsPath(false, 'arm64')]
}
x86Debug {
jniLibs.srcDirs = [getJniLibsPath(true, 'x86')]
}
x86Release {
jniLibs.srcDirs = [getJniLibsPath(false, 'x86')]
}
}
// Ignore default 'debug' and 'release' build types
variantFilter { variant ->
if(variant.buildType.name.equals('release') || variant.buildType.name.equals('debug')) {
variant.setIgnore(true);
}
}
// Call our custom NDK Build task using flavor parameters
tasks.all {
compileTask ->
Pattern pattern = Pattern.compile(/^compile[A-Z][\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
Matcher matcher = pattern.matcher(compileTask.name)
if (!matcher.find()) {
return
}
def taskName = "ndkbuild" + compileTask.name
tasks.create(name: taskName, type: Exec) {
def debug = compileTask.name.contains("Debug")
def arch = matcher.group(1)
commandLine getNdkDir(),
'APP_BUILD_SCRIPT=../jni/Android.mk',
'NDK_APPLICATION_MK=../jni/Application.mk',
'NDK_LIBS_OUT=' + getJniLibsPath(debug, arch),
'NDK_OUT=' + getTargetDir(debug, arch) + '/apk/obj',
'NDK_DEBUG=' + (debug ? '1' : '0'),
'APP_ABI=' + getNDKAbi(arch),
'SERVO_TARGET_DIR=' + getTargetDir(debug, arch)
}
compileTask.dependsOn taskName
}
project.afterEvaluate {
android.libraryVariants.all { variant ->
Pattern pattern = Pattern.compile(/^[\w\d]+([A-Z][\w\d]+)(Debug|Release)/)
Matcher matcher = pattern.matcher(variant.name)
if (!matcher.find()) {
throw new GradleException("Invalid variant name for output: " + variant.name)
}
def arch = matcher.group(1)
def debug = variant.name.contains("Debug")
def finalFolder = getTargetDir(debug, arch)
def finalFile = new File(finalFolder, "servoview.aar")
variant.outputs.all { output ->
Task copyAndRenameAARTask = project.task("copyAndRename${variant.name.capitalize()}AAR", type: Copy) {
from output.outputFile.getParent()
into finalFolder
include output.outputFileName
rename(output.outputFileName, finalFile.getName())
}
variant.assemble.finalizedBy(copyAndRenameAARTask)
}
}
}
}
dependencies {
//Dependency list
def deps = [
new ServoDependency("blurdroid.jar", "blurdroid")
]
// Iterate all build types and dependencies
// For each dependency call the proper implementation command and set the correct dependency path
def list = ['arm', 'armv7', 'arm64', 'x86']
for (arch in list) {
for (debug in [true, false]) {
String basePath = getTargetDir(debug, arch) + "/build"
String cmd = arch + (debug ? "Debug" : "Release") + "Implementation"
for (ServoDependency dep : deps) {
String path = findDependencyPath(basePath, dep.fileName, dep.folderFilter)
if (path) {
"${cmd}" files(path)
}
}
}
}
googlevrImplementation 'com.google.vr:sdk-base:1.140.0'
googlevrImplementation(name: 'GVRService', ext: 'aar')
oculusvrImplementation(name: 'OVRService', ext: 'aar')
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
}
// folderFilter can be used to improve search performance
static String findDependencyPath(String basePath, String filename, String folderFilter) {
File path = new File(basePath);
if (!path.exists()) {
return ''
}
if (folderFilter) {
path.eachDir {
if (it.name.contains(folderFilter)) {
path = new File(it.absolutePath)
}
}
}
def result = ''
path.eachFileRecurse(FileType.FILES) {
if(it.name.equals(filename)) {
result = it.absolutePath
}
}
return result
}
class ServoDependency {
ServoDependency(String fileName, String folderFilter = null) {
this.fileName = fileName;
this.folderFilter = folderFilter;
}
public String fileName;
public String folderFilter;
}

View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mozilla.servoview">
<application>
<activity android:name=".MainActivity"
android:screenOrientation="landscape"
android:enableVrMode="@string/gvr_vr_mode_component"
android:resizeableActivity="false">
<!-- Intent filter that enables this app to be launched from the
Daydream Home menu. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.google.intent.category.DAYDREAM"/>
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->

View file

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mozilla.servoview" />

View file

@ -0,0 +1,57 @@
/* -*- 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;
/**
* 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() {
System.loadLibrary("c++_shared");
System.loadLibrary("simpleservo");
}
public interface ReadFileCallback {
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,32 @@
/* -*- 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,280 @@
/* -*- 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.Context;
import android.content.res.AssetManager;
import android.net.Uri;
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 {
private static final String LOGTAG = "ServoView";
private Activity mActivity;
private NativeServo mServo;
private Client mClient = null;
private Uri mInitialUri = null;
private boolean mAnimating;
private String mServoArgs = "";
public ServoView(Context context, AttributeSet attrs) {
super(context, attrs);
mActivity = (Activity) context;
setFocusable(true);
setFocusableInTouchMode(true);
setWillNotCacheDrawing(false);
setEGLContextClientVersion(3);
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;
}
public void reload() {
queueEvent(() -> mServo.reload());
}
public void goBack() {
queueEvent(() -> mServo.goBack());
}
public void goForward() {
queueEvent(() -> mServo.goForward());
}
public void stop() {
queueEvent(() -> mServo.stop());
}
public void onSurfaceResized(int width, int height) {
queueEvent(() -> mServo.resize(width, height));
}
public void loadUri(Uri uri) {
if (mServo != null) {
queueEvent(() -> mServo.loadUri(uri.toString()));
} else {
mInitialUri = uri;
}
}
class WakeupCallback implements NativeServo.WakeupCallback {
public void wakeup() {
queueEvent(() -> mServo.performUpdates());
};
}
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;
}
}
}
class ServoCallbacks implements NativeServo.ServoCallbacks {
public void flush() {
requestRender();
}
public void onLoadStarted() {
if (mClient != null) {
post(() -> mClient.onLoadStarted());
}
}
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 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(() -> {
String uri = mInitialUri == null ? null : mInitialUri.toString();
mServo.init(mActivity, mServoArgs, uri, c1, c2, c3, 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) {
mFlinging = false;
queueEvent(() -> mServo.scrollEnd(0, 0, mCurX, mCurY));
if (!mAnimating) {
// Not scrolling. Not animating. We don't need to schedule
// another frame.
return;
}
}
if (mFlinging) {
mScroller.computeScrollOffset();
mCurX = mScroller.getCurrX();
mCurY = mScroller.getCurrY();
}
int dx = mCurX - mLastX;
int dy = mCurY - mLastY;
mLastX = mCurX;
mLastY = mCurY;
if (dx != 0 || dy != 0) {
queueEvent(() -> mServo.scroll(dx, dy, mCurX, mCurY));
} else {
if (mAnimating) {
requestRender();
}
}
Choreographer.getInstance().postFrameCallback(this);
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
mFlinging = true;
// FIXME: magic values
// https://github.com/servo/servo/issues/20361
int mPageWidth = 80000;
int mPageHeight = 80000;
mCurX = velocityX < 0 ? mPageWidth : 0;
mLastX = mCurX;
mCurY = velocityY < 0 ? mPageHeight : 0;
mLastY = mCurY;
mScroller.fling(mCurX, mCurY, (int)velocityX, (int)velocityY, 0, mPageWidth, 0, mPageHeight);
return true;
}
public boolean onDown(MotionEvent e) {
mScroller.forceFinished(true);
return true;
}
public boolean onTouchEvent(final MotionEvent e) {
mGestureDetector.onTouchEvent(e);
int action = e.getActionMasked();
switch(action) {
case (MotionEvent.ACTION_DOWN):
mCurX = (int)e.getX();
mLastX = mCurX;
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();
return true;
case (MotionEvent.ACTION_UP):
case (MotionEvent.ACTION_CANCEL):
if (!mFlinging) {
queueEvent(() -> mServo.scrollEnd(0, 0, mCurX, mCurY));
Choreographer.getInstance().removeFrameCallback(this);
}
return true;
default:
return true;
}
}
public boolean onSingleTapUp(MotionEvent e) {
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) { }
}

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">servoview</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mozilla.servoview">>
<application>
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
<activity android:name=".MainActivity" android:screenOrientation="landscape">
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->