Browse Source

Merged in AuahDark/love-android-sdl2/java-fix (pull request #22)

Various fixes related to manifest and Java code.
Miku AuahDark 6 years ago
parent
commit
b9f27c0534

+ 2 - 2
app/build.gradle

@@ -5,8 +5,8 @@ android {
     buildToolsVersion "28.0.3"
     defaultConfig {
         applicationId "org.love2d.android"
-        versionCode 26
-        versionName "11.1"
+        versionCode 27
+        versionName "11.2"
         minSdkVersion 14
         targetSdkVersion 28
     }

+ 30 - 12
app/src/main/AndroidManifest.xml

@@ -1,11 +1,10 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest package="org.love2d.android.executable"
-      android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android">
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.VIBRATE"/>
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
-    <!-- Allow writing to external storage -->
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+  android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android">
+  <uses-permission android:name="android.permission.INTERNET"/>
+  <uses-permission android:name="android.permission.VIBRATE"/>
+  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
 
   <!-- OpenGL ES 2.0 -->
   <uses-feature android:glEsVersion="0x00020000" />
@@ -14,18 +13,20 @@
       android:allowBackup="true"
       android:icon="@drawable/love"
       android:label="LÖVE for Android"
-      android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+      android:usesCleartextTraffic="true" >
       <service android:name="org.love2d.android.DownloadService" />
       <activity
         android:name="org.love2d.android.GameActivity"
-        android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation"
+        android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
         android:label="LÖVE for Android"
-        android:launchMode="singleTop"
-        android:screenOrientation="landscape" >
+        android:launchMode="singleTask"
+        android:screenOrientation="landscape"
+        android:resizeableActivity="false"
+        android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
         <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
-          <category android:name="tv.ouya.intent.category.GAME"/>
+          <category android:name="tv.ouya.intent.category.GAME" />
         </intent-filter>
         <intent-filter>
           <action android:name="android.intent.action.VIEW" />
@@ -36,10 +37,27 @@
           <data android:pathPattern=".*\\.love" />
           <data android:host="*" />
         </intent-filter>
+        <intent-filter>
+          <action android:name="android.intent.action.VIEW" />
+          <category android:name="android.intent.category.DEFAULT" />
+          <category android:name="android.intent.category.BROWSABLE" />
+          <data android:scheme="content" />
+          <data android:host="*" />
+          <data android:mimeType="application/x-love-game" />
+        </intent-filter>
+        <intent-filter>
+          <action android:name="android.intent.action.VIEW" />
+          <category android:name="android.intent.category.DEFAULT" />
+          <category android:name="android.intent.category.BROWSABLE" />
+          <data android:scheme="content" />
+          <data android:host="*" />
+          <data android:mimeType="application/octet-stream" />
+        </intent-filter>
       </activity>
       <activity
         android:name="org.love2d.android.DownloadActivity"
-        android:noHistory="true" >
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoTitleBar" >
         <intent-filter>
           <action android:name="android.intent.action.VIEW" />
           <category android:name="android.intent.category.DEFAULT" />

+ 0 - 2
love/src/jni/love/Android.mk

@@ -107,8 +107,6 @@ LOCAL_STATIC_LIBRARIES := libvorbis libogg libtheora libmodplug libfreetype libl
 # $(info liblove: include dirs $(LOCAL_C_INCLUDES))
 # $(info liblove: src files $(LOCAL_SRC_FILES))
 
-SDL_PATH := ../SDL2-2.0.9
-LOCAL_SRC_FILES += $(SDL_PATH)/src/main/android/SDL_android_main.c 
 LOCAL_LDLIBS := -lz -lGLESv1_CM -lGLESv2 -ldl -landroid
 LOCAL_LDFLAGS := -Wl,--allow-multiple-definition
 

+ 61 - 7
love/src/main/java/org/love2d/android/DownloadActivity.java

@@ -1,24 +1,78 @@
 package org.love2d.android;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.Manifest;
 import android.net.Uri;
 import android.os.Bundle;
+import android.util.Log;
+
+import androidx.core.app.ActivityCompat;
 
 public class DownloadActivity extends Activity {
+    public static final int EXTERNAL_STORAGE_REQUEST_CODE = 2;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        Uri uri = this.getIntent().getData();
+        if (android.os.Build.VERSION.SDK_INT >= 29 ||
+            ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
+        {
+            runDownloader();
+            finish();
+        } else {
+            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 
-        if (uri.getScheme().equals("http")) {
-            String url = uri.toString();
-            Intent intent = new Intent(this, DownloadService.class);
-            intent.putExtra("url", url);
-            startService(intent);
+        if (grantResults.length > 0) {
+            Log.d("DownloadActivity", "Received a request permission result");
+
+            if (requestCode == EXTERNAL_STORAGE_REQUEST_CODE) {
+                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    runDownloader();
+                    finish();
+                } else {
+                    Log.d("DownloadActivity", "Did not get permission.");
+                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+                        showExternalStoragePermissionMissingDialog();
+                    }
+                }
+            }
         }
+    }
 
-        finish();
+    protected void showExternalStoragePermissionMissingDialog() {
+        AlertDialog dialog = new AlertDialog.Builder(this)
+            .setTitle("Storage Permission Missing")
+            .setMessage("LÖVE for Android will not be able to download games without storage permission.")
+            .setNeutralButton("Continue", new DialogInterface.OnClickListener() {
+                public void onClick(DialogInterface dialog, int whichButton) {
+                    dialog.dismiss();
+                    finish();
+                }
+            })
+            .create();
+        dialog.show();
+    }
+
+    protected void runDownloader()
+    {
+        Intent intent = getIntent();
+        
+        if (intent != null) {
+            Uri uri = intent.getData();
+            Intent targetIntent = new Intent(this, DownloadService.class);
+            targetIntent.putExtra("url", uri.toString());
+            startService(targetIntent);
+        }
     }
 }

+ 2 - 2
love/src/main/java/org/love2d/android/DownloadService.java

@@ -33,9 +33,9 @@ public class DownloadService extends IntentService {
         String url = intent.getStringExtra("url");
         Uri uri = Uri.parse(url);
 
-        Log.d("DownloadService", "Downloading from url: " + url + "file = " + uri.getLastPathSegment());
+        Log.d("DownloadService", "Downloading from url: " + url + " file = " + uri.getLastPathSegment());
 
-        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
+        DownloadManager.Request request = new DownloadManager.Request(uri);
         request.setDescription("LÖVE Game Download");
         request.setTitle(uri.getLastPathSegment());
         request.setMimeType("application/x-love-game");

+ 87 - 46
love/src/main/java/org/love2d/android/GameActivity.java

@@ -20,13 +20,14 @@ import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Vibrator;
-import androidx.annotation.Keep;
-import androidx.core.app.ActivityCompat;
 import android.util.Log;
 import android.util.DisplayMetrics;
 import android.view.*;
 import android.content.pm.PackageManager;
 
+import androidx.annotation.Keep;
+import androidx.core.app.ActivityCompat;
+
 public class GameActivity extends SDLActivity {
     private static DisplayMetrics metrics = new DisplayMetrics();
     private static String gamePath = "";
@@ -36,6 +37,7 @@ public class GameActivity extends SDLActivity {
     public static final int EXTERNAL_STORAGE_REQUEST_CODE = 1;
     private static boolean immersiveActive = false;
     private static boolean mustCacheArchive = false;
+    private boolean storagePermissionUnnecessary = false;
     public int safeAreaTop = 0;
     public int safeAreaLeft = 0;
     public int safeAreaBottom = 0;
@@ -44,28 +46,47 @@ public class GameActivity extends SDLActivity {
     @Override
     protected String[] getLibraries() {
         return new String[]{
-                "c++_shared",
-                "mpg123",
-                "openal",
-                "hidapi",
-                "love",
+            "c++_shared",
+            "mpg123",
+            "openal",
+            "hidapi",
+            "love",
         };
     }
 
+    @Override
+    protected String getMainSharedObject() {
+        String[] libs = getLibraries();
+        String libname = "lib" + libs[libs.length - 1] + ".so";
+
+        // Since Lollipop, you can simply pass "libname.so" to dlopen
+        // and it will resolve correct paths and load correct library.
+        // This is mandatory for extractNativeLibs=false support in
+        // Marshmallow.
+        if (android.os.Build.VERSION.SDK_INT >= 21) {
+            return libname;
+        } else {
+            return getContext().getApplicationInfo().nativeLibraryDir + "/" + libname;
+        }
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         Log.d("GameActivity", "started");
 
         context = this.getApplicationContext();
 
-        String permission = "android.permission.VIBRATE";
-        int res = context.checkCallingOrSelfPermission(permission);
+        int res = context.checkCallingOrSelfPermission(Manifest.permission.VIBRATE);
         if (res == PackageManager.PERMISSION_GRANTED) {
             vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         } else {
             Log.d("GameActivity", "Vibration disabled: could not get vibration permission.");
         }
 
+        // These 2 variables must be reset or it will use the existing value.
+        gamePath = "";
+        storagePermissionUnnecessary = false;
+
         handleIntent(this.getIntent());
 
         super.onCreate(savedInstanceState);
@@ -88,27 +109,47 @@ public class GameActivity extends SDLActivity {
         Uri game = intent.getData();
 
         if (game != null) {
+            String scheme = game.getScheme();
+            String path = game.getPath();
             // If we have a game via the intent data we we try to figure out how we have to load it. We
             // support the following variations:
             // * a main.lua file: set gamePath to the directory containing main.lua
             // * otherwise: set gamePath to the file
-            if (game.getScheme().equals("file")) {
-                Log.d("GameActivity", "Received intent with path: " + game.getPath());
+            if (scheme.equals("file")) {
+                Log.d("GameActivity", "Received file:// intent with path: " + path);
                 // If we were given the path of a main.lua then use its
                 // directory. Otherwise use full path.
                 List<String> path_segments = game.getPathSegments();
                 if (path_segments.get(path_segments.size() - 1).equals("main.lua")) {
-                    gamePath = game.getPath().substring(0, game.getPath().length() - "main.lua".length());
+                    gamePath = path.substring(0, path.length() - "main.lua".length());
                 } else {
-                    gamePath = game.getPath();
+                    gamePath = path;
+                }
+            } else if (scheme.equals("content")) {
+                Log.d("GameActivity", "Received content:// intent with path: " + path);
+                try {
+                    String filename = "game.love";
+                    String[] pathSegments = path.split("/");
+                    if (pathSegments != null && pathSegments.length > 0) {
+                        filename = pathSegments[pathSegments.length - 1];
+                    }
+
+                    String destination_file = this.getCacheDir().getPath() + "/" + filename;
+                    InputStream data = getContentResolver().openInputStream(game);
+
+                    // copyAssetFile automatically closes the InputStream
+                    if (copyAssetFile(data, destination_file)) {
+                        gamePath = destination_file;
+                        storagePermissionUnnecessary = true;
+                    }
+                } catch (Exception e) {
+                    Log.d("GameActivity", "could not read content uri " + game.toString() + ": " + e.getMessage());
                 }
-            // FIXME: Add support for "content://" URI, which is mandatory in Android Nougat
-            // as using "file://" protocol in that (and later) version is forbidden!
             } else {
                 Log.e("GameActivity", "Unsupported scheme: '" + game.getScheme() + "'.");
 
                 AlertDialog.Builder alert_dialog = new AlertDialog.Builder(this);
-                alert_dialog.setMessage("Could not load LÖVE game '" + game.getPath()
+                alert_dialog.setMessage("Could not load LÖVE game '" + path
                         + "' as it uses unsupported scheme '" + game.getScheme()
                         + "'. Please contact the developer.");
                 alert_dialog.setTitle("LÖVE for Android Error");
@@ -136,10 +177,22 @@ public class GameActivity extends SDLActivity {
                 // If we have a game.love in our assets folder copy it to the cache folder
                 // so that we can load it from native LÖVE code
                 String destination_file = this.getCacheDir().getPath() + "/game.love";
-                if (mustCacheArchive && copyAssetFile("game.love", destination_file))
-                    gamePath = destination_file;
-                else
-                    gamePath = "game.love";
+                
+                try {
+                    InputStream gameStream = getAssets().open("game.love");
+                    if (mustCacheArchive && copyAssetFile(gameStream, destination_file))
+                        gamePath = destination_file;
+                    else
+                        gamePath = "game.love";
+                    storagePermissionUnnecessary = true;
+                } catch (IOException e) {
+                    Log.d("GameActivity", "Could not open game.love from assets: " + e.getMessage());
+                    gamePath = "";
+                    storagePermissionUnnecessary = false;
+                }
+            } else {
+                gamePath = "";
+                storagePermissionUnnecessary = false;
             }
         }
 
@@ -148,13 +201,14 @@ public class GameActivity extends SDLActivity {
 
     protected void checkLovegameFolder() {
         // If no game.love was found fall back to the game in <external storage>/lovegame
+        Log.d("GameActivity", "fallback to lovegame folder");
         if (hasExternalStoragePermission()) {
             File ext = Environment.getExternalStorageDirectory();
             if ((new File(ext, "/lovegame/main.lua")).exists()) {
                 gamePath = ext.getPath() + "/lovegame/";
             }
         } else {
-            Log.d("GameActivity", "Cannot load game from external storage: permission not granted");
+            Log.d("GameActivity", "Cannot load game from /sdcard/lovegame: permission not granted");
         }
     }
 
@@ -251,7 +305,7 @@ public class GameActivity extends SDLActivity {
         Log.d("GameActivity", "called getGamePath(), game path = " + gamePath);
 
         if (gamePath.length() > 0) {
-            if(self.hasExternalStoragePermission()) {
+            if(self.storagePermissionUnnecessary || self.hasExternalStoragePermission()) {
                 return gamePath;
             } else {
                 Log.d("GameActivity", "cannot open game " + gamePath + ": no external storage permission given!");
@@ -295,17 +349,9 @@ public class GameActivity extends SDLActivity {
      *
      * @return true if successful
      */
-    boolean copyAssetFile(String fileName, String destinationFileName) {
+    boolean copyAssetFile(InputStream source_stream, String destinationFileName) {
         boolean success = false;
 
-        // open source and destination streams
-        InputStream source_stream = null;
-        try {
-            source_stream = getAssets().open(fileName);
-        } catch (IOException e) {
-            Log.d("GameActivity", "Could not open game.love from assets: " + e.getMessage());
-        }
-
         BufferedOutputStream destination_stream = null;
         try {
             destination_stream = new BufferedOutputStream(new FileOutputStream(destinationFileName, false));
@@ -340,9 +386,7 @@ public class GameActivity extends SDLActivity {
             Log.d("GameActivity", "Copying failed: " + e.getMessage());
         }
 
-        Log.d("GameActivity", "Successfully copied " + fileName
-                + " to " + destinationFileName
-                + " (" + bytes_written + " bytes written).");
+        Log.d("GameActivity", "Successfully copied stream to " + destinationFileName + " (" + bytes_written + " bytes written).");
         return success;
     }
 
@@ -353,14 +397,11 @@ public class GameActivity extends SDLActivity {
     }
 
     public void showExternalStoragePermissionMissingDialog() {
-        AlertDialog.Builder builder = new AlertDialog.Builder(mSingleton);
-
-        builder.setTitle ("Storage Permission Missing")
-                .setMessage("LÖVE for Android will not be able to run non-packaged games without storage permission.");
-
-        builder.setNeutralButton("Continue", null);
-
-        AlertDialog dialog = builder.create();
+        AlertDialog dialog = new AlertDialog.Builder(mSingleton)
+            .setTitle("Storage Permission Missing")
+            .setMessage("LÖVE for Android will not be able to run non-packaged games without storage permission.")
+            .setNeutralButton("Continue", null)
+            .create();
         dialog.show();
     }
 
@@ -376,7 +417,7 @@ public class GameActivity extends SDLActivity {
                     Log.d("GameActivity", "Permission granted");
                 } else {
                     Log.d("GameActivity", "Did not get permission.");
-                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                         showExternalStoragePermissionMissingDialog();
                     }
                 }
@@ -393,13 +434,13 @@ public class GameActivity extends SDLActivity {
     @Keep
     public boolean hasExternalStoragePermission() {
         if (ActivityCompat.checkSelfPermission(this,
-                Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                Manifest.permission.READ_EXTERNAL_STORAGE)
                 == PackageManager.PERMISSION_GRANTED) {
             return true;
         }
 
         Log.d("GameActivity", "Requesting permission and locking LÖVE thread until we have an answer.");
-        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
+        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQUEST_CODE);
 
         synchronized (externalStorageRequestDummy) {
             try {
@@ -410,7 +451,7 @@ public class GameActivity extends SDLActivity {
             }
         }
 
-        return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+        return ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
     }
 
     @Keep