mirror of https://github.com/fltk/fltk
Commiting Android sample project structure
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.4@12694 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
parent
f01eb0ec25
commit
9e29ea8a00
|
@ -0,0 +1,53 @@
|
|||
Native Plasma
|
||||
=============
|
||||
Native Plasma is an Android sample that renders a plasma effect in a Bitmap from C code using [Native Activity](http://developer.android.com/reference/android/app/NativeActivity.html).
|
||||
|
||||
This sample uses the new [Android Studio CMake plugin](http://tools.android.com/tech-docs/external-c-builds) with C++ support.
|
||||
|
||||
Pre-requisites
|
||||
--------------
|
||||
- Android Studio 2.2+ with [NDK](https://developer.android.com/ndk/) bundle.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
1. [Download Android Studio](http://developer.android.com/sdk/index.html)
|
||||
1. Launch Android Studio.
|
||||
1. Open the sample directory.
|
||||
1. Open *File/Project Structure...*
|
||||
- Click *Download* or *Select NDK location*.
|
||||
1. Click *Tools/Android/Sync Project with Gradle Files*.
|
||||
1. Click *Run/Run 'app'*.
|
||||
|
||||
Screenshots
|
||||
-----------
|
||||
![screenshot](screenshot.png)
|
||||
|
||||
Support
|
||||
-------
|
||||
If you've found an error in these samples, please [file an issue](https://github.com/googlesamples/android-ndk/issues/new).
|
||||
|
||||
Patches are encouraged, and may be submitted by [forking this project](https://github.com/googlesamples/android-ndk/fork) and
|
||||
submitting a pull request through GitHub. Please see [CONTRIBUTING.md](../CONTRIBUTING.md) for more details.
|
||||
|
||||
- [Stack Overflow](http://stackoverflow.com/questions/tagged/android-ndk)
|
||||
- [Google+ Community](https://plus.google.com/communities/105153134372062985968)
|
||||
- [Android Tools Feedbacks](http://tools.android.com/feedback)
|
||||
|
||||
License
|
||||
-------
|
||||
Copyright 2015 Google, Inc.
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
license agreements. See the NOTICE file distributed with this work for
|
||||
additional information regarding copyright ownership. The ASF licenses this
|
||||
file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
use this file except in compliance with the License. You may obtain a copy of
|
||||
the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations under
|
||||
the License.
|
|
@ -0,0 +1,23 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
|
||||
defaultConfig {
|
||||
applicationId 'com.example.native_plasma'
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 25
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||
'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path 'src/main/cpp/CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.example.native_plasma"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:hasCode="false">
|
||||
<activity android:name="android.app.NativeActivity"
|
||||
android:label="@string/app_name">
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="native-plasma" />
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,43 @@
|
|||
#
|
||||
# Copyright (C) The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# FIXME: add as a second argument the binary build dir
|
||||
# so that the first argument can link directly to FLTK
|
||||
# instead of the fltk symlink, which is bad for SVN
|
||||
add_subdirectory(fltk)
|
||||
|
||||
# now build app's shared lib
|
||||
add_library(native-plasma SHARED
|
||||
plasma.cxx)
|
||||
|
||||
target_include_directories(native-plasma PRIVATE
|
||||
/Users/matt/dev/fltk-1.4.svn
|
||||
/Users/matt/dev/fltk-1.4.svn/Android)
|
||||
|
||||
# Export ANativeActivity_onCreate(),
|
||||
# Refer to: https://github.com/android-ndk/ndk/issues/381.
|
||||
set(CMAKE_SHARED_LINKER_FLAGS
|
||||
"${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate")
|
||||
|
||||
# add lib dependencies
|
||||
target_link_libraries(native-plasma
|
||||
fltk
|
||||
android
|
||||
log
|
||||
m)
|
||||
|
|
@ -0,0 +1,560 @@
|
|||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
//#include <android_native_app_glue.h>
|
||||
#include <src/drivers/Android/Fl_Android_Screen_Driver.H>
|
||||
|
||||
#include <FL/Fl_Window.H>
|
||||
#include <FL/Fl_Button.H>
|
||||
|
||||
Fl_Window *win;
|
||||
Fl_Button *btn;
|
||||
|
||||
#include <errno.h>
|
||||
#include <jni.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <android/log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define LOG_TAG "libplasma"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
|
||||
/* Set to 1 to enable debug log traces. */
|
||||
#define DEBUG 0
|
||||
|
||||
/* Set to 1 to optimize memory stores when generating plasma. */
|
||||
#define OPTIMIZE_WRITES 1
|
||||
|
||||
/* Return current time in milliseconds */
|
||||
static double now_ms(void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return tv.tv_sec*1000. + tv.tv_usec/1000.;
|
||||
}
|
||||
|
||||
/* We're going to perform computations for every pixel of the target
|
||||
* bitmap. floating-point operations are very slow on ARMv5, and not
|
||||
* too bad on ARMv7 with the exception of trigonometric functions.
|
||||
*
|
||||
* For better performance on all platforms, we're going to use fixed-point
|
||||
* arithmetic and all kinds of tricks
|
||||
*/
|
||||
|
||||
typedef int32_t Fixed;
|
||||
|
||||
#define FIXED_BITS 16
|
||||
#define FIXED_ONE (1 << FIXED_BITS)
|
||||
#define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1)
|
||||
|
||||
#define FIXED_FROM_INT(x) ((x) << FIXED_BITS)
|
||||
#define FIXED_TO_INT(x) ((x) >> FIXED_BITS)
|
||||
|
||||
#define FIXED_FROM_FLOAT(x) ((Fixed)((x)*FIXED_ONE))
|
||||
#define FIXED_TO_FLOAT(x) ((x)/(1.*FIXED_ONE))
|
||||
|
||||
#define FIXED_MUL(x,y) (((int64_t)(x) * (y)) >> FIXED_BITS)
|
||||
#define FIXED_DIV(x,y) (((int64_t)(x) * FIXED_ONE) / (y))
|
||||
|
||||
#define FIXED_DIV2(x) ((x) >> 1)
|
||||
#define FIXED_AVERAGE(x,y) (((x) + (y)) >> 1)
|
||||
|
||||
#define FIXED_FRAC(x) ((x) & ((1 << FIXED_BITS)-1))
|
||||
#define FIXED_TRUNC(x) ((x) & ~((1 << FIXED_BITS)-1))
|
||||
|
||||
#define FIXED_FROM_INT_FLOAT(x,f) (Fixed)((x)*(FIXED_ONE*(f)))
|
||||
|
||||
typedef int32_t Angle;
|
||||
|
||||
#define ANGLE_BITS 9
|
||||
|
||||
#if ANGLE_BITS < 8
|
||||
# error ANGLE_BITS must be at least 8
|
||||
#endif
|
||||
|
||||
#define ANGLE_2PI (1 << ANGLE_BITS)
|
||||
#define ANGLE_PI (1 << (ANGLE_BITS-1))
|
||||
#define ANGLE_PI2 (1 << (ANGLE_BITS-2))
|
||||
#define ANGLE_PI4 (1 << (ANGLE_BITS-3))
|
||||
|
||||
#define ANGLE_FROM_FLOAT(x) (Angle)((x)*ANGLE_PI/M_PI)
|
||||
#define ANGLE_TO_FLOAT(x) ((x)*M_PI/ANGLE_PI)
|
||||
|
||||
#if ANGLE_BITS <= FIXED_BITS
|
||||
# define ANGLE_FROM_FIXED(x) (Angle)((x) >> (FIXED_BITS - ANGLE_BITS))
|
||||
# define ANGLE_TO_FIXED(x) (Fixed)((x) << (FIXED_BITS - ANGLE_BITS))
|
||||
#else
|
||||
# define ANGLE_FROM_FIXED(x) (Angle)((x) << (ANGLE_BITS - FIXED_BITS))
|
||||
# define ANGLE_TO_FIXED(x) (Fixed)((x) >> (ANGLE_BITS - FIXED_BITS))
|
||||
#endif
|
||||
|
||||
static Fixed angle_sin_tab[ANGLE_2PI+1];
|
||||
|
||||
static void init_angles(void)
|
||||
{
|
||||
int nn;
|
||||
for (nn = 0; nn < ANGLE_2PI+1; nn++) {
|
||||
double radians = nn*M_PI/ANGLE_PI;
|
||||
angle_sin_tab[nn] = FIXED_FROM_FLOAT(sin(radians));
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ Fixed angle_sin( Angle a )
|
||||
{
|
||||
return angle_sin_tab[(uint32_t)a & (ANGLE_2PI-1)];
|
||||
}
|
||||
|
||||
static __inline__ Fixed angle_cos( Angle a )
|
||||
{
|
||||
return angle_sin(a + ANGLE_PI2);
|
||||
}
|
||||
|
||||
static __inline__ Fixed fixed_sin( Fixed f )
|
||||
{
|
||||
return angle_sin(ANGLE_FROM_FIXED(f));
|
||||
}
|
||||
|
||||
static __inline__ Fixed fixed_cos( Fixed f )
|
||||
{
|
||||
return angle_cos(ANGLE_FROM_FIXED(f));
|
||||
}
|
||||
|
||||
/* Color palette used for rendering the plasma */
|
||||
#define PALETTE_BITS 8
|
||||
#define PALETTE_SIZE (1 << PALETTE_BITS)
|
||||
|
||||
#if PALETTE_BITS > FIXED_BITS
|
||||
# error PALETTE_BITS must be smaller than FIXED_BITS
|
||||
#endif
|
||||
|
||||
static uint16_t palette[PALETTE_SIZE];
|
||||
|
||||
static uint16_t make565(int red, int green, int blue)
|
||||
{
|
||||
return (uint16_t)( ((red << 8) & 0xf800) |
|
||||
((green << 3) & 0x07e0) |
|
||||
((blue >> 3) & 0x001f) );
|
||||
}
|
||||
|
||||
static void init_palette(void)
|
||||
{
|
||||
int nn, mm = 0;
|
||||
/* fun with colors */
|
||||
for (nn = 0; nn < PALETTE_SIZE/4; nn++) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(255, jj, 255-jj);
|
||||
}
|
||||
|
||||
for ( mm = nn; nn < PALETTE_SIZE/2; nn++ ) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(255-jj, 255, jj);
|
||||
}
|
||||
|
||||
for ( mm = nn; nn < PALETTE_SIZE*3/4; nn++ ) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(0, 255-jj, 255);
|
||||
}
|
||||
|
||||
for ( mm = nn; nn < PALETTE_SIZE; nn++ ) {
|
||||
int jj = (nn-mm)*4*255/PALETTE_SIZE;
|
||||
palette[nn] = make565(jj, 0, 255);
|
||||
}
|
||||
}
|
||||
|
||||
static __inline__ uint16_t palette_from_fixed( Fixed x )
|
||||
{
|
||||
if (x < 0) x = -x;
|
||||
if (x >= FIXED_ONE) x = FIXED_ONE-1;
|
||||
int idx = FIXED_FRAC(x) >> (FIXED_BITS - PALETTE_BITS);
|
||||
return palette[idx & (PALETTE_SIZE-1)];
|
||||
}
|
||||
|
||||
/* Angles expressed as fixed point radians */
|
||||
|
||||
static void init_tables(void)
|
||||
{
|
||||
init_palette();
|
||||
init_angles();
|
||||
}
|
||||
|
||||
static void fill_plasma(ANativeWindow_Buffer* buffer, double t)
|
||||
{
|
||||
Fixed yt1 = FIXED_FROM_FLOAT(t/1230.);
|
||||
Fixed yt2 = yt1;
|
||||
Fixed xt10 = FIXED_FROM_FLOAT(t/3000.);
|
||||
Fixed xt20 = xt10;
|
||||
|
||||
#define YT1_INCR FIXED_FROM_FLOAT(1/100.)
|
||||
#define YT2_INCR FIXED_FROM_FLOAT(1/163.)
|
||||
|
||||
void* pixels = buffer->bits;
|
||||
//LOGI("width=%d height=%d stride=%d format=%d", buffer->width, buffer->height,
|
||||
// buffer->stride, buffer->format);
|
||||
|
||||
int yy;
|
||||
for (yy = 0; yy < buffer->height; yy++) {
|
||||
uint16_t* line = (uint16_t*)pixels;
|
||||
Fixed base = fixed_sin(yt1) + fixed_sin(yt2);
|
||||
Fixed xt1 = xt10;
|
||||
Fixed xt2 = xt20;
|
||||
|
||||
yt1 += YT1_INCR;
|
||||
yt2 += YT2_INCR;
|
||||
|
||||
#define XT1_INCR FIXED_FROM_FLOAT(1/173.)
|
||||
#define XT2_INCR FIXED_FROM_FLOAT(1/242.)
|
||||
|
||||
#if OPTIMIZE_WRITES
|
||||
/* optimize memory writes by generating one aligned 32-bit store
|
||||
* for every pair of pixels.
|
||||
*/
|
||||
uint16_t* line_end = line + buffer->width;
|
||||
|
||||
if (line < line_end) {
|
||||
if (((uint32_t)(uintptr_t)line & 3) != 0) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
line[0] = palette_from_fixed(ii >> 2);
|
||||
line++;
|
||||
}
|
||||
|
||||
while (line + 2 <= line_end) {
|
||||
Fixed i1 = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
Fixed i2 = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
uint32_t pixel = ((uint32_t)palette_from_fixed(i1 >> 2) << 16) |
|
||||
(uint32_t)palette_from_fixed(i2 >> 2);
|
||||
|
||||
((uint32_t*)line)[0] = pixel;
|
||||
line += 2;
|
||||
}
|
||||
|
||||
if (line < line_end) {
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
line[0] = palette_from_fixed(ii >> 2);
|
||||
line++;
|
||||
}
|
||||
}
|
||||
#else /* !OPTIMIZE_WRITES */
|
||||
int xx;
|
||||
for (xx = 0; xx < buffer->width; xx++) {
|
||||
|
||||
Fixed ii = base + fixed_sin(xt1) + fixed_sin(xt2);
|
||||
|
||||
xt1 += XT1_INCR;
|
||||
xt2 += XT2_INCR;
|
||||
|
||||
line[xx] = palette_from_fixed(ii / 4);
|
||||
}
|
||||
#endif /* !OPTIMIZE_WRITES */
|
||||
|
||||
// go to next line
|
||||
pixels = (uint16_t*)pixels + buffer->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/* simple stats management */
|
||||
typedef struct {
|
||||
double renderTime;
|
||||
double frameTime;
|
||||
} FrameStats;
|
||||
|
||||
#define MAX_FRAME_STATS 200
|
||||
#define MAX_PERIOD_MS 1500
|
||||
|
||||
typedef struct {
|
||||
double firstTime;
|
||||
double lastTime;
|
||||
double frameTime;
|
||||
|
||||
int firstFrame;
|
||||
int numFrames;
|
||||
FrameStats frames[ MAX_FRAME_STATS ];
|
||||
} Stats;
|
||||
|
||||
static void
|
||||
stats_init( Stats* s )
|
||||
{
|
||||
s->lastTime = now_ms();
|
||||
s->firstTime = 0.;
|
||||
s->firstFrame = 0;
|
||||
s->numFrames = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
stats_startFrame( Stats* s )
|
||||
{
|
||||
s->frameTime = now_ms();
|
||||
}
|
||||
|
||||
static void
|
||||
stats_endFrame( Stats* s )
|
||||
{
|
||||
double now = now_ms();
|
||||
double renderTime = now - s->frameTime;
|
||||
double frameTime = now - s->lastTime;
|
||||
int nn;
|
||||
|
||||
if (now - s->firstTime >= MAX_PERIOD_MS) {
|
||||
if (s->numFrames > 0) {
|
||||
double minRender, maxRender, avgRender;
|
||||
double minFrame, maxFrame, avgFrame;
|
||||
int count;
|
||||
|
||||
nn = s->firstFrame;
|
||||
minRender = maxRender = avgRender = s->frames[nn].renderTime;
|
||||
minFrame = maxFrame = avgFrame = s->frames[nn].frameTime;
|
||||
for (count = s->numFrames; count > 0; count-- ) {
|
||||
nn += 1;
|
||||
if (nn >= MAX_FRAME_STATS)
|
||||
nn -= MAX_FRAME_STATS;
|
||||
double render = s->frames[nn].renderTime;
|
||||
if (render < minRender) minRender = render;
|
||||
if (render > maxRender) maxRender = render;
|
||||
double frame = s->frames[nn].frameTime;
|
||||
if (frame < minFrame) minFrame = frame;
|
||||
if (frame > maxFrame) maxFrame = frame;
|
||||
avgRender += render;
|
||||
avgFrame += frame;
|
||||
}
|
||||
avgRender /= s->numFrames;
|
||||
avgFrame /= s->numFrames;
|
||||
|
||||
LOGI("frame/s (avg,min,max) = (%.1f,%.1f,%.1f) "
|
||||
"render time ms (avg,min,max) = (%.1f,%.1f,%.1f)\n",
|
||||
1000./avgFrame, 1000./maxFrame, 1000./minFrame,
|
||||
avgRender, minRender, maxRender);
|
||||
}
|
||||
s->numFrames = 0;
|
||||
s->firstFrame = 0;
|
||||
s->firstTime = now;
|
||||
}
|
||||
|
||||
nn = s->firstFrame + s->numFrames;
|
||||
if (nn >= MAX_FRAME_STATS)
|
||||
nn -= MAX_FRAME_STATS;
|
||||
|
||||
s->frames[nn].renderTime = renderTime;
|
||||
s->frames[nn].frameTime = frameTime;
|
||||
|
||||
if (s->numFrames < MAX_FRAME_STATS) {
|
||||
s->numFrames += 1;
|
||||
} else {
|
||||
s->firstFrame += 1;
|
||||
if (s->firstFrame >= MAX_FRAME_STATS)
|
||||
s->firstFrame -= MAX_FRAME_STATS;
|
||||
}
|
||||
|
||||
s->lastTime = now;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
struct engine {
|
||||
struct android_app* app;
|
||||
|
||||
Stats stats;
|
||||
|
||||
int animating;
|
||||
};
|
||||
|
||||
ANativeWindow_Buffer* gAGraphicsBuffer = 0;
|
||||
|
||||
static int64_t start_ms;
|
||||
static void engine_draw_frame(struct engine* engine) {
|
||||
if (engine->app->window == NULL) {
|
||||
// No window.
|
||||
return;
|
||||
}
|
||||
|
||||
ANativeWindow_Buffer buffer;
|
||||
if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0) {
|
||||
LOGW("Unable to lock window buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
stats_startFrame(&engine->stats);
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
int64_t time_ms = (((int64_t)now.tv_sec)*1000000000LL + now.tv_nsec)/1000000;
|
||||
time_ms -= start_ms;
|
||||
|
||||
/* Now fill the values with a nice little plasma */
|
||||
#if 0
|
||||
fill_plasma(&buffer, time_ms);
|
||||
#else
|
||||
// LOGE("Fl::flush()");
|
||||
gAGraphicsBuffer = &buffer;
|
||||
Fl::damage(FL_DAMAGE_ALL);
|
||||
win->redraw();
|
||||
Fl::flush();
|
||||
#endif
|
||||
|
||||
ANativeWindow_unlockAndPost(engine->app->window);
|
||||
|
||||
stats_endFrame(&engine->stats);
|
||||
}
|
||||
|
||||
static void engine_term_display(struct engine* engine) {
|
||||
engine->animating = 0;
|
||||
}
|
||||
|
||||
static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) {
|
||||
struct engine* engine = (struct engine*)app->userData;
|
||||
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
|
||||
engine->animating = 1;
|
||||
Fl::e_x = Fl::e_x_root = AMotionEvent_getX(event, 0) * 600 / ANativeWindow_getWidth(app->window);
|
||||
Fl::e_y = Fl::e_y_root = AMotionEvent_getY(event, 0) * 800 / ANativeWindow_getHeight(app->window);
|
||||
Fl::e_state = FL_BUTTON1;
|
||||
Fl::e_keysym = FL_Button+1;
|
||||
if (AMotionEvent_getAction(event)==AMOTION_EVENT_ACTION_DOWN) {
|
||||
Fl::e_is_click = 1;
|
||||
Fl::handle(FL_PUSH, Fl::first_window());
|
||||
LOGE("Mouse push %d at %d, %d", Fl::event_button(), Fl::event_x(), Fl::event_y());
|
||||
} else if (AMotionEvent_getAction(event)==AMOTION_EVENT_ACTION_MOVE) {
|
||||
Fl::handle(FL_DRAG, Fl::first_window());
|
||||
} else if (AMotionEvent_getAction(event)==AMOTION_EVENT_ACTION_UP) {
|
||||
Fl::e_state = 0;
|
||||
Fl::handle(FL_RELEASE, Fl::first_window());
|
||||
}
|
||||
return 1;
|
||||
} else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {
|
||||
LOGI("Key event: action=%d keyCode=%d metaState=0x%x",
|
||||
AKeyEvent_getAction(event),
|
||||
AKeyEvent_getKeyCode(event),
|
||||
AKeyEvent_getMetaState(event));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void engine_handle_cmd(struct android_app* app, int32_t cmd) {
|
||||
static int32_t format = WINDOW_FORMAT_RGB_565;
|
||||
struct engine* engine = (struct engine*)app->userData;
|
||||
switch (cmd) {
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
if (engine->app->window != NULL) {
|
||||
// fill_plasma() assumes 565 format, get it here
|
||||
format = ANativeWindow_getFormat(app->window);
|
||||
ANativeWindow_setBuffersGeometry(app->window,
|
||||
#if 1
|
||||
600, //ANativeWindow_getWidth(app->window),
|
||||
800, //ANativeWindow_getHeight(app->window),
|
||||
#else
|
||||
ANativeWindow_getWidth(app->window),
|
||||
ANativeWindow_getHeight(app->window),
|
||||
#endif
|
||||
WINDOW_FORMAT_RGB_565);
|
||||
engine_draw_frame(engine);
|
||||
}
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
engine_term_display(engine);
|
||||
ANativeWindow_setBuffersGeometry(app->window,
|
||||
#if 1
|
||||
600, //ANativeWindow_getWidth(app->window),
|
||||
800, //ANativeWindow_getHeight(app->window),
|
||||
#else
|
||||
ANativeWindow_getWidth(app->window),
|
||||
ANativeWindow_getHeight(app->window),
|
||||
#endif
|
||||
format);
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
engine->animating = 0;
|
||||
engine_draw_frame(engine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void android_main(struct android_app* state) {
|
||||
static int init;
|
||||
|
||||
struct engine engine;
|
||||
|
||||
memset(&engine, 0, sizeof(engine));
|
||||
state->userData = &engine;
|
||||
state->onAppCmd = engine_handle_cmd;
|
||||
state->onInputEvent = engine_handle_input;
|
||||
engine.app = state;
|
||||
|
||||
if (!init) {
|
||||
init_tables();
|
||||
init = 1;
|
||||
}
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
start_ms = (((int64_t)now.tv_sec)*1000000000LL + now.tv_nsec)/1000000;
|
||||
|
||||
stats_init(&engine.stats);
|
||||
|
||||
win = new Fl_Window(10, 10, 600, 400, "Hallo");
|
||||
btn = new Fl_Button(190, 200, 220, 35, "Hello, world!");
|
||||
btn = new Fl_Button(190, 240, 220, 35, "O/\\!*()|");
|
||||
win->show();
|
||||
|
||||
|
||||
// loop waiting for stuff to do.
|
||||
|
||||
while (1) {
|
||||
// Read all pending events.
|
||||
int ident;
|
||||
int events;
|
||||
struct android_poll_source* source;
|
||||
|
||||
// If not animating, we will block forever waiting for events.
|
||||
// If animating, we loop until all events are read, then continue
|
||||
// to draw the next frame of animation.
|
||||
while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events,
|
||||
(void**)&source)) >= 0) {
|
||||
|
||||
// Process this event.
|
||||
if (source != NULL) {
|
||||
source->process(state, source);
|
||||
}
|
||||
|
||||
// Check if we are exiting.
|
||||
if (state->destroyRequested != 0) {
|
||||
LOGI("Engine thread destroy requested!");
|
||||
engine_term_display(&engine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (engine.animating) {
|
||||
engine_draw_frame(&engine);
|
||||
}
|
||||
}
|
||||
}
|
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 |
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Hello Android</string>
|
||||
</resources>
|
|
@ -0,0 +1,17 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.0.1'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
google()
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
#Sun Feb 05 20:05:03 IST 2017
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
|
@ -0,0 +1,90 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1 @@
|
|||
include ':app'
|
|
@ -1,95 +1,35 @@
|
|||
README.Android.txt - Building and using FLTK with CMake for the Android platform
|
||||
--------------------------------------------------------------------------------
|
||||
README.Android.txt - Building and using FLTK with Android Studio 3
|
||||
------------------------------------------------------------------
|
||||
|
||||
|
||||
WARNING: BUILDING FLTK FOR ANDROID IS WORK IN PROGRESS IN A PRETTY EARLY STAGE.
|
||||
WARNING: THIS FILE MERELY CONTAINS A ROUGH LAYOUT AND SOME SIMPLE IDEAS HOW
|
||||
FLTK WILL BUILD FOR ANDROID EVENTUALLY.
|
||||
WARNING: FLTK FOR ANDROID IS WORK IN PROGRESS IN A PRETTY EARLY STAGE.
|
||||
|
||||
|
||||
CONTENTS
|
||||
==========
|
||||
|
||||
1 INTRODUCTION TO CMAKE
|
||||
2 USING CMAKE TO BUILD FLTK
|
||||
2.1 Prerequisites
|
||||
2.2 Options
|
||||
2.3 Building under Linux with Unix Makefiles
|
||||
2.4 Crosscompiling
|
||||
3 USING CMAKE WITH FLTK
|
||||
3.1 Library names
|
||||
3.2 Using Fluid files
|
||||
4 DOCUMENT HISTORY
|
||||
1 Building FLTK with Android Studio 3
|
||||
2 DOCUMENT HISTORY
|
||||
|
||||
|
||||
INTRODUCTION TO CMAKE
|
||||
=======================
|
||||
BUILDING FLTK SAMPLE WITH ANDROID STUDIO 3
|
||||
============================================
|
||||
|
||||
Please read README.CMake.txt in the same directory to learn about CMake.
|
||||
March 2nd, 2018: This is a first commit to SVN to have a file structure in
|
||||
place. None of this compiles or runs at this point.
|
||||
|
||||
More information on CMake can be found on its web site http://www.cmake.org.
|
||||
There is no need to ever write a single line of Java.
|
||||
|
||||
Download and install AndroidStudio on your developer machine. If you use
|
||||
AndroidStudio for the first time, use the IDE to download and build the
|
||||
"Native Plasm" sample app. In the process, all resources required to build
|
||||
FLTK will be installed. Once Native Plasm runs on your emulator or physical
|
||||
device, you are ready to install FLTK.
|
||||
|
||||
|
||||
USING CMAKE TO BUILD FLTK FOR ANDROID ON OS X AND LINUX
|
||||
=========================================================
|
||||
|
||||
|
||||
PREREQUISITES
|
||||
---------------
|
||||
|
||||
Get CMake 3.2.3 or newer.
|
||||
|
||||
Go to https://github.com/taka-no-me/android-cmake and download the content
|
||||
as a zip file.
|
||||
|
||||
Go into the FLTK base directory, then:
|
||||
|
||||
> mkdir build
|
||||
> cd build
|
||||
> unzip ~/Downloads/android-cmake-master.zip
|
||||
> mv android-cmake-master Android
|
||||
> cd Android
|
||||
> cmake -DCMAKE_TOOLCHAIN_FILE=android.toolchain.cmake -DANDROID_NDK=~/dev/android-ndk-r10e -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="armeabi-v7a" ../..
|
||||
> make
|
||||
|
||||
At this time, compilation will fail relatively soon, but we are working hard to make porting easier,
|
||||
and to prove our work, we poert FLTK to Android at the same time. Using CMake to cross-compile
|
||||
is one of the many steps required to make life easier and maintenance more flexible.
|
||||
|
||||
|
||||
OPTIONS
|
||||
---------
|
||||
Options can be specified to cmake with the -D flag:
|
||||
|
||||
cmake -D <OPTION_NAME>=<OPTION_VALUE>
|
||||
|
||||
|
||||
|
||||
WARNING: Old outdated information:
|
||||
|
||||
Building FLTK on Android (pre alpha, don't expect miracles)
|
||||
|
||||
|
||||
1. cd to the root of its source code ('cd android/hello')
|
||||
|
||||
2. Run ndk-build. This builds the native code, and should result in some .so files being put into the libs directory.
|
||||
|
||||
3. android update project --path . --name hello
|
||||
|
||||
4. ant debug (or similar). This will build the Java code and create an .apk. Crucially, the build process will pick up the .so files left within the libs directory and include them into the .apk.
|
||||
|
||||
5. adb install bin/name-of-project.apk
|
||||
|
||||
6. Then launch as normal using the Android GUI or using an am start command such as you give.
|
||||
6a. emulator -avd Intel_x86
|
||||
|
||||
7. adb shell am start -n com.example.native_activity
|
||||
|
||||
8. adb uninstall com.example.native_activity
|
||||
|
||||
TODO: there will be an Android Studio project in the Android/ directory soon.
|
||||
|
||||
DOCUMENT HISTORY
|
||||
==================
|
||||
|
||||
Mar 2 2018 - matt: rewriting Android port and documentation from scratch
|
||||
Feb 9 2016 - matt: recreated document with more warnings and active support
|
||||
|
|
Loading…
Reference in New Issue