mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-15 02:29:51 +01:00
Merge pull request #22745 from hrydgard/android-file-grants
Android: Manage our persisted URI grants before the system drops them
This commit is contained in:
8 files changed
+329
-1
No files matched your search
+39
-1
@@ -1397,7 +1397,8 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
||||
bSaveSettings = true;
|
||||
|
||||
IniFile iniFile;
|
||||
if (!iniFile.Load(iniFilename_)) {
|
||||
loadedFromFile_ = iniFile.Load(iniFilename_);
|
||||
if (!loadedFromFile_) {
|
||||
WARN_LOG(Log::Config, "Failed to read '%s'. Setting main config to default.", iniFilename_.c_str());
|
||||
// Continue anyway to initialize the config.
|
||||
}
|
||||
@@ -1449,6 +1450,18 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) {
|
||||
}
|
||||
}
|
||||
|
||||
vShortcutUris.clear();
|
||||
if (const Section *shortcuts = iniFile.GetSection("Shortcuts")) {
|
||||
for (int i = 0; i < MAX_SHORTCUT_URIS; i++) {
|
||||
char keyName[64];
|
||||
snprintf(keyName, sizeof(keyName), "Uri%d", i);
|
||||
std::string uri;
|
||||
if (shortcuts->Get(keyName, &uri) && !uri.empty()) {
|
||||
vShortcutUris.push_back(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default values for post process shaders
|
||||
bool postShadersInitialized = iniFile.HasSection("PostShaderList");
|
||||
Section *postShaderChain = iniFile.GetOrCreateSection("PostShaderList");
|
||||
@@ -1573,6 +1586,18 @@ bool Config::Save(const char *saveReason) {
|
||||
pinnedPaths->Set(keyName, vPinnedPaths[i]);
|
||||
}
|
||||
|
||||
if (vShortcutUris.empty()) {
|
||||
iniFile.DeleteSection("Shortcuts");
|
||||
} else {
|
||||
Section *shortcuts = iniFile.GetOrCreateSection("Shortcuts");
|
||||
shortcuts->Clear();
|
||||
for (size_t i = 0; i < vShortcutUris.size(); ++i) {
|
||||
char keyName[64];
|
||||
snprintf(keyName, sizeof(keyName), "Uri%d", (int)i);
|
||||
shortcuts->Set(keyName, vShortcutUris[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsGameSpecific()) {
|
||||
// These settings can be game specific, and so are handled in SaveGameConfig().
|
||||
|
||||
@@ -1841,6 +1866,19 @@ void Config::DismissUpgrade() {
|
||||
sUpgradeMessage.clear();
|
||||
}
|
||||
|
||||
bool Config::AddShortcutUri(std::string_view uri) {
|
||||
auto iter = std::find(vShortcutUris.begin(), vShortcutUris.end(), uri);
|
||||
const bool added = iter == vShortcutUris.end();
|
||||
if (!added) {
|
||||
vShortcutUris.erase(iter);
|
||||
}
|
||||
vShortcutUris.insert(vShortcutUris.begin(), std::string(uri));
|
||||
if ((int)vShortcutUris.size() > MAX_SHORTCUT_URIS) {
|
||||
vShortcutUris.resize(MAX_SHORTCUT_URIS);
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
void Config::SetSearchPath(const Path &searchPath) {
|
||||
searchPath_ = searchPath;
|
||||
}
|
||||
|
||||
@@ -280,6 +280,12 @@ public:
|
||||
|
||||
std::string sReportHost;
|
||||
std::vector<std::string> vPinnedPaths;
|
||||
// Android: the content URIs that home screen shortcuts were most recently created for or launched with, newest
|
||||
// first. Their file grants are never released (see ContentUri.manageUriGrants).
|
||||
std::vector<std::string> vShortcutUris;
|
||||
static constexpr int MAX_SHORTCUT_URIS = 64; // An eighth of Android's limit on persisted URI grants.
|
||||
// Moves the URI to the front. Returns true if it wasn't in the list before.
|
||||
bool AddShortcutUri(std::string_view uri);
|
||||
std::string sLanguageIni;
|
||||
|
||||
std::string sIgnoreCompatSettings;
|
||||
@@ -764,6 +770,8 @@ public:
|
||||
|
||||
bool HasGameConfig(std::string_view gameId);
|
||||
bool IsGameSpecific() const { return !gameId_.empty(); }
|
||||
// False if the last Load() couldn't read the ini (missing, or the memory stick isn't reachable) and fell back to defaults.
|
||||
bool LoadedFromFile() const { return loadedFromFile_; }
|
||||
|
||||
void SetSearchPath(const Path &path);
|
||||
|
||||
@@ -830,6 +838,7 @@ private:
|
||||
// Always the paths to the main configs, doesn't change with game-specific overlay.
|
||||
Path iniFilename_;
|
||||
Path controllerIniFilename_;
|
||||
bool loadedFromFile_ = false;
|
||||
|
||||
Path searchPath_;
|
||||
Path appendedConfigFileName_;
|
||||
|
||||
@@ -101,6 +101,12 @@ void RecentFilesManager::Load(const Section *recent, int maxRecent) {
|
||||
// Just zapping any pending command, since they won't matter after this.
|
||||
// TODO: Maybe we should let adds through...
|
||||
WipePendingCommandsUnderLock();
|
||||
// Visible right away, for callers that read the list straight after loading the config (Android's
|
||||
// URI grant pruning). The queued ReplaceAll still runs, in case a command was mid-flight.
|
||||
{
|
||||
std::lock_guard<std::mutex> recentGuard(recentLock_);
|
||||
recentFiles_ = newRecent;
|
||||
}
|
||||
cmds_.push(RecentCommand{ RecentCmd::ReplaceAll, std::make_unique<std::vector<std::string>>(newRecent) });
|
||||
cmdCondVar_.notify_one();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
|
||||
@@ -77,6 +78,7 @@ struct JNIEnv {};
|
||||
#include "Common/System/Request.h"
|
||||
#include "Common/Thread/ThreadUtil.h"
|
||||
#include "Common/File/Path.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/File/DirListing.h"
|
||||
#include "Common/File/VFS/VFS.h"
|
||||
#include "Common/File/VFS/DirectoryReader.h"
|
||||
@@ -102,6 +104,7 @@ struct JNIEnv {};
|
||||
#include "Core/ConfigValues.h"
|
||||
#include "Core/Loaders.h"
|
||||
#include "Core/KeyMap.h"
|
||||
#include "Core/Util/RecentFiles.h"
|
||||
#include "Core/System.h"
|
||||
#include "Core/EmuThread.h"
|
||||
#include "Core/HLE/sceUsbCam.h"
|
||||
@@ -609,6 +612,75 @@ extern "C" jstring Java_org_ppsspp_ppsspp_NativeApp_queryConfig
|
||||
return jresult;
|
||||
}
|
||||
|
||||
// False if the config in memory may not be the user's: the ini couldn't be read (say the memory stick is on an
|
||||
// SD card that isn't mounted), or NativeInit couldn't reach the memory stick in memstick_dir.txt and fell
|
||||
// back to the default one, where an old ppsspp.ini may be lying around.
|
||||
static bool HaveUsersConfig() {
|
||||
if (!g_Config.LoadedFromFile() || g_Config.memStickDirectory.empty()) {
|
||||
return false;
|
||||
}
|
||||
const Path memstickDirFile = g_Config.internalDataDirectory / "memstick_dir.txt";
|
||||
if (!File::Exists(memstickDirFile)) {
|
||||
return true;
|
||||
}
|
||||
std::string memstickDir;
|
||||
return File::ReadTextFileToString(memstickDirFile, &memstickDir) && Path(memstickDir) == g_Config.memStickDirectory;
|
||||
}
|
||||
|
||||
// For ContentUri.manageUriGrants. Anything that stores the URI of a picked file or folder should be listed
|
||||
// here, or the grant for it may eventually be released. A grant is kept if it's one of these, or a folder
|
||||
// one of these is in. Returns null if we don't have the user's config, since then we don't know what's in use.
|
||||
extern "C" jobjectArray Java_org_ppsspp_ppsspp_NativeApp_getReferencedUris(JNIEnv *env, jclass) {
|
||||
if (!HaveUsersConfig()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<std::string> uris = g_recentFiles.GetRecentFiles();
|
||||
uris.push_back(g_Config.memStickDirectory.ToString());
|
||||
uris.push_back(g_Config.currentDirectory.ToString());
|
||||
uris.push_back(g_Config.sRemoteISOSharedDir);
|
||||
uris.insert(uris.end(), g_Config.vPinnedPaths.begin(), g_Config.vPinnedPaths.end());
|
||||
uris.insert(uris.end(), g_Config.vShortcutUris.begin(), g_Config.vShortcutUris.end());
|
||||
uris.push_back(g_Config.sAchievementsUnlockAudioFile);
|
||||
uris.push_back(g_Config.sAchievementsLeaderboardSubmitAudioFile);
|
||||
uris.erase(std::remove_if(uris.begin(), uris.end(), [](const std::string &uri) {
|
||||
return !Android_IsContentUri(uri);
|
||||
}), uris.end());
|
||||
|
||||
jclass stringClass = env->FindClass("java/lang/String");
|
||||
jobjectArray result = env->NewObjectArray((jsize)uris.size(), stringClass, nullptr);
|
||||
for (size_t i = 0; i < uris.size(); i++) {
|
||||
jstring juri = env->NewStringUTF(uris[i].c_str());
|
||||
env->SetObjectArrayElement(result, (jsize)i, juri);
|
||||
env->DeleteLocalRef(juri);
|
||||
}
|
||||
env->DeleteLocalRef(stringClass);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Only called from PpssppActivity.Initialize, before the render thread runs, so g_Config is ours to change.
|
||||
// Returns false if they couldn't be saved, so the Java side keeps them for next time.
|
||||
extern "C" jboolean Java_org_ppsspp_ppsspp_NativeApp_addShortcutUris(JNIEnv *env, jclass, jobjectArray juris) {
|
||||
if (!HaveUsersConfig()) {
|
||||
return false;
|
||||
}
|
||||
const std::vector<std::string> before = g_Config.vShortcutUris;
|
||||
const jsize count = env->GetArrayLength(juris);
|
||||
// Oldest first, so the newest ends up at the front.
|
||||
for (jsize i = 0; i < count; i++) {
|
||||
jstring juri = (jstring)env->GetObjectArrayElement(juris, i);
|
||||
std::string uri = GetJavaString(env, juri);
|
||||
env->DeleteLocalRef(juri);
|
||||
if (Android_IsContentUri(uri) && g_Config.AddShortcutUri(uri)) {
|
||||
INFO_LOG(Log::System, "New home screen shortcut target: %s", uri.c_str());
|
||||
}
|
||||
}
|
||||
// Also when only the order changed, so the ones launched lately aren't the first to go.
|
||||
if (g_Config.vShortcutUris != before) {
|
||||
return g_Config.Save("ShortcutUrisAdded");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Need to use raw Android logging before NativeInit.
|
||||
#define EARLY_LOG(...) __android_log_print(ANDROID_LOG_INFO, "PPSSPP", __VA_ARGS__)
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ package org.ppsspp.ppsspp;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.UriPermission;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
@@ -10,6 +14,7 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.system.Os;
|
||||
import android.system.StructStatVfs;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
@@ -18,7 +23,13 @@ import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class ContentUri {
|
||||
private static final String TAG = "PpssppActivity"; // don't want to add yet another tag
|
||||
@@ -416,6 +427,183 @@ public class ContentUri {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Android only lets an app keep so many persisted URI grants (512, or 128 before Android 11). When it
|
||||
// goes over, the system drops the oldest ones, whatever they are. We take one for every single file
|
||||
// picked (games loaded through the file picker, home screen shortcuts, sound effects) and never gave any
|
||||
// back, so sooner or later the one dropped would be the folder the user set up as the memory stick.
|
||||
//
|
||||
// Two things at startup keep that from happening:
|
||||
// * Re-take the grants for the folders we use (the memory stick, the Games tab, pinned folders, the Remote
|
||||
// ISO shared folder). That makes them the newest, so they're the last the system would drop.
|
||||
// * Past half the limit, release the oldest single-file grants that nothing points at anymore. Folder
|
||||
// grants are never released. Home screen shortcuts can't be enumerated, so ppsspp.ini remembers the
|
||||
// ones most recently created or launched (Config::MAX_SHORTCUT_URIS), and those are spared too.
|
||||
private static final String GRANT_TAG = "PPSSPPUriGrants";
|
||||
|
||||
private static boolean isTreeUri(Uri uri) {
|
||||
// Same check as DocumentsContract.isTreeUri, which is API 24+.
|
||||
List<String> segments = uri.getPathSegments();
|
||||
return segments.size() >= 2 && "tree".equals(segments.get(0));
|
||||
}
|
||||
|
||||
// True if something points at the grant's URI, or at something inside it (if it's a folder).
|
||||
static boolean isReferenced(Uri grantUri, Set<String> referencedUris) {
|
||||
final String uri = grantUri.toString();
|
||||
if (referencedUris.contains(uri)) {
|
||||
return true;
|
||||
}
|
||||
final String prefix = uri + "/";
|
||||
for (String referenced : referencedUris) {
|
||||
if (referenced.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int grantFlags(UriPermission grant) {
|
||||
int flags = 0;
|
||||
if (grant.isReadPermission()) {
|
||||
flags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
}
|
||||
if (grant.isWritePermission()) {
|
||||
flags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Returns the grants to release to get down to maxGrants, oldest first. Doesn't release anything.
|
||||
static ArrayList<UriPermission> chooseGrantsToRelease(List<UriPermission> grants, int maxGrants, Set<String> referencedUris) {
|
||||
ArrayList<UriPermission> toRelease = new ArrayList<>();
|
||||
int excess = grants.size() - maxGrants;
|
||||
if (excess <= 0) {
|
||||
return toRelease;
|
||||
}
|
||||
ArrayList<UriPermission> oldestFirst = new ArrayList<>(grants);
|
||||
Collections.sort(oldestFirst, (a, b) -> Long.compare(a.getPersistedTime(), b.getPersistedTime()));
|
||||
for (UriPermission grant : oldestFirst) {
|
||||
if (toRelease.size() >= excess) {
|
||||
break;
|
||||
}
|
||||
if (isTreeUri(grant.getUri()) || isReferenced(grant.getUri(), referencedUris)) {
|
||||
continue;
|
||||
}
|
||||
toRelease.add(grant);
|
||||
}
|
||||
return toRelease;
|
||||
}
|
||||
|
||||
// Call once at startup, after the native side has loaded the config and been handed the queued shortcut URIs.
|
||||
public static void manageUriGrants(Context context) {
|
||||
try {
|
||||
final ContentResolver resolver = context.getContentResolver();
|
||||
final List<UriPermission> grants = resolver.getPersistedUriPermissions();
|
||||
final int systemLimit = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R ? 512 : 128;
|
||||
final int maxGrants = systemLimit / 2;
|
||||
final String[] referenced = NativeApp.getReferencedUris();
|
||||
if (referenced == null) {
|
||||
// We can't tell what's in use, so leave everything alone.
|
||||
Log.w(GRANT_TAG, "Don't have the user's config, not touching any of our " + grants.size() + " URI grants.");
|
||||
return;
|
||||
}
|
||||
final Set<String> referencedUris = new HashSet<>(Arrays.asList(referenced));
|
||||
|
||||
int folders = 0;
|
||||
for (UriPermission grant : grants) {
|
||||
if (isTreeUri(grant.getUri())) {
|
||||
folders++;
|
||||
}
|
||||
}
|
||||
Log.i(GRANT_TAG, "Holding " + grants.size() + " persisted URI grants: " + folders + " folders, " + (grants.size() - folders)
|
||||
+ " files. The system drops the oldest above " + systemLimit + ", we start releasing old files above " + maxGrants + ".");
|
||||
|
||||
// Release before re-taking: a take can make the system prune, which would leave our list out of date.
|
||||
if (grants.size() > maxGrants) {
|
||||
Log.i(GRANT_TAG, "The recent list, shortcuts and settings reference " + referencedUris.size() + " URIs.");
|
||||
ArrayList<UriPermission> toRelease = chooseGrantsToRelease(grants, maxGrants, referencedUris);
|
||||
int released = 0;
|
||||
for (UriPermission grant : toRelease) {
|
||||
Log.i(GRANT_TAG, "Releasing unused file grant: " + grant.getUri() + " taken " + new Date(grant.getPersistedTime()));
|
||||
try {
|
||||
resolver.releasePersistableUriPermission(grant.getUri(), grantFlags(grant));
|
||||
released++;
|
||||
} catch (Exception e) {
|
||||
Log.w(GRANT_TAG, "Couldn't release " + grant.getUri() + ": " + e);
|
||||
}
|
||||
}
|
||||
if (toRelease.isEmpty()) {
|
||||
Log.w(GRANT_TAG, "Over our limit, but everything is a folder or still in use. Nothing to release.");
|
||||
} else {
|
||||
Log.i(GRANT_TAG, "Released " + released + " file grants, " + (grants.size() - released) + " remain.");
|
||||
}
|
||||
}
|
||||
|
||||
for (UriPermission grant : grants) {
|
||||
if (!isTreeUri(grant.getUri())) {
|
||||
continue;
|
||||
}
|
||||
if (!isReferenced(grant.getUri(), referencedUris)) {
|
||||
Log.i(GRANT_TAG, "Folder grant (kept, not in use): " + grant.getUri() + " taken " + new Date(grant.getPersistedTime()));
|
||||
continue;
|
||||
}
|
||||
// Taking a grant we already hold just moves its time up.
|
||||
try {
|
||||
resolver.takePersistableUriPermission(grant.getUri(), grantFlags(grant));
|
||||
Log.i(GRANT_TAG, "Folder grant (in use, re-taken): " + grant.getUri() + " taken " + new Date(grant.getPersistedTime()));
|
||||
} catch (Exception e) {
|
||||
Log.w(GRANT_TAG, "Couldn't re-take folder grant " + grant.getUri() + ": " + e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(GRANT_TAG, "Exception while managing URI grants: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// ShortcutActivity can't write ppsspp.ini (the config isn't loaded there, and a running PpssppActivity would
|
||||
// overwrite it), so the URIs of shortcuts created or launched wait here until the next startup hands them over.
|
||||
private static final String SHORTCUT_PREFS = "PendingShortcutUris";
|
||||
private static final String SHORTCUT_PREFS_KEY = "uris";
|
||||
private static final int MAX_PENDING_SHORTCUT_URIS = 64;
|
||||
|
||||
private static ArrayList<String> readPendingShortcutUris(SharedPreferences prefs) {
|
||||
ArrayList<String> uris = new ArrayList<>();
|
||||
for (String uri : prefs.getString(SHORTCUT_PREFS_KEY, "").split("\n")) {
|
||||
if (!uri.isEmpty()) {
|
||||
uris.add(uri);
|
||||
}
|
||||
}
|
||||
return uris;
|
||||
}
|
||||
|
||||
public static void queueShortcutUri(Context context, String uri) {
|
||||
if (uri == null || !uri.startsWith("content://")) {
|
||||
return;
|
||||
}
|
||||
SharedPreferences prefs = context.getSharedPreferences(SHORTCUT_PREFS, Context.MODE_PRIVATE);
|
||||
ArrayList<String> uris = readPendingShortcutUris(prefs);
|
||||
uris.remove(uri);
|
||||
uris.add(uri); // Newest last.
|
||||
while (uris.size() > MAX_PENDING_SHORTCUT_URIS) {
|
||||
uris.remove(0);
|
||||
}
|
||||
prefs.edit().putString(SHORTCUT_PREFS_KEY, TextUtils.join("\n", uris)).commit();
|
||||
}
|
||||
|
||||
// Call once at startup, after the native side has loaded the config.
|
||||
public static void flushShortcutUris(Context context) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(SHORTCUT_PREFS, Context.MODE_PRIVATE);
|
||||
ArrayList<String> uris = readPendingShortcutUris(prefs);
|
||||
if (uris.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!NativeApp.addShortcutUris(uris.toArray(new String[0]))) {
|
||||
Log.w(GRANT_TAG, "Couldn't save " + uris.size() + " shortcut URIs to the config, keeping them for next time.");
|
||||
return;
|
||||
}
|
||||
Log.i(GRANT_TAG, "Saved " + uris.size() + " shortcut URIs to the config.");
|
||||
prefs.edit().remove(SHORTCUT_PREFS_KEY).commit();
|
||||
}
|
||||
|
||||
@Keep
|
||||
@SuppressWarnings("unused")
|
||||
public static boolean isExternalStoragePreservedLegacy() {
|
||||
|
||||
@@ -82,6 +82,12 @@ public class NativeApp {
|
||||
public static native void setAchievementsHostOverride(String host);
|
||||
public static native void clearAchievementsHostOverride();
|
||||
public static native String queryConfig(String queryName);
|
||||
// The content URIs that the recent list, the remembered shortcuts and the settings still point at.
|
||||
// Null if the config in memory may not be the user's (see HaveUsersConfig).
|
||||
public static native String[] getReferencedUris();
|
||||
// Remembers these as home screen shortcut targets, oldest first, and saves the config. Only call during
|
||||
// Initialize. False if that didn't work out.
|
||||
public static native boolean addShortcutUris(String[] uris);
|
||||
|
||||
public static native int getSelectedCamera();
|
||||
public static native int getDisplayFramerateMode();
|
||||
|
||||
@@ -512,6 +512,10 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
NativeApp.audioConfig(optimalFramesPerBuffer, optimalSampleRate);
|
||||
NativeApp.init(model, deviceType, languageRegion, apkFilePath, dataDir, extStorageDir, externalFilesDir, nativeLibDir, additionalStorageDirs, cacheDir, shortcut, installerName, Build.VERSION.SDK_INT, Build.BOARD);
|
||||
|
||||
// Both need the config.
|
||||
ContentUri.flushShortcutUris(this);
|
||||
ContentUri.manageUriGrants(this);
|
||||
|
||||
sendInitialGrants();
|
||||
|
||||
// OK, config should be initialized, we can query for screen rotation.
|
||||
@@ -743,6 +747,8 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
Log.i(TAG, "Found Shortcut Parameter in data, passing on: " + shortcutParam);
|
||||
setShortcutParam(shortcutParam);
|
||||
}
|
||||
// So its file grant is kept (see ContentUri.manageUriGrants).
|
||||
ContentUri.queueShortcutUri(this, getIntent().getStringExtra(SHORTCUT_EXTRA_KEY));
|
||||
|
||||
String achievementsHostOverride = AchievementsHostOverrideReceiver.getAchievementsHostOverride(this);
|
||||
if (achievementsHostOverride != null && !achievementsHostOverride.isEmpty()) {
|
||||
@@ -2164,6 +2170,7 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
} else {
|
||||
// Someone launched a shortcut while we were running....
|
||||
setIntent(intent);
|
||||
ContentUri.queueShortcutUri(this, intent.getStringExtra(SHORTCUT_EXTRA_KEY));
|
||||
String value = parseIntent(intent);
|
||||
if (value != null) {
|
||||
// TODO: Actually send a command to the native code to launch the new game.
|
||||
|
||||
@@ -73,6 +73,8 @@ public class ShortcutActivity extends Activity {
|
||||
// Some pickers don't hand out a persistable grant.
|
||||
Log.w(TAG, "Couldn't take URI permission: " + e);
|
||||
}
|
||||
// So its grant is kept (see ContentUri.manageUriGrants).
|
||||
ContentUri.queueShortcutUri(this, selectedFile.toString());
|
||||
Log.i(TAG, "Browse file finished:" + selectedFile);
|
||||
respondToShortcutRequest(selectedFile); // finishes.
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user