mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-20 12:40:21 +02:00
Merge pull request #489 from mresnick67/android-step-bridge
This commit is contained in:
@@ -50,6 +50,29 @@ chosen file under the app save directory as `picked_rom.gb`,
|
|||||||
from that folder on Choose / refocus; see `docs/launcher.md`. The APK payload
|
from that folder on Choose / refocus; see `docs/launcher.md`. The APK payload
|
||||||
itself remains data-free (no embedded ROM or generated cache).
|
itself remains data-free (no embedded ROM or generated cache).
|
||||||
|
|
||||||
|
### Step bridge (Pokéwalker mod)
|
||||||
|
|
||||||
|
`love.system.syncHealthSteps()` → `GameActivity.syncHealthSteps` (same
|
||||||
|
JNI route as the picker: `common/android.cpp` →
|
||||||
|
`modules/system/System.cpp` → `wrap_System.cpp`). The Java side does a
|
||||||
|
one-shot read of the hardware `TYPE_STEP_COUNTER` sensor (cumulative
|
||||||
|
since boot, counted by the OS whether or not any app runs), anchors the
|
||||||
|
reading in `SharedPreferences` so a walk is never credited twice
|
||||||
|
(a reading below the anchor means the phone rebooted → re-anchor without
|
||||||
|
crediting), and stages the delta as `steps_pending.json` in the save
|
||||||
|
identity dir — the same contract as the iOS `GRHealthBridge`. Nothing in
|
||||||
|
the base game calls it; the consumer is the
|
||||||
|
[Pokéwalker mod](https://github.com/mresnick67/Gen1ReComp-Pokewalker),
|
||||||
|
installed as a mod `.zip` at runtime (its SYNC STEPS option defaults
|
||||||
|
off).
|
||||||
|
|
||||||
|
Android 10+ gates the sensor behind the `ACTIVITY_RECOGNITION` runtime
|
||||||
|
permission (declared in `app/src/main/AndroidManifest.xml`; keep it out
|
||||||
|
of the build script's permission trim). The first
|
||||||
|
`syncHealthSteps()` call shows the system prompt; on grant the sensor
|
||||||
|
read runs immediately (`onRequestPermissionsResult`,
|
||||||
|
`STEP_PERMISSION_REQUEST_CODE`).
|
||||||
|
|
||||||
### SDK / NDK
|
### SDK / NDK
|
||||||
|
|
||||||
love-android 11.5a expects:
|
love-android 11.5a expects:
|
||||||
|
|||||||
@@ -8,6 +8,11 @@
|
|||||||
the link screen shows as "(Operation not permitted)" (issue #287).
|
the link screen shows as "(Operation not permitted)" (issue #287).
|
||||||
scripts/build_android.sh must not strip this again. -->
|
scripts/build_android.sh must not strip this again. -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<!-- Step bridge: love.system.syncHealthSteps reads the hardware step
|
||||||
|
counter, which Android 10+ gates behind this runtime permission.
|
||||||
|
Requested only on the first sync call (the Pokéwalker mod's SYNC
|
||||||
|
STEPS option); scripts/build_android.sh must not strip it. -->
|
||||||
|
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||||
<!-- OpenGL ES 2.0 -->
|
<!-- OpenGL ES 2.0 -->
|
||||||
<uses-feature android:glEsVersion="0x00020000" />
|
<uses-feature android:glEsVersion="0x00020000" />
|
||||||
<!-- Touchscreen support -->
|
<!-- Touchscreen support -->
|
||||||
|
|||||||
@@ -219,6 +219,18 @@ bool showCreateDocument(const char *suggestedName)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool syncHealthSteps()
|
||||||
|
{
|
||||||
|
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||||
|
jclass activity = env->FindClass("org/love2d/android/GameActivity");
|
||||||
|
|
||||||
|
jmethodID method = env->GetStaticMethodID(activity, "syncHealthSteps", "()Z");
|
||||||
|
jboolean result = env->CallStaticBooleanMethod(activity, method);
|
||||||
|
|
||||||
|
env->DeleteLocalRef(activity);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper functions for the filesystem module
|
* Helper functions for the filesystem module
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -74,6 +74,13 @@ bool showFilePicker(const char *destFilename = nullptr);
|
|||||||
**/
|
**/
|
||||||
bool showCreateDocument(const char *suggestedName = nullptr);
|
bool showCreateDocument(const char *suggestedName = nullptr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokéwalker step bridge: asks GameActivity to read the hardware step
|
||||||
|
* counter and stage steps_pending.json in the save identity dir (see
|
||||||
|
* GameActivity.syncHealthSteps). Returns whether a sync could start.
|
||||||
|
*/
|
||||||
|
bool syncHealthSteps();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper functions for the filesystem module
|
* Helper functions for the filesystem module
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -212,6 +212,15 @@ bool System::createFile(const char *suggestedName) const
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool System::syncHealthSteps() const
|
||||||
|
{
|
||||||
|
#ifdef LOVE_ANDROID
|
||||||
|
return love::android::syncHealthSteps();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
bool System::hasBackgroundMusic() const
|
bool System::hasBackgroundMusic() const
|
||||||
{
|
{
|
||||||
#if defined(LOVE_ANDROID)
|
#if defined(LOVE_ANDROID)
|
||||||
|
|||||||
@@ -127,6 +127,12 @@ public:
|
|||||||
**/
|
**/
|
||||||
virtual bool createFile(const char *suggestedName = nullptr) const;
|
virtual bool createFile(const char *suggestedName = nullptr) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokéwalker: stage pending real-world steps (steps_pending.json in the
|
||||||
|
* save dir) from the platform step source. Android-only; false elsewhere.
|
||||||
|
*/
|
||||||
|
virtual bool syncHealthSteps() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets if the user is playing music on background.
|
* Gets if the user is playing music on background.
|
||||||
* Throws an exception on unsupported platforms.
|
* Throws an exception on unsupported platforms.
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ int w_createFile(lua_State *L)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w_syncHealthSteps(lua_State *L)
|
||||||
|
{
|
||||||
|
luax_pushboolean(L, instance()->syncHealthSteps());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
int w_hasBackgroundMusic(lua_State *L)
|
int w_hasBackgroundMusic(lua_State *L)
|
||||||
{
|
{
|
||||||
lua_pushboolean(L, instance()->hasBackgroundMusic());
|
lua_pushboolean(L, instance()->hasBackgroundMusic());
|
||||||
@@ -126,6 +132,7 @@ static const luaL_Reg functions[] =
|
|||||||
{ "vibrate", w_vibrate },
|
{ "vibrate", w_vibrate },
|
||||||
{ "pickFile", w_pickFile },
|
{ "pickFile", w_pickFile },
|
||||||
{ "createFile", w_createFile },
|
{ "createFile", w_createFile },
|
||||||
|
{ "syncHealthSteps", w_syncHealthSteps },
|
||||||
{ "hasBackgroundMusic", w_hasBackgroundMusic },
|
{ "hasBackgroundMusic", w_hasBackgroundMusic },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,13 +41,20 @@ import android.app.AlertDialog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
@@ -67,6 +74,7 @@ public class GameActivity extends SDLActivity {
|
|||||||
public static final int RECORD_AUDIO_REQUEST_CODE = 3;
|
public static final int RECORD_AUDIO_REQUEST_CODE = 3;
|
||||||
public static final int FILE_PICKER_REQUEST_CODE = 4;
|
public static final int FILE_PICKER_REQUEST_CODE = 4;
|
||||||
public static final int FILE_CREATE_REQUEST_CODE = 5;
|
public static final int FILE_CREATE_REQUEST_CODE = 5;
|
||||||
|
public static final int STEP_PERMISSION_REQUEST_CODE = 6;
|
||||||
/** @deprecated Prefer FILE_PICKER_REQUEST_CODE; kept for older call sites. */
|
/** @deprecated Prefer FILE_PICKER_REQUEST_CODE; kept for older call sites. */
|
||||||
public static final int ROM_PICKER_REQUEST_CODE = FILE_PICKER_REQUEST_CODE;
|
public static final int ROM_PICKER_REQUEST_CODE = FILE_PICKER_REQUEST_CODE;
|
||||||
// Mirrors conf.lua's t.identity ("pokemon-love2d"): where the picked file
|
// Mirrors conf.lua's t.identity ("pokemon-love2d"): where the picked file
|
||||||
@@ -83,6 +91,17 @@ public class GameActivity extends SDLActivity {
|
|||||||
// basename as its body, so RomImporter:focus can say so in the launcher
|
// basename as its body, so RomImporter:focus can say so in the launcher
|
||||||
// instead of leaving the player on "No ROM imported" (issue #442).
|
// instead of leaving the player on "No ROM imported" (issue #442).
|
||||||
private static final String PICK_ERROR_FILENAME = "pick_error.flag";
|
private static final String PICK_ERROR_FILENAME = "pick_error.flag";
|
||||||
|
// Step bridge (love.system.syncHealthSteps): pending-steps delivery
|
||||||
|
// consumed by the Pokéwalker mod, same contract as the iOS
|
||||||
|
// GRHealthBridge. Steps come from the hardware TYPE_STEP_COUNTER
|
||||||
|
// (cumulative since boot, counted by the OS whether or not any app is
|
||||||
|
// running), anchored in SharedPreferences so a walk is never credited
|
||||||
|
// twice.
|
||||||
|
private static final String PENDING_STEPS_FILENAME = "steps_pending.json";
|
||||||
|
private static final String STEP_PREFS = "pokewalker_steps";
|
||||||
|
private static final String STEP_PREF_ANCHOR = "anchor";
|
||||||
|
private static final String STEP_PREF_ANCHOR_WALLTIME = "anchor_walltime";
|
||||||
|
private static final long STEP_MAX_PER_SYNC = 50000;
|
||||||
// Destination basename for the in-flight SAF pick (set by showFilePicker).
|
// Destination basename for the in-flight SAF pick (set by showFilePicker).
|
||||||
// Saved/restored across instance state: the picker is a separate activity
|
// Saved/restored across instance state: the picker is a separate activity
|
||||||
// and Android may destroy this one while it is up (memory pressure, or
|
// and Android may destroy this one while it is up (memory pressure, or
|
||||||
@@ -532,6 +551,160 @@ public class GameActivity extends SDLActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step sync, called from Lua as love.system.syncHealthSteps()
|
||||||
|
* (see modules/system/wrap_System.cpp). Asynchronous like the picker:
|
||||||
|
* returns whether a sync could be started; the result lands later as
|
||||||
|
* steps_pending.json in the save identity dir, where the Pokéwalker
|
||||||
|
* mod's poll consumes it.
|
||||||
|
*
|
||||||
|
* Android 10+ gates the step counter behind the ACTIVITY_RECOGNITION
|
||||||
|
* runtime permission; the first call shows the system prompt and a later
|
||||||
|
* sync (the mod retries on save load / option change) delivers.
|
||||||
|
*/
|
||||||
|
@Keep
|
||||||
|
public static boolean syncHealthSteps() {
|
||||||
|
final GameActivity self = (GameActivity) mSingleton;
|
||||||
|
if (self == null) return false;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 29
|
||||||
|
&& ActivityCompat.checkSelfPermission(self,
|
||||||
|
Manifest.permission.ACTIVITY_RECOGNITION)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
self.runOnUiThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ActivityCompat.requestPermissions(self,
|
||||||
|
new String[]{Manifest.permission.ACTIVITY_RECOGNITION},
|
||||||
|
STEP_PERMISSION_REQUEST_CODE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
self.startStepSensorRead();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One-shot read of the cumulative hardware step counter. The sensor
|
||||||
|
* usually reports its cached value moments after registration; some
|
||||||
|
* devices hold the event until the next physical step, so the listener
|
||||||
|
* is given 20 seconds before being torn down (the next sync retries).
|
||||||
|
*/
|
||||||
|
private void startStepSensorRead() {
|
||||||
|
final SensorManager manager =
|
||||||
|
(SensorManager) getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
if (manager == null) return;
|
||||||
|
Sensor counter = manager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
|
||||||
|
if (counter == null) {
|
||||||
|
Log.d("GameActivity", "no step counter sensor on this device");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final SensorEventListener listener = new SensorEventListener() {
|
||||||
|
private boolean delivered = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
if (delivered || event.values.length == 0) return;
|
||||||
|
delivered = true;
|
||||||
|
manager.unregisterListener(this);
|
||||||
|
deliverSteps((long) event.values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (!manager.registerListener(listener, counter,
|
||||||
|
SensorManager.SENSOR_DELAY_NORMAL)) {
|
||||||
|
Log.d("GameActivity", "step counter listener registration failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// No-op if the listener already delivered and unregistered.
|
||||||
|
manager.unregisterListener(listener);
|
||||||
|
}
|
||||||
|
}, 20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a cumulative counter reading into pending steps. The counter
|
||||||
|
* resets to zero on reboot: a reading below the stored anchor re-anchors
|
||||||
|
* without crediting (steps walked between the reboot and this sync are
|
||||||
|
* lost, which errs on the honest side).
|
||||||
|
*/
|
||||||
|
private void deliverSteps(long counterNow) {
|
||||||
|
SharedPreferences prefs = getSharedPreferences(STEP_PREFS, MODE_PRIVATE);
|
||||||
|
long anchor = prefs.getLong(STEP_PREF_ANCHOR, -1);
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (anchor < 0 || counterNow < anchor) {
|
||||||
|
prefs.edit()
|
||||||
|
.putLong(STEP_PREF_ANCHOR, counterNow)
|
||||||
|
.putLong(STEP_PREF_ANCHOR_WALLTIME, now)
|
||||||
|
.apply();
|
||||||
|
Log.d("GameActivity", "step anchor set at " + counterNow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long steps = Math.min(counterNow - anchor, STEP_MAX_PER_SYNC);
|
||||||
|
long fromWalltime = prefs.getLong(STEP_PREF_ANCHOR_WALLTIME, now);
|
||||||
|
if (steps <= 0) return;
|
||||||
|
prefs.edit()
|
||||||
|
.putLong(STEP_PREF_ANCHOR, counterNow)
|
||||||
|
.putLong(STEP_PREF_ANCHOR_WALLTIME, now)
|
||||||
|
.apply();
|
||||||
|
|
||||||
|
File dir = saveIdentityDir();
|
||||||
|
if (!dir.isDirectory() && !dir.mkdirs()) {
|
||||||
|
Log.d("GameActivity", "cannot create save dir for steps: " + dir);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
File pending = new File(dir, PENDING_STEPS_FILENAME);
|
||||||
|
long total = steps;
|
||||||
|
// Merge with an unconsumed earlier delivery so steps are never lost
|
||||||
|
// (same contract as the iOS bridge).
|
||||||
|
if (pending.isFile()) {
|
||||||
|
try {
|
||||||
|
byte[] raw = new byte[(int) Math.min(pending.length(), 4096)];
|
||||||
|
FileInputStream in = new FileInputStream(pending);
|
||||||
|
int read = in.read(raw);
|
||||||
|
in.close();
|
||||||
|
if (read > 0) {
|
||||||
|
org.json.JSONObject old =
|
||||||
|
new org.json.JSONObject(new String(raw, 0, read, "UTF-8"));
|
||||||
|
total += Math.max(0, old.optLong("steps", 0));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("GameActivity", "ignoring unreadable pending steps: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
org.json.JSONObject payload = new org.json.JSONObject();
|
||||||
|
payload.put("steps", total);
|
||||||
|
payload.put("from", isoTime(fromWalltime));
|
||||||
|
payload.put("to", isoTime(now));
|
||||||
|
File tmp = new File(dir, PENDING_STEPS_FILENAME + ".tmp");
|
||||||
|
FileOutputStream out = new FileOutputStream(tmp);
|
||||||
|
out.write(payload.toString().getBytes("UTF-8"));
|
||||||
|
out.close();
|
||||||
|
if (!tmp.renameTo(pending)) {
|
||||||
|
tmp.delete();
|
||||||
|
Log.d("GameActivity", "could not publish pending steps");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.d("GameActivity", total + " steps pending for the Pokewalker mod");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.d("GameActivity", "could not write pending steps: " + e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String isoTime(long millis) {
|
||||||
|
java.text.SimpleDateFormat format =
|
||||||
|
new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.US);
|
||||||
|
format.setTimeZone(java.util.TimeZone.getTimeZone("UTC"));
|
||||||
|
return format.format(new java.util.Date(millis));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean copyFileToUri(File source, Uri destUri) {
|
private boolean copyFileToUri(File source, Uri destUri) {
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
@@ -762,6 +935,17 @@ public class GameActivity extends SDLActivity {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case STEP_PERMISSION_REQUEST_CODE: {
|
||||||
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d("GameActivity", "Step permission granted");
|
||||||
|
// Deliver right away so the sync the player just
|
||||||
|
// opted into doesn't wait for the next launch.
|
||||||
|
startStepSensorRead();
|
||||||
|
} else {
|
||||||
|
Log.d("GameActivity", "Did not get step permission.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,10 @@ pack_game_love() {
|
|||||||
rm -f "$LOVE_FILE"
|
rm -f "$LOVE_FILE"
|
||||||
# tools/save-editor ships with the app: the launcher's Edit button on a save
|
# tools/save-editor ships with the app: the launcher's Edit button on a save
|
||||||
# row opens it in-process, so it must be inside the archive (see build.sh).
|
# row opens it in-process, so it must be inside the archive (see build.sh).
|
||||||
|
# Deliberately NO fused mods: a mod inside game.love sits in the read-only
|
||||||
|
# APK, so the mod manager's Delete can't remove it and it reappears every
|
||||||
|
# launch. Pokewalker ships as an importable .zip instead, which gives it
|
||||||
|
# a real install/upgrade/delete lifecycle.
|
||||||
(cd "$ROOT" && zip -q -9 -r "$LOVE_FILE" \
|
(cd "$ROOT" && zip -q -9 -r "$LOVE_FILE" \
|
||||||
main.lua conf.lua src data assets tools/save-editor \
|
main.lua conf.lua src data assets tools/save-editor \
|
||||||
tools/rom_manifest.json tools/rom_manifest_blue.json \
|
tools/rom_manifest.json tools/rom_manifest_blue.json \
|
||||||
@@ -290,10 +294,32 @@ require_android_sdk() {
|
|||||||
# --------------------------------------------------------------- gradle
|
# --------------------------------------------------------------- gradle
|
||||||
run_gradle() {
|
run_gradle() {
|
||||||
local task="assembleEmbedNoRecordDebug"
|
local task="assembleEmbedNoRecordDebug"
|
||||||
say "building APK ($task)"
|
local build_dir="$ANDROID_DIR"
|
||||||
|
|
||||||
|
# ndk-build is GNU make underneath and cannot cope with spaces anywhere in
|
||||||
|
# the project path ("Your APP_BUILD_SCRIPT points to an unknown file").
|
||||||
|
# When this checkout lives at a spaced path (e.g. "~/xCode Projects/..."),
|
||||||
|
# shadow the android tree to a space-free location and build there; the
|
||||||
|
# shadow persists across runs so gradle/ndk builds stay incremental.
|
||||||
|
case "$ANDROID_DIR" in
|
||||||
|
*" "*)
|
||||||
|
build_dir="${TMPDIR:-/tmp}/gen1recomp-android-shadow"
|
||||||
|
say "path contains spaces (ndk-build cannot handle them);"
|
||||||
|
say "shadow-building in: $build_dir"
|
||||||
|
mkdir -p "$build_dir"
|
||||||
|
rsync -a --delete \
|
||||||
|
--exclude=".gradle" --exclude="app/build" --exclude="love/build" \
|
||||||
|
--exclude="local.properties" \
|
||||||
|
"$ANDROID_DIR/" "$build_dir/"
|
||||||
|
if [ -f "$ANDROID_DIR/local.properties" ]; then
|
||||||
|
cp "$ANDROID_DIR/local.properties" "$build_dir/local.properties"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
say "building APK ($task)"
|
||||||
if ! (
|
if ! (
|
||||||
cd "$ANDROID_DIR"
|
cd "$build_dir"
|
||||||
./gradlew --no-daemon "$task"
|
./gradlew --no-daemon "$task"
|
||||||
); then
|
); then
|
||||||
fail "gradle $task failed.
|
fail "gradle $task failed.
|
||||||
@@ -302,7 +328,7 @@ run_gradle() {
|
|||||||
You can still iterate on the .love payload with: scripts/build_android.sh --package-only"
|
You can still iterate on the .love payload with: scripts/build_android.sh --package-only"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local out_dir="$ANDROID_DIR/app/build/outputs/apk/embedNoRecord/debug"
|
local out_dir="$build_dir/app/build/outputs/apk/embedNoRecord/debug"
|
||||||
if [ -d "$out_dir" ]; then
|
if [ -d "$out_dir" ]; then
|
||||||
say "APK output:"
|
say "APK output:"
|
||||||
find "$out_dir" -name '*.apk' -exec ls -lh {} \;
|
find "$out_dir" -name '*.apk' -exec ls -lh {} \;
|
||||||
|
|||||||
Reference in New Issue
Block a user