1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

Adroid test-server client

This commit is contained in:
Alexander Bruines 2016-05-28 13:31:35 +02:00 committed by Andy Green
parent 3358c53445
commit d6fba75433
26 changed files with 2705 additions and 0 deletions

View file

@ -0,0 +1,51 @@
/*
* libwebsockets Android client - libwebsockets test application for Android
*
* Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
This directory contains an Android Studio (2.1.1) project that builds
libwebsockets (+ openssl + zlib) and an Android application that is able
to connect to the 'dumb-increment-protocol' of the libwebsockets test server.
Building the native libraries requires the Android NDK which can be
installed using the SDK manager.
The app/src/main/jni/NativeLibs.mk is fully integraded with Gradle but will
only work on Linux and requires the following applications to be available
in addition to the NDK:
awk cmake egrep git tar wget makedepend
(makedepend can be installed from (Debian) xutils-dev)
To build the project:
- Open an 'existing project' with Android Studio and select this directory.
(answer yes/ok to the question to integrate with Gradle).
- Open the file app/src/main/jni/Application.mk and make sure NDK_ROOT
is set correctly and that APP_PLATFORM is set to the appropriate API level.
- Build the project with CTRL+F9
(open the gradle console to follow the build progress).
- Install APK to device and run.
- Connect to libwebsockets test server.

View file

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="LwsClient" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android-gradle" name="Android-Gradle">
<configuration>
<option name="GRADLE_PROJECT_PATH" value=":app" />
</configuration>
</facet>
<facet type="android" name="Android">
<configuration>
<option name="SELECTED_BUILD_VARIANT" value="debug" />
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
<option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
<option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
<afterSyncTasks>
<task>generateDebugSources</task>
</afterSyncTasks>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/intermediates/classes/debug" />
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/debug" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/animated-vector-drawable/23.3.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.3.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.3.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-vector-drawable/23.3.0/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/instant-run-support" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" scope="TEST" name="hamcrest-core-1.3" level="project" />
<orderEntry type="library" exported="" name="support-v4-23.3.0" level="project" />
<orderEntry type="library" exported="" scope="TEST" name="junit-4.12" level="project" />
<orderEntry type="library" exported="" name="support-annotations-23.3.0" level="project" />
<orderEntry type="library" exported="" name="support-vector-drawable-23.3.0" level="project" />
<orderEntry type="library" exported="" name="animated-vector-drawable-23.3.0" level="project" />
<orderEntry type="library" exported="" name="appcompat-v7-23.3.0" level="project" />
</component>
</module>

View file

@ -0,0 +1,42 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "org.libwebsockets.client"
minSdkVersion 17
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
jni.srcDirs = []
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
}
task buildNativeLibs(type: Exec, description: "compile the native libraries") {
commandLine 'make', '-f', 'NativeLibs.mk', '-C', 'src/main/jni', 'all'
}
task cleanNativeLibs(type: Exec, description: "clean the native libraries source tree") {
commandLine 'make', '-f', 'NativeLibs.mk', '-C', 'src/main/jni', 'clean-ndk'
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn buildNativeLibs }
clean.dependsOn 'cleanNativeLibs'

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.libwebsockets.client">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:launchMode="singleTop"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="org.libwebsockets.client.MainActivity"
android:windowSoftInputMode="stateHidden|adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="org.libwebsockets.client.LwsService" />
</application>
</manifest>

View file

@ -0,0 +1,132 @@
/*
* LwsService.java - libwebsockets test service for Android
*
* Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
package org.libwebsockets.client;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
public class LwsService extends ThreadService {
/**
* Commands that can be send to this service
*/
public final static int MSG_SET_CONNECTION_PARAMETERS = 1;
/**
* Messages that may be send to output Messenger
* Clients should handle these messages.
**/
public final static int MSG_DUMB_INCREMENT_PROTOCOL_COUNTER = 1;
public final static int MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 2;
public final static int MSG_LWS_CALLBACK_CLIENT_ESTABLISHED = 3;
public static class ConnectionParameters {
String serverAddress;
int serverPort;
ConnectionParameters(
String serverAddress,
int serverPort
){
this.serverAddress = serverAddress;
this.serverPort = serverPort;
}
}
/**
* Handle incoming messages from clients of this service
*/
@Override
public void handleInputMessage(Message msg) {
Message m;
switch(msg.what) {
case MSG_SET_CONNECTION_PARAMETERS: {
LwsService.ConnectionParameters parameters = (ConnectionParameters) msg.obj;
setConnectionParameters(
parameters.serverAddress,
parameters.serverPort
);
break;
}
default:
super.handleInputMessage(msg);
break;
}
}
/**
* The run() function for the thread.
* For this test we implement a very long lived task
* that sends many messages back to the client.
* **/
public void workerThreadRun() {
initLws();
connectLws();
while(true) {
// service the websockets
serviceLws();
// Check if we must quit or suspend
synchronized (mThreadLock){
while(mMustSuspend) {
// We are asked to suspend the thread
try {
mThreadLock.wait();
} catch (InterruptedException e) {}
}
if(mMustQuit) {
// The signal to quit was given
break;
}
}
// Throttle the loop so that it iterates once every 50ms
try {
Thread.sleep(50);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
exitLws();
}
/** Load the native libwebsockets code */
static {
try {
System.loadLibrary("lwsservice");
}
catch(UnsatisfiedLinkError ule) {
Log.e("LwsService", "Warning: Could not load native library: " + ule.getMessage());
}
}
public native boolean initLws();
public native void exitLws();
public native void serviceLws();
public native void setConnectionParameters(String serverAddress, int serverPort);
public native boolean connectLws();
}

View file

@ -0,0 +1,246 @@
/*
* MainActivity.java - libwebsockets test service for Android
*
* Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
package org.libwebsockets.client;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements LwsService.OutputInterface {
/** This is the Messenger that handles output from the Service */
private Messenger mMessenger = null;
/** The Messenger for sending commands to the Service */
private Messenger mService = null;
private ServiceConnection mServiceConnection = null;
private boolean mThreadIsRunning = false;
private boolean mThreadIsSuspended = false;
private TextView tvCounter;
private EditText etServer;
private EditText etPort;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get the layout items
tvCounter = (TextView) findViewById(R.id.textView_counter);
etServer = (EditText) findViewById(R.id.editText_serverLocation);
etPort = (EditText) findViewById(R.id.editText_portNumber);
// Create the Messenger for handling output from the service
mMessenger = new Messenger(new LwsService.OutputHandler(this));
// Restore state from the Bundle when restarting due to a device change.
if(savedInstanceState!=null) {
mThreadIsRunning = savedInstanceState.getBoolean("mThreadIsRunning");
}
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
try {
// Set the output messenger by starting the thread
Message msg = Message.obtain(null, LwsService.MSG_SET_OUTPUT_HANDLER, 0, 0);
msg.replyTo = mMessenger;
mService.send(msg);
if(mThreadIsRunning){
// If the thread is already running at this point it means
// that the application was restarted after a device change.
// This implies that the thread was suspended by the onStop method.
msg = Message.obtain(null, LwsService.MSG_THREAD_RESUME, 0, 0);
mService.send(msg);
mThreadIsSuspended = false;
}
}
catch(RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("MainActivity","onServiceDisconnected !");
mService = null;
}
};
if(savedInstanceState==null){
startService(new Intent(getBaseContext(), LwsService.class));
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("mThreadIsRunning", mThreadIsRunning);
}
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(getBaseContext(), LwsService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if(mThreadIsRunning) {
if (!mThreadIsSuspended) {
try {
mService.send(Message.obtain(null, LwsService.MSG_THREAD_SUSPEND, 0, 0));
} catch (RemoteException e) {
e.printStackTrace();
}
mThreadIsSuspended = true;
}
}
unbindService(mServiceConnection);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(isFinishing()){
stopService(new Intent(getBaseContext(), LwsService.class));
}
}
/** Implement the interface to receive output from the LwsService */
@Override
public void handleOutputMessage(Message message) {
switch(message.what) {
case LwsService.MSG_DUMB_INCREMENT_PROTOCOL_COUNTER:
tvCounter.setText((String)message.obj);
break;
case LwsService.MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
connectErrorListener();
break;
case LwsService.MSG_LWS_CALLBACK_CLIENT_ESTABLISHED:
break;
case LwsService.MSG_THREAD_STARTED:
// The thread was started
mThreadIsRunning = true;
mThreadIsSuspended = false;
break;
case LwsService.MSG_THREAD_STOPPED:
// The thread was stopped
mThreadIsRunning = false;
mThreadIsSuspended = false;
break;
case LwsService.MSG_THREAD_SUSPENDED:
// The thread is suspended
mThreadIsRunning = true;
mThreadIsSuspended = true;
break;
case LwsService.MSG_THREAD_RESUMED:
// the thread was resumed
mThreadIsRunning = true;
mThreadIsSuspended = false;
break;
default:
break;
}
}
private void connectErrorListener(){
try {
Message msg;
if(mThreadIsRunning) {
msg = Message.obtain(null, LwsService.MSG_THREAD_STOP);
mService.send(msg);
}
AlertDialog.Builder adb = new AlertDialog.Builder(this);
adb.setTitle("Error");
adb.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
}
});
adb.setMessage("Could not connect to the server.");
adb.show();
}
catch (RemoteException e){}
}
/**
* Start/Stop Button Handler
*/
public void clickStart(View v) {
if(!mThreadIsRunning) {
View view = this.getCurrentFocus();
if (view != null) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
mThreadIsRunning = true;
mThreadIsSuspended = false;
try {
Message msg = Message.obtain(null, LwsService.MSG_SET_CONNECTION_PARAMETERS, 0, 0);
int port = 0;
if(!etPort.getText().toString().equals("")) // prevent NumberformatException
port = Integer.parseInt(etPort.getText().toString());
LwsService.ConnectionParameters parameters = new LwsService.ConnectionParameters(
etServer.getText().toString(),
port
);
msg.obj = parameters;
mService.send(msg);
msg = Message.obtain(null, LwsService.MSG_THREAD_START, 0, 0);
mService.send(msg);
}
catch(RemoteException e) {
e.printStackTrace();
}
}
else {
try {
mService.send(Message.obtain(null, LwsService.MSG_THREAD_STOP, 0, 0));
}
catch(RemoteException e) {
e.printStackTrace();
}
mThreadIsRunning = false;
mThreadIsSuspended = false;
}
}
}

View file

@ -0,0 +1,284 @@
/*
* ThreadService.java - libwebsockets test service for Android
*
* Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
package org.libwebsockets.client;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import java.lang.ref.WeakReference;
public abstract class ThreadService extends Service {
/** Messages that can be send to the Service: **/
public final static int MSG_SET_OUTPUT_HANDLER = 1001;
public final static int MSG_THREAD_START = 1002;
public final static int MSG_THREAD_STOP = 1003;
public final static int MSG_THREAD_SUSPEND = 1004;
public final static int MSG_THREAD_RESUME = 1005;
/**
* Messages that may be send from the Service
* (Clients should handle these messages)
**/
public final static int MSG_THREAD_STARTED = 2001;
public final static int MSG_THREAD_STOPPED = 2002;
public final static int MSG_THREAD_SUSPENDED = 2003;
public final static int MSG_THREAD_RESUMED = 2004;
/** Data accessed by both worker thread and the UI-thread must be synchronized **/
public final Object mThreadLock = new Object();;
public volatile boolean mMustQuit;
public volatile boolean mWorkThreadIsRunning;
public volatile boolean mMustSuspend;
/** Handler for incoming messages **/
public static class InputHandler extends Handler {
private final WeakReference<ThreadService> mService;
InputHandler(ThreadService service) {
mService = new WeakReference<ThreadService>(service);
}
@Override
public void handleMessage(Message msg) {
ThreadService service = mService.get();
if(service != null) {
service.handleInputMessage(msg);
}
}
}
/**
* Interface and Handler for outgoing messages to clients of this service.
* (Must be implemented by the client.)
*/
public interface OutputInterface {
void handleOutputMessage(Message message);
}
public static class OutputHandler extends Handler {
// Notice that we do NOT use a WeakReference here
// (If we did the service would lose mOutputMessenger the moment
// that garbage collection is performed by the Java VM)
private final OutputInterface mInterface;
OutputHandler(OutputInterface object) {
mInterface = object;
}
@Override
public void handleMessage(Message msg) {
mInterface.handleOutputMessage(msg);
}
}
/** The Messengers used to communicate with the clients of this service **/
public final Messenger mInputMessenger = new Messenger(new InputHandler(this));
public Messenger mOutputMessenger;
/** The worker thread and its runnable **/
public static class WorkerThreadRunnable implements Runnable {
private final WeakReference<ThreadService> mService;
WorkerThreadRunnable(ThreadService service){
mService = new WeakReference<ThreadService>(service);
}
@Override
public void run() {
ThreadService service = mService.get();
if(service != null) {
service.mWorkThreadIsRunning = true;
service.workerThreadRun();
service.mWorkThreadIsRunning = false;
}
}
}
public Thread mWorkerThread;
/** Handle incoming messages from the client **/
public void handleInputMessage(Message msg) {
try {
Message m;
switch(msg.what) {
case MSG_SET_OUTPUT_HANDLER:
// set the output messenger then
// send a message indicating the thread status
mOutputMessenger = msg.replyTo;
break;
case MSG_THREAD_START:
try {
// reset thread vars
synchronized (mThreadLock) {
// thread allready running?
if(!mWorkThreadIsRunning){
// no, start it
mMustQuit = false;
mMustSuspend = false;
mWorkerThread = new Thread(new WorkerThreadRunnable(this));
mWorkerThread.start();
}
else {
// yes, resume it
mMustQuit = false;
mMustSuspend = false;
mThreadLock.notifyAll();
}
}
}
catch(NullPointerException e) {
e.printStackTrace();
}
if(mOutputMessenger != null) {
m = Message.obtain(null, MSG_THREAD_STARTED, 0, 0);
mOutputMessenger.send(m);
}
break;
case MSG_THREAD_STOP:
try {
synchronized(mThreadLock) {
if(mWorkThreadIsRunning) {
mMustQuit = true;
mMustSuspend = false;
mThreadLock.notifyAll();
}
}
mWorkerThread.join();
}
catch(InterruptedException e) {
Log.e("ThreadService","handleInputMessage join() interrupted");
}
if(mOutputMessenger != null) {
m = Message.obtain(null, MSG_THREAD_STOPPED, 0, 0);
mOutputMessenger.send(m);
}
break;
case MSG_THREAD_SUSPEND:
synchronized (mThreadLock) {
if(mWorkThreadIsRunning) {
mMustSuspend = true;
}
}
if(mOutputMessenger != null) {
m = Message.obtain(null, MSG_THREAD_SUSPENDED, 0, 0);
mOutputMessenger.send(m);
}
break;
case MSG_THREAD_RESUME:
synchronized (mThreadLock) {
if(mWorkThreadIsRunning) {
mMustSuspend = false;
mThreadLock.notifyAll();
}
}
if(mOutputMessenger != null) {
m = Message.obtain(null, MSG_THREAD_RESUMED, 0, 0);
mOutputMessenger.send(m);
}
break;
default:
break;
}
}
catch(RemoteException e) {
e.printStackTrace();
}
}
/**
* This can be called from the JNI functions to send output messages to the client
*/
public void sendMessage(int msg, Object obj){
Message m = Message.obtain(null, msg, 0, 0);
m.obj = obj;
try {
mOutputMessenger.send(m);
}
catch(RemoteException e) {
e.printStackTrace();
}
}
/** The run() function for the worker thread **/
public abstract void workerThreadRun();
/**
* Called when the service is being created.
* ie. When the first client calls bindService() or startService().
**/
@Override
public void onCreate() {
super.onCreate();
// initialize variables
mWorkThreadIsRunning = false;
mMustQuit = false;
mOutputMessenger = null;
mWorkerThread = null;
}
/**
* Called when the first client is binding to the service with bindService()
*
* If the service was started with bindService() it will automatically stop when the last
* client unbinds from the service. If you want the service to continue running even if it
* is not bound to anything then start the service with startService() before
* calling bindService(). In this case stopService() must be called after unbinding
* to stop the service.
*/
@Override
public IBinder onBind(Intent intent) {
return mInputMessenger.getBinder();
}
/** Called if the service is started with startService(). */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
/** Called when the first client is binds to the service with bindService() */
@Override
public void onRebind(Intent intent) {}
/** Called when all clients have unbound with unbindService() */
@Override
public boolean onUnbind(Intent intent) {
//mOutputMessenger = null;
return false; // do not allow to rebind.
}
/** Called when the service is no longer used and is being destroyed */
@Override
public void onDestroy() {
super.onDestroy();
try {
if(mWorkThreadIsRunning){
synchronized(mThreadLock) {
mMustQuit = true;
mMustSuspend = false;
mThreadLock.notifyAll();
}
mWorkerThread.join();
}
}
catch(NullPointerException | InterruptedException e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,41 @@
# get current directory
LOCAL_PATH := $(call my-dir)
# libz.a
#
include $(CLEAR_VARS)
LOCAL_MODULE := libz
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/lib/libz.a
include $(PREBUILT_STATIC_LIBRARY)
# libssl.a
#
include $(CLEAR_VARS)
LOCAL_MODULE := libssl
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/lib/libssl.a
include $(PREBUILT_STATIC_LIBRARY)
# libcrypto.a
#
include $(CLEAR_VARS)
LOCAL_MODULE := libcrypto
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/lib/libcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
# libwebsockets.a
#
include $(CLEAR_VARS)
LOCAL_MODULE := libwebsockets
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/lib/libwebsockets.a
include $(PREBUILT_STATIC_LIBRARY)
# liblwsservice.so
#
include $(CLEAR_VARS)
LOCAL_DISABLE_FATAL_LINKER_WARNINGS := true
LOCAL_MODULE := lwsservice
LOCAL_SRC_FILES := LwsService.cpp
LOCAL_C_INCLUDES := $(LOCAL_PATH) $(TARGET_ARCH_ABI)/include
LOCAL_STATIC_LIBRARIES := websockets z ssl crypto
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

View file

@ -0,0 +1,49 @@
#
# Zlib, OpenSSL and libwebsockets will be downloaded automatically unless you place
# their source .tar.gz files in the jni directory...
#
# The location of the NDK
#
NDK_ROOT := /opt/Android/Sdk/ndk-bundle
# Update these to the latest versions before building
#
ZLIB_VERSION := 1.2.8
OPENSSL_VERSION := 1.0.2h
# This will be executed as 'git clone $(LIBWEBSOCKETS_GIT_URL)'
#
LIBWEBSOCKETS_GIT_URL := --branch master https://github.com/warmcat/libwebsockets.git
#
# Note: If you build for API level 21 or higher in APP_PLATFORM,
# the resulting application will only run on API 21+ devices.
# Even if minSdkVersion has been set to a lower level!
# This is the result of API changes for the native signal() function.
# The recommended solution is to build two packages, one for API 17+ and the other for API 21+ devices.
# http://stackoverflow.com/questions/28740315/android-ndk-getting-java-lang-unsatisfiedlinkerror-dlopen-failed-cannot-loca
#
# Note: If you change the API level the JNI code must be rebuild completely.
# (Run 'make clean' from the app/src/main/jni directory.)
#
APP_PLATFORM := android-23
# Builds for armeabi armeabi-v7a x86 mips arm64-v8a x86_64 mips64
#
#APP_ABI := all
# The same as above.
#
#APP_ABI := armeabi armeabi-v7a x86 mips arm64-v8a x86_64 mips64
# Good enough for most current devices + x86 AVD
#
APP_ABI := armeabi-v7a x86
# Enable (GNU) c++11 extentions
APP_CPPFLAGS += -std=gnu++11
# Use the GNU standard template library
APP_STL := gnustl_shared

View file

@ -0,0 +1,307 @@
/*
* LwsService.cpp - libwebsockets test service for Android
*
* Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The person who associated a work with this deed has dedicated
* the work to the public domain by waiving all of his or her rights
* to the work worldwide under copyright law, including all related
* and neighboring rights, to the extent allowed by law. You can copy,
* modify, distribute and perform the work, even for commercial purposes,
* all without asking permission.
*
* The test apps are intended to be adapted for use in your code, which
* may be proprietary. So unlike the library itself, they are licensed
* Public Domain.
*/
#include <libwebsockets.h>
#include <jni.h>
#include <android/log.h>
#define printf(...) __android_log_print(ANDROID_LOG_VERBOSE, "LwsService", ##__VA_ARGS__)
/////////////////////////////////////////////////////////
// Code executed when loading the dynamic link library //
/////////////////////////////////////////////////////////
// The Java class the native functions shall be part of
#define JNIREG_CLASS "org/libwebsockets/client/LwsService"
JavaVM* gJvm = NULL;
JNIEnv* gEnv = 0;
JNIEXPORT jboolean JNICALL jni_initLws(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL jni_exitLws(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL jni_setConnectionParameters(JNIEnv *env, jobject obj, jstring serverAddress, jint serverPort);
JNIEXPORT jboolean JNICALL jni_connectLws(JNIEnv *env, jobject obj);
static JNINativeMethod gMethods[] = {
{ "initLws", "()Z", (void*)jni_initLws },
{ "exitLws", "()V", (void*)jni_exitLws },
{ "serviceLws", "()V", (void*)jni_serviceLws },
{ "setConnectionParameters", "(Ljava/lang/String;I)V", (void*)jni_setConnectionParameters },
{ "connectLws", "()Z", (void*)jni_connectLws },
};
static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods)
{
jclass cls;
cls = env->FindClass(className);
if(cls == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(cls, gMethods, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env)
{
if(!registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void * reserved) {
jint result = -1;
gJvm = vm;
if(vm->GetEnv((void**)&gEnv, JNI_VERSION_1_6) != JNI_OK) goto bail;
if(vm->AttachCurrentThread(&gEnv, NULL) < 0) goto bail;
if(registerNatives(gEnv) != JNI_TRUE) goto bail;
result = JNI_VERSION_1_6;
bail:
return result;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
gJvm = NULL;
}
////////////////////////////////////////////////////
// JNI functions to export: //
////////////////////////////////////////////////////
static jclass gLwsServiceCls;
static jobject gLwsServiceObj;
static jmethodID sendMessageId;
static const int MSG_DUMB_INCREMENT_PROTOCOL_COUNTER = 1;
static const int MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 2;
static const int MSG_LWS_CALLBACK_CLIENT_ESTABLISHED = 3;
#define BUFFER_SIZE 4096
static struct lws_context *context = NULL;
static struct lws_context_creation_info info;
static struct lws *wsi = NULL;
// prevents sending messages after jni_exitLws had been called
static int isExit = 0;
enum websocket_protocols {
PROTOCOL_DUMB_INCREMENT = 0,
PROTOCOL_COUNT
};
struct per_session_data {
;// no data
};
static int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len );
static struct lws_protocols protocols[] = {
{
"dumb-increment-protocol",
callback,
sizeof( struct per_session_data ),
BUFFER_SIZE,
},
{ NULL, NULL, 0, 0 } // end of list
};
static const struct lws_extension exts[] = {
{
"deflate-frame",
lws_extension_callback_pm_deflate,
"deflate_frame"
},
{ NULL, NULL, NULL }
};
static int port = 0;
static int use_ssl = 0;
static int use_ssl_client = 0;
static char address[8192];
static char ca_cert[8192];
static char client_cert[8192];
static char client_cert_key[8192];
static int deny_deflate = 0;
static int deny_mux = 0;
// Logging function for libwebsockets
static void emit_log(int level, const char *msg)
{
printf("%s", msg);
}
JNIEXPORT jboolean JNICALL jni_initLws(JNIEnv *env, jobject obj)
{
if(context) return JNI_TRUE;
// Attach the java virtual machine to this thread
gJvm->AttachCurrentThread(&gEnv, NULL);
// Set java global references to the class and object
jclass cls = env->GetObjectClass(obj);
gLwsServiceCls = (jclass) env->NewGlobalRef(cls);
gLwsServiceObj = env->NewGlobalRef(obj);
// Get the sendMessage method from the LwsService class (inherited from class ThreadService)
sendMessageId = gEnv->GetMethodID(gLwsServiceCls, "sendMessage", "(ILjava/lang/Object;)V");
memset(&info, 0, sizeof(info));
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
#ifndef LWS_NO_EXTENSIONS
info.extensions = exts;
#endif
info.gid = -1;
info.uid = -1;
lws_set_log_level( LLL_NOTICE | LLL_INFO | LLL_ERR | LLL_WARN | LLL_CLIENT, emit_log );
context = lws_create_context(&info);
if( context == NULL ){
emit_log(LLL_ERR, "Creating libwebsocket context failed");
return JNI_FALSE;
}
isExit = 0;
return JNI_TRUE;
}
// Send a message to the client of the service
// (must call jni_initLws() first)
static inline void sendMessage(int id, jobject obj)
{
if(!isExit) gEnv->CallVoidMethod(gLwsServiceObj, sendMessageId, id, obj);
}
JNIEXPORT void JNICALL jni_exitLws(JNIEnv *env, jobject obj)
{
if(context){
isExit = 1;
lws_context_destroy(context);
context = NULL;
env->DeleteGlobalRef(gLwsServiceObj);
env->DeleteGlobalRef(gLwsServiceCls);
}
}
static int callback(
struct lws *wsi,
enum lws_callback_reasons reason,
void *user,
void *in,
size_t len
)
{
switch(reason){
case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
sendMessage(MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL);
break;
case LWS_CALLBACK_CLIENT_ESTABLISHED:
sendMessage(MSG_LWS_CALLBACK_CLIENT_ESTABLISHED, NULL);
break;
case LWS_CALLBACK_CLIENT_RECEIVE:
((char *)in)[len] = '\0';
sendMessage(MSG_DUMB_INCREMENT_PROTOCOL_COUNTER, gEnv->NewStringUTF((const char*)in));
break;
case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
if ((strcmp((const char*)in, "deflate-stream") == 0) && deny_deflate) {
emit_log(LLL_ERR, "websocket: denied deflate-stream extension");
return 1;
}
if ((strcmp((const char*)in, "deflate-frame") == 0) && deny_deflate) {
emit_log(LLL_ERR, "websocket: denied deflate-frame extension");
return 1;
}
if ((strcmp((const char*)in, "x-google-mux") == 0) && deny_mux) {
emit_log(LLL_ERR, "websocket: denied x-google-mux extension");
return 1;
}
break;
default:
break;
}
return 0;
}
JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj)
{
if(context){
lws_service( context, 0 );
}
}
JNIEXPORT void JNICALL jni_setConnectionParameters(
JNIEnv *env,
jobject obj,
jstring serverAddress,
jint serverPort
)
{
address[0] = 0;
port = serverPort;
use_ssl = 0;
use_ssl_client = 0;
snprintf(address, sizeof(address), "%s", env->GetStringUTFChars(serverAddress, 0));
}
JNIEXPORT jboolean JNICALL jni_connectLws(JNIEnv *env, jobject obj)
{
struct lws_client_connect_info info_ws;
memset(&info_ws, 0, sizeof(info_ws));
info_ws.port = port;
info_ws.address = address;
info_ws.path = "/";
info_ws.context = context;
info_ws.ssl_connection = use_ssl;
info_ws.host = address;
info_ws.origin = address;
info_ws.ietf_version_or_minus_one = -1;
info_ws.client_exts = exts;
info_ws.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
// connect
wsi = lws_client_connect_via_info(&info_ws);
if(wsi == NULL ){
// Error
emit_log(LLL_ERR, "Protocol failed to connect.");
return JNI_FALSE;
}
return JNI_TRUE;
}

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scrollView"
tools:context="org.libwebsockets.client.MainActivity"
android:theme="@android:style/Theme.Holo.Light"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:src="@drawable/warmcat" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/editText_serverLocation"
android:hint="Hostname or IP address"
android:background="@android:drawable/editbox_background"
android:textAppearance="@style/Base.TextAppearance.AppCompat"
android:singleLine="true"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_below="@+id/imageView"
android:layout_alignParentStart="true" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:ems="10"
android:id="@+id/editText_portNumber"
android:background="@android:drawable/editbox_background"
android:hint="Port number"
android:text="7681"
android:textAppearance="@style/Base.TextAppearance.AppCompat"
android:singleLine="true"
android:layout_below="@+id/editText_serverLocation"
android:layout_alignParentStart="true"
android:layout_marginTop="@dimen/activity_vertical_margin"
android:layout_marginBottom="@dimen/activity_vertical_margin" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="(dis)connect"
android:id="@+id/button"
android:textAppearance="@style/Base.TextAppearance.AppCompat"
android:onClick="clickStart"
android:layout_below="@+id/editText_portNumber"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/activity_vertical_margin" />
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="dumb-increment-protocol"
android:id="@+id/textView_counter"
android:layout_centerHorizontal="true"
android:gravity="center_vertical|center_horizontal"
android:layout_alignParentBottom="true"
android:background="@android:drawable/alert_light_frame"
android:singleLine="false"
android:layout_below="@+id/button" />
</RelativeLayout>
</ScrollView>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View file

@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View file

@ -0,0 +1,5 @@
<resources>
<string name="app_name">libwebsockets Android Client</string>
<string name="start">Start/Stop thread</string>
<string name="suspend">Suspend/Resume thread</string>
</resources>

View file

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View file

@ -0,0 +1,23 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -0,0 +1,20 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useDeprecatedNdk=true

View file

@ -0,0 +1 @@
include ':app'