mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 12:32:52 +00:00
Merge pull request #11213 from farmerbb/play_feature_delivery
Implement support for Play Store builds using Play Feature Delivery (Java/Gradle)
This commit is contained in:
commit
1898a4bec5
@ -1,5 +1,14 @@
|
||||
package com.retroarch.browser.retroactivity;
|
||||
|
||||
import com.google.android.play.core.splitinstall.SplitInstallManager;
|
||||
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory;
|
||||
import com.google.android.play.core.splitinstall.SplitInstallRequest;
|
||||
import com.google.android.play.core.splitinstall.SplitInstallSessionState;
|
||||
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener;
|
||||
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus;
|
||||
import com.google.android.play.core.tasks.OnFailureListener;
|
||||
import com.google.android.play.core.tasks.OnSuccessListener;
|
||||
import com.retroarch.BuildConfig;
|
||||
import com.retroarch.browser.preferences.util.UserPreferences;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.NativeActivity;
|
||||
@ -10,6 +19,8 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.media.AudioAttributes;
|
||||
import android.os.Bundle;
|
||||
import android.system.Os;
|
||||
import android.view.InputDevice;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
@ -20,7 +31,14 @@ import android.os.PowerManager;
|
||||
import android.os.Vibrator;
|
||||
import android.os.VibrationEffect;
|
||||
import android.util.Log;
|
||||
import java.lang.Math;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.Locale;
|
||||
|
||||
@ -40,9 +58,60 @@ public class RetroActivityCommon extends NativeActivity
|
||||
public static int FRONTEND_ORIENTATION_270 = 3;
|
||||
public static int RETRO_RUMBLE_STRONG = 0;
|
||||
public static int RETRO_RUMBLE_WEAK = 1;
|
||||
public static int INSTALL_STATUS_DOWNLOADING = 0;
|
||||
public static int INSTALL_STATUS_INSTALLING = 1;
|
||||
public static int INSTALL_STATUS_INSTALLED = 2;
|
||||
public static int INSTALL_STATUS_FAILED = 3;
|
||||
public boolean sustainedPerformanceMode = true;
|
||||
public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
|
||||
private final SplitInstallStateUpdatedListener listener = new SplitInstallStateUpdatedListener() {
|
||||
@Override
|
||||
public void onStateUpdate(SplitInstallSessionState state) {
|
||||
List<String> moduleNames = state.moduleNames();
|
||||
String[] coreNames = new String[moduleNames.size()];
|
||||
|
||||
for(int i = 0; i < moduleNames.size(); i++) {
|
||||
coreNames[i] = unsanitizeCoreName(moduleNames.get(i));
|
||||
}
|
||||
|
||||
switch(state.status()) {
|
||||
case SplitInstallSessionStatus.DOWNLOADING:
|
||||
coreInstallStatusChanged(coreNames, INSTALL_STATUS_DOWNLOADING, state.bytesDownloaded(), state.totalBytesToDownload());
|
||||
break;
|
||||
case SplitInstallSessionStatus.INSTALLING:
|
||||
coreInstallStatusChanged(coreNames, INSTALL_STATUS_INSTALLING, state.bytesDownloaded(), state.totalBytesToDownload());
|
||||
break;
|
||||
case SplitInstallSessionStatus.INSTALLED:
|
||||
updateSymlinks();
|
||||
|
||||
coreInstallStatusChanged(coreNames, INSTALL_STATUS_INSTALLED, state.bytesDownloaded(), state.totalBytesToDownload());
|
||||
break;
|
||||
case SplitInstallSessionStatus.FAILED:
|
||||
coreInstallStatusChanged(coreNames, INSTALL_STATUS_FAILED, state.bytesDownloaded(), state.totalBytesToDownload());
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
updateSymlinks();
|
||||
|
||||
SplitInstallManager manager = SplitInstallManagerFactory.create(this);
|
||||
manager.registerListener(listener);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
SplitInstallManager manager = SplitInstallManagerFactory.create(this);
|
||||
manager.unregisterListener(listener);
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void doVibrate(int id, int effect, int strength, int oneShot)
|
||||
{
|
||||
Vibrator vibrator = null;
|
||||
@ -297,4 +366,240 @@ public class RetroActivityCommon extends NativeActivity
|
||||
|
||||
Log.i("RetroActivity", "hasOldOrientation? " + hasOldOrientation + " newOrientation: " + newConfig.orientation + " oldOrientation: " + oldOrientation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version of RetroArch is a Play Store build.
|
||||
*
|
||||
* @return true if this is a Play Store build, false otherwise
|
||||
*/
|
||||
public boolean isPlayStoreBuild() {
|
||||
Log.i("RetroActivity", "isPlayStoreBuild: " + BuildConfig.PLAY_STORE_BUILD);
|
||||
|
||||
return BuildConfig.PLAY_STORE_BUILD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of available cores that can be downloaded as Dynamic Feature Modules.
|
||||
*
|
||||
* @return the list of available cores
|
||||
*/
|
||||
public String[] getAvailableCores() {
|
||||
int id = getResources().getIdentifier("module_names_" + sanitizeCoreName(Build.CPU_ABI), "array", getPackageName());
|
||||
|
||||
String[] returnVal = getResources().getStringArray(id);
|
||||
Log.i("RetroActivity", "getAvailableCores: " + Arrays.toString(returnVal));
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of cores that are currently installed as Dynamic Feature Modules.
|
||||
*
|
||||
* @return the list of installed cores
|
||||
*/
|
||||
public String[] getInstalledCores() {
|
||||
SplitInstallManager manager = SplitInstallManagerFactory.create(this);
|
||||
String[] modules = manager.getInstalledModules().toArray(new String[0]);
|
||||
List<String> cores = new ArrayList<>();
|
||||
|
||||
SharedPreferences prefs = UserPreferences.getPreferences(this);
|
||||
|
||||
for(int i = 0; i < modules.length; i++) {
|
||||
String coreName = unsanitizeCoreName(modules[i]);
|
||||
if(!prefs.getBoolean("core_deleted_" + coreName, false)) {
|
||||
cores.add(coreName);
|
||||
}
|
||||
}
|
||||
|
||||
String[] returnVal = cores.toArray(new String[0]);
|
||||
Log.i("RetroActivity", "getInstalledCores: " + Arrays.toString(returnVal));
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the system to download a core.
|
||||
*
|
||||
* @param coreName Name of the core to install
|
||||
*/
|
||||
public void downloadCore(final String coreName) {
|
||||
Log.i("RetroActivity", "downloadCore: " + coreName);
|
||||
|
||||
SharedPreferences prefs = UserPreferences.getPreferences(this);
|
||||
prefs.edit().remove("core_deleted_" + coreName).apply();
|
||||
|
||||
SplitInstallManager manager = SplitInstallManagerFactory.create(this);
|
||||
SplitInstallRequest request = SplitInstallRequest.newBuilder()
|
||||
.addModule(sanitizeCoreName(coreName))
|
||||
.build();
|
||||
|
||||
manager.startInstall(request)
|
||||
.addOnSuccessListener(new OnSuccessListener<Integer>() {
|
||||
@Override
|
||||
public void onSuccess(Integer result) {
|
||||
coreInstallInitiated(coreName, true);
|
||||
}
|
||||
})
|
||||
|
||||
.addOnFailureListener(new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
coreInstallInitiated(coreName, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the system to delete a core.
|
||||
*
|
||||
* Note that the actual module deletion will not happen immediately (the OS will delete
|
||||
* it whenever it feels like it), but the symlink will still be immediately removed.
|
||||
*
|
||||
* @param coreName Name of the core to delete
|
||||
*/
|
||||
public void deleteCore(String coreName) {
|
||||
Log.i("RetroActivity", "deleteCore: " + coreName);
|
||||
|
||||
String newFilename = getCorePath() + coreName + "_libretro_android.so";
|
||||
new File(newFilename).delete();
|
||||
|
||||
SharedPreferences prefs = UserPreferences.getPreferences(this);
|
||||
prefs.edit().putBoolean("core_deleted_" + coreName, true).apply();
|
||||
|
||||
SplitInstallManager manager = SplitInstallManagerFactory.create(this);
|
||||
manager.deferredUninstall(Collections.singletonList(sanitizeCoreName(coreName)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////// JNI methods ///////////////
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called when a core install is initiated.
|
||||
*
|
||||
* @param coreName Name of the core that the install is initiated for.
|
||||
* @param successful true if success, false if failure
|
||||
*/
|
||||
private native void coreInstallInitiated(String coreName, boolean successful);
|
||||
|
||||
/**
|
||||
* Called when the status of a core install has changed.
|
||||
*
|
||||
* @param coreNames Names of all cores that are currently being downloaded.
|
||||
* @param status One of INSTALL_STATUS_DOWNLOADING, INSTALL_STATUS_INSTALLING,
|
||||
* INSTALL_STATUS_INSTALLED, or INSTALL_STATUS_FAILED
|
||||
* @param bytesDownloaded Number of bytes downloaded.
|
||||
* @param totalBytesToDownload Total number of bytes to download.
|
||||
*/
|
||||
private native void coreInstallStatusChanged(String[] coreNames, int status, long bytesDownloaded, long totalBytesToDownload);
|
||||
|
||||
|
||||
|
||||
/////////////// Private methods ///////////////
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sanitizes a core name so that it can be used when dealing with
|
||||
* Dynamic Feature Modules. Needed because Gradle modules cannot use
|
||||
* dashes, but we have at least one core name ("mesen-s") that uses them.
|
||||
*
|
||||
* @param coreName Name of the core to sanitize.
|
||||
* @return The sanitized core name.
|
||||
*/
|
||||
private String sanitizeCoreName(String coreName) {
|
||||
return coreName.replace('-', '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsanitizes a core name from its module name.
|
||||
*
|
||||
* @param coreName Name of the core to unsanitize.
|
||||
* @return The unsanitized core name.
|
||||
*/
|
||||
private String unsanitizeCoreName(String coreName) {
|
||||
if(coreName.equals("mesen_s")) {
|
||||
return "mesen-s";
|
||||
}
|
||||
|
||||
return coreName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to the RetroArch cores directory.
|
||||
*
|
||||
* @return The path to the RetroArch cores directory
|
||||
*/
|
||||
private String getCorePath() {
|
||||
return getApplicationInfo().dataDir + "/cores/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a symlink update in the known places that Dynamic Feature Modules
|
||||
* are installed to.
|
||||
*/
|
||||
private void updateSymlinks() {
|
||||
traverseFilesystem(getFilesDir());
|
||||
traverseFilesystem(new File(getApplicationInfo().nativeLibraryDir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the filesystem, looking for native libraries.
|
||||
* Symlinks any libraries it finds to the main RetroArch "cores" folder,
|
||||
* updating any existing symlinks with the correct path to the native libraries.
|
||||
*
|
||||
* This is necessary because Dynamic Feature Modules are first downloaded
|
||||
* and installed to a temporary location on disk, before being moved
|
||||
* to a more permanent location by the system at a later point.
|
||||
*
|
||||
* This could probably be done in native code instead, if that's preferred.
|
||||
*
|
||||
* @param file The parent directory of the tree to traverse.
|
||||
* @param cores List of cores to update.
|
||||
* @param filenames List of filenames to update.
|
||||
*/
|
||||
private void traverseFilesystem(File file) {
|
||||
File[] list = file.listFiles();
|
||||
if(list == null) return;
|
||||
|
||||
// Check each file in a directory to see if it's a native library.
|
||||
for(int i = 0; i < list.length; i++) {
|
||||
File child = list[i];
|
||||
String name = child.getName();
|
||||
|
||||
if(name.startsWith("lib") && name.endsWith(".so") && !name.contains("retroarch-activity")) {
|
||||
// Found a native library!
|
||||
String core = name.subSequence(3, name.length() - 3).toString();
|
||||
String filename = child.getAbsolutePath();
|
||||
|
||||
SharedPreferences prefs = UserPreferences.getPreferences(this);
|
||||
if(!prefs.getBoolean("core_deleted_" + core, false)) {
|
||||
// Generate the destination filename and delete any existing symlinks / cores
|
||||
String newFilename = getCorePath() + core + "_libretro_android.so";
|
||||
new File(newFilename).delete();
|
||||
|
||||
try {
|
||||
// On Android 5.0+, use the official API for creating a symlink.
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Os.symlink(filename, newFilename);
|
||||
} else {
|
||||
// On older versions, resort to using reflection instead.
|
||||
Class<?> clazz = Class.forName("libcore.io.Libcore");
|
||||
Field field = clazz.getDeclaredField("os");
|
||||
field.setAccessible(true);
|
||||
|
||||
Object os = field.get(null);
|
||||
Method method = os.getClass().getMethod("symlink", String.class, String.class);
|
||||
method.invoke(os, filename, newFilename);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Symlink failed to be created. Should never happen.
|
||||
}
|
||||
}
|
||||
} else if(file.isDirectory()) {
|
||||
// Found another directory, so traverse it
|
||||
traverseFilesystem(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
pkg/android/phoenix/.gitignore
vendored
8
pkg/android/phoenix/.gitignore
vendored
@ -2,4 +2,10 @@
|
||||
.externalNativeBuild
|
||||
build
|
||||
phoenix.iml
|
||||
|
||||
output.json
|
||||
keystore.properties
|
||||
modules/
|
||||
settings.gradle
|
||||
dynamic_features.gradle
|
||||
res/values/core_names.xml
|
||||
res/values/module_names_*.xml
|
||||
|
@ -16,12 +16,14 @@
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:name="com.google.android.play.core.splitcompat.SplitCompatApplication"
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:hasCode="true"
|
||||
android:isGame="true"
|
||||
android:banner="@drawable/banner"
|
||||
android:extractNativeLibs="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<activity android:name="com.retroarch.browser.mainmenu.MainMenuActivity" android:exported="true" android:launchMode="singleInstance">
|
||||
<intent-filter>
|
||||
|
@ -37,12 +37,14 @@ android {
|
||||
productFlavors {
|
||||
normal {
|
||||
resValue "string", "app_name", "RetroArch"
|
||||
buildConfigField "boolean", "PLAY_STORE_BUILD", "false"
|
||||
|
||||
dimension "variant"
|
||||
}
|
||||
aarch64 {
|
||||
applicationIdSuffix '.aarch64'
|
||||
resValue "string", "app_name", "RetroArch (AArch64)"
|
||||
buildConfigField "boolean", "PLAY_STORE_BUILD", "false"
|
||||
|
||||
dimension "variant"
|
||||
ndk {
|
||||
@ -52,12 +54,29 @@ android {
|
||||
ra32 {
|
||||
applicationIdSuffix '.ra32'
|
||||
resValue "string", "app_name", "RetroArch (32-bit)"
|
||||
buildConfigField "boolean", "PLAY_STORE_BUILD", "false"
|
||||
|
||||
dimension "variant"
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'x86'
|
||||
}
|
||||
}
|
||||
playStoreNormal {
|
||||
resValue "string", "app_name", "RetroArch"
|
||||
buildConfigField "boolean", "PLAY_STORE_BUILD", "true"
|
||||
|
||||
dimension "variant"
|
||||
}
|
||||
playStoreAarch64 {
|
||||
applicationIdSuffix '.aarch64'
|
||||
resValue "string", "app_name", "RetroArch (AArch64)"
|
||||
buildConfigField "boolean", "PLAY_STORE_BUILD", "true"
|
||||
|
||||
dimension "variant"
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a', 'x86_64'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -67,10 +86,10 @@ android {
|
||||
java.srcDirs = ['src', '../phoenix-common/src']
|
||||
jniLibs.srcDir '../phoenix-common/libs'
|
||||
jni.srcDirs = []
|
||||
res.srcDirs = ['../phoenix-common/res']
|
||||
res.srcDirs = ['res', '../phoenix-common/res']
|
||||
}
|
||||
aarch64 {
|
||||
res.srcDirs = ['res64']
|
||||
res.srcDirs = ['res', 'res64']
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,3 +123,12 @@ android {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.play:core:1.8.0'
|
||||
}
|
||||
|
||||
def dynamicFeatures = file("dynamic_features.gradle")
|
||||
if(dynamicFeatures.exists()) {
|
||||
apply from: "dynamic_features.gradle"
|
||||
}
|
||||
|
100
pkg/android/phoenix/init_modules.sh
Executable file
100
pkg/android/phoenix/init_modules.sh
Executable file
@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script generates Gradle modules for each Android core,
|
||||
# so that they can be served by Google Play as Dynamic Feature Modules.
|
||||
# Run "./init_modules.sh" to generate modules, or "./init_modules.sh clean" to remove them
|
||||
|
||||
# These paths assume that this script is running inside libretro-super,
|
||||
# and that the compiled Android cores are available while this script is run
|
||||
RECIPES_PATH="../../../../recipes/android"
|
||||
INFO_PATH="../../../../dist/info"
|
||||
CORES_PATH="../../../../dist/android"
|
||||
|
||||
# Get the list of Android cores to generate modules for
|
||||
CORES_LIST=$(cat module_list.txt)
|
||||
|
||||
# The below command would generate a module for every single Android core,
|
||||
# but Dynamic Feature Modules enforces a 50-module limit
|
||||
#CORES_LIST=$(find $RECIPES_PATH -type f ! -name '*.*' -exec cat {} + | awk '{ split($1, test, " "); print test[1] }' | grep "\S")
|
||||
|
||||
# Delete any leftover files from previous script runs
|
||||
rm -rf modules
|
||||
rm -f res/values/core_names.xml
|
||||
rm -f res/values/module_names_*.xml
|
||||
rm -f dynamic_features.gradle
|
||||
rm -f settings.gradle
|
||||
|
||||
if [[ $1 = clean ]] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Make directory for modules to be stored in
|
||||
mkdir -p modules
|
||||
mkdir -p res/values
|
||||
|
||||
# Begin generating files with necessary metadata
|
||||
# for compiling Dynamic Feature Modules
|
||||
echo "<resources>" >> res/values/core_names.xml
|
||||
echo "android {" >> dynamic_features.gradle
|
||||
echo "dynamicFeatures = [" >> dynamic_features.gradle
|
||||
|
||||
for arch in armeabi-v7a arm64-v8a x86 x86_64
|
||||
do
|
||||
SANITIZED_ARCH_NAME=$(echo $arch | sed "s/-/_/g")
|
||||
echo "<resources>" >> res/values/module_names_$arch.xml
|
||||
echo "<string-array name=\"module_names_$SANITIZED_ARCH_NAME\">" >> res/values/module_names_$arch.xml
|
||||
done
|
||||
|
||||
# Time to generate a module for each core!
|
||||
while IFS= read -r core; do
|
||||
SANITIZED_CORE_NAME=$(echo $core | sed "s/-/_/g")
|
||||
DISPLAY_NAME=$(cat $INFO_PATH/${core}_libretro.info | grep "display_name" | cut -d'"' -f 2)
|
||||
|
||||
echo "Generating module for $core..."
|
||||
|
||||
# Make a copy of the template
|
||||
cp -r module_template modules/$SANITIZED_CORE_NAME
|
||||
|
||||
# Write the name of the core into AndroidManifest.xml
|
||||
if [[ "$OSTYPE" == "darwin"* ]]
|
||||
then
|
||||
sed -i '' "s/%CORE_NAME%/$SANITIZED_CORE_NAME/g" modules/$SANITIZED_CORE_NAME/AndroidManifest.xml
|
||||
else
|
||||
sed -i "s/%CORE_NAME%/$SANITIZED_CORE_NAME/g" modules/$SANITIZED_CORE_NAME/AndroidManifest.xml
|
||||
fi
|
||||
|
||||
# Create a libs directory for each architecture,
|
||||
# and copy the libretro core into each directory
|
||||
for arch in armeabi-v7a arm64-v8a x86 x86_64
|
||||
do
|
||||
mkdir -p modules/$SANITIZED_CORE_NAME/libs/$arch
|
||||
|
||||
if [[ -e $CORES_PATH/$arch/${core}_libretro_android.so ]]
|
||||
then
|
||||
ln -s ../../../../$CORES_PATH/$arch/${core}_libretro_android.so modules/$SANITIZED_CORE_NAME/libs/$arch/lib$core.so
|
||||
else
|
||||
touch modules/$SANITIZED_CORE_NAME/libs/$arch/lib$core.so
|
||||
fi
|
||||
|
||||
if [[ -s "modules/$SANITIZED_CORE_NAME/libs/$arch/lib$core.so" ]]
|
||||
then
|
||||
echo "<item>$core</item>" >> res/values/module_names_$arch.xml
|
||||
fi
|
||||
done
|
||||
|
||||
# Write metadata about the module into the corresponding files
|
||||
echo "<string name=\"core_name_$SANITIZED_CORE_NAME\">$DISPLAY_NAME</string>" >> res/values/core_names.xml
|
||||
echo "':modules:$SANITIZED_CORE_NAME'," >> dynamic_features.gradle
|
||||
echo "include ':modules:$SANITIZED_CORE_NAME'" >> settings.gradle
|
||||
done <<< "$CORES_LIST"
|
||||
|
||||
# Finish generating the metadata files
|
||||
echo "</resources>" >> res/values/core_names.xml
|
||||
echo "]" >> dynamic_features.gradle
|
||||
echo "}" >> dynamic_features.gradle
|
||||
|
||||
for arch in armeabi-v7a arm64-v8a x86 x86_64
|
||||
do
|
||||
echo "</string-array>" >> res/values/module_names_$arch.xml
|
||||
echo "</resources>" >> res/values/module_names_$arch.xml
|
||||
done
|
5
pkg/android/phoenix/module_list.txt
Normal file
5
pkg/android/phoenix/module_list.txt
Normal file
@ -0,0 +1,5 @@
|
||||
genesis_plus_gx
|
||||
mesen-s
|
||||
dolphin
|
||||
mupen64plus_next_gles3
|
||||
flycast
|
17
pkg/android/phoenix/module_template/AndroidManifest.xml
Normal file
17
pkg/android/phoenix/module_template/AndroidManifest.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:dist="http://schemas.android.com/apk/distribution"
|
||||
package="com.retroarch.core_modules.%CORE_NAME%">
|
||||
|
||||
<dist:module dist:title="@string/core_name_%CORE_NAME%">
|
||||
<dist:delivery>
|
||||
<dist:on-demand />
|
||||
</dist:delivery>
|
||||
<dist:fusing dist:include="true" />
|
||||
</dist:module>
|
||||
|
||||
<application
|
||||
android:hasCode="false"
|
||||
android:extractNativeLibs="true" />
|
||||
|
||||
</manifest>
|
49
pkg/android/phoenix/module_template/build.gradle
Normal file
49
pkg/android/phoenix/module_template/build.gradle
Normal file
@ -0,0 +1,49 @@
|
||||
apply plugin: 'com.android.dynamic-feature'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
}
|
||||
|
||||
flavorDimensions "variant"
|
||||
|
||||
productFlavors {
|
||||
normal {
|
||||
dimension "variant"
|
||||
}
|
||||
aarch64 {
|
||||
dimension "variant"
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a', 'x86_64'
|
||||
}
|
||||
}
|
||||
ra32 {
|
||||
dimension "variant"
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'x86'
|
||||
}
|
||||
}
|
||||
playStoreNormal {
|
||||
dimension "variant"
|
||||
}
|
||||
playStoreAarch64 {
|
||||
dimension "variant"
|
||||
ndk {
|
||||
abiFilters 'arm64-v8a', 'x86_64'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation rootProject
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user