diff --git a/distribute.sh b/distribute.sh index 3b898f6233..e5d3b4e49c 100755 --- a/distribute.sh +++ b/distribute.sh @@ -725,6 +725,7 @@ function run_distribute() { debug "Copy libs" try mkdir -p libs/$ARCH try cp -a $BUILD_PATH/libs/* libs/$ARCH/ + rm "$DIST_PATH"/libs/$ARCH/*.jar || true debug "Copy java files from various libs" cp -a $BUILD_PATH/java/* src diff --git a/recipes/sl4acompat/recipe.sh b/recipes/sl4acompat/recipe.sh new file mode 100755 index 0000000000..ed7b12d28b --- /dev/null +++ b/recipes/sl4acompat/recipe.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +URL_sl4acompat=https://github.com/damonkohler/sl4a/raw/dcfa832f80/android/script_for_android_template.zip +DEPS_sl4acompat=() +MD5_sl4acompat=eed6a34bdf63e57a94b3c61528b6a577 +BUILD_sl4acompat=$BUILD_PATH/sl4acompat/ +RECIPE_sl4acompat=$RECIPES_PATH/sl4acompat + +function prebuild_sl4acompat() { + true +} + +function shouldbuild_sl4acompat() { + true +} + +function build_sl4acompat() { + true +} + +function postbuild_sl4acompat() { + cd $BUILD_sl4acompat + mkdir "$DIST_PATH"/libs + cp -a libs/* "$DIST_PATH"/libs/ + cd - + true +} diff --git a/src/build.py b/src/build.py index 0915930a02..add0aa7e4f 100755 --- a/src/build.py +++ b/src/build.py @@ -383,6 +383,8 @@ def make_package(args): ap.add_argument('--add-jar', dest='add_jar', action='append', help='Add a Java .jar to the libs, so you can access its classes with pyjnius. You can specify this argument more than once to include multiple jars') ap.add_argument('--meta-data', dest='meta_data', action='append', help='Custom key=value to add in application metadata') + ap.add_argument('--sl4acompat', dest='sl4acompat', action='store_true', + help='Provide this argument to include SL4A jars and RPC service into the app.') args = ap.parse_args() diff --git a/src/src/org/alanjds/sl4acompat/MinimalService.java b/src/src/org/alanjds/sl4acompat/MinimalService.java new file mode 100644 index 0000000000..8c162a0426 --- /dev/null +++ b/src/src/org/alanjds/sl4acompat/MinimalService.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010 Google Inc. + * Copyright (C) 2014 Alan Justino + * + * 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. + */ + +package org.alanjds.sl4acompat; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.AsyncTask; + +import com.googlecode.android_scripting.AndroidProxy; +import com.googlecode.android_scripting.ForegroundService; +import com.googlecode.android_scripting.Log; +import com.googlecode.android_scripting.NotificationIdFactory; +import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager; + +import java.net.InetSocketAddress; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; + +/** + * A service that allows scripts and the RPC server to run in the background. + * + * @author Alexey Reznichenko (alexey.reznichenko@gmail.com) + * @author Manuel Naranjo (manuel@aircable.net) + * @author Alan Justino (alan.justino@yahoo.com.br) + */ +public class MinimalService extends ForegroundService { + private final static int NOTIFICATION_ID = NotificationIdFactory.create(); + private final CountDownLatch mLatch = new CountDownLatch(1); + private final IBinder mBinder; + + //private InterpreterConfiguration mInterpreterConfiguration; + private RpcReceiverManager mFacadeManager; + private AndroidProxy mProxy; + private InetSocketAddress mAddressWithPort; + private String mProxyAddress; + + public class LocalBinder extends Binder { + public MinimalService getService() { + return MinimalService.this; + } + } + + public MinimalService() { + super(NOTIFICATION_ID); + mBinder = new LocalBinder(); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + @Override + public void onCreate() { + super.onCreate(); + } + + @Override + public void onStart(Intent intent, final int startId) { + Log.v("Starting MinimalService"); + super.onStart(intent, startId); + + mProxy = new AndroidProxy(this, null, true); + mProxyAddress = startProxy(); + mLatch.countDown(); + } + + private String startProxy() { + Log.v("Starting AndroidProxy"); + mAddressWithPort = mProxy.startLocal(); + Log.v("Started AndroidProxy"); + + if (mAddressWithPort == null) { Log.w("Oops: mAddressWithPort == null"); } + Log.v(String.format("Started at %s", mAddressWithPort.toString())); + + String host = mAddressWithPort.getHostName(); + Integer iPort = mAddressWithPort.getPort(); + String port = iPort.toString(); + String handshake = mProxy.getSecret(); + String proxyAddress = String.format("%s@%s:%s", handshake, host, port); + Log.d(String.format("AndroidProxy at: %s@%s:%s", handshake, host, port)); + + Intent netaddress = new Intent("org.alanjds.sl4acompat.STORE_RPC_NETADDRESS"); + netaddress.putExtra("host", host); + netaddress.putExtra("port", port); + netaddress.putExtra("handshake", handshake); + sendBroadcast(netaddress); + Log.v("Sent 'org.alanjds.sl4acompat.STORE_RPC_NETADDRESS'"); + + return proxyAddress; + } + + RpcReceiverManager getRpcReceiverManager() throws InterruptedException { + mLatch.await(); + if (mFacadeManager==null) { // Facade manage may not be available on startup. + mFacadeManager = mProxy.getRpcReceiverManagerFactory() + .getRpcReceiverManagers().get(0); + } + return mFacadeManager; + } + + @Override + protected Notification createNotification() { + PendingIntent contentIntent = PendingIntent.getService(this, 0, new Intent(), 0); + // This contentIntent is a noop. + + Notification notification = new Notification(); + //.setContentTitle("SL4A Facade") + //.setContentText("minimal SL4A facade running") + //.setContentIntent(contentIntent) + //.build(); + + //NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + notification.flags |= Notification.FLAG_AUTO_CANCEL; // hide the notification after its selected + + //notificationManager.notify(0, notification); + return notification; + } +} diff --git a/src/src/org/alanjds/sl4acompat/MinimalServiceStarter.java b/src/src/org/alanjds/sl4acompat/MinimalServiceStarter.java new file mode 100644 index 0000000000..487fbbf798 --- /dev/null +++ b/src/src/org/alanjds/sl4acompat/MinimalServiceStarter.java @@ -0,0 +1,21 @@ +package org.alanjds.sl4acompat; + +import com.googlecode.android_scripting.Log; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + + +// Usage: ./adb shell am broadcast -a org.alanjds.sl4acompat.START_MINIMAL_SERVICE + +public class MinimalServiceStarter extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + Log.v("Received intent!"); + Intent serviceIntent = new Intent(context, MinimalService.class); + context.startService(serviceIntent); + Log.v("Service started?"); + } +} \ No newline at end of file diff --git a/src/src/org/alanjds/sl4acompat/ScriptActivity.java b/src/src/org/alanjds/sl4acompat/ScriptActivity.java new file mode 100644 index 0000000000..2d40b67af2 --- /dev/null +++ b/src/src/org/alanjds/sl4acompat/ScriptActivity.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Google Inc. + * + * 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. + */ + +package org.alanjds.sl4acompat; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; + +import com.googlecode.android_scripting.Constants; +import com.googlecode.android_scripting.facade.ActivityResultFacade; +import com.googlecode.android_scripting.jsonrpc.RpcReceiverManager; + +/** + * @author Alexey Reznichenko (alexey.reznichenko@gmail.com) + */ +public class ScriptActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (Constants.ACTION_LAUNCH_SCRIPT_FOR_RESULT.equals(getIntent().getAction())) { + setTheme(android.R.style.Theme_Dialog); + //setContentView(R.layout.dialog); + ServiceConnection connection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + MinimalService scriptService = ((MinimalService.LocalBinder) service).getService(); + try { + RpcReceiverManager manager = scriptService.getRpcReceiverManager(); + ActivityResultFacade resultFacade = manager.getReceiver(ActivityResultFacade.class); + resultFacade.setActivity(ScriptActivity.this); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + // Ignore. + } + }; + bindService(new Intent(this, MinimalService.class), connection, Context.BIND_AUTO_CREATE); + startService(new Intent(this, MinimalService.class)); + } else { + //ScriptApplication application = (ScriptApplication) getApplication(); + //if (application.readyToStart()) { + startService(new Intent(this, MinimalService.class)); + //} + finish(); + } + } +} diff --git a/src/src/org/alanjds/sl4acompat/Sl4aCompatibleApplication.java b/src/src/org/alanjds/sl4acompat/Sl4aCompatibleApplication.java new file mode 100644 index 0000000000..324b64108c --- /dev/null +++ b/src/src/org/alanjds/sl4acompat/Sl4aCompatibleApplication.java @@ -0,0 +1,33 @@ +package org.alanjds.sl4acompat; + +import com.googlecode.android_scripting.BaseApplication; +import com.googlecode.android_scripting.Log; +import com.googlecode.android_scripting.interpreter.InterpreterConfiguration; +import com.googlecode.android_scripting.interpreter.InterpreterConstants; +import com.googlecode.android_scripting.interpreter.InterpreterConfiguration.ConfigurationObserver; + +import java.util.concurrent.CountDownLatch; + +public class Sl4aCompatibleApplication extends BaseApplication implements ConfigurationObserver { + + private volatile boolean receivedConfigUpdate = true; + private final CountDownLatch mLatch = new CountDownLatch(1); + + @Override + public void onCreate() { + mLatch.countDown(); + } + + @Override + public void onConfigurationChanged() { } + + public boolean readyToStart() { + try { + mLatch.await(); + } catch (InterruptedException e) { + Log.e(e); + } + return receivedConfigUpdate; + } + +} diff --git a/src/templates/AndroidManifest.tmpl.xml b/src/templates/AndroidManifest.tmpl.xml index 1eafea4355..908a249795 100644 --- a/src/templates/AndroidManifest.tmpl.xml +++ b/src/templates/AndroidManifest.tmpl.xml @@ -9,6 +9,9 @@ {% for m in args.meta_data %} @@ -70,6 +73,33 @@ android:process=":PythonService"/> {% endif %} + {% if args.sl4acompat %} + + + + + + + + + + + + + + + + + + + + {% endif %} + {% if args.billing_pubkey %}