Android: Update mobile and TV to use game covers
Using covers should give a consistent look to dolphin's library.
This commit is contained in:
parent
84c24516b1
commit
3f21975d2a
|
@ -37,7 +37,7 @@ public final class GameRowPresenter extends Presenter
|
|||
ImageCardView gameCard = new ImageCardView(parent.getContext());
|
||||
|
||||
gameCard.setMainImageAdjustViewBounds(true);
|
||||
gameCard.setMainImageDimensions(480, 320);
|
||||
gameCard.setMainImageDimensions(240, 336);
|
||||
gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
|
||||
gameCard.setFocusable(true);
|
||||
|
|
|
@ -199,6 +199,7 @@ public final class SettingsFragmentPresenter
|
|||
|
||||
sl.add(new SubmenuSetting(null, null, R.string.gamecube_submenu, 0, MenuTag.CONFIG_GAME_CUBE));
|
||||
sl.add(new SubmenuSetting(null, null, R.string.wii_submenu, 0, MenuTag.CONFIG_WII));
|
||||
sl.add(new HeaderSetting(null, null, R.string.gametdb_thanks, 0));
|
||||
}
|
||||
|
||||
private void addGeneralSettings(ArrayList<SettingsItem> sl)
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package org.dolphinemu.dolphinemu.model;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Environment;
|
||||
|
||||
import org.dolphinemu.dolphinemu.utils.CoverHelper;
|
||||
|
||||
public class GameFile
|
||||
{
|
||||
private long mPointer; // Do not rename or move without editing the native code
|
||||
|
@ -19,12 +23,24 @@ public class GameFile
|
|||
public native String getDescription();
|
||||
public native String getCompany();
|
||||
public native int getCountry();
|
||||
public native int getRegion();
|
||||
public native String getPath();
|
||||
public native String getGameId();
|
||||
public native int[] getBanner();
|
||||
public native int getBannerWidth();
|
||||
public native int getBannerHeight();
|
||||
|
||||
public String getCoverPath()
|
||||
{
|
||||
return Environment.getExternalStorageDirectory().getPath() +
|
||||
"/dolphin-emu/Cache/GameCovers/" + getGameId() + ".png";
|
||||
}
|
||||
|
||||
public String getCustomCoverPath()
|
||||
{
|
||||
return getPath().substring(0, getPath().lastIndexOf(".")) + ".cover.png";
|
||||
}
|
||||
|
||||
public String getScreenshotPath()
|
||||
{
|
||||
String gameId = getGameId();
|
||||
|
|
|
@ -12,8 +12,6 @@ import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
|||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
|
|
|
@ -154,6 +154,7 @@ public class SyncProgramsJobService extends JobService
|
|||
.setTitle(game.getTitle())
|
||||
.setDescription(game.getDescription())
|
||||
.setPosterArtUri(banner)
|
||||
.setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3)
|
||||
.setIntentUri(appLinkUri);
|
||||
return builder.build();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.ui.main;
|
|||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v17.leanback.app.BrowseFragment;
|
||||
import android.support.v17.leanback.app.BrowseSupportFragment;
|
||||
|
@ -197,8 +196,6 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||
GameFileCacheService.startLoad(this);
|
||||
}
|
||||
|
||||
mRowsAdapter.add(buildSettingsRow());
|
||||
|
||||
for (Platform platform : Platform.values())
|
||||
{
|
||||
ListRow row = buildGamesRow(platform, GameFileCacheService.getGameFilesForPlatform(platform));
|
||||
|
@ -210,6 +207,8 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
|||
}
|
||||
}
|
||||
|
||||
mRowsAdapter.add(buildSettingsRow());
|
||||
|
||||
mBrowseFragment.setAdapter(mRowsAdapter);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package org.dolphinemu.dolphinemu.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
public final class CoverHelper
|
||||
{
|
||||
private static String baseUrl = "https://art.gametdb.com/wii/cover/%s/%s.png";
|
||||
|
||||
public static String buildGameTDBUrl(GameFile game, String region)
|
||||
{
|
||||
String gameId = game.getGameId();
|
||||
if(game.getPlatform() == 2) // WiiWare
|
||||
gameId = gameId.substring(0,4);
|
||||
return String.format(baseUrl, region, gameId);
|
||||
}
|
||||
|
||||
public static String getRegion(GameFile game)
|
||||
{
|
||||
String region;
|
||||
switch(game.getRegion())
|
||||
{
|
||||
case 0: // NTSC_J
|
||||
region = "JA";
|
||||
break;
|
||||
case 1: // NTSC_U
|
||||
region = "US";
|
||||
break;
|
||||
case 4: // NTSC_K
|
||||
region = "KO";
|
||||
break;
|
||||
case 2: // PAL
|
||||
switch (game.getCountry())
|
||||
{
|
||||
case 2: // German
|
||||
region = "DE";
|
||||
break;
|
||||
case 3: // French
|
||||
region = "FR";
|
||||
break;
|
||||
case 4: // Spanish
|
||||
region = "ES";
|
||||
break;
|
||||
case 5: // Italian
|
||||
region = "IT";
|
||||
break;
|
||||
case 6: // Dutch
|
||||
region = "NL";
|
||||
break;
|
||||
case 1: // English
|
||||
default:
|
||||
region = "EN";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 3: // Unknown
|
||||
default:
|
||||
region = "EN";
|
||||
break;
|
||||
}
|
||||
return region;
|
||||
}
|
||||
|
||||
public static void saveCover(Bitmap cover, String path)
|
||||
{
|
||||
try
|
||||
{
|
||||
FileOutputStream out = new FileOutputStream(path);
|
||||
cover.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
out.close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package org.dolphinemu.dolphinemu.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
import com.squareup.picasso.Request;
|
||||
import com.squareup.picasso.RequestHandler;
|
||||
|
||||
import org.dolphinemu.dolphinemu.NativeLibrary;
|
||||
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
public class GameBannerRequestHandler extends RequestHandler {
|
||||
GameFile mGameFile;
|
||||
|
||||
public GameBannerRequestHandler(GameFile gameFile)
|
||||
{
|
||||
mGameFile = gameFile;
|
||||
}
|
||||
@Override
|
||||
public boolean canHandleRequest(Request data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result load(Request request, int networkPolicy) {
|
||||
int[] vector = mGameFile.getBanner();
|
||||
int width = mGameFile.getBannerWidth();
|
||||
int height = mGameFile.getBannerHeight();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
bitmap.setPixels(vector, 0, width, 0, 0, width, height);
|
||||
return new Result(bitmap, Picasso.LoadedFrom.DISK);
|
||||
}
|
||||
}
|
|
@ -1,46 +1,114 @@
|
|||
package org.dolphinemu.dolphinemu.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.squareup.picasso.Callback;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
public class PicassoUtils {
|
||||
public static void loadGameBanner(ImageView imageView, GameFile gameFile) {
|
||||
File screenshotFile = new File(URI.create(gameFile.getScreenshotPath()));
|
||||
if (screenshotFile.exists()) {
|
||||
// Fill in the view contents.
|
||||
public static void loadGameBanner(ImageView imageView, GameFile gameFile)
|
||||
{
|
||||
File cover = new File(gameFile.getCustomCoverPath());
|
||||
if (cover.exists())
|
||||
{
|
||||
Picasso.with(imageView.getContext())
|
||||
.load(gameFile.getScreenshotPath())
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.RGB_565)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView);
|
||||
} else {
|
||||
Picasso picassoInstance = new Picasso.Builder(imageView.getContext())
|
||||
.addRequestHandler(new GameBannerRequestHandler(gameFile))
|
||||
.build();
|
||||
|
||||
picassoInstance
|
||||
.load(Uri.parse("iso:/" + gameFile.getPath()))
|
||||
.fit()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.RGB_565)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView);
|
||||
.load(cover)
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.ARGB_8888)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView);
|
||||
}
|
||||
else if ((cover = new File(gameFile.getCoverPath())).exists())
|
||||
{
|
||||
Picasso.with(imageView.getContext())
|
||||
.load(cover)
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.ARGB_8888)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView);
|
||||
}
|
||||
/**
|
||||
* GameTDB has a pretty close to complete collection for US/EN covers. First pass at getting
|
||||
* the cover will be by the disk's region, second will be the US cover, and third EN.
|
||||
*/
|
||||
else
|
||||
{
|
||||
Picasso.with(imageView.getContext())
|
||||
.load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile)))
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.ARGB_8888)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView, new Callback()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess()
|
||||
{
|
||||
CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(),
|
||||
gameFile.getCoverPath());
|
||||
}
|
||||
@Override
|
||||
public void onError() // Second pass using US region
|
||||
{
|
||||
Picasso.with(imageView.getContext())
|
||||
.load(CoverHelper.buildGameTDBUrl(gameFile, "US"))
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.ARGB_8888)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView, new Callback()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess()
|
||||
{
|
||||
CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(),
|
||||
gameFile.getCoverPath());
|
||||
}
|
||||
@Override
|
||||
public void onError() // Third and last pass using EN region
|
||||
{
|
||||
Picasso.with(imageView.getContext())
|
||||
.load(CoverHelper.buildGameTDBUrl(gameFile, "EN"))
|
||||
.fit()
|
||||
.centerCrop()
|
||||
.noFade()
|
||||
.noPlaceholder()
|
||||
.config(Bitmap.Config.ARGB_8888)
|
||||
.error(R.drawable.no_banner)
|
||||
.into(imageView, new Callback()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess()
|
||||
{
|
||||
CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(),
|
||||
gameFile.getCoverPath());
|
||||
}
|
||||
@Override
|
||||
public void onError()
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,8 +154,8 @@ public class TvUtil
|
|||
}
|
||||
|
||||
/**
|
||||
* Leanback lanucher requires a uri for poster art, so we take the banner vector,
|
||||
* make a bitmap, save that bitmap, then return the file provider uri.
|
||||
* Leanback lanucher requires a uri for poster art so we create a contentUri and
|
||||
* pass that to LEANBACK_PACKAGE
|
||||
*/
|
||||
public static Uri buildBanner(GameFile game, Context context)
|
||||
{
|
||||
|
@ -163,33 +163,14 @@ public class TvUtil
|
|||
|
||||
try
|
||||
{
|
||||
//Substring needed to strip "file:" from the path beginning
|
||||
File screenshotFile = new File(game.getScreenshotPath().substring(5));
|
||||
if (screenshotFile.exists())
|
||||
File cover = new File(game.getCustomCoverPath());
|
||||
if(cover.exists())
|
||||
{
|
||||
contentUri = getUriForFile(context, getFilePrivider(context), screenshotFile);
|
||||
contentUri = getUriForFile(context, getFileProvider(context), cover);
|
||||
}
|
||||
else
|
||||
else if ((cover = new File(game.getCoverPath())).exists())
|
||||
{
|
||||
File file = new File(buildBannerFilename(game.getGameId()));
|
||||
if (!file.exists())
|
||||
{
|
||||
int[] vector = game.getBanner();
|
||||
int width = game.getBannerWidth();
|
||||
int height = game.getBannerHeight();
|
||||
|
||||
if (vector.length > 0 || width > 0 || height > 0)
|
||||
{
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
bitmap.setPixels(vector, 0, width, 0, 0, width, height);
|
||||
FileOutputStream out = new FileOutputStream(file);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
|
||||
out.close();
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
contentUri = getUriForFile(context, getFilePrivider(context), file);
|
||||
contentUri = getUriForFile(context, getFileProvider(context), cover);
|
||||
}
|
||||
context.grantUriPermission(LEANBACK_PACKAGE, contentUri,
|
||||
FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
@ -203,16 +184,10 @@ public class TvUtil
|
|||
return contentUri;
|
||||
}
|
||||
|
||||
private static String buildBannerFilename(String gameId)
|
||||
{
|
||||
return Environment.getExternalStorageDirectory().getPath() +
|
||||
"/dolphin-emu/Cache/" + gameId + "_banner.png";
|
||||
}
|
||||
|
||||
/**
|
||||
* Needed since debug builds append '.debug' to the end of the package
|
||||
*/
|
||||
private static String getFilePrivider(Context context)
|
||||
private static String getFileProvider(Context context)
|
||||
{
|
||||
return context.getPackageName() + ".filesprovider";
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
tools:layout_width="224dp"
|
||||
android:layout_height="256dp"
|
||||
tools:layout_width="160dp"
|
||||
android:layout_height="368dp"
|
||||
android:transitionName="card_game"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="game_grid_columns">4</integer>
|
||||
<integer name="game_grid_columns">6</integer>
|
||||
</resources>
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="game_grid_columns">2</integer>
|
||||
<integer name="game_grid_columns">3</integer>
|
||||
</resources>
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="game_grid_columns">3</integer>
|
||||
<integer name="game_grid_columns">4</integer>
|
||||
</resources>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<integer name="game_grid_columns">1</integer>
|
||||
<integer name="game_grid_columns">2</integer>
|
||||
|
||||
<!-- Default GameCube landscape layout -->
|
||||
<integer name="BUTTON_A_X">865</integer>
|
||||
|
@ -81,4 +81,4 @@
|
|||
<integer name="CLASSIC_TRIGGER_L_Y">429</integer>
|
||||
<integer name="CLASSIC_TRIGGER_R_X">737</integer>
|
||||
<integer name="CLASSIC_TRIGGER_R_Y">311</integer>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
@ -135,6 +135,7 @@
|
|||
<string name="wiimote_speaker_description">Enable sound output through the speaker on a real Wiimote (DolphinBar required).</string>
|
||||
<string name="audio_stretch">Audio Stretching</string>
|
||||
<string name="audio_stretch_description">Stretches audio to reduce stuttering. Increases latency.</string>
|
||||
<string name="gametdb_thanks">Thanks to GameTDB.com for providing GameCube and Wii covers!</string>
|
||||
|
||||
<!-- Interface Preference Fragment -->
|
||||
<string name="interface_submenu">Interface</string>
|
||||
|
|
|
@ -52,6 +52,8 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCompa
|
|||
jobject obj);
|
||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCountry(JNIEnv* env,
|
||||
jobject obj);
|
||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getRegion(JNIEnv* env,
|
||||
jobject obj);
|
||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getPath(JNIEnv* env,
|
||||
jobject obj);
|
||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getGameId(JNIEnv* env,
|
||||
|
@ -99,6 +101,12 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCountry(
|
|||
return static_cast<jint>(GetRef(env, obj)->GetCountry());
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getRegion(JNIEnv* env,
|
||||
jobject obj)
|
||||
{
|
||||
return static_cast<jint>(GetRef(env, obj)->GetRegion());
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getPath(JNIEnv* env,
|
||||
jobject obj)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue