Android: Add game directory list editor

This commit is contained in:
Connor McLaughlin
2021-01-24 16:43:53 +10:00
parent 6a122623fa
commit 59810bf8db
12 changed files with 447 additions and 37 deletions

View File

@ -677,6 +677,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
}
private boolean mSustainedPerformanceModeEnabled = false;
private void updateSustainedPerformanceMode() {
final boolean enabled = getBooleanSetting("Main/SustainedPerformanceMode", false);
if (mSustainedPerformanceModeEnabled == enabled)

View File

@ -0,0 +1,279 @@
package com.github.stenzek.duckstation;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.util.Property;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListAdapter;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.ListFragment;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
public class GameDirectoriesActivity extends AppCompatActivity {
private static final int REQUEST_ADD_DIRECTORY_TO_GAME_LIST = 1;
private class DirectoryListAdapter extends RecyclerView.Adapter {
private class Entry {
private String mPath;
private boolean mRecursive;
public Entry(String path, boolean recursive) {
mPath = path;
mRecursive = recursive;
}
public String getPath() {
return mPath;
}
public boolean isRecursive() {
return mRecursive;
}
public void toggleRecursive() {
mRecursive = !mRecursive;
}
}
private class EntryComparator implements Comparator<Entry> {
@Override
public int compare(Entry left, Entry right) {
return left.getPath().compareTo(right.getPath());
}
}
private Context mContext;
private Entry[] mEntries;
public DirectoryListAdapter(Context context) {
mContext = context;
reload();
}
public void reload() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
ArrayList<Entry> entries = new ArrayList<>();
try {
Set<String> paths = prefs.getStringSet("GameList/Paths", null);
if (paths != null) {
for (String path : paths)
entries.add(new Entry(path, false));
}
} catch (Exception e) {
}
try {
Set<String> paths = prefs.getStringSet("GameList/RecursivePaths", null);
if (paths != null) {
for (String path : paths)
entries.add(new Entry(path, true));
}
} catch (Exception e) {
}
mEntries = new Entry[entries.size()];
entries.toArray(mEntries);
Arrays.sort(mEntries, new EntryComparator());
notifyDataSetChanged();
}
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private int mPosition;
private Entry mEntry;
private TextView mPathView;
private TextView mRecursiveView;
private ImageButton mToggleRecursiveView;
private ImageButton mRemoveView;
public ViewHolder(View rootView) {
super(rootView);
mPathView = rootView.findViewById(R.id.path);
mRecursiveView = rootView.findViewById(R.id.recursive);
mToggleRecursiveView = rootView.findViewById(R.id.toggle_recursive);
mToggleRecursiveView.setOnClickListener(this);
mRemoveView = rootView.findViewById(R.id.remove);
mRemoveView.setOnClickListener(this);
}
public void bindData(int position, Entry entry) {
mPosition = position;
mEntry = entry;
updateText();
}
private void updateText() {
mPathView.setText(mEntry.getPath());
mRecursiveView.setText(getString(mEntry.isRecursive() ? R.string.game_directories_scanning_subdirectories : R.string.game_directories_not_scanning_subdirectories));
mToggleRecursiveView.setImageDrawable(getDrawable(mEntry.isRecursive() ? R.drawable.ic_baseline_folder_24 : R.drawable.ic_baseline_folder_open_24));
}
@Override
public void onClick(View v) {
if (mToggleRecursiveView == v) {
removeSearchDirectory(mContext, mEntry.getPath(), mEntry.isRecursive());
mEntry.toggleRecursive();
addSearchDirectory(mContext, mEntry.getPath(), mEntry.isRecursive());
updateText();
} else if (mRemoveView == v) {
removeSearchDirectory(mContext, mEntry.getPath(), mEntry.isRecursive());
reload();
}
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
((ViewHolder) holder).bindData(position, mEntries[position]);
}
@Override
public int getItemViewType(int position) {
return R.layout.layout_game_directory_entry;
}
@Override
public long getItemId(int position) {
return mEntries[position].getPath().hashCode();
}
@Override
public int getItemCount() {
return mEntries.length;
}
}
DirectoryListAdapter mDirectoryListAdapter;
RecyclerView mRecyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_directories);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
mDirectoryListAdapter = new DirectoryListAdapter(this);
mRecyclerView = findViewById(R.id.recycler_view);
mRecyclerView.setAdapter(mDirectoryListAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
findViewById(R.id.fab).setOnClickListener((v) -> startAddGameDirectory());
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
public static String getPathFromTreeUri(Context context, Uri treeUri) {
String path = FileUtil.getFullPathFromTreeUri(treeUri, context);
if (path.length() < 5) {
new AlertDialog.Builder(context)
.setTitle(R.string.main_activity_error)
.setMessage(R.string.main_activity_get_path_from_directory_error)
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
})
.create()
.show();
return null;
}
return path;
}
public static void addSearchDirectory(Context context, String path, boolean recursive) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = recursive ? "GameList/RecursivePaths" : "GameList/Paths";
PreferenceHelpers.addToStringList(prefs, key, path);
Log.i("GameDirectoriesActivity", "Added path '" + path + "' to game list search directories");
}
public static void removeSearchDirectory(Context context, String path, boolean recursive) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
final String key = recursive ? "GameList/RecursivePaths" : "GameList/Paths";
PreferenceHelpers.removeFromStringList(prefs, key, path);
Log.i("GameDirectoriesActivity", "Removed path '" + path + "' from game list search directories");
}
private void startAddGameDirectory() {
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
i.addCategory(Intent.CATEGORY_DEFAULT);
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
startActivityForResult(Intent.createChooser(i, getString(R.string.main_activity_choose_directory)),
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_ADD_DIRECTORY_TO_GAME_LIST: {
if (resultCode != RESULT_OK)
return;
String path = getPathFromTreeUri(this, data.getData());
if (path == null)
return;
addSearchDirectory(this, path, true);
mDirectoryListAdapter.reload();
}
break;
}
}
}

View File

@ -43,6 +43,7 @@ public class MainActivity extends AppCompatActivity {
private static final int REQUEST_IMPORT_BIOS_IMAGE = 3;
private static final int REQUEST_START_FILE = 4;
private static final int REQUEST_SETTINGS = 5;
private static final int REQUEST_EDIT_GAME_DIRECTORIES = 6;
private GameList mGameList;
private ListView mGameListView;
@ -209,8 +210,10 @@ public class MainActivity extends AppCompatActivity {
startEmulation(null, false);
} else if (id == R.id.action_start_file) {
startStartFile();
} else if (id == R.id.action_add_game_directory) {
startAddGameDirectory();
} else if (id == R.id.action_edit_game_directories) {
Intent intent = new Intent(this, GameDirectoriesActivity.class);
startActivityForResult(intent, REQUEST_EDIT_GAME_DIRECTORIES);
return true;
} else if (id == R.id.action_scan_for_new_games) {
mGameList.refresh(false, false, this);
} else if (id == R.id.action_rescan_all_games) {
@ -255,22 +258,6 @@ public class MainActivity extends AppCompatActivity {
return path;
}
private String getPathFromTreeUri(Uri treeUri) {
String path = FileUtil.getFullPathFromTreeUri(treeUri, this);
if (path.length() < 5) {
new AlertDialog.Builder(this)
.setTitle(R.string.main_activity_error)
.setMessage(R.string.main_activity_get_path_from_directory_error)
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
})
.create()
.show();
return null;
}
return path;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@ -280,20 +267,11 @@ public class MainActivity extends AppCompatActivity {
if (resultCode != RESULT_OK)
return;
String path = getPathFromTreeUri(data.getData());
String path = GameDirectoriesActivity.getPathFromTreeUri(this, data.getData());
if (path == null)
return;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Set<String> currentValues = prefs.getStringSet("GameList/RecursivePaths", null);
if (currentValues == null)
currentValues = new HashSet<String>();
currentValues.add(path);
SharedPreferences.Editor editor = prefs.edit();
editor.putStringSet("GameList/RecursivePaths", currentValues);
editor.apply();
Log.i("MainActivity", "Added path '" + path + "' to game list search directories");
GameDirectoriesActivity.addSearchDirectory(this, path, true);
mGameList.refresh(false, false, this);
}
break;
@ -322,6 +300,11 @@ public class MainActivity extends AppCompatActivity {
loadSettings();
}
break;
case REQUEST_EDIT_GAME_DIRECTORIES: {
mGameList.refresh(false, false, this);
}
break;
}
}

View File

@ -46,8 +46,14 @@ public class PreferenceHelpers {
public static boolean addToStringList(SharedPreferences prefs, String keyName, String valueToAdd) {
Set<String> values = getStringSet(prefs, keyName);
if (values == null)
if (values == null) {
values = new ArraySet<>();
} else {
// We need to copy it otherwise the put doesn't save.
Set<String> valuesCopy = new ArraySet<>();
valuesCopy.addAll(values);
values = valuesCopy;
}
final boolean result = values.add(valueToAdd);
prefs.edit().putStringSet(keyName, values).commit();
@ -59,6 +65,11 @@ public class PreferenceHelpers {
if (values == null)
return false;
// We need to copy it otherwise the put doesn't save.
Set<String> valuesCopy = new ArraySet<>();
valuesCopy.addAll(values);
values = valuesCopy;
final boolean result = values.remove(valueToRemove);
prefs.edit().putStringSet(keyName, values).commit();
return result;