mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-23 06:15:38 +00:00
Android: Add "Choose Cover Image" to game list menu
This commit is contained in:
parent
f68836206b
commit
4ab283a2ae
|
@ -168,16 +168,17 @@ public class AndroidHostInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
static private AndroidHostInterface mInstance;
|
static private AndroidHostInterface mInstance;
|
||||||
|
static private String mUserDirectory;
|
||||||
|
|
||||||
static public boolean createInstance(Context context) {
|
static public boolean createInstance(Context context) {
|
||||||
// Set user path.
|
// Set user path.
|
||||||
String externalStorageDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
|
mUserDirectory = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||||
if (externalStorageDirectory.isEmpty())
|
if (mUserDirectory.isEmpty())
|
||||||
externalStorageDirectory = "/sdcard";
|
mUserDirectory = "/sdcard";
|
||||||
|
|
||||||
externalStorageDirectory += "/duckstation";
|
mUserDirectory += "/duckstation";
|
||||||
Log.i("AndroidHostInterface", "User directory: " + externalStorageDirectory);
|
Log.i("AndroidHostInterface", "User directory: " + mUserDirectory);
|
||||||
mInstance = create(context, externalStorageDirectory);
|
mInstance = create(context, mUserDirectory);
|
||||||
return mInstance != null;
|
return mInstance != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +190,8 @@ public class AndroidHostInterface {
|
||||||
return mInstance;
|
return mInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public String getUserDirectory() { return mUserDirectory; }
|
||||||
|
|
||||||
static public boolean hasInstanceAndEmulationThreadIsRunning() {
|
static public boolean hasInstanceAndEmulationThreadIsRunning() {
|
||||||
return hasInstance() && getInstance().isEmulationThreadRunning();
|
return hasInstance() && getInstance().isEmulationThreadRunning();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.ImageDecoder;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
|
@ -272,4 +275,19 @@ public final class FileUtil {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Bitmap loadBitmapFromUri(final Context context, final Uri uri) {
|
||||||
|
InputStream stream = null;
|
||||||
|
try {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
||||||
|
final ImageDecoder.Source source =ImageDecoder.createSource(context.getContentResolver(), uri);
|
||||||
|
return ImageDecoder.decodeBitmap(source);
|
||||||
|
} else {
|
||||||
|
return MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -32,6 +32,10 @@ public class GameList {
|
||||||
public void removeRefreshListener(OnRefreshListener listener) {
|
public void removeRefreshListener(OnRefreshListener listener) {
|
||||||
mRefreshListeners.remove(listener);
|
mRefreshListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
public void fireRefreshListeners() {
|
||||||
|
for (OnRefreshListener listener : mRefreshListeners)
|
||||||
|
listener.onGameListRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
private class GameListEntryComparator implements Comparator<GameListEntry> {
|
private class GameListEntryComparator implements Comparator<GameListEntry> {
|
||||||
@Override
|
@Override
|
||||||
|
@ -56,8 +60,7 @@ public class GameList {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
mEntries = newEntries;
|
mEntries = newEntries;
|
||||||
for (OnRefreshListener listener : mRefreshListeners)
|
fireRefreshListeners();
|
||||||
listener.onGameListRefresh();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -69,4 +72,13 @@ public class GameList {
|
||||||
public GameListEntry getEntry(int index) {
|
public GameListEntry getEntry(int index) {
|
||||||
return mEntries[index];
|
return mEntries[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GameListEntry getEntryForPath(String path) {
|
||||||
|
for (final GameListEntry entry : mEntries) {
|
||||||
|
if (entry.getPath().equals(path))
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,8 @@ public class GameListEntry {
|
||||||
|
|
||||||
public String getCoverPath() { return mCoverPath; }
|
public String getCoverPath() { return mCoverPath; }
|
||||||
|
|
||||||
|
public void setCoverPath(String coverPath) { mCoverPath = coverPath; }
|
||||||
|
|
||||||
public static String getFileNameForPath(String path) {
|
public static String getFileNameForPath(String path) {
|
||||||
int lastSlash = path.lastIndexOf('/');
|
int lastSlash = path.lastIndexOf('/');
|
||||||
if (lastSlash > 0 && lastSlash < path.length() - 1)
|
if (lastSlash > 0 && lastSlash < path.length() - 1)
|
||||||
|
|
|
@ -8,6 +8,8 @@ import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -29,9 +31,12 @@ import androidx.core.content.ContextCompat;
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
@ -41,6 +46,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private static final int REQUEST_START_FILE = 4;
|
private static final int REQUEST_START_FILE = 4;
|
||||||
private static final int REQUEST_SETTINGS = 5;
|
private static final int REQUEST_SETTINGS = 5;
|
||||||
private static final int REQUEST_EDIT_GAME_DIRECTORIES = 6;
|
private static final int REQUEST_EDIT_GAME_DIRECTORIES = 6;
|
||||||
|
private static final int REQUEST_CHOOSE_COVER_IMAGE = 7;
|
||||||
|
|
||||||
private GameList mGameList;
|
private GameList mGameList;
|
||||||
private ListView mGameListView;
|
private ListView mGameListView;
|
||||||
|
@ -48,6 +54,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private GameGridFragment mGameGridFragment;
|
private GameGridFragment mGameGridFragment;
|
||||||
private boolean mHasExternalStoragePermissions = false;
|
private boolean mHasExternalStoragePermissions = false;
|
||||||
private boolean mIsShowingGameGrid = false;
|
private boolean mIsShowingGameGrid = false;
|
||||||
|
private String mPathForChosenCoverImage = null;
|
||||||
|
|
||||||
public GameList getGameList() {
|
public GameList getGameList() {
|
||||||
return mGameList;
|
return mGameList;
|
||||||
|
@ -297,6 +304,16 @@ public class MainActivity extends AppCompatActivity {
|
||||||
mGameList.refresh(false, false, this);
|
mGameList.refresh(false, false, this);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case REQUEST_CHOOSE_COVER_IMAGE: {
|
||||||
|
final String gamePath = mPathForChosenCoverImage;
|
||||||
|
mPathForChosenCoverImage = null;
|
||||||
|
if (resultCode != RESULT_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
finishChooseCoverImage(gamePath, data.getData());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,6 +372,9 @@ public class MainActivity extends AppCompatActivity {
|
||||||
} else if (id == R.id.game_list_entry_menu_properties) {
|
} else if (id == R.id.game_list_entry_menu_properties) {
|
||||||
openGameProperties(entry.getPath());
|
openGameProperties(entry.getPath());
|
||||||
return true;
|
return true;
|
||||||
|
} else if (id == R.id.game_list_entry_menu_choose_cover_image) {
|
||||||
|
startChooseCoverImage(entry.getPath());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
@ -387,6 +407,48 @@ public class MainActivity extends AppCompatActivity {
|
||||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_disc_image)), REQUEST_START_FILE);
|
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_disc_image)), REQUEST_START_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startChooseCoverImage(String gamePath) {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.setType("image/*");
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
mPathForChosenCoverImage = gamePath;
|
||||||
|
startActivityForResult(Intent.createChooser(intent, getString(R.string.menu_game_list_entry_choose_cover_image)),
|
||||||
|
REQUEST_CHOOSE_COVER_IMAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void finishChooseCoverImage(String gamePath, Uri uri) {
|
||||||
|
final GameListEntry gameListEntry = mGameList.getEntryForPath(gamePath);
|
||||||
|
if (gameListEntry == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final Bitmap bitmap = FileUtil.loadBitmapFromUri(this, uri);
|
||||||
|
if (bitmap == null) {
|
||||||
|
Toast.makeText(this, "Failed to open/decode image.", Toast.LENGTH_LONG).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String coverFileName = String.format("%s/covers/%s.png",
|
||||||
|
AndroidHostInterface.getUserDirectory(), gameListEntry.getTitle());
|
||||||
|
try {
|
||||||
|
final File file = new File(coverFileName);
|
||||||
|
final OutputStream outputStream = new FileOutputStream(file);
|
||||||
|
final boolean result = bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
|
||||||
|
outputStream.close();;
|
||||||
|
if (!result) {
|
||||||
|
file.delete();
|
||||||
|
throw new Exception("Failed to compress bitmap.");
|
||||||
|
}
|
||||||
|
|
||||||
|
gameListEntry.setCoverPath(coverFileName);
|
||||||
|
mGameList.fireRefreshListeners();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(this, "Failed to save image.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
private boolean doBIOSCheck() {
|
private boolean doBIOSCheck() {
|
||||||
if (AndroidHostInterface.getInstance().hasAnyBIOSImages())
|
if (AndroidHostInterface.getInstance().hasAnyBIOSImages())
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -10,4 +10,7 @@
|
||||||
<item
|
<item
|
||||||
android:id="@+id/game_list_entry_menu_properties"
|
android:id="@+id/game_list_entry_menu_properties"
|
||||||
android:title="@string/menu_game_list_entry_game_properties" />
|
android:title="@string/menu_game_list_entry_game_properties" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/game_list_entry_menu_choose_cover_image"
|
||||||
|
android:title="@string/menu_game_list_entry_choose_cover_image" />
|
||||||
</menu>
|
</menu>
|
|
@ -311,4 +311,5 @@
|
||||||
<string name="memory_card_editor_import_card_read_failed">Failed to read \'%s\'.</string>
|
<string name="memory_card_editor_import_card_read_failed">Failed to read \'%s\'.</string>
|
||||||
<string name="memory_card_editor_import_card_failed">Failed to import card \'%s\'. It may not be a supported format.</string>
|
<string name="memory_card_editor_import_card_failed">Failed to import card \'%s\'. It may not be a supported format.</string>
|
||||||
<string name="memory_card_editor_import_card_success">Imported card \'%s\'.</string>
|
<string name="memory_card_editor_import_card_success">Imported card \'%s\'.</string>
|
||||||
|
<string name="menu_game_list_entry_choose_cover_image">Choose Cover Image</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue