Android: Warn when path in config is unavailable

Content URIs stop working if Dolphin loses permissions,
which happens for instance when reinstalling Dolphin.
This commit is contained in:
JosJuice 2020-11-09 23:15:35 +01:00
parent 6df543fbc9
commit 161f8c3fad
7 changed files with 88 additions and 4 deletions

View File

@ -36,6 +36,7 @@ import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
@ -170,13 +171,38 @@ public final class EmulationActivity extends AppCompatActivity
if (sIgnoreLaunchRequests) if (sIgnoreLaunchRequests)
return; return;
new AfterDirectoryInitializationRunner().run(activity, true, () ->
{
if (FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_DEFAULT_ISO) &&
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_FS_PATH) &&
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_DUMP_PATH) &&
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_LOAD_PATH) &&
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_RESOURCEPACK_PATH) &&
FileBrowserHelper.isPathEmptyOrValid(StringSetting.MAIN_SD_PATH))
{
launchWithoutChecks(activity, filePaths);
}
else
{
AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.DolphinDialogBase);
builder.setMessage(R.string.unavailable_paths);
builder.setPositiveButton(R.string.yes, (dialogInterface, i) ->
SettingsActivity.launch(activity, MenuTag.CONFIG_PATHS));
builder.setNeutralButton(R.string.continue_anyway, (dialogInterface, i) ->
launchWithoutChecks(activity, filePaths));
builder.show();
}
});
}
private static void launchWithoutChecks(FragmentActivity activity, String[] filePaths)
{
sIgnoreLaunchRequests = true; sIgnoreLaunchRequests = true;
Intent launcher = new Intent(activity, EmulationActivity.class); Intent launcher = new Intent(activity, EmulationActivity.class);
launcher.putExtra(EXTRA_SELECTED_GAMES, filePaths); launcher.putExtra(EXTRA_SELECTED_GAMES, filePaths);
new AfterDirectoryInitializationRunner().run(activity, true, activity.startActivity(launcher);
() -> activity.startActivity(launcher));
} }
public static void stopIgnoringLaunchRequests() public static void stopIgnoringLaunchRequests()

View File

@ -1,5 +1,6 @@
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; package org.dolphinemu.dolphinemu.features.settings.ui.viewholder;
import android.graphics.drawable.Drawable;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
@ -10,6 +11,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
public final class FilePickerViewHolder extends SettingViewHolder public final class FilePickerViewHolder extends SettingViewHolder
{ {
@ -19,6 +21,8 @@ public final class FilePickerViewHolder extends SettingViewHolder
private TextView mTextSettingName; private TextView mTextSettingName;
private TextView mTextSettingDescription; private TextView mTextSettingDescription;
private Drawable mDefaultBackground;
public FilePickerViewHolder(View itemView, SettingsAdapter adapter) public FilePickerViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView, adapter); super(itemView, adapter);
@ -29,6 +33,8 @@ public final class FilePickerViewHolder extends SettingViewHolder
{ {
mTextSettingName = root.findViewById(R.id.text_setting_name); mTextSettingName = root.findViewById(R.id.text_setting_name);
mTextSettingDescription = root.findViewById(R.id.text_setting_description); mTextSettingDescription = root.findViewById(R.id.text_setting_description);
mDefaultBackground = root.getBackground();
} }
@Override @Override
@ -37,6 +43,17 @@ public final class FilePickerViewHolder extends SettingViewHolder
mFilePicker = (FilePicker) item; mFilePicker = (FilePicker) item;
mItem = item; mItem = item;
String path = mFilePicker.getSelectedValue(getAdapter().getSettings());
if (FileBrowserHelper.isPathEmptyOrValid(path))
{
itemView.setBackground(mDefaultBackground);
}
else
{
itemView.setBackgroundResource(R.drawable.invalid_setting_background);
}
mTextSettingName.setText(item.getNameId()); mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0) if (item.getDescriptionId() > 0)
@ -45,8 +62,6 @@ public final class FilePickerViewHolder extends SettingViewHolder
} }
else else
{ {
String path = mFilePicker.getSelectedValue(getAdapter().getSettings());
if (TextUtils.isEmpty(path)) if (TextUtils.isEmpty(path))
{ {
String defaultPathRelative = mFilePicker.getDefaultPathRelativeToUserDirectory(); String defaultPathRelative = mFilePicker.getDefaultPathRelativeToUserDirectory();

View File

@ -52,6 +52,24 @@ public class ContentHandler
} }
} }
public static boolean exists(@NonNull String uri)
{
try
{
final String[] projection = new String[]{Document.COLUMN_MIME_TYPE, Document.COLUMN_SIZE};
try (Cursor cursor = getContentResolver().query(Uri.parse(uri), projection, null, null, null))
{
return cursor != null && cursor.getCount() > 0;
}
}
catch (SecurityException e)
{
Log.error("Tried to check if " + uri + " exists without permission");
}
return false;
}
@Nullable @Nullable
public static String getDisplayName(@NonNull Uri uri) public static String getDisplayName(@NonNull Uri uri)
{ {

View File

@ -14,6 +14,7 @@ import com.nononsenseapps.filepicker.Utils;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.CustomFilePickerActivity; import org.dolphinemu.dolphinemu.activities.CustomFilePickerActivity;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import java.io.File; import java.io.File;
@ -94,6 +95,16 @@ public final class FileBrowserHelper
return null; return null;
} }
public static boolean isPathEmptyOrValid(StringSetting path)
{
return isPathEmptyOrValid(path.getStringGlobal());
}
public static boolean isPathEmptyOrValid(String path)
{
return !path.startsWith("content://") || ContentHandler.exists(path);
}
public static void runAfterExtensionCheck(Context context, Uri uri, Set<String> validExtensions, public static void runAfterExtensionCheck(Context context, Uri uri, Set<String> validExtensions,
Runnable runnable) Runnable runnable)
{ {
@ -132,6 +143,7 @@ public final class FileBrowserHelper
.setMessage(message) .setMessage(message)
.setPositiveButton(R.string.yes, (dialogInterface, i) -> runnable.run()) .setPositiveButton(R.string.yes, (dialogInterface, i) -> runnable.run())
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)
.setCancelable(false)
.show(); .show();
} }

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:attr/selectableItemBackground"/>
<item>
<shape>
<solid android:color="@color/invalid_setting_overlay" />
</shape>
</item>
</layer-list>

View File

@ -11,4 +11,6 @@
<color name="tv_card_unselected">#444444</color> <color name="tv_card_unselected">#444444</color>
<color name="invalid_setting_overlay">#36ff0000</color>
</resources> </resources>

View File

@ -311,6 +311,7 @@
<string name="clear">Clear</string> <string name="clear">Clear</string>
<string name="disabled">Disabled</string> <string name="disabled">Disabled</string>
<string name="other">Other</string> <string name="other">Other</string>
<string name="continue_anyway">Continue Anyway</string>
<!-- Game Grid Screen--> <!-- Game Grid Screen-->
<string name="add_directory_title">Add Folder to Library</string> <string name="add_directory_title">Add Folder to Library</string>
@ -431,6 +432,7 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="no_file_extension">The selected file does not appear to have a file name extension.\n\nContinue anyway?</string> <string name="no_file_extension">The selected file does not appear to have a file name extension.\n\nContinue anyway?</string>
<string name="wrong_file_extension_single">The selected file has the file name extension \"%1$s\", but \"%2$s\" was expected.\n\nContinue anyway?</string> <string name="wrong_file_extension_single">The selected file has the file name extension \"%1$s\", but \"%2$s\" was expected.\n\nContinue anyway?</string>
<string name="wrong_file_extension_multiple">The selected file has the file name extension \"%1$s\", but one of these extensions was expected: %2$s\n\nContinue anyway?</string> <string name="wrong_file_extension_multiple">The selected file has the file name extension \"%1$s\", but one of these extensions was expected: %2$s\n\nContinue anyway?</string>
<string name="unavailable_paths">Dolphin does not have permission to access one or more configured paths. Would you like to fix this before starting?</string>
<!-- Misc --> <!-- Misc -->
<string name="pitch">Total Pitch</string> <string name="pitch">Total Pitch</string>