Browse Source

Impelement "Share to LOVE" support.

This allows you to share either .love game to launch as game (for the
non-fused APKs and instance of LOVE isn't running) or any file that
gets passed as love.filedropped.

Caveat: Ensure the intended game is running before sending files to LOVE app.
Otherwise, LOVE will attempt to open it as *.love file, which is may not what
you want.
Miku AuahDark 3 years ago
parent
commit
21a4c8eadc

+ 15 - 1
app/src/main/AndroidManifest.xml

@@ -32,7 +32,7 @@
             android:exported="true"
             android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
-            android:launchMode="singleTask"
+            android:launchMode="singleInstance"
             android:screenOrientation="landscape">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -43,5 +43,19 @@
                 <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
             </intent-filter>
         </activity>
+        <activity
+            android:name=".IntentReceiverActivity"
+            android:exported="true"
+            android:theme="@android:style/Theme.Translucent.NoTitleBar"
+            android:taskAffinity=""
+            android:excludeFromRecents="true"
+            android:relinquishTaskIdentity="true">
+            <intent-filter>
+                <action android:name="android.intent.action.SEND" />
+                <category android:name="android.intent.category.OPENABLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="*/*" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>

+ 1 - 1
app/src/main/cpp/love

@@ -1 +1 @@
-Subproject commit 28790741776db99907806d55fcb33d509116d8fb
+Subproject commit 7b66e50cfc72e85dee2ec4e28b756aad952be80e

+ 31 - 24
app/src/main/java/org/love2d/android/GameActivity.java

@@ -43,7 +43,6 @@ import android.util.Log;
 import android.view.DisplayCutout;
 import android.view.WindowManager;
 
-import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -58,8 +57,8 @@ public class GameActivity extends SDLActivity {
     protected Vibrator vibrator;
     protected boolean shortEdgesMode;
     protected final int[] recordAudioRequestDummy = new int[1];
-    private int delayedFd = -1;
-    private String[] args = new String[0];
+    private Uri delayedUri = null;
+    private String[] args;
     private boolean isFused;
 
     private static native void nativeSetDefaultStreamValues(int sampleRate, int framesPerBurst);
@@ -95,13 +94,16 @@ public class GameActivity extends SDLActivity {
     protected void onCreate(Bundle savedInstanceState) {
         Log.d(TAG, "started");
         isFused = hasEmbeddedGame();
+        args = new String[0];
 
         if (checkCallingOrSelfPermission(Manifest.permission.VIBRATE) == PackageManager.PERMISSION_GRANTED) {
             vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
         }
 
         Intent intent = getIntent();
-        handleIntent(intent);
+        // Prevent SDL sending filedropped event. Let us do that instead.
+        setIntent(null);
+        handleIntent(intent, true);
 
         super.onCreate(savedInstanceState);
 
@@ -118,17 +120,17 @@ public class GameActivity extends SDLActivity {
             shortEdgesMode = false;
         }
 
-        if (delayedFd != -1) {
+        if (delayedUri != null) {
             // This delayed fd is only sent if an embedded game is present.
-            sendFileDescriptorAsDroppedFile(delayedFd);
-            delayedFd = -1;
+            sendUriAsDroppedFile(delayedUri);
+            delayedUri = null;
         }
     }
 
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
-        handleIntent(intent);
+        handleIntent(intent, false);
     }
 
     @Override
@@ -330,6 +332,11 @@ public class GameActivity extends SDLActivity {
         }
     }
 
+    @Keep
+    public int convertToFileDescriptor(String uri) {
+        return convertToFileDescriptor(Uri.parse(uri));
+    }
+
     public int getAudioSMP() {
         int smp = 256;
         AudioManager a = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
@@ -363,32 +370,28 @@ public class GameActivity extends SDLActivity {
         return false;
     }
 
-    public void sendFileDescriptorAsDroppedFile(int fd) {
-        if (fd != -1) {
-            SDLActivity.onNativeDropFile("love2d://fd/" + fd);
-        }
+    public void sendUriAsDroppedFile(Uri uri) {
+        SDLActivity.onNativeDropFile(uri.toString());
     }
 
-    private void handleIntent(Intent intent) {
+    private void handleIntent(Intent intent, boolean onCreate) {
         Uri game = intent.getData();
         if (game == null) {
             return;
         }
 
-        if (mSingleton == null) {
-            // Game is not running, consider setting the currentGameInfo here
-
+        if (onCreate) {
+            // Game is not running
             if (isFused) {
                 // Send it as dropped file later
-                delayedFd = convertToFileDescriptor(game);
+                delayedUri = game;
             } else {
                 // Process for arguments
                 processOpenGame(game);
             }
         } else {
             // Game is already running. Send it as dropped file.
-            int fd = convertToFileDescriptor(game);
-            sendFileDescriptorAsDroppedFile(fd);
+            sendUriAsDroppedFile(game);
         }
     }
 
@@ -433,14 +436,17 @@ public class GameActivity extends SDLActivity {
     }
 
     private int convertToFileDescriptor(Uri uri) {
+        int fd = -1;
+
         try {
             ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(uri, "r");
-            return pfd.dup().detachFd();
-        } catch (IOException e) {
-            Log.d(TAG, "Failed attempt to convert " + uri.toString() + " to file descriptor", e);
+            fd = pfd.dup().detachFd();
+            pfd.close();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed attempt to convert " + uri.toString() + " to file descriptor", e);
         }
 
-        return -1;
+        return fd;
     }
 
     private void processOpenGame(Uri game) {
@@ -448,9 +454,10 @@ public class GameActivity extends SDLActivity {
         String path = game.getPath();
 
         if (scheme.equals("content")) {
-            // The intent may have more information about the filename
+            // Convert the URI to file descriptor.
             args = new String[] {"/love2d://fd/" + convertToFileDescriptor(game)};
         } else if (scheme.equals("file")) {
+            // Regular file, pass as-is.
             args = new String[] {path};
         }
     }

+ 28 - 0
app/src/main/java/org/love2d/android/IntentReceiverActivity.java

@@ -0,0 +1,28 @@
+package org.love2d.android;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+public class IntentReceiverActivity extends Activity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent mainIntent = getIntent();
+        Intent intent = new Intent(this, GameActivity.class);
+
+        if (mainIntent.getAction().equals(Intent.ACTION_SEND)) {
+            // Convert to simpler intent that our GameActivity can process.
+            Uri uri = mainIntent.getParcelableExtra(Intent.EXTRA_STREAM);
+            intent.setData(uri);
+            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        }
+
+        startActivity(intent);
+        finish();
+    }
+}

+ 1 - 1
app/src/normal/AndroidManifest.xml

@@ -7,7 +7,7 @@
         <activity
             android:name=".MainActivity"
             android:exported="true"
-            android:launchMode="singleTask"
+            android:launchMode="singleTop"
             android:theme="@style/Theme.AppCompat.DayNight">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />