From a496bb7d52943517467819a0a2305961a8ba6a0c Mon Sep 17 00:00:00 2001 From: Tae Hanazono Date: Sat, 3 Nov 2018 10:39:29 +0800 Subject: [PATCH 1/5] Place Android Java codes to platform --HG-- branch : android-fix --- platform/android/AndroidManifest.xml | 3 + .../org/love2d/android/DownloadActivity.java | 24 ++ .../org/love2d/android/DownloadService.java | 96 ++++++ .../java/org/love2d/android/GameActivity.java | 314 ++++++++++++++++++ 4 files changed, 437 insertions(+) create mode 100644 platform/android/AndroidManifest.xml create mode 100644 platform/android/java/org/love2d/android/DownloadActivity.java create mode 100644 platform/android/java/org/love2d/android/DownloadService.java create mode 100644 platform/android/java/org/love2d/android/GameActivity.java diff --git a/platform/android/AndroidManifest.xml b/platform/android/AndroidManifest.xml new file mode 100644 index 000000000..8078c41d0 --- /dev/null +++ b/platform/android/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/platform/android/java/org/love2d/android/DownloadActivity.java b/platform/android/java/org/love2d/android/DownloadActivity.java new file mode 100644 index 000000000..44dd77154 --- /dev/null +++ b/platform/android/java/org/love2d/android/DownloadActivity.java @@ -0,0 +1,24 @@ +package org.love2d.android; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class DownloadActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Uri uri = this.getIntent().getData(); + + if (uri.getScheme().equals("http")) { + String url = uri.toString(); + Intent intent = new Intent(this, DownloadService.class); + intent.putExtra("url", url); + startService(intent); + } + + finish(); + } +} diff --git a/platform/android/java/org/love2d/android/DownloadService.java b/platform/android/java/org/love2d/android/DownloadService.java new file mode 100644 index 000000000..aee4961fe --- /dev/null +++ b/platform/android/java/org/love2d/android/DownloadService.java @@ -0,0 +1,96 @@ +package org.love2d.android; + +import java.util.List; + +import android.app.DownloadManager; +import android.app.IntentService; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.util.Log; + +public class DownloadService extends IntentService { + public DownloadService() { + super("DownloadService"); + } + + @Override + public void onDestroy() { + Log.d("DownloadService", "destroying"); + unregisterReceiver(downloadReceiver); + } + + @Override + protected void onHandleIntent(Intent intent) { + Log.d("DownloadService", "service started"); + + String url = intent.getStringExtra("url"); + Uri uri = Uri.parse(url); + + Log.d("DownloadService", "Downloading from url: " + url + "file = " + uri.getLastPathSegment()); + + DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); + request.setDescription("LÖVE Game Download"); + request.setTitle(uri.getLastPathSegment()); + request.setMimeType("application/x-love-game"); + + // in order for this if to run, you must use the android 3.2 to compile your app + if (Build.VERSION.SDK_INT >= 11) { + DownloadRequestSettings_API11 settings = new DownloadRequestSettings_API11(); + settings.setup(request); + } + request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, uri.getLastPathSegment()); + // get download service and enqueue file + + Log.d("DownloadActivity", "creating manager"); + DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); + Log.d("DownloadActivity", "enqueuing download"); + manager.enqueue(request); + + Log.d("DownloadActivity", "download receiver = " + downloadReceiver); + IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); + registerReceiver(downloadReceiver, intentFilter); + } + + /** + * @param context used to check the device version and DownloadManager information + * @return true if the download manager is available + */ + public static boolean isDownloadManagerAvailable(Context context) { + try { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { + return false; + } + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + intent.setClassName("com.android.providers.downloads.ui", "com.android.providers.downloads.ui.DownloadList"); + List list = context.getPackageManager().queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY); + return list.size() > 0; + } catch (Exception e) { + return false; + } + } + + private BroadcastReceiver downloadReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + Log.d("DownloadActivity", "downloadReceiver intent called"); + + } + }; +} + +class DownloadRequestSettings_API11 { + public static void setup(DownloadManager.Request request) { + request.allowScanningByMediaScanner(); + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); + } +} diff --git a/platform/android/java/org/love2d/android/GameActivity.java b/platform/android/java/org/love2d/android/GameActivity.java new file mode 100644 index 000000000..9e75db664 --- /dev/null +++ b/platform/android/java/org/love2d/android/GameActivity.java @@ -0,0 +1,314 @@ +package org.love2d.android; + +import org.libsdl.app.SDLActivity; + +import java.util.Arrays; +import java.util.List; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.DownloadManager; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.media.AudioManager; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.PowerManager; +import android.os.ResultReceiver; +import android.os.Vibrator; +import android.support.annotation.Keep; +import android.util.Log; +import android.util.DisplayMetrics; +import android.widget.Toast; +import android.view.*; +import android.content.pm.PackageManager; + +public class GameActivity extends SDLActivity { + private static DisplayMetrics metrics = new DisplayMetrics(); + private static String gamePath = ""; + private static Context context; + private static Vibrator vibrator = null; + private static boolean immersiveActive = false; + private static boolean mustCacheArchive = false; + + @Override + protected String[] getLibraries() { + return new String[]{ + "c++_shared", + "mpg123", + "openal", + "love", + }; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.d("GameActivity", "started"); + + context = this.getApplicationContext(); + + String permission = "android.permission.VIBRATE"; + int res = context.checkCallingOrSelfPermission(permission); + if (res == PackageManager.PERMISSION_GRANTED) { + vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + } else { + Log.d("GameActivity", "Vibration disabled: could not get vibration permission."); + } + + handleIntent(this.getIntent()); + + super.onCreate(savedInstanceState); + getWindowManager().getDefaultDisplay().getMetrics(metrics); + } + + @Override + protected void onNewIntent(Intent intent) { + Log.d("GameActivity", "onNewIntent() with " + intent); + handleIntent(intent); + resetNative(); + startNative(); + } + + protected void handleIntent(Intent intent) { + Uri game = intent.getData(); + + if (game != null) { + // 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 we were given the path of a main.lua then use its + // directory. Otherwise use full path. + List 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()); + } else { + gamePath = game.getPath(); + } + } 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() + + "' as it uses unsupported scheme '" + game.getScheme() + + "'. Please contact the developer."); + alert_dialog.setTitle("LÖVE for Android Error"); + alert_dialog.setPositiveButton("Exit", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + finish(); + } + }); + alert_dialog.setCancelable(false); + alert_dialog.create().show(); + } + } else { + // No game specified via the intent data -> check whether we have a game.love in our assets. + boolean game_love_in_assets = false; + try { + List assets = Arrays.asList(getAssets().list("")); + game_love_in_assets = assets.contains("game.love"); + } catch (Exception e) { + Log.d("GameActivity", "could not list application assets:" + e.getMessage()); + } + + if (game_love_in_assets) { + // 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"; + } else { + // If no game.love was found fall back to the game in /lovegame + File ext = Environment.getExternalStorageDirectory(); + if ((new File(ext, "/lovegame/main.lua")).exists()) { + gamePath = ext.getPath() + "/lovegame/"; + } + } + } + + Log.d("GameActivity", "new gamePath: " + gamePath); + } + + @Override + protected void onDestroy() { + if (vibrator != null) { + Log.d("GameActivity", "Cancelling vibration"); + vibrator.cancel(); + } + super.onDestroy(); + } + + @Override + protected void onPause() { + if (vibrator != null) { + Log.d("GameActivity", "Cancelling vibration"); + vibrator.cancel(); + } + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + + if (immersiveActive) { + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } + } + + public void setImmersiveMode(boolean immersive_mode) { + if (android.os.Build.VERSION.SDK_INT < 11) { + // The API getWindow().getDecorView().setSystemUiVisibility() was + // added in Android 11 (a.k.a. Honeycomb, a.k.a. 3.0.x). If we run + // on this we do nothing. + return; + } + + immersiveActive = immersive_mode; + + final Object lock = new Object(); + final boolean immersive_enabled = immersive_mode; + synchronized (lock) { + runOnUiThread(new Runnable() { + @Override + public void run() { + synchronized (lock) { + if (immersive_enabled) { + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); + } else { + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + ); + } + + lock.notify(); + } + } + }); + } + ; + } + + public boolean getImmersiveMode() { + return immersiveActive; + } + + public static String getGamePath() { + Log.d("GameActivity", "called getGamePath(), game path = " + gamePath); + return gamePath; + } + + public static DisplayMetrics getMetrics() { + return metrics; + } + + public static void vibrate(double seconds) { + if (vibrator != null) { + vibrator.vibrate((long) (seconds * 1000.)); + } + } + + public static void openURL(String url) { + Log.d("GameActivity", "opening url = " + url); + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(i); + } + + /** + * Copies a given file from the assets folder to the destination. + * + * @return true if successful + */ + boolean copyAssetFile(String fileName, 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)); + } catch (IOException e) { + Log.d("GameActivity", "Could not open destination file: " + e.getMessage()); + } + + // perform the copying + int chunk_read = 0; + int bytes_written = 0; + + assert (source_stream != null && destination_stream != null); + + try { + byte[] buf = new byte[1024]; + chunk_read = source_stream.read(buf); + do { + destination_stream.write(buf, 0, chunk_read); + bytes_written += chunk_read; + chunk_read = source_stream.read(buf); + } while (chunk_read != -1); + } catch (IOException e) { + Log.d("GameActivity", "Copying failed:" + e.getMessage()); + } + + // close streams + try { + if (source_stream != null) source_stream.close(); + if (destination_stream != null) destination_stream.close(); + success = true; + } catch (IOException e) { + Log.d("GameActivity", "Copying failed: " + e.getMessage()); + } + + Log.d("GameActivity", "Successfully copied " + fileName + + " to " + destinationFileName + + " (" + bytes_written + " bytes written)."); + return success; + } + + @Keep + public boolean hasBackgroundMusic() { + AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + return audioManager.isMusicActive(); + } +} From 4c13278784a0034f2684e751deb28d53f0a83382 Mon Sep 17 00:00:00 2001 From: Tae Hanazono Date: Sat, 3 Nov 2018 15:19:55 +0800 Subject: [PATCH 2/5] Modify openURL to return boolean from Java side --HG-- branch : android-fix --- platform/android/AndroidManifest.xml | 3 --- src/common/android.cpp | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) delete mode 100644 platform/android/AndroidManifest.xml diff --git a/platform/android/AndroidManifest.xml b/platform/android/AndroidManifest.xml deleted file mode 100644 index 8078c41d0..000000000 --- a/platform/android/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/common/android.cpp b/src/common/android.cpp index 8e758852e..022ed0e60 100644 --- a/src/common/android.cpp +++ b/src/common/android.cpp @@ -123,14 +123,14 @@ bool openURL(const std::string &url) JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv(); jclass activity = env->FindClass("org/love2d/android/GameActivity"); - jmethodID openURL= env->GetStaticMethodID(activity, "openURL", "(Ljava/lang/String;)V"); + jmethodID openURL = env->GetStaticMethodID(activity, "openURL", "(Ljava/lang/String;)Z"); jstring url_jstring = (jstring) env->NewStringUTF(url.c_str()); - env->CallStaticVoidMethod(activity, openURL, url_jstring); + jboolean result = env->CallStaticBooleanMethod(activity, openURL, url_jstring); env->DeleteLocalRef(url_jstring); env->DeleteLocalRef(activity); - return true; + return result; } void vibrate(double seconds) From 18e4f81f87b119fe8cba0dbfc1bc8003cbd8e913 Mon Sep 17 00:00:00 2001 From: Tae Hanazono Date: Sat, 3 Nov 2018 15:54:50 +0800 Subject: [PATCH 3/5] Request external storage --HG-- branch : android-fix --- .../java/org/love2d/android/GameActivity.java | 88 +++++++++++++++++-- 1 file changed, 79 insertions(+), 9 deletions(-) diff --git a/platform/android/java/org/love2d/android/GameActivity.java b/platform/android/java/org/love2d/android/GameActivity.java index 9e75db664..baab879ad 100644 --- a/platform/android/java/org/love2d/android/GameActivity.java +++ b/platform/android/java/org/love2d/android/GameActivity.java @@ -14,6 +14,7 @@ import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; +import android.Manifest; import android.app.Activity; import android.app.AlertDialog; import android.app.DownloadManager; @@ -32,6 +33,7 @@ import android.os.PowerManager; import android.os.ResultReceiver; import android.os.Vibrator; import android.support.annotation.Keep; +import android.support.v4.app.ActivityCompat; import android.util.Log; import android.util.DisplayMetrics; import android.widget.Toast; @@ -42,6 +44,7 @@ public class GameActivity extends SDLActivity { private static DisplayMetrics metrics = new DisplayMetrics(); private static String gamePath = ""; private static Context context; + private static GameActivity singleton; private static Vibrator vibrator = null; private static boolean immersiveActive = false; private static boolean mustCacheArchive = false; @@ -52,6 +55,7 @@ public class GameActivity extends SDLActivity { "c++_shared", "mpg123", "openal", + "hidapi", "love", }; } @@ -60,6 +64,7 @@ public class GameActivity extends SDLActivity { protected void onCreate(Bundle savedInstanceState) { Log.d("GameActivity", "started"); + singleton = this; context = this.getApplicationContext(); String permission = "android.permission.VIBRATE"; @@ -140,9 +145,12 @@ public class GameActivity extends SDLActivity { gamePath = "game.love"; } else { // If no game.love was found fall back to the game in /lovegame - File ext = Environment.getExternalStorageDirectory(); - if ((new File(ext, "/lovegame/main.lua")).exists()) { - gamePath = ext.getPath() + "/lovegame/"; + if (singleton.hasExternalStoragePermission()) + { + File ext = Environment.getExternalStorageDirectory(); + if ((new File(ext, "/lovegame/main.lua")).exists()) { + gamePath = ext.getPath() + "/lovegame/"; + } } } } @@ -229,7 +237,10 @@ public class GameActivity extends SDLActivity { public static String getGamePath() { Log.d("GameActivity", "called getGamePath(), game path = " + gamePath); - return gamePath; + if (gamePath.length() > 0 && singleton.hasExternalStoragePermission()) + return gamePath; + else + return ""; } public static DisplayMetrics getMetrics() { @@ -242,12 +253,19 @@ public class GameActivity extends SDLActivity { } } - public static void openURL(String url) { + public static boolean openURL(String url) { Log.d("GameActivity", "opening url = " + url); - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); + try { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(i); + return true; + } + catch (Exception e) + { + return false; + } } /** @@ -311,4 +329,56 @@ public class GameActivity extends SDLActivity { AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); return audioManager.isMusicActive(); } + + protected final int[] externalStorageRequestRes = new int[1]; + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) + { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + if (requestCode == 1) + { + synchronized (externalStorageRequestRes) + { + externalStorageRequestRes[0] = grantResults[0]; + externalStorageRequestRes.notify(); + } + } + } + + protected Activity self; + @Keep + public boolean hasExternalStoragePermission() + { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) + { + if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) + { + self = this; + runOnUiThread(new Runnable() { + @Override + public void run() + { + ActivityCompat.requestPermissions(self, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); + } + }); + + synchronized (externalStorageRequestRes) + { + try + { + externalStorageRequestRes.wait(); + } + catch (InterruptedException ex) { + ex.printStackTrace(); + return false; + } + + } + return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } + } else + return true; + return false; + } } From 07fcc1e151895b1a17b8dd98a9959ac4ca8401b2 Mon Sep 17 00:00:00 2001 From: Tae Hanazono Date: Sat, 3 Nov 2018 16:30:49 +0800 Subject: [PATCH 4/5] Rationale confusion --HG-- branch : android-fix --- .../java/org/love2d/android/GameActivity.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/platform/android/java/org/love2d/android/GameActivity.java b/platform/android/java/org/love2d/android/GameActivity.java index baab879ad..469f4175d 100644 --- a/platform/android/java/org/love2d/android/GameActivity.java +++ b/platform/android/java/org/love2d/android/GameActivity.java @@ -143,21 +143,24 @@ public class GameActivity extends SDLActivity { gamePath = destination_file; else gamePath = "game.love"; - } else { - // If no game.love was found fall back to the game in /lovegame - if (singleton.hasExternalStoragePermission()) - { - File ext = Environment.getExternalStorageDirectory(); - if ((new File(ext, "/lovegame/main.lua")).exists()) { - gamePath = ext.getPath() + "/lovegame/"; - } - } } } Log.d("GameActivity", "new gamePath: " + gamePath); } + protected void checkLovegameFolder() + { + // If no game.love was found fall back to the game in /lovegame + if (singleton.hasExternalStoragePermission()) + { + File ext = Environment.getExternalStorageDirectory(); + if ((new File(ext, "/lovegame/main.lua")).exists()) { + gamePath = ext.getPath() + "/lovegame/"; + } + } + } + @Override protected void onDestroy() { if (vibrator != null) { @@ -240,7 +243,13 @@ public class GameActivity extends SDLActivity { if (gamePath.length() > 0 && singleton.hasExternalStoragePermission()) return gamePath; else - return ""; + { + singleton.checkLovegameFolder(); + if (gamePath.length() > 0) + return gamePath; + } + + return ""; } public static DisplayMetrics getMetrics() { @@ -352,7 +361,7 @@ public class GameActivity extends SDLActivity { { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { self = this; runOnUiThread(new Runnable() { From c00849d573d8a8a57b4aea4c57458c8be26d7d78 Mon Sep 17 00:00:00 2001 From: Tae Hanazono Date: Sun, 4 Nov 2018 19:43:48 +0800 Subject: [PATCH 5/5] Move java files to love-android repo --HG-- branch : android-fix --- .../org/love2d/android/DownloadActivity.java | 24 -- .../org/love2d/android/DownloadService.java | 96 ----- .../java/org/love2d/android/GameActivity.java | 393 ------------------ 3 files changed, 513 deletions(-) delete mode 100644 platform/android/java/org/love2d/android/DownloadActivity.java delete mode 100644 platform/android/java/org/love2d/android/DownloadService.java delete mode 100644 platform/android/java/org/love2d/android/GameActivity.java diff --git a/platform/android/java/org/love2d/android/DownloadActivity.java b/platform/android/java/org/love2d/android/DownloadActivity.java deleted file mode 100644 index 44dd77154..000000000 --- a/platform/android/java/org/love2d/android/DownloadActivity.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.love2d.android; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; - -public class DownloadActivity extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Uri uri = this.getIntent().getData(); - - if (uri.getScheme().equals("http")) { - String url = uri.toString(); - Intent intent = new Intent(this, DownloadService.class); - intent.putExtra("url", url); - startService(intent); - } - - finish(); - } -} diff --git a/platform/android/java/org/love2d/android/DownloadService.java b/platform/android/java/org/love2d/android/DownloadService.java deleted file mode 100644 index aee4961fe..000000000 --- a/platform/android/java/org/love2d/android/DownloadService.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.love2d.android; - -import java.util.List; - -import android.app.DownloadManager; -import android.app.IntentService; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.util.Log; - -public class DownloadService extends IntentService { - public DownloadService() { - super("DownloadService"); - } - - @Override - public void onDestroy() { - Log.d("DownloadService", "destroying"); - unregisterReceiver(downloadReceiver); - } - - @Override - protected void onHandleIntent(Intent intent) { - Log.d("DownloadService", "service started"); - - String url = intent.getStringExtra("url"); - Uri uri = Uri.parse(url); - - Log.d("DownloadService", "Downloading from url: " + url + "file = " + uri.getLastPathSegment()); - - DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); - request.setDescription("LÖVE Game Download"); - request.setTitle(uri.getLastPathSegment()); - request.setMimeType("application/x-love-game"); - - // in order for this if to run, you must use the android 3.2 to compile your app - if (Build.VERSION.SDK_INT >= 11) { - DownloadRequestSettings_API11 settings = new DownloadRequestSettings_API11(); - settings.setup(request); - } - request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, uri.getLastPathSegment()); - // get download service and enqueue file - - Log.d("DownloadActivity", "creating manager"); - DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); - Log.d("DownloadActivity", "enqueuing download"); - manager.enqueue(request); - - Log.d("DownloadActivity", "download receiver = " + downloadReceiver); - IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE); - registerReceiver(downloadReceiver, intentFilter); - } - - /** - * @param context used to check the device version and DownloadManager information - * @return true if the download manager is available - */ - public static boolean isDownloadManagerAvailable(Context context) { - try { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) { - return false; - } - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.setClassName("com.android.providers.downloads.ui", "com.android.providers.downloads.ui.DownloadList"); - List list = context.getPackageManager().queryIntentActivities(intent, - PackageManager.MATCH_DEFAULT_ONLY); - return list.size() > 0; - } catch (Exception e) { - return false; - } - } - - private BroadcastReceiver downloadReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - Log.d("DownloadActivity", "downloadReceiver intent called"); - - } - }; -} - -class DownloadRequestSettings_API11 { - public static void setup(DownloadManager.Request request) { - request.allowScanningByMediaScanner(); - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - } -} diff --git a/platform/android/java/org/love2d/android/GameActivity.java b/platform/android/java/org/love2d/android/GameActivity.java deleted file mode 100644 index 469f4175d..000000000 --- a/platform/android/java/org/love2d/android/GameActivity.java +++ /dev/null @@ -1,393 +0,0 @@ -package org.love2d.android; - -import org.libsdl.app.SDLActivity; - -import java.util.Arrays; -import java.util.List; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.URL; - -import android.Manifest; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.DownloadManager; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.media.AudioManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.PowerManager; -import android.os.ResultReceiver; -import android.os.Vibrator; -import android.support.annotation.Keep; -import android.support.v4.app.ActivityCompat; -import android.util.Log; -import android.util.DisplayMetrics; -import android.widget.Toast; -import android.view.*; -import android.content.pm.PackageManager; - -public class GameActivity extends SDLActivity { - private static DisplayMetrics metrics = new DisplayMetrics(); - private static String gamePath = ""; - private static Context context; - private static GameActivity singleton; - private static Vibrator vibrator = null; - private static boolean immersiveActive = false; - private static boolean mustCacheArchive = false; - - @Override - protected String[] getLibraries() { - return new String[]{ - "c++_shared", - "mpg123", - "openal", - "hidapi", - "love", - }; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - Log.d("GameActivity", "started"); - - singleton = this; - context = this.getApplicationContext(); - - String permission = "android.permission.VIBRATE"; - int res = context.checkCallingOrSelfPermission(permission); - if (res == PackageManager.PERMISSION_GRANTED) { - vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); - } else { - Log.d("GameActivity", "Vibration disabled: could not get vibration permission."); - } - - handleIntent(this.getIntent()); - - super.onCreate(savedInstanceState); - getWindowManager().getDefaultDisplay().getMetrics(metrics); - } - - @Override - protected void onNewIntent(Intent intent) { - Log.d("GameActivity", "onNewIntent() with " + intent); - handleIntent(intent); - resetNative(); - startNative(); - } - - protected void handleIntent(Intent intent) { - Uri game = intent.getData(); - - if (game != null) { - // 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 we were given the path of a main.lua then use its - // directory. Otherwise use full path. - List 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()); - } else { - gamePath = game.getPath(); - } - } 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() - + "' as it uses unsupported scheme '" + game.getScheme() - + "'. Please contact the developer."); - alert_dialog.setTitle("LÖVE for Android Error"); - alert_dialog.setPositiveButton("Exit", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - finish(); - } - }); - alert_dialog.setCancelable(false); - alert_dialog.create().show(); - } - } else { - // No game specified via the intent data -> check whether we have a game.love in our assets. - boolean game_love_in_assets = false; - try { - List assets = Arrays.asList(getAssets().list("")); - game_love_in_assets = assets.contains("game.love"); - } catch (Exception e) { - Log.d("GameActivity", "could not list application assets:" + e.getMessage()); - } - - if (game_love_in_assets) { - // 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"; - } - } - - Log.d("GameActivity", "new gamePath: " + gamePath); - } - - protected void checkLovegameFolder() - { - // If no game.love was found fall back to the game in /lovegame - if (singleton.hasExternalStoragePermission()) - { - File ext = Environment.getExternalStorageDirectory(); - if ((new File(ext, "/lovegame/main.lua")).exists()) { - gamePath = ext.getPath() + "/lovegame/"; - } - } - } - - @Override - protected void onDestroy() { - if (vibrator != null) { - Log.d("GameActivity", "Cancelling vibration"); - vibrator.cancel(); - } - super.onDestroy(); - } - - @Override - protected void onPause() { - if (vibrator != null) { - Log.d("GameActivity", "Cancelling vibration"); - vibrator.cancel(); - } - super.onPause(); - } - - @Override - public void onResume() { - super.onResume(); - - if (immersiveActive) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } - } - - public void setImmersiveMode(boolean immersive_mode) { - if (android.os.Build.VERSION.SDK_INT < 11) { - // The API getWindow().getDecorView().setSystemUiVisibility() was - // added in Android 11 (a.k.a. Honeycomb, a.k.a. 3.0.x). If we run - // on this we do nothing. - return; - } - - immersiveActive = immersive_mode; - - final Object lock = new Object(); - final boolean immersive_enabled = immersive_mode; - synchronized (lock) { - runOnUiThread(new Runnable() { - @Override - public void run() { - synchronized (lock) { - if (immersive_enabled) { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY); - } else { - getWindow().getDecorView().setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - ); - } - - lock.notify(); - } - } - }); - } - ; - } - - public boolean getImmersiveMode() { - return immersiveActive; - } - - public static String getGamePath() { - Log.d("GameActivity", "called getGamePath(), game path = " + gamePath); - if (gamePath.length() > 0 && singleton.hasExternalStoragePermission()) - return gamePath; - else - { - singleton.checkLovegameFolder(); - if (gamePath.length() > 0) - return gamePath; - } - - return ""; - } - - public static DisplayMetrics getMetrics() { - return metrics; - } - - public static void vibrate(double seconds) { - if (vibrator != null) { - vibrator.vibrate((long) (seconds * 1000.)); - } - } - - public static boolean openURL(String url) { - Log.d("GameActivity", "opening url = " + url); - try { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); - return true; - } - catch (Exception e) - { - return false; - } - } - - /** - * Copies a given file from the assets folder to the destination. - * - * @return true if successful - */ - boolean copyAssetFile(String fileName, 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)); - } catch (IOException e) { - Log.d("GameActivity", "Could not open destination file: " + e.getMessage()); - } - - // perform the copying - int chunk_read = 0; - int bytes_written = 0; - - assert (source_stream != null && destination_stream != null); - - try { - byte[] buf = new byte[1024]; - chunk_read = source_stream.read(buf); - do { - destination_stream.write(buf, 0, chunk_read); - bytes_written += chunk_read; - chunk_read = source_stream.read(buf); - } while (chunk_read != -1); - } catch (IOException e) { - Log.d("GameActivity", "Copying failed:" + e.getMessage()); - } - - // close streams - try { - if (source_stream != null) source_stream.close(); - if (destination_stream != null) destination_stream.close(); - success = true; - } catch (IOException e) { - Log.d("GameActivity", "Copying failed: " + e.getMessage()); - } - - Log.d("GameActivity", "Successfully copied " + fileName - + " to " + destinationFileName - + " (" + bytes_written + " bytes written)."); - return success; - } - - @Keep - public boolean hasBackgroundMusic() { - AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - return audioManager.isMusicActive(); - } - - protected final int[] externalStorageRequestRes = new int[1]; - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) - { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - - if (requestCode == 1) - { - synchronized (externalStorageRequestRes) - { - externalStorageRequestRes[0] = grantResults[0]; - externalStorageRequestRes.notify(); - } - } - } - - protected Activity self; - @Keep - public boolean hasExternalStoragePermission() - { - if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) - { - if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) - { - self = this; - runOnUiThread(new Runnable() { - @Override - public void run() - { - ActivityCompat.requestPermissions(self, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); - } - }); - - synchronized (externalStorageRequestRes) - { - try - { - externalStorageRequestRes.wait(); - } - catch (InterruptedException ex) { - ex.printStackTrace(); - return false; - } - - } - return ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; - } - } else - return true; - return false; - } -}