Android: Convert StartupHandler to Kotlin
This commit is contained in:
1 parent
f074cdb08b
commit
b5b5b6fb7d
2 files changed
+118
-129
No files matched your search
@@ -1,129 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.dolphinemu.dolphinemu.utils;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.ClipData;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import androidx.fragment.app.FragmentActivity;
|
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.NativeLibrary;
|
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public final class StartupHandler
|
|
||||||
{
|
|
||||||
private static final String SESSION_TIMESTAMP = "SESSION_TIMESTAMP";
|
|
||||||
|
|
||||||
public static void HandleInit(FragmentActivity parent)
|
|
||||||
{
|
|
||||||
// Ask the user if he wants to enable analytics if we haven't yet.
|
|
||||||
Analytics.checkAnalyticsInit(parent);
|
|
||||||
|
|
||||||
// Set up and/or sync Android TV channels
|
|
||||||
if (TvUtil.isLeanback(parent))
|
|
||||||
TvUtil.scheduleSyncingChannel(parent);
|
|
||||||
|
|
||||||
String[] gamesToLaunch = getGamesFromIntent(parent.getIntent());
|
|
||||||
if (gamesToLaunch != null && gamesToLaunch.length > 0)
|
|
||||||
{
|
|
||||||
// Start the emulation activity, send the ISO passed in and finish the main activity
|
|
||||||
EmulationActivity.launch(parent, gamesToLaunch, false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] getGamesFromIntent(Intent intent)
|
|
||||||
{
|
|
||||||
// Priority order when looking for game paths in an intent:
|
|
||||||
//
|
|
||||||
// Specifying multiple discs (for multi-disc games) is prioritized over specifying a single
|
|
||||||
// disc. But most of the time, only a single disc will have been specified anyway.
|
|
||||||
//
|
|
||||||
// Specifying content URIs (compatible with scoped storage) is prioritized over raw paths.
|
|
||||||
// The intention is that if a frontend app specifies both a content URI and a raw path, newer
|
|
||||||
// versions of Dolphin will work correctly under scoped storage, while older versions of Dolphin
|
|
||||||
// (which don't use scoped storage and don't support content URIs) will also work.
|
|
||||||
|
|
||||||
// 1. Content URI, multiple
|
|
||||||
ClipData clipData = intent.getClipData();
|
|
||||||
if (clipData != null)
|
|
||||||
{
|
|
||||||
String[] uris = new String[clipData.getItemCount()];
|
|
||||||
for (int i = 0; i < uris.length; i++)
|
|
||||||
{
|
|
||||||
uris[i] = Objects.toString(clipData.getItemAt(i).getUri());
|
|
||||||
}
|
|
||||||
return uris;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Content URI, single
|
|
||||||
Uri uri = intent.getData();
|
|
||||||
if (uri != null)
|
|
||||||
return new String[]{uri.toString()};
|
|
||||||
|
|
||||||
Bundle extras = intent.getExtras();
|
|
||||||
if (extras != null)
|
|
||||||
{
|
|
||||||
// 3. File path, multiple
|
|
||||||
String[] paths = extras.getStringArray("AutoStartFiles");
|
|
||||||
if (paths != null)
|
|
||||||
return paths;
|
|
||||||
|
|
||||||
// 4. File path, single
|
|
||||||
String path = extras.getString("AutoStartFile");
|
|
||||||
if (!TextUtils.isEmpty(path))
|
|
||||||
return new String[]{path};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing was found
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Instant getSessionTimestamp(Context context)
|
|
||||||
{
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
long timestamp = preferences.getLong(SESSION_TIMESTAMP, 0);
|
|
||||||
return Instant.ofEpochMilli(timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on activity stop / to set timestamp to "now".
|
|
||||||
*/
|
|
||||||
public static void updateSessionTimestamp(Context context)
|
|
||||||
{
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
SharedPreferences.Editor sPrefsEditor = preferences.edit();
|
|
||||||
sPrefsEditor.putLong(SESSION_TIMESTAMP, Instant.now().toEpochMilli());
|
|
||||||
sPrefsEditor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called on activity start. Generates analytics start event if it's a fresh start of the app, or
|
|
||||||
* if it's a start after a long period of the app not being used (during which time the process
|
|
||||||
* may be restarted for power/memory saving reasons, although app state persists).
|
|
||||||
*/
|
|
||||||
public static void reportStartToAnalytics(Context context, boolean firstStart)
|
|
||||||
{
|
|
||||||
final Instant sessionTimestamp = getSessionTimestamp(context);
|
|
||||||
final Instant now = Instant.now();
|
|
||||||
if (firstStart || now.isAfter(sessionTimestamp.plus(6, ChronoUnit.HOURS)))
|
|
||||||
{
|
|
||||||
// Just in case: ensure start event won't be accidentally sent too often.
|
|
||||||
updateSessionTimestamp(context);
|
|
||||||
|
|
||||||
new AfterDirectoryInitializationRunner().runWithoutLifecycle(
|
|
||||||
NativeLibrary::ReportStartToAnalytics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.dolphinemu.dolphinemu.utils
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.TextUtils
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import org.dolphinemu.dolphinemu.NativeLibrary
|
||||||
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
|
object StartupHandler {
|
||||||
|
private const val SESSION_TIMESTAMP = "SESSION_TIMESTAMP"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun HandleInit(parent: FragmentActivity) {
|
||||||
|
// Ask the user if he wants to enable analytics if we haven't yet.
|
||||||
|
Analytics.checkAnalyticsInit(parent)
|
||||||
|
|
||||||
|
// Set up and/or sync Android TV channels
|
||||||
|
if (TvUtil.isLeanback(parent)) {
|
||||||
|
TvUtil.scheduleSyncingChannel(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
val gamesToLaunch = getGamesFromIntent(parent.intent)
|
||||||
|
if (gamesToLaunch != null && gamesToLaunch.isNotEmpty()) {
|
||||||
|
// Start the emulation activity, send the ISO passed in and finish the main activity
|
||||||
|
EmulationActivity.launch(parent, gamesToLaunch, false, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGamesFromIntent(intent: Intent): Array<String>? {
|
||||||
|
// Priority order when looking for game paths in an intent:
|
||||||
|
//
|
||||||
|
// Specifying multiple discs (for multi-disc games) is prioritized over specifying a single
|
||||||
|
// disc. But most of the time, only a single disc will have been specified anyway.
|
||||||
|
//
|
||||||
|
// Specifying content URIs (compatible with scoped storage) is prioritized over raw paths.
|
||||||
|
// The intention is that if a frontend app specifies both a content URI and a raw path, newer
|
||||||
|
// versions of Dolphin will work correctly under scoped storage, while older versions of Dolphin
|
||||||
|
// (which don't use scoped storage and don't support content URIs) will also work.
|
||||||
|
|
||||||
|
// 1. Content URI, multiple
|
||||||
|
val clipData: ClipData? = intent.clipData
|
||||||
|
if (clipData != null) {
|
||||||
|
val uris = Array(clipData.itemCount) { i ->
|
||||||
|
clipData.getItemAt(i).uri?.toString() ?: "null"
|
||||||
|
}
|
||||||
|
return uris
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Content URI, single
|
||||||
|
val uri: Uri? = intent.data
|
||||||
|
if (uri != null) {
|
||||||
|
return arrayOf(uri.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
val extras: Bundle? = intent.extras
|
||||||
|
if (extras != null) {
|
||||||
|
// 3. File path, multiple
|
||||||
|
val paths = extras.getStringArray("AutoStartFiles")
|
||||||
|
if (paths != null) {
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. File path, single
|
||||||
|
val path = extras.getString("AutoStartFile")
|
||||||
|
if (!TextUtils.isEmpty(path)) {
|
||||||
|
return arrayOf(path!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing was found
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSessionTimestamp(context: Context): Instant {
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val timestamp = preferences.getLong(SESSION_TIMESTAMP, 0)
|
||||||
|
return Instant.ofEpochMilli(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on activity stop / to set timestamp to "now".
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun updateSessionTimestamp(context: Context) {
|
||||||
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val prefsEditor = preferences.edit()
|
||||||
|
prefsEditor.putLong(SESSION_TIMESTAMP, Instant.now().toEpochMilli())
|
||||||
|
prefsEditor.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called on activity start. Generates analytics start event if it's a fresh start of the app, or
|
||||||
|
* if it's a start after a long period of the app not being used (during which time the process
|
||||||
|
* may be restarted for power/memory saving reasons, although app state persists).
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun reportStartToAnalytics(context: Context, firstStart: Boolean) {
|
||||||
|
val sessionTimestamp = getSessionTimestamp(context)
|
||||||
|
val now = Instant.now()
|
||||||
|
if (firstStart || now.isAfter(sessionTimestamp.plus(6, ChronoUnit.HOURS))) {
|
||||||
|
// Just in case: ensure start event won't be accidentally sent too often.
|
||||||
|
updateSessionTimestamp(context)
|
||||||
|
|
||||||
|
AfterDirectoryInitializationRunner().runWithoutLifecycle(
|
||||||
|
NativeLibrary::ReportStartToAnalytics
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user