Skip to content
Permalink
Browse files
Merge pull request #9696 from JosJuice/android-scoped-storage
Android: Scoped storage [To be merged in October]
  • Loading branch information
JosJuice committed Oct 15, 2021
2 parents 7d63933 + 4e7aaba commit 6caf51f
Show file tree
Hide file tree
Showing 15 changed files with 331 additions and 99 deletions.
@@ -25,7 +25,7 @@ android {
// TODO If this is ever modified, change application_id in strings.xml
applicationId "org.dolphinemu.dolphinemu"
minSdkVersion 21
targetSdkVersion 29
targetSdkVersion 30

versionCode(getBuildVersionCode())

@@ -1,5 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.dolphinemu.dolphinemu">

<uses-feature
@@ -30,12 +29,15 @@
android:name="android.permission.VIBRATE"
android:required="false"/>

<!-- Once compileSdkVersion is 31, add: android:dataExtractionRules="@xml/backup_rules_api_31" -->
<application
android:name=".DolphinApplication"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher"
android:requestLegacyExternalStorage="true"
android:allowBackup="false"
android:preserveLegacyExternalStorage="true"
android:allowBackup="true"
android:fullBackupContent="@xml/backup_rules"
android:supportsRtl="true"
android:isGame="true"
android:banner="@drawable/banner_tv">
@@ -118,6 +120,12 @@
android:exported="false"
android:theme="@style/DolphinBase" />

<activity
android:name=".activities.UserDataActivity"
android:exported="false"
android:label="@string/user_data_submenu"
android:theme="@style/DolphinSettingsBase" />

<service
android:name=".utils.DirectoryInitialization"
android:exported="false"/>
@@ -0,0 +1,79 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.activities;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;

public class UserDataActivity extends AppCompatActivity implements View.OnClickListener
{
public static void launch(Context context)
{
Intent launcher = new Intent(context, UserDataActivity.class);
context.startActivity(launcher);
}

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_user_data);

TextView textType = findViewById(R.id.text_type);
TextView textPath = findViewById(R.id.text_path);
TextView textAndroid11 = findViewById(R.id.text_android_11);
Button buttonOpenSystemFileManager = findViewById(R.id.button_open_system_file_manager);

textType.setText(DirectoryInitialization.isUsingLegacyUserDirectory() ?
R.string.user_data_old_location : R.string.user_data_new_location);

textPath.setText(DirectoryInitialization.getUserDirectory());

boolean show_android_11_text = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
!DirectoryInitialization.isUsingLegacyUserDirectory();
textAndroid11.setVisibility(show_android_11_text ? View.VISIBLE : View.GONE);

boolean show_file_manager_button = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
buttonOpenSystemFileManager.setVisibility(show_file_manager_button ? View.VISIBLE : View.GONE);

buttonOpenSystemFileManager.setOnClickListener(this);
}

@Override
public void onClick(View v)
{
try
{
startActivity(getFileManagerIntent());
}
catch (ActivityNotFoundException e)
{
new AlertDialog.Builder(this, R.style.DolphinDialogBase)
.setMessage(R.string.user_data_open_system_file_manager_failed)
.setPositiveButton(R.string.ok, null)
.show();
}
}

private Intent getFileManagerIntent()
{
// Fragile, but some phones don't expose the system file manager in any better way
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClassName("com.android.documentsui", "com.android.documentsui.files.FilesActivity");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
}
@@ -44,6 +44,7 @@
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;

import java.io.File;
import java.io.IOException;
@@ -300,7 +301,17 @@ public void onFilePickerDirectoryClick(SettingsItem item, int position)
mClickedItem = item;
mClickedPosition = position;

FileBrowserHelper.openDirectoryPicker(mView.getActivity(), FileBrowserHelper.GAME_EXTENSIONS);
if (!PermissionsHandler.isExternalStorageLegacy())
{
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, R.style.DolphinDialogBase);
builder.setMessage(R.string.path_not_changeable_scoped_storage);
builder.setPositiveButton(R.string.ok, (dialog, i) -> dialog.dismiss());
builder.show();
}
else
{
FileBrowserHelper.openDirectoryPicker(mView.getActivity(), FileBrowserHelper.GAME_EXTENSIONS);
}
}

public void onFilePickerFileClick(SettingsItem item, int position)
@@ -10,6 +10,7 @@
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.UserDataActivity;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting;
@@ -256,6 +257,8 @@ private void addConfigSettings(ArrayList<SettingsItem> sl)
sl.add(new SubmenuSetting(mContext, R.string.advanced_submenu, MenuTag.CONFIG_ADVANCED));
sl.add(new SubmenuSetting(mContext, R.string.log_submenu, MenuTag.CONFIG_LOG));
sl.add(new SubmenuSetting(mContext, R.string.debug_submenu, MenuTag.DEBUG));
sl.add(new RunRunnable(mContext, R.string.user_data_submenu, 0, 0, 0,
() -> UserDataActivity.launch(mContext)));
}

private void addGeneralSettings(ArrayList<SettingsItem> sl)
@@ -72,7 +72,7 @@ protected void onCreate(Bundle savedInstanceState)
if (savedInstanceState == null)
StartupHandler.HandleInit(this);

if (PermissionsHandler.hasWriteAccess(this))
if (!DirectoryInitialization.isWaitingForWriteAccess(this))
{
new AfterDirectoryInitializationRunner()
.run(this, false, this::setPlatformTabsAndStartGameFileCacheService);
@@ -249,16 +249,14 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis

if (requestCode == PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION)
{
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
if (grantResults[0] == PackageManager.PERMISSION_DENIED)
{
DirectoryInitialization.start(this);
new AfterDirectoryInitializationRunner()
.run(this, false, this::setPlatformTabsAndStartGameFileCacheService);
}
else
{
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_LONG).show();
PermissionsHandler.setWritePermissionDenied();
}

DirectoryInitialization.start(this);
new AfterDirectoryInitializationRunner()
.run(this, false, this::setPlatformTabsAndStartGameFileCacheService);
}
}

@@ -30,6 +30,7 @@
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
@@ -287,15 +288,13 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis

if (requestCode == PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION)
{
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
if (grantResults[0] == PackageManager.PERMISSION_DENIED)
{
DirectoryInitialization.start(this);
GameFileCacheService.startLoad(this);
}
else
{
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_LONG).show();
PermissionsHandler.setWritePermissionDenied();
}

DirectoryInitialization.start(this);
GameFileCacheService.startLoad(this);
}
}

@@ -314,7 +313,7 @@ private void buildRowsAdapter()
ArrayObjectAdapter rowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
mGameRows.clear();

if (PermissionsHandler.hasWriteAccess(this))
if (!DirectoryInitialization.isWaitingForWriteAccess(this))
{
GameFileCacheService.startLoad(this);
}
@@ -62,7 +62,7 @@ public void run(Context context, boolean abortOnFailure, Runnable runnable)
runnable.run();
}
else if (abortOnFailure &&
showErrorMessage(context, DirectoryInitialization.getDolphinDirectoriesState(context)))
showErrorMessage(context, DirectoryInitialization.getDolphinDirectoriesState()))
{
runFinishedCallback();
}
@@ -115,10 +115,6 @@ private static boolean showErrorMessage(Context context, DirectoryInitialization
{
switch (state)
{
case EXTERNAL_STORAGE_PERMISSION_NEEDED:
Toast.makeText(context, R.string.write_permission_needed, Toast.LENGTH_LONG).show();
return true;

case CANT_FIND_EXTERNAL_STORAGE:
Toast.makeText(context, R.string.external_storage_not_mounted, Toast.LENGTH_LONG).show();
return true;

0 comments on commit 6caf51f

Please sign in to comment.