mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-24 04:55:52 +00:00
698 lines
22 KiB
Java
698 lines
22 KiB
Java
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
|
|
Portions of this file are from JUCE.
|
|
Copyright (c) 2013 - Raw Material Software Ltd.
|
|
Please visit http://www.juce.com
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
package com.beast;
|
|
|
|
import android.app.Activity;
|
|
import android.app.AlertDialog;
|
|
import android.content.DialogInterface;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.res.Configuration;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.view.*;
|
|
import android.view.inputmethod.BaseInputConnection;
|
|
import android.view.inputmethod.EditorInfo;
|
|
import android.view.inputmethod.InputConnection;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.graphics.*;
|
|
import android.opengl.*;
|
|
import android.text.ClipboardManager;
|
|
import android.text.InputType;
|
|
import java.io.BufferedInputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.URL;
|
|
import java.net.HttpURLConnection;
|
|
import javax.microedition.khronos.egl.EGLConfig;
|
|
import javax.microedition.khronos.opengles.GL10;
|
|
import android.media.MediaScannerConnection;
|
|
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
|
|
|
//==============================================================================
|
|
public final class BeastAppActivity extends Activity
|
|
{
|
|
//==============================================================================
|
|
static
|
|
{
|
|
System.loadLibrary ("beast_jni");
|
|
}
|
|
|
|
@Override
|
|
public final void onCreate (Bundle savedInstanceState)
|
|
{
|
|
super.onCreate (savedInstanceState);
|
|
|
|
viewHolder = new ViewHolder (this);
|
|
setContentView (viewHolder);
|
|
|
|
setVolumeControlStream (AudioManager.STREAM_MUSIC);
|
|
}
|
|
|
|
@Override
|
|
protected final void onDestroy()
|
|
{
|
|
quitApp();
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
protected final void onPause()
|
|
{
|
|
if (viewHolder != null)
|
|
viewHolder.onPause();
|
|
|
|
suspendApp();
|
|
super.onPause();
|
|
}
|
|
|
|
@Override
|
|
protected final void onResume()
|
|
{
|
|
super.onResume();
|
|
|
|
if (viewHolder != null)
|
|
viewHolder.onResume();
|
|
|
|
resumeApp();
|
|
}
|
|
|
|
@Override
|
|
public void onConfigurationChanged (Configuration cfg)
|
|
{
|
|
super.onConfigurationChanged (cfg);
|
|
setContentView (viewHolder);
|
|
}
|
|
|
|
private void callAppLauncher()
|
|
{
|
|
launchApp (getApplicationInfo().publicSourceDir,
|
|
getApplicationInfo().dataDir);
|
|
}
|
|
|
|
//==============================================================================
|
|
private native void launchApp (String appFile, String appDataDir);
|
|
private native void quitApp();
|
|
private native void suspendApp();
|
|
private native void resumeApp();
|
|
private native void setScreenSize (int screenWidth, int screenHeight);
|
|
|
|
//==============================================================================
|
|
public native void deliverMessage (long value);
|
|
private android.os.Handler messageHandler = new android.os.Handler();
|
|
|
|
public final void postMessage (long value)
|
|
{
|
|
messageHandler.post (new MessageCallback (value));
|
|
}
|
|
|
|
private final class MessageCallback implements Runnable
|
|
{
|
|
public MessageCallback (long value_) { value = value_; }
|
|
public final void run() { deliverMessage (value); }
|
|
|
|
private long value;
|
|
}
|
|
|
|
//==============================================================================
|
|
private ViewHolder viewHolder;
|
|
|
|
public final ComponentPeerView createNewView (boolean opaque)
|
|
{
|
|
ComponentPeerView v = new ComponentPeerView (this, opaque);
|
|
viewHolder.addView (v);
|
|
return v;
|
|
}
|
|
|
|
public final void deleteView (ComponentPeerView view)
|
|
{
|
|
ViewGroup group = (ViewGroup) (view.getParent());
|
|
|
|
if (group != null)
|
|
group.removeView (view);
|
|
}
|
|
|
|
final class ViewHolder extends ViewGroup
|
|
{
|
|
public ViewHolder (Context context)
|
|
{
|
|
super (context);
|
|
setDescendantFocusability (ViewGroup.FOCUS_AFTER_DESCENDANTS);
|
|
setFocusable (false);
|
|
}
|
|
|
|
protected final void onLayout (boolean changed, int left, int top, int right, int bottom)
|
|
{
|
|
setScreenSize (getWidth(), getHeight());
|
|
|
|
if (isFirstResize)
|
|
{
|
|
isFirstResize = false;
|
|
callAppLauncher();
|
|
}
|
|
}
|
|
|
|
public final void onPause()
|
|
{
|
|
for (int i = getChildCount(); --i >= 0;)
|
|
{
|
|
View v = getChildAt (i);
|
|
|
|
if (v instanceof ComponentPeerView)
|
|
((ComponentPeerView) v).onPause();
|
|
}
|
|
}
|
|
|
|
public final void onResume()
|
|
{
|
|
for (int i = getChildCount(); --i >= 0;)
|
|
{
|
|
View v = getChildAt (i);
|
|
|
|
if (v instanceof ComponentPeerView)
|
|
((ComponentPeerView) v).onResume();
|
|
}
|
|
}
|
|
|
|
private boolean isFirstResize = true;
|
|
}
|
|
|
|
public final void excludeClipRegion (android.graphics.Canvas canvas, float left, float top, float right, float bottom)
|
|
{
|
|
canvas.clipRect (left, top, right, bottom, android.graphics.Region.Op.DIFFERENCE);
|
|
}
|
|
|
|
//==============================================================================
|
|
public final String getClipboardContent()
|
|
{
|
|
ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
|
|
return clipboard.getText().toString();
|
|
}
|
|
|
|
public final void setClipboardContent (String newText)
|
|
{
|
|
ClipboardManager clipboard = (ClipboardManager) getSystemService (CLIPBOARD_SERVICE);
|
|
clipboard.setText (newText);
|
|
}
|
|
|
|
//==============================================================================
|
|
public final void showMessageBox (String title, String message, final long callback)
|
|
{
|
|
AlertDialog.Builder builder = new AlertDialog.Builder (this);
|
|
builder.setTitle (title)
|
|
.setMessage (message)
|
|
.setCancelable (true)
|
|
.setPositiveButton ("OK", new DialogInterface.OnClickListener()
|
|
{
|
|
public void onClick (DialogInterface dialog, int id)
|
|
{
|
|
dialog.cancel();
|
|
BeastAppActivity.this.alertDismissed (callback, 0);
|
|
}
|
|
});
|
|
|
|
builder.create().show();
|
|
}
|
|
|
|
public final void showOkCancelBox (String title, String message, final long callback)
|
|
{
|
|
AlertDialog.Builder builder = new AlertDialog.Builder (this);
|
|
builder.setTitle (title)
|
|
.setMessage (message)
|
|
.setCancelable (true)
|
|
.setPositiveButton ("OK", new DialogInterface.OnClickListener()
|
|
{
|
|
public void onClick (DialogInterface dialog, int id)
|
|
{
|
|
dialog.cancel();
|
|
BeastAppActivity.this.alertDismissed (callback, 1);
|
|
}
|
|
})
|
|
.setNegativeButton ("Cancel", new DialogInterface.OnClickListener()
|
|
{
|
|
public void onClick (DialogInterface dialog, int id)
|
|
{
|
|
dialog.cancel();
|
|
BeastAppActivity.this.alertDismissed (callback, 0);
|
|
}
|
|
});
|
|
|
|
builder.create().show();
|
|
}
|
|
|
|
public final void showYesNoCancelBox (String title, String message, final long callback)
|
|
{
|
|
AlertDialog.Builder builder = new AlertDialog.Builder (this);
|
|
builder.setTitle (title)
|
|
.setMessage (message)
|
|
.setCancelable (true)
|
|
.setPositiveButton ("Yes", new DialogInterface.OnClickListener()
|
|
{
|
|
public void onClick (DialogInterface dialog, int id)
|
|
{
|
|
dialog.cancel();
|
|
BeastAppActivity.this.alertDismissed (callback, 1);
|
|
}
|
|
})
|
|
.setNegativeButton ("No", new DialogInterface.OnClickListener()
|
|
{
|
|
public void onClick (DialogInterface dialog, int id)
|
|
{
|
|
dialog.cancel();
|
|
BeastAppActivity.this.alertDismissed (callback, 2);
|
|
}
|
|
})
|
|
.setNeutralButton ("Cancel", new DialogInterface.OnClickListener()
|
|
{
|
|
public void onClick (DialogInterface dialog, int id)
|
|
{
|
|
dialog.cancel();
|
|
BeastAppActivity.this.alertDismissed (callback, 0);
|
|
}
|
|
});
|
|
|
|
builder.create().show();
|
|
}
|
|
|
|
public native void alertDismissed (long callback, int id);
|
|
|
|
//==============================================================================
|
|
public final class ComponentPeerView extends ViewGroup
|
|
implements View.OnFocusChangeListener
|
|
{
|
|
public ComponentPeerView (Context context, boolean opaque_)
|
|
{
|
|
super (context);
|
|
setWillNotDraw (false);
|
|
opaque = opaque_;
|
|
|
|
setFocusable (true);
|
|
setFocusableInTouchMode (true);
|
|
setOnFocusChangeListener (this);
|
|
requestFocus();
|
|
}
|
|
|
|
//==============================================================================
|
|
private native void handlePaint (Canvas canvas);
|
|
|
|
@Override
|
|
public void draw (Canvas canvas)
|
|
{
|
|
super.draw (canvas);
|
|
handlePaint (canvas);
|
|
}
|
|
|
|
@Override
|
|
public boolean isOpaque()
|
|
{
|
|
return opaque;
|
|
}
|
|
|
|
private boolean opaque;
|
|
|
|
//==============================================================================
|
|
private native void handleMouseDown (int index, float x, float y, long time);
|
|
private native void handleMouseDrag (int index, float x, float y, long time);
|
|
private native void handleMouseUp (int index, float x, float y, long time);
|
|
|
|
@Override
|
|
public boolean onTouchEvent (MotionEvent event)
|
|
{
|
|
int action = event.getAction();
|
|
long time = event.getEventTime();
|
|
|
|
switch (action & MotionEvent.ACTION_MASK)
|
|
{
|
|
case MotionEvent.ACTION_DOWN:
|
|
handleMouseDown (event.getPointerId(0), event.getX(), event.getY(), time);
|
|
return true;
|
|
|
|
case MotionEvent.ACTION_CANCEL:
|
|
case MotionEvent.ACTION_UP:
|
|
handleMouseUp (event.getPointerId(0), event.getX(), event.getY(), time);
|
|
return true;
|
|
|
|
case MotionEvent.ACTION_MOVE:
|
|
{
|
|
int n = event.getPointerCount();
|
|
for (int i = 0; i < n; ++i)
|
|
handleMouseDrag (event.getPointerId(i), event.getX(i), event.getY(i), time);
|
|
|
|
return true;
|
|
}
|
|
|
|
case MotionEvent.ACTION_POINTER_UP:
|
|
{
|
|
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
|
handleMouseUp (event.getPointerId(i), event.getX(i), event.getY(i), time);
|
|
return true;
|
|
}
|
|
|
|
case MotionEvent.ACTION_POINTER_DOWN:
|
|
{
|
|
int i = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
|
|
handleMouseDown (event.getPointerId(i), event.getX(i), event.getY(i), time);
|
|
return true;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//==============================================================================
|
|
private native void handleKeyDown (int keycode, int textchar);
|
|
private native void handleKeyUp (int keycode, int textchar);
|
|
|
|
public void showKeyboard (boolean shouldShow)
|
|
{
|
|
InputMethodManager imm = (InputMethodManager) getSystemService (Context.INPUT_METHOD_SERVICE);
|
|
|
|
if (imm != null)
|
|
{
|
|
if (shouldShow)
|
|
imm.showSoftInput (this, InputMethodManager.SHOW_FORCED);
|
|
else
|
|
imm.hideSoftInputFromWindow (getWindowToken(), 0);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyDown (int keyCode, KeyEvent event)
|
|
{
|
|
handleKeyDown (keyCode, event.getUnicodeChar());
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyUp (int keyCode, KeyEvent event)
|
|
{
|
|
handleKeyUp (keyCode, event.getUnicodeChar());
|
|
return true;
|
|
}
|
|
|
|
// this is here to make keyboard entry work on a Galaxy Tab2 10.1
|
|
@Override
|
|
public InputConnection onCreateInputConnection (EditorInfo outAttrs)
|
|
{
|
|
outAttrs.actionLabel = "";
|
|
outAttrs.hintText = "";
|
|
outAttrs.initialCapsMode = 0;
|
|
outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
|
|
outAttrs.label = "";
|
|
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI;
|
|
outAttrs.inputType = InputType.TYPE_NULL;
|
|
|
|
return new BaseInputConnection (this, false);
|
|
}
|
|
|
|
//==============================================================================
|
|
@Override
|
|
protected void onSizeChanged (int w, int h, int oldw, int oldh)
|
|
{
|
|
super.onSizeChanged (w, h, oldw, oldh);
|
|
viewSizeChanged();
|
|
}
|
|
|
|
@Override
|
|
protected void onLayout (boolean changed, int left, int top, int right, int bottom)
|
|
{
|
|
for (int i = getChildCount(); --i >= 0;)
|
|
requestTransparentRegion (getChildAt (i));
|
|
}
|
|
|
|
private native void viewSizeChanged();
|
|
|
|
@Override
|
|
public void onFocusChange (View v, boolean hasFocus)
|
|
{
|
|
if (v == this)
|
|
focusChanged (hasFocus);
|
|
}
|
|
|
|
private native void focusChanged (boolean hasFocus);
|
|
|
|
public void setViewName (String newName) {}
|
|
|
|
public boolean isVisible() { return getVisibility() == VISIBLE; }
|
|
public void setVisible (boolean b) { setVisibility (b ? VISIBLE : INVISIBLE); }
|
|
|
|
public boolean containsPoint (int x, int y)
|
|
{
|
|
return true; //xxx needs to check overlapping views
|
|
}
|
|
|
|
public final void onPause()
|
|
{
|
|
for (int i = getChildCount(); --i >= 0;)
|
|
{
|
|
View v = getChildAt (i);
|
|
|
|
if (v instanceof OpenGLView)
|
|
((OpenGLView) v).onPause();
|
|
}
|
|
}
|
|
|
|
public final void onResume()
|
|
{
|
|
for (int i = getChildCount(); --i >= 0;)
|
|
{
|
|
View v = getChildAt (i);
|
|
|
|
if (v instanceof OpenGLView)
|
|
((OpenGLView) v).onResume();
|
|
}
|
|
}
|
|
|
|
public OpenGLView createGLView()
|
|
{
|
|
OpenGLView glView = new OpenGLView (getContext());
|
|
addView (glView);
|
|
return glView;
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
public final class OpenGLView extends GLSurfaceView
|
|
implements GLSurfaceView.Renderer
|
|
{
|
|
OpenGLView (Context context)
|
|
{
|
|
super (context);
|
|
setEGLContextClientVersion (2);
|
|
setRenderer (this);
|
|
setRenderMode (RENDERMODE_WHEN_DIRTY);
|
|
}
|
|
|
|
@Override
|
|
public void onSurfaceCreated (GL10 unused, EGLConfig config)
|
|
{
|
|
contextCreated();
|
|
}
|
|
|
|
@Override
|
|
public void onSurfaceChanged (GL10 unused, int width, int height)
|
|
{
|
|
contextChangedSize();
|
|
}
|
|
|
|
@Override
|
|
public void onDrawFrame (GL10 unused)
|
|
{
|
|
render();
|
|
}
|
|
|
|
private native void contextCreated();
|
|
private native void contextChangedSize();
|
|
private native void render();
|
|
}
|
|
|
|
//==============================================================================
|
|
public final int[] renderGlyph (char glyph, Paint paint, android.graphics.Matrix matrix, Rect bounds)
|
|
{
|
|
Path p = new Path();
|
|
paint.getTextPath (String.valueOf (glyph), 0, 1, 0.0f, 0.0f, p);
|
|
|
|
RectF boundsF = new RectF();
|
|
p.computeBounds (boundsF, true);
|
|
matrix.mapRect (boundsF);
|
|
|
|
boundsF.roundOut (bounds);
|
|
bounds.left--;
|
|
bounds.right++;
|
|
|
|
final int w = bounds.width();
|
|
final int h = Math.max (1, bounds.height());
|
|
|
|
Bitmap bm = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
|
|
|
|
Canvas c = new Canvas (bm);
|
|
matrix.postTranslate (-bounds.left, -bounds.top);
|
|
c.setMatrix (matrix);
|
|
c.drawPath (p, paint);
|
|
|
|
final int sizeNeeded = w * h;
|
|
if (cachedRenderArray.length < sizeNeeded)
|
|
cachedRenderArray = new int [sizeNeeded];
|
|
|
|
bm.getPixels (cachedRenderArray, 0, w, 0, 0, w, h);
|
|
bm.recycle();
|
|
return cachedRenderArray;
|
|
}
|
|
|
|
private int[] cachedRenderArray = new int [256];
|
|
|
|
//==============================================================================
|
|
public static class HTTPStream
|
|
{
|
|
public HTTPStream (HttpURLConnection connection_) throws IOException
|
|
{
|
|
connection = connection_;
|
|
inputStream = new BufferedInputStream (connection.getInputStream());
|
|
}
|
|
|
|
public final void release()
|
|
{
|
|
try
|
|
{
|
|
inputStream.close();
|
|
}
|
|
catch (IOException e)
|
|
{}
|
|
|
|
connection.disconnect();
|
|
}
|
|
|
|
public final int read (byte[] buffer, int numBytes)
|
|
{
|
|
int num = 0;
|
|
|
|
try
|
|
{
|
|
num = inputStream.read (buffer, 0, numBytes);
|
|
}
|
|
catch (IOException e)
|
|
{}
|
|
|
|
if (num > 0)
|
|
position += num;
|
|
|
|
return num;
|
|
}
|
|
|
|
public final long getPosition() { return position; }
|
|
public final long getTotalLength() { return -1; }
|
|
public final boolean isExhausted() { return false; }
|
|
public final boolean setPosition (long newPos) { return false; }
|
|
|
|
private HttpURLConnection connection;
|
|
private InputStream inputStream;
|
|
private long position;
|
|
}
|
|
|
|
public static final HTTPStream createHTTPStream (String address, boolean isPost, byte[] postData,
|
|
String headers, int timeOutMs,
|
|
java.lang.StringBuffer responseHeaders)
|
|
{
|
|
try
|
|
{
|
|
HttpURLConnection connection = (HttpURLConnection) (new URL (address).openConnection());
|
|
|
|
if (connection != null)
|
|
{
|
|
try
|
|
{
|
|
if (isPost)
|
|
{
|
|
connection.setConnectTimeout (timeOutMs);
|
|
connection.setDoOutput (true);
|
|
connection.setChunkedStreamingMode (0);
|
|
|
|
OutputStream out = connection.getOutputStream();
|
|
out.write (postData);
|
|
out.flush();
|
|
}
|
|
|
|
return new HTTPStream (connection);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
connection.disconnect();
|
|
}
|
|
}
|
|
}
|
|
catch (Throwable e)
|
|
{}
|
|
|
|
return null;
|
|
}
|
|
|
|
public final void launchURL (String url)
|
|
{
|
|
startActivity (new Intent (Intent.ACTION_VIEW, Uri.parse (url)));
|
|
}
|
|
|
|
public static final String getLocaleValue (boolean isRegion)
|
|
{
|
|
java.util.Locale locale = java.util.Locale.getDefault();
|
|
|
|
return isRegion ? locale.getDisplayCountry (java.util.Locale.US)
|
|
: locale.getDisplayLanguage (java.util.Locale.US);
|
|
}
|
|
|
|
//==============================================================================
|
|
private final class SingleMediaScanner implements MediaScannerConnectionClient
|
|
{
|
|
public SingleMediaScanner (Context context, String filename)
|
|
{
|
|
file = filename;
|
|
msc = new MediaScannerConnection (context, this);
|
|
msc.connect();
|
|
}
|
|
|
|
@Override
|
|
public void onMediaScannerConnected()
|
|
{
|
|
msc.scanFile (file, null);
|
|
}
|
|
|
|
@Override
|
|
public void onScanCompleted (String path, Uri uri)
|
|
{
|
|
msc.disconnect();
|
|
}
|
|
|
|
private MediaScannerConnection msc;
|
|
private String file;
|
|
}
|
|
|
|
public final void scanFile (String filename)
|
|
{
|
|
new SingleMediaScanner (this, filename);
|
|
}
|
|
}
|