Skip to content
This repository was archived by the owner on Jan 24, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Webmaker for Android

# Please file all issues related to Webmaker Android at https://github.com/mozilla/webmaker-core/issues. You can use the android tag if your issue applies specifically to this repo or the Android platform.
#### Please file all issues related to Webmaker Android at https://github.com/mozilla/webmaker-core/issues. You can use the android tag if your issue applies specifically to this repo or the Android platform.

[![Build Status](https://travis-ci.org/mozilla/webmaker-android.svg?branch=develop)](https://travis-ci.org/mozilla/webmaker-android)

Expand Down Expand Up @@ -40,7 +40,6 @@ For more details on which environment variables are used by webmaker-core, pleas

This repository is home to the native Android wrapper for the Webmaker app. `webmaker-android` is a hybrid mobile application that is primarily web-based (HTML/CSS/JS) but uses this wrapper to communicate with the native Android SDK. To make changes or to test the app, we recommend you use [Android Studio](http://developer.android.com/sdk/index.html).


- Compile the webview code with `npm run build`.
- Install and configure [Android Studio](http://developer.android.com/sdk)
- Open Android Studio and select "Import Project"
Expand All @@ -58,7 +57,7 @@ Each fragment within `webmaker-android` is actually just a web page! You can fin

For local development, it's recommended to use `npm link` ([read more](https://docs.npmjs.com/cli/link)) with a local copy of [webmaker-core](https://github.com/mozilla/webmaker-core), in which you'll do any webview related work separately.

When changes are compiled in [webmaker-core](https://github.com/mozilla/webmaker-core) you'll need to run `npm run copy:core` before building in Android Studio. Alternatively, you can create a symbolic link from `app/src/main/assets/www/` to `./node_modules/webmaker-core/dest/` to avoid having to run this extra command.
Gradle will automatically run `npm run copy:core` before building in Android Studio. This makes it convenient to watch your local copy of [webmaker-core](https://github.com/mozilla/webmaker-core) while you are testing on device or with an emulator.

## Contact Us
IRC: `#webmaker` on `irc.mozilla.org`
Expand Down
38 changes: 31 additions & 7 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'com.moowork.node'

android {
compileSdkVersion 22
Expand All @@ -8,8 +9,8 @@ android {
applicationId "org.mozilla.webmaker"
minSdkVersion 14
targetSdkVersion 22
versionCode 7
versionName "1.1.0"
versionCode 8
versionName "1.2.0"
}

buildTypes {
Expand All @@ -28,14 +29,14 @@ android {

productFlavors {
x86 {
flavorDimension "abi"
dimension "abi"
ndk {
abiFilters "x86", ""
}
versionCode = 3
}
armv7 {
flavorDimension "abi"
dimension "abi"
ndk {
abiFilters "armeabi-v7a", ""
}
Expand All @@ -52,6 +53,20 @@ android {
}
}

task npmRunBuild (type: NpmTask, dependsOn: ['npmInstall']) {
args = ['run', 'build']
}

task npmRunCopyCore (type: NpmTask, dependsOn: ['npmInstall']) {
args = ['run', 'copy:core']
}

tasks.whenTaskAdded { task ->
if (task.name == 'assembleProductionRelease') {
task.dependsOn npmRunBuild
}
}

repositories {
mavenCentral()
maven {
Expand All @@ -62,7 +77,16 @@ repositories {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v13:22.1.1'
compile 'com.android.support:appcompat-v7:22.1.1'
compile 'com.google.android.gms:play-services-analytics:7.3.0'
compile 'com.android.support:appcompat-v7:22.2.1'
compile 'com.google.android.gms:play-services-analytics:7.5.0'
compile 'org.xwalk:xwalk_core_library:13.42.319.12'
}
}

node {
version = '0.12.2'
npmVersion = '2.7.4'
distBaseUrl = 'http://nodejs.org/dist'
download = true
}

preBuild.dependsOn npmRunCopyCore
2 changes: 1 addition & 1 deletion app/src/main/java/org/mozilla/webmaker/BaseActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import android.app.Activity;
import android.content.SharedPreferences;

import org.mozilla.webmaker.javascript.WebAppInterface;
import org.mozilla.webmaker.web.javascript.WebAppInterface;

public class BaseActivity extends Activity {
public void goBack() {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/org/mozilla/webmaker/activity/Play.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.json.JSONObject;
import org.mozilla.webmaker.R;
import org.mozilla.webmaker.WebmakerActivity;
import org.mozilla.webmaker.javascript.WebAppInterface;
import org.mozilla.webmaker.web.javascript.WebAppInterface;
import org.mozilla.webmaker.router.Router;
import org.mozilla.webmaker.util.Share;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import org.mozilla.webmaker.R;
import org.mozilla.webmaker.client.WebClient;
import org.mozilla.webmaker.javascript.WebAppInterface;
import org.mozilla.webmaker.web.javascript.WebAppInterface;

public class WebmakerWebView extends XWalkView {

Expand All @@ -26,6 +26,6 @@ public WebmakerWebView(Context context, Activity activity, String pageName, JSON
this.load("file:///android_asset/www/pages/" + pageName + "/index.html", null);
this.setResourceClient(new WebClient(this));
this.setBackgroundColor(getResources().getColor(R.color.light_gray));
this.addJavascriptInterface(new WebAppInterface(context, routeParams), "Platform");
this.addJavascriptInterface(new WebAppInterface(this, context, routeParams), "Platform");
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package org.mozilla.webmaker.javascript;
package org.mozilla.webmaker.web.javascript;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.util.Log;

Expand All @@ -15,6 +17,7 @@
import org.json.JSONObject;
import org.mozilla.webmaker.WebmakerApplication;
import org.mozilla.webmaker.util.Share;
import org.mozilla.webmaker.view.WebmakerWebView;
import org.xwalk.core.JavascriptInterface;

import org.mozilla.webmaker.BaseActivity;
Expand All @@ -34,21 +37,26 @@ public class WebAppInterface {
protected String mPrefKey;
protected String mPageState;

protected WebmakerAPI api;

public static final String SHARED_PREFIX = "prefs::".concat(BuildConfig.VERSION_NAME);
public static final String ROUTE_KEY = "route::data";
public static final String USER_SESSION_KEY = "user::session";

public WebAppInterface(Context context) {
this(context, null);
public WebAppInterface(WebmakerWebView view, Context context) {
this(view, context, null);
}

public WebAppInterface(Context context, JSONObject routeParams) {
public WebAppInterface(WebmakerWebView view, Context context, JSONObject routeParams) {
mContext = context;
mActivity = (BaseActivity) context;
mPrefKey = "::".concat(mContext.getClass().getSimpleName());
mPrefs = mContext.getSharedPreferences(mPrefKey, 0);
mUserPrefs = mContext.getSharedPreferences(USER_SESSION_KEY, 0);
mRoute = routeParams;
api = WebmakerAPI.getInstance();
api.setView(view);
api.setActivity(mActivity);
Log.v("wm", "getting state " + mPrefKey + ": " + mPageState);
}

Expand Down Expand Up @@ -293,6 +301,14 @@ public boolean isDebugBuild() {
return BuildConfig.DEBUG;
}

/**
* ----------------------------------------
* Get a reference to the API class
* ----------------------------------------
*/
@JavascriptInterface
public WebmakerAPI getAPI() { return api; }

/**
* ----------------------------------------
* Get System Locale
Expand All @@ -303,4 +319,18 @@ public String getSystemLanguage() {
// Change underscores to dashes (The browser uses dashes instead)
return Locale.getDefault().toString().replace("_", "-");
}

/**
* ----------------------------------------
* Determine network availability
* ----------------------------------------
*/
@JavascriptInterface
public boolean isNetworkAvailable() {
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();

return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}

}
167 changes: 167 additions & 0 deletions app/src/main/java/org/mozilla/webmaker/web/javascript/WebmakerAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package org.mozilla.webmaker.web.javascript;

import android.util.Log;

import org.mozilla.webmaker.BaseActivity;
import org.mozilla.webmaker.WebmakerActivity;
import org.mozilla.webmaker.view.WebmakerWebView;
import org.xwalk.core.JavascriptInterface;

import java.util.ArrayList;
import java.util.HashMap;

/**
* ...
*/
public class WebmakerAPI {

// Static behaviour

protected static WebmakerAPI instance = new WebmakerAPI();

@JavascriptInterface
public static WebmakerAPI getInstance() {
return WebmakerAPI.instance;
}

// Instance behaviour

/**
* Dictionary class for storing arrays of String data tied to specific keys.
*/
protected class Queue extends HashMap<String, ArrayList<String>> {
public void queue(String origin, String payload) {
if (this.get(origin) == null) {
this.put(origin, new ArrayList<String>());
}
this.get(origin).add(payload);
}
public int getQueueSize(String origin) {
ArrayList<String> queue = this.get(origin);
if (queue == null) return -1;
return queue.size();
}
public String getPayload(String origin, int idx) {
return this.get(origin).get(idx);
}
public void dequeue(String origin, int idx) {
this.get(origin).remove(idx);
}
}

// used for triggering JS on the main UI thread
protected WebmakerWebView currentView = null;
protected BaseActivity currentActivity = null;

protected Queue queue;

public WebmakerAPI() {
queue = new Queue();
}

public void setView(WebmakerWebView view) {
currentView = view;
}

public void setActivity(BaseActivity activity) {
currentActivity = activity;
}

@JavascriptInterface
public void queue(String origin, String data) {
queue.queue(origin, data);
}

/**
* If we need to iterate over all available queues on
* the JS side, we need to be able to ask which keys exist.
* @return JSON serialized array of keys
*/
@JavascriptInterface
public String getQueueOrigins() {
String keys = "";
for (String s: queue.keySet()) {
keys += '"' + s + '"' + ',';
}
if (!keys.trim().equals("")) {
keys = keys.substring(0, keys.length() - 1);
}
return "[" + keys + "]";
}

/**
* Checks the size of a specific key's queue.
* @param origin Dictionary key
* @return int size of the array associated with the key
*/
@JavascriptInterface
public int getQueueSize(String origin) {
return queue.getQueueSize(origin);
}

/**
* Get the array of payloads
* @param origin the array key
* @return JSON serialized array of payloads strings
*/
@JavascriptInterface
public String getPayloads(String origin) {
String json = "";
ArrayList<String> payloads = queue.get(origin);
if (payloads == null || payloads.size() == 0) {
return null;
}
for (String s: payloads) {
json += s + ",";
}
json = "[" + json.substring(0, json.length()-1) + "]";
return json;
}

/**
* Empty an array associated with a specific key
* @param origin the key for which to clear the array
*/
@JavascriptInterface
public void clearPayloads(String origin) {
ArrayList<String> payloads = queue.get(origin);
if (payloads != null) {
payloads.clear();
}
}

/**
* Get a single payload from a keyed array
* @param origin The array's key
* @param idx the position in the array
* @return the String data as it was queued
*/
@JavascriptInterface
public String getPayload(String origin, int idx) {
return queue.getPayload(origin, idx);
}

/**
* Remove a specific payload from a keyed array
* @param origin the array's key
* @param idx the position at which to remove a payload from the array
*/
@JavascriptInterface
public void removePayload(String origin, int idx) {
queue.dequeue(origin, idx);
}

/**
* Run some arbitrary JavaScript code inside the currently active web view.
* @param js The JavaScript source code that should be evaluated by the view
*/
public void runJavaScript(final String js) {
// Not exposed to JS, because it used for internal JAVA -> JS bridging
currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
currentView.evaluateJavascript(js, null);
}
});
}
}
Loading