Use UICommon's game list code on Android
Deduplicates code, and gets rid of some problems the old code had (such as: bad performance when calling native functions, only one disc showing up for multi-disc games, Wii banners being low-res, unnecessarily much effort being needed for adding more metadata).
This commit is contained in:
parent
daee5a4b43
commit
1c027bc148
|
@ -71,13 +71,7 @@
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<service android:name=".services.DirectoryInitializationService"/>
|
<service android:name=".services.DirectoryInitializationService"/>
|
||||||
|
<service android:name=".services.GameFileCacheService"/>
|
||||||
<provider
|
|
||||||
android:name=".model.GameProvider"
|
|
||||||
android:authorities="${applicationId}.provider"
|
|
||||||
android:enabled="true"
|
|
||||||
android:exported="false">
|
|
||||||
</provider>
|
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
|
|
@ -2,22 +2,19 @@ package org.dolphinemu.dolphinemu;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
|
||||||
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
||||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||||
|
|
||||||
public class DolphinApplication extends Application
|
public class DolphinApplication extends Application
|
||||||
{
|
{
|
||||||
public static GameDatabase databaseHelper;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate()
|
public void onCreate()
|
||||||
{
|
{
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
|
System.loadLibrary("main");
|
||||||
|
|
||||||
if (PermissionsHandler.hasWriteAccess(getApplicationContext()))
|
if (PermissionsHandler.hasWriteAccess(getApplicationContext()))
|
||||||
DirectoryInitializationService.startService(getApplicationContext());
|
DirectoryInitializationService.startService(getApplicationContext());
|
||||||
|
|
||||||
databaseHelper = new GameDatabase(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -260,34 +260,6 @@ public final class NativeLibrary
|
||||||
*/
|
*/
|
||||||
public static native void SetConfig(String configFile, String Section, String Key, String Value);
|
public static native void SetConfig(String configFile, String Section, String Key, String Value);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the embedded banner within the given ISO/ROM.
|
|
||||||
*
|
|
||||||
* @param filename the file path to the ISO/ROM.
|
|
||||||
*
|
|
||||||
* @return an integer array containing the color data for the banner.
|
|
||||||
*/
|
|
||||||
public static native int[] GetBanner(String filename);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the embedded title of the given ISO/ROM.
|
|
||||||
*
|
|
||||||
* @param filename The file path to the ISO/ROM.
|
|
||||||
*
|
|
||||||
* @return the embedded title of the ISO/ROM.
|
|
||||||
*/
|
|
||||||
public static native String GetTitle(String filename);
|
|
||||||
|
|
||||||
public static native String GetDescription(String filename);
|
|
||||||
public static native String GetGameId(String filename);
|
|
||||||
|
|
||||||
public static native int GetCountry(String filename);
|
|
||||||
|
|
||||||
public static native String GetCompany(String filename);
|
|
||||||
public static native long GetFilesize(String filename);
|
|
||||||
|
|
||||||
public static native int GetPlatform(String filename);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the Dolphin version string.
|
* Gets the Dolphin version string.
|
||||||
*
|
*
|
||||||
|
@ -394,27 +366,6 @@ public final class NativeLibrary
|
||||||
*/
|
*/
|
||||||
public static native void RefreshWiimotes();
|
public static native void RefreshWiimotes();
|
||||||
|
|
||||||
/**
|
|
||||||
* The methods C++ uses to find references to Java classes and methods
|
|
||||||
* are really expensive. Rather than calling them every time we want to
|
|
||||||
* run them, do it once when we load the native library.
|
|
||||||
*/
|
|
||||||
private static native void CacheClassesAndMethods();
|
|
||||||
|
|
||||||
static
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
System.loadLibrary("main");
|
|
||||||
}
|
|
||||||
catch (UnsatisfiedLinkError ex)
|
|
||||||
{
|
|
||||||
Log.error("[NativeLibrary] " + ex.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheClassesAndMethods();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean alertResult = false;
|
private static boolean alertResult = false;
|
||||||
public static boolean displayAlertMsg(final String caption, final String text, final boolean yesNo)
|
public static boolean displayAlertMsg(final String caption, final String text, final boolean yesNo)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
|
import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
|
||||||
import org.dolphinemu.dolphinemu.fragments.MenuFragment;
|
import org.dolphinemu.dolphinemu.fragments.MenuFragment;
|
||||||
import org.dolphinemu.dolphinemu.fragments.SaveLoadStateFragment;
|
import org.dolphinemu.dolphinemu.fragments.SaveLoadStateFragment;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
import org.dolphinemu.dolphinemu.ui.main.MainActivity;
|
import org.dolphinemu.dolphinemu.ui.main.MainActivity;
|
||||||
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
|
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
|
@ -74,10 +75,12 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
private boolean activityRecreated;
|
private boolean activityRecreated;
|
||||||
private String mScreenPath;
|
private String mScreenPath;
|
||||||
private String mSelectedTitle;
|
private String mSelectedTitle;
|
||||||
|
private int mPlatform;
|
||||||
private String mPath;
|
private String mPath;
|
||||||
|
|
||||||
public static final String EXTRA_SELECTED_GAME = "SelectedGame";
|
public static final String EXTRA_SELECTED_GAME = "SelectedGame";
|
||||||
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
|
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
|
||||||
|
public static final String EXTRA_PLATFORM = "Platform";
|
||||||
public static final String EXTRA_SCREEN_PATH = "ScreenPath";
|
public static final String EXTRA_SCREEN_PATH = "ScreenPath";
|
||||||
public static final String EXTRA_GRID_POSITION = "GridPosition";
|
public static final String EXTRA_GRID_POSITION = "GridPosition";
|
||||||
|
|
||||||
|
@ -146,13 +149,14 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
|
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void launch(FragmentActivity activity, String path, String title, String screenshotPath, int position, View sharedView)
|
public static void launch(FragmentActivity activity, GameFile gameFile, int position, View sharedView)
|
||||||
{
|
{
|
||||||
Intent launcher = new Intent(activity, EmulationActivity.class);
|
Intent launcher = new Intent(activity, EmulationActivity.class);
|
||||||
|
|
||||||
launcher.putExtra(EXTRA_SELECTED_GAME, path);
|
launcher.putExtra(EXTRA_SELECTED_GAME, gameFile.getPath());
|
||||||
launcher.putExtra(EXTRA_SELECTED_TITLE, title);
|
launcher.putExtra(EXTRA_SELECTED_TITLE, gameFile.getTitle());
|
||||||
launcher.putExtra(EXTRA_SCREEN_PATH, screenshotPath);
|
launcher.putExtra(EXTRA_PLATFORM, gameFile.getPlatform());
|
||||||
|
launcher.putExtra(EXTRA_SCREEN_PATH, gameFile.getScreenshotPath());
|
||||||
launcher.putExtra(EXTRA_GRID_POSITION, position);
|
launcher.putExtra(EXTRA_GRID_POSITION, position);
|
||||||
|
|
||||||
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||||
|
@ -176,6 +180,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
Intent gameToEmulate = getIntent();
|
Intent gameToEmulate = getIntent();
|
||||||
mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME);
|
mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME);
|
||||||
mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
|
mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
|
||||||
|
mPlatform = gameToEmulate.getIntExtra(EXTRA_PLATFORM, 0);
|
||||||
mScreenPath = gameToEmulate.getStringExtra(EXTRA_SCREEN_PATH);
|
mScreenPath = gameToEmulate.getStringExtra(EXTRA_SCREEN_PATH);
|
||||||
mPosition = gameToEmulate.getIntExtra(EXTRA_GRID_POSITION, -1);
|
mPosition = gameToEmulate.getIntExtra(EXTRA_GRID_POSITION, -1);
|
||||||
activityRecreated = false;
|
activityRecreated = false;
|
||||||
|
@ -186,7 +191,9 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
restoreState(savedInstanceState);
|
restoreState(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
sIsGameCubeGame = Platform.fromNativeInt(NativeLibrary.GetPlatform(mPath)) == Platform.GAMECUBE;
|
// TODO: The accurate way to find out which console we're emulating is to
|
||||||
|
// first launch emulation and then ask the core which console we're emulating
|
||||||
|
sIsGameCubeGame = Platform.fromNativeInt(mPlatform) == Platform.GAMECUBE;
|
||||||
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
|
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
|
||||||
mControllerMappingHelper = new ControllerMappingHelper();
|
mControllerMappingHelper = new ControllerMappingHelper();
|
||||||
|
|
||||||
|
@ -284,6 +291,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
mEmulationFragment.saveTemporaryState();
|
mEmulationFragment.saveTemporaryState();
|
||||||
outState.putString(EXTRA_SELECTED_GAME, mPath);
|
outState.putString(EXTRA_SELECTED_GAME, mPath);
|
||||||
outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
|
outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
|
||||||
|
outState.putInt(EXTRA_PLATFORM, mPlatform);
|
||||||
outState.putString(EXTRA_SCREEN_PATH, mScreenPath);
|
outState.putString(EXTRA_SCREEN_PATH, mScreenPath);
|
||||||
outState.putInt(EXTRA_GRID_POSITION, mPosition);
|
outState.putInt(EXTRA_GRID_POSITION, mPosition);
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
@ -293,6 +301,7 @@ public final class EmulationActivity extends AppCompatActivity
|
||||||
{
|
{
|
||||||
mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME);
|
mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME);
|
||||||
mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
|
mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
|
||||||
|
mPlatform = savedInstanceState.getInt(EXTRA_PLATFORM);
|
||||||
mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH);
|
mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH);
|
||||||
mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
|
mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@ package org.dolphinemu.dolphinemu.adapters;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.DataSetObserver;
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
@ -14,38 +12,30 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
||||||
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.PicassoUtils;
|
import org.dolphinemu.dolphinemu.utils.PicassoUtils;
|
||||||
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
||||||
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
|
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
|
||||||
* This adapter gets its information from a database Cursor. This fact, paired with the usage of
|
|
||||||
* ContentProviders and Loaders, allows for efficient display of a limited view into a (possibly)
|
|
||||||
* large dataset.
|
|
||||||
*/
|
|
||||||
public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements
|
public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements
|
||||||
View.OnClickListener,
|
View.OnClickListener,
|
||||||
View.OnLongClickListener
|
View.OnLongClickListener
|
||||||
{
|
{
|
||||||
private Cursor mCursor;
|
private List<GameFile> mGameFiles;
|
||||||
private GameDataSetObserver mObserver;
|
|
||||||
|
|
||||||
private boolean mDatasetValid;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the adapter's observer, which watches for changes to the dataset. The adapter will
|
* Initializes the adapter's observer, which watches for changes to the dataset. The adapter will
|
||||||
* display no data until a Cursor is supplied by a CursorLoader.
|
* display no data until swapDataSet is called.
|
||||||
*/
|
*/
|
||||||
public GameAdapter()
|
public GameAdapter()
|
||||||
{
|
{
|
||||||
mDatasetValid = false;
|
mGameFiles = new ArrayList<>();
|
||||||
mObserver = new GameDataSetObserver();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,34 +70,13 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(GameViewHolder holder, int position)
|
public void onBindViewHolder(GameViewHolder holder, int position)
|
||||||
{
|
{
|
||||||
if (mDatasetValid)
|
GameFile gameFile = mGameFiles.get(position);
|
||||||
{
|
PicassoUtils.loadGameBanner(holder.imageScreenshot, gameFile);
|
||||||
if (mCursor.moveToPosition(position))
|
|
||||||
{
|
|
||||||
String screenPath = mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH);
|
|
||||||
PicassoUtils.loadGameBanner(holder.imageScreenshot, screenPath, mCursor.getString(GameDatabase.GAME_COLUMN_PATH));
|
|
||||||
|
|
||||||
holder.textGameTitle.setText(mCursor.getString(GameDatabase.GAME_COLUMN_TITLE));
|
holder.textGameTitle.setText(gameFile.getTitle());
|
||||||
holder.textCompany.setText(mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY));
|
holder.textCompany.setText(gameFile.getCompany());
|
||||||
|
|
||||||
// TODO These shouldn't be necessary once the move to a DB-based model is complete.
|
holder.gameFile = gameFile;
|
||||||
holder.gameId = mCursor.getString(GameDatabase.GAME_COLUMN_GAME_ID);
|
|
||||||
holder.path = mCursor.getString(GameDatabase.GAME_COLUMN_PATH);
|
|
||||||
holder.title = mCursor.getString(GameDatabase.GAME_COLUMN_TITLE);
|
|
||||||
holder.description = mCursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION);
|
|
||||||
holder.country = mCursor.getInt(GameDatabase.GAME_COLUMN_COUNTRY);
|
|
||||||
holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY);
|
|
||||||
holder.screenshotPath = mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.error("[GameAdapter] Can't bind view; Cursor is not valid.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.error("[GameAdapter] Can't bind view; dataset is not valid.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,84 +87,27 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount()
|
public int getItemCount()
|
||||||
{
|
{
|
||||||
if (mDatasetValid && mCursor != null)
|
return mGameFiles.size();
|
||||||
{
|
|
||||||
return mCursor.getCount();
|
|
||||||
}
|
|
||||||
Log.error("[GameAdapter] Dataset is not valid.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the contents of the _id column for a given row.
|
|
||||||
*
|
|
||||||
* @param position The row for which Android wants an ID.
|
|
||||||
* @return A valid ID from the database, or 0 if not available.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position)
|
|
||||||
{
|
|
||||||
if (mDatasetValid && mCursor != null)
|
|
||||||
{
|
|
||||||
if (mCursor.moveToPosition(position))
|
|
||||||
{
|
|
||||||
return mCursor.getLong(GameDatabase.COLUMN_DB_ID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.error("[GameAdapter] Dataset is not valid.");
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell Android whether or not each item in the dataset has a stable identifier.
|
* Tell Android whether or not each item in the dataset has a stable identifier.
|
||||||
* Which it does, because it's a database, so always tell Android 'true'.
|
|
||||||
*
|
*
|
||||||
* @param hasStableIds ignored.
|
* @param hasStableIds ignored.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setHasStableIds(boolean hasStableIds)
|
public void setHasStableIds(boolean hasStableIds)
|
||||||
{
|
{
|
||||||
super.setHasStableIds(true);
|
super.setHasStableIds(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a load is finished, call this to replace the existing data with the newly-loaded
|
* When a load is finished, call this to replace the existing data
|
||||||
* data.
|
* with the newly-loaded data.
|
||||||
*
|
|
||||||
* @param cursor The newly-loaded Cursor.
|
|
||||||
*/
|
*/
|
||||||
public void swapCursor(Cursor cursor)
|
public void swapDataSet(List<GameFile> gameFiles)
|
||||||
{
|
{
|
||||||
// Sanity check.
|
mGameFiles = gameFiles;
|
||||||
if (cursor == mCursor)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Before getting rid of the old cursor, disassociate it from the Observer.
|
|
||||||
final Cursor oldCursor = mCursor;
|
|
||||||
if (oldCursor != null && mObserver != null)
|
|
||||||
{
|
|
||||||
oldCursor.unregisterDataSetObserver(mObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
mCursor = cursor;
|
|
||||||
if (mCursor != null)
|
|
||||||
{
|
|
||||||
// Attempt to associate the new Cursor with the Observer.
|
|
||||||
if (mObserver != null)
|
|
||||||
{
|
|
||||||
mCursor.registerDataSetObserver(mObserver);
|
|
||||||
}
|
|
||||||
|
|
||||||
mDatasetValid = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mDatasetValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,9 +122,7 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
||||||
GameViewHolder holder = (GameViewHolder) view.getTag();
|
GameViewHolder holder = (GameViewHolder) view.getTag();
|
||||||
|
|
||||||
EmulationActivity.launch((FragmentActivity) view.getContext(),
|
EmulationActivity.launch((FragmentActivity) view.getContext(),
|
||||||
holder.path,
|
holder.gameFile,
|
||||||
holder.title,
|
|
||||||
holder.screenshotPath,
|
|
||||||
holder.getAdapterPosition(),
|
holder.getAdapterPosition(),
|
||||||
holder.imageScreenshot);
|
holder.imageScreenshot);
|
||||||
}
|
}
|
||||||
|
@ -227,13 +137,10 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
||||||
public boolean onLongClick(View view)
|
public boolean onLongClick(View view)
|
||||||
{
|
{
|
||||||
GameViewHolder holder = (GameViewHolder) view.getTag();
|
GameViewHolder holder = (GameViewHolder) view.getTag();
|
||||||
|
String gameId = holder.gameFile.getGameId();
|
||||||
// Get the ID of the game we want to look at.
|
|
||||||
String gameId = (String) holder.gameId;
|
|
||||||
|
|
||||||
FragmentActivity activity = (FragmentActivity) view.getContext();
|
FragmentActivity activity = (FragmentActivity) view.getContext();
|
||||||
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.setTitle("Game Settings")
|
builder.setTitle("Game Settings")
|
||||||
.setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener() {
|
.setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener() {
|
||||||
|
@ -290,25 +197,4 @@ public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> impl
|
||||||
outRect.top = space;
|
outRect.top = space;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class GameDataSetObserver extends DataSetObserver
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onChanged()
|
|
||||||
{
|
|
||||||
super.onChanged();
|
|
||||||
|
|
||||||
mDatasetValid = true;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInvalidated()
|
|
||||||
{
|
|
||||||
super.onInvalidated();
|
|
||||||
|
|
||||||
mDatasetValid = false;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,9 @@ import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.model.Game;
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
||||||
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
||||||
import org.dolphinemu.dolphinemu.utils.PicassoUtils;
|
import org.dolphinemu.dolphinemu.utils.PicassoUtils;
|
||||||
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
||||||
|
@ -50,28 +51,19 @@ public final class GameRowPresenter extends Presenter
|
||||||
public void onBindViewHolder(ViewHolder viewHolder, Object item)
|
public void onBindViewHolder(ViewHolder viewHolder, Object item)
|
||||||
{
|
{
|
||||||
TvGameViewHolder holder = (TvGameViewHolder) viewHolder;
|
TvGameViewHolder holder = (TvGameViewHolder) viewHolder;
|
||||||
Game game = (Game) item;
|
GameFile gameFile = (GameFile) item;
|
||||||
|
|
||||||
String screenPath = game.getScreenshotPath();
|
|
||||||
|
|
||||||
holder.imageScreenshot.setImageDrawable(null);
|
holder.imageScreenshot.setImageDrawable(null);
|
||||||
PicassoUtils.loadGameBanner(holder.imageScreenshot, screenPath, game.getPath());
|
PicassoUtils.loadGameBanner(holder.imageScreenshot, gameFile);
|
||||||
|
|
||||||
holder.cardParent.setTitleText(game.getTitle());
|
holder.cardParent.setTitleText(gameFile.getTitle());
|
||||||
holder.cardParent.setContentText(game.getCompany());
|
holder.cardParent.setContentText(gameFile.getCompany());
|
||||||
|
|
||||||
// TODO These shouldn't be necessary once the move to a DB-based model is complete.
|
holder.gameFile = gameFile;
|
||||||
holder.gameId = game.getGameId();
|
|
||||||
holder.path = game.getPath();
|
|
||||||
holder.title = game.getTitle();
|
|
||||||
holder.description = game.getDescription();
|
|
||||||
holder.country = game.getCountry();
|
|
||||||
holder.company = game.getCompany();
|
|
||||||
holder.screenshotPath = game.getScreenshotPath();
|
|
||||||
|
|
||||||
// Set the platform-dependent background color of the card
|
// Set the platform-dependent background color of the card
|
||||||
int backgroundId;
|
int backgroundId;
|
||||||
switch (game.getPlatform())
|
switch (Platform.fromNativeInt(gameFile.getPlatform()))
|
||||||
{
|
{
|
||||||
case GAMECUBE:
|
case GAMECUBE:
|
||||||
backgroundId = R.drawable.tv_card_background_gamecube;
|
backgroundId = R.drawable.tv_card_background_gamecube;
|
||||||
|
@ -93,7 +85,7 @@ public final class GameRowPresenter extends Presenter
|
||||||
public boolean onLongClick(View view)
|
public boolean onLongClick(View view)
|
||||||
{
|
{
|
||||||
FragmentActivity activity = (FragmentActivity) view.getContext();
|
FragmentActivity activity = (FragmentActivity) view.getContext();
|
||||||
|
String gameId = gameFile.getGameId();
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||||
builder.setTitle("Game Settings")
|
builder.setTitle("Game Settings")
|
||||||
|
@ -101,23 +93,23 @@ public final class GameRowPresenter extends Presenter
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case 0:
|
case 0:
|
||||||
SettingsActivity.launch(activity, SettingsFile.FILE_NAME_DOLPHIN, game.getGameId());
|
SettingsActivity.launch(activity, SettingsFile.FILE_NAME_DOLPHIN, gameId);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
SettingsActivity.launch(activity, SettingsFile.FILE_NAME_GFX, game.getGameId());
|
SettingsActivity.launch(activity, SettingsFile.FILE_NAME_GFX, gameId);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
String path = DirectoryInitializationService.getUserDirectory() + "/GameSettings/" + game.getGameId() + ".ini";
|
String path = DirectoryInitializationService.getUserDirectory() + "/GameSettings/" + gameId + ".ini";
|
||||||
File gameSettingsFile = new File(path);
|
File gameSettingsFile = new File(path);
|
||||||
if (gameSettingsFile.exists())
|
if (gameSettingsFile.exists())
|
||||||
{
|
{
|
||||||
if (gameSettingsFile.delete())
|
if (gameSettingsFile.delete())
|
||||||
{
|
{
|
||||||
Toast.makeText(view.getContext(), "Cleared settings for " + game.getGameId(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(view.getContext(), "Cleared settings for " + gameId, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Toast.makeText(view.getContext(), "Unable to clear settings for " + game.getGameId(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(view.getContext(), "Unable to clear settings for " + gameId, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -12,32 +12,24 @@ import android.widget.TextView;
|
||||||
|
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.DolphinApplication;
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
|
|
||||||
import de.hdodenhof.circleimageview.CircleImageView;
|
import de.hdodenhof.circleimageview.CircleImageView;
|
||||||
|
|
||||||
public final class GameDetailsDialog extends DialogFragment
|
public final class GameDetailsDialog extends DialogFragment
|
||||||
{
|
{
|
||||||
private static final String ARG_GAME_TITLE = "game_title";
|
|
||||||
private static final String ARG_GAME_DESCRIPTION = "game_description";
|
|
||||||
private static final String ARG_GAME_COUNTRY = "game_country";
|
|
||||||
private static final String ARG_GAME_DATE = "game_date";
|
|
||||||
private static final String ARG_GAME_PATH = "game_path";
|
private static final String ARG_GAME_PATH = "game_path";
|
||||||
private static final String ARG_GAME_SCREENSHOT_PATH = "game_screenshot_path";
|
|
||||||
|
|
||||||
// TODO Add all of this to the Loader in GameActivity.java
|
public static GameDetailsDialog newInstance(String gamePath)
|
||||||
public static GameDetailsDialog newInstance(String title, String description, int country, String company, String path, String screenshotPath)
|
|
||||||
{
|
{
|
||||||
GameDetailsDialog fragment = new GameDetailsDialog();
|
GameDetailsDialog fragment = new GameDetailsDialog();
|
||||||
|
|
||||||
Bundle arguments = new Bundle();
|
Bundle arguments = new Bundle();
|
||||||
arguments.putString(ARG_GAME_TITLE, title);
|
arguments.putString(ARG_GAME_PATH, gamePath);
|
||||||
arguments.putString(ARG_GAME_DESCRIPTION, description);
|
|
||||||
arguments.putInt(ARG_GAME_COUNTRY, country);
|
|
||||||
arguments.putString(ARG_GAME_DATE, company);
|
|
||||||
arguments.putString(ARG_GAME_PATH, path);
|
|
||||||
arguments.putString(ARG_GAME_SCREENSHOT_PATH, screenshotPath);
|
|
||||||
fragment.setArguments(arguments);
|
fragment.setArguments(arguments);
|
||||||
|
|
||||||
return fragment;
|
return fragment;
|
||||||
|
@ -46,42 +38,38 @@ public final class GameDetailsDialog extends DialogFragment
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState)
|
public Dialog onCreateDialog(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
|
GameFile gameFile = GameFileCacheService.addOrGet(getArguments().getString(ARG_GAME_PATH));
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||||
ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater().inflate(R.layout.dialog_game_details, null);
|
ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater().inflate(R.layout.dialog_game_details, null);
|
||||||
|
|
||||||
final ImageView imageGameScreen = (ImageView) contents.findViewById(R.id.image_game_screen);
|
final ImageView imageGameScreen = contents.findViewById(R.id.image_game_screen);
|
||||||
CircleImageView circleBanner = (CircleImageView) contents.findViewById(R.id.circle_banner);
|
CircleImageView circleBanner = contents.findViewById(R.id.circle_banner);
|
||||||
|
|
||||||
TextView textTitle = (TextView) contents.findViewById(R.id.text_game_title);
|
TextView textTitle = contents.findViewById(R.id.text_game_title);
|
||||||
TextView textDescription = (TextView) contents.findViewById(R.id.text_company);
|
TextView textDescription = contents.findViewById(R.id.text_description);
|
||||||
|
|
||||||
TextView textCountry = (TextView) contents.findViewById(R.id.text_country);
|
TextView textCountry = contents.findViewById(R.id.text_country);
|
||||||
TextView textDate = (TextView) contents.findViewById(R.id.text_date);
|
TextView textCompany = contents.findViewById(R.id.text_company);
|
||||||
|
|
||||||
FloatingActionButton buttonLaunch = (FloatingActionButton) contents.findViewById(R.id.button_launch);
|
FloatingActionButton buttonLaunch = contents.findViewById(R.id.button_launch);
|
||||||
|
|
||||||
int countryIndex = getArguments().getInt(ARG_GAME_COUNTRY);
|
String country = getResources().getStringArray(R.array.countryNames)[gameFile.getCountry()];
|
||||||
String country = getResources().getStringArray(R.array.countryNames)[countryIndex];
|
|
||||||
|
|
||||||
textTitle.setText(getArguments().getString(ARG_GAME_TITLE));
|
textTitle.setText(gameFile.getTitle());
|
||||||
textDescription.setText(getArguments().getString(ARG_GAME_DESCRIPTION));
|
textDescription.setText(gameFile.getDescription());
|
||||||
textCountry.setText(country);
|
textCountry.setText(country);
|
||||||
textDate.setText(getArguments().getString(ARG_GAME_DATE));
|
textCompany.setText(gameFile.getCompany());
|
||||||
|
|
||||||
buttonLaunch.setOnClickListener(view ->
|
buttonLaunch.setOnClickListener(view ->
|
||||||
{
|
{
|
||||||
// Start the emulation activity and send the path of the clicked ROM to it.
|
// Start the emulation activity and send the path of the clicked ROM to it.
|
||||||
EmulationActivity.launch(getActivity(),
|
EmulationActivity.launch(getActivity(), gameFile, -1, imageGameScreen);
|
||||||
getArguments().getString(ARG_GAME_PATH),
|
|
||||||
getArguments().getString(ARG_GAME_TITLE),
|
|
||||||
getArguments().getString(ARG_GAME_SCREENSHOT_PATH),
|
|
||||||
-1,
|
|
||||||
imageGameScreen);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fill in the view contents.
|
// Fill in the view contents.
|
||||||
Picasso.with(imageGameScreen.getContext())
|
Picasso.with(imageGameScreen.getContext())
|
||||||
.load(getArguments().getString(ARG_GAME_SCREENSHOT_PATH))
|
.load(getArguments().getString(gameFile.getScreenshotPath()))
|
||||||
.fit()
|
.fit()
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
.noFade()
|
.noFade()
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
package org.dolphinemu.dolphinemu.model;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.NativeLibrary;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class FileListItem implements Comparable<FileListItem>
|
|
||||||
{
|
|
||||||
public static final int TYPE_FOLDER = 0;
|
|
||||||
public static final int TYPE_GC = 1;
|
|
||||||
public static final int TYPE_WII = 2;
|
|
||||||
public static final int TYPE_WII_WARE = 3;
|
|
||||||
public static final int TYPE_OTHER = 4;
|
|
||||||
|
|
||||||
private int mType;
|
|
||||||
private String mFilename;
|
|
||||||
private String mPath;
|
|
||||||
|
|
||||||
public FileListItem(File file)
|
|
||||||
{
|
|
||||||
mPath = file.getAbsolutePath();
|
|
||||||
mFilename = file.getName();
|
|
||||||
|
|
||||||
if (file.isDirectory())
|
|
||||||
{
|
|
||||||
mType = TYPE_FOLDER;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int extensionStart = mPath.lastIndexOf('.');
|
|
||||||
if (extensionStart < 1)
|
|
||||||
{
|
|
||||||
// Ignore hidden files & files without extensions.
|
|
||||||
mType = TYPE_OTHER;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
String fileExtension = mPath.substring(extensionStart);
|
|
||||||
|
|
||||||
// The extensions we care about.
|
|
||||||
Set<String> allowedExtensions = new HashSet<String>(Arrays.asList(
|
|
||||||
".ciso", ".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".tgc", ".wad", ".wbfs"));
|
|
||||||
|
|
||||||
// Check that the file has an extension we care about before trying to read out of it.
|
|
||||||
if (allowedExtensions.contains(fileExtension.toLowerCase()))
|
|
||||||
{
|
|
||||||
// Add 1 because 0 = TYPE_FOLDER
|
|
||||||
mType = NativeLibrary.GetPlatform(mPath) + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mType = TYPE_OTHER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getType()
|
|
||||||
{
|
|
||||||
return mType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFilename()
|
|
||||||
{
|
|
||||||
return mFilename;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath()
|
|
||||||
{
|
|
||||||
return mPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(FileListItem theOther)
|
|
||||||
{
|
|
||||||
if (theOther.getType() == getType())
|
|
||||||
{
|
|
||||||
return getFilename().toLowerCase().compareTo(theOther.getFilename().toLowerCase());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (getType() > theOther.getType())
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
package org.dolphinemu.dolphinemu.model;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
|
||||||
|
|
||||||
public final class Game
|
|
||||||
{
|
|
||||||
// Copied from IVolume::ECountry. Update these if that is ever modified.
|
|
||||||
public static final int COUNTRY_EUROPE = 0;
|
|
||||||
public static final int COUNTRY_JAPAN = 1;
|
|
||||||
public static final int COUNTRY_USA = 2;
|
|
||||||
public static final int COUNTRY_AUSTRALIA = 3;
|
|
||||||
public static final int COUNTRY_FRANCE = 4;
|
|
||||||
public static final int COUNTRY_GERMANY = 5;
|
|
||||||
public static final int COUNTRY_ITALY = 6;
|
|
||||||
public static final int COUNTRY_KOREA = 7;
|
|
||||||
public static final int COUNTRY_NETHERLANDS = 8;
|
|
||||||
public static final int COUNTRY_RUSSIA = 9;
|
|
||||||
public static final int COUNTRY_SPAIN = 10;
|
|
||||||
public static final int COUNTRY_TAIWAN = 11;
|
|
||||||
public static final int COUNTRY_WORLD = 12;
|
|
||||||
public static final int COUNTRY_UNKNOWN = 13;
|
|
||||||
|
|
||||||
private static final String PATH_SCREENSHOT_FOLDER = "file://" + Environment.getExternalStorageDirectory().getPath() + "/dolphin-emu/ScreenShots/";
|
|
||||||
|
|
||||||
private String mTitle;
|
|
||||||
private String mDescription;
|
|
||||||
private String mPath;
|
|
||||||
private String mGameId;
|
|
||||||
private String mScreenshotPath;
|
|
||||||
private String mCompany;
|
|
||||||
|
|
||||||
private Platform mPlatform;
|
|
||||||
private int mCountry;
|
|
||||||
|
|
||||||
public Game(Platform platform, String title, String description, int country, String path, String gameId, String company, String screenshotPath)
|
|
||||||
{
|
|
||||||
mPlatform = platform;
|
|
||||||
mTitle = title;
|
|
||||||
mDescription = description;
|
|
||||||
mCountry = country;
|
|
||||||
mPath = path;
|
|
||||||
mGameId = gameId;
|
|
||||||
mCompany = company;
|
|
||||||
mScreenshotPath = screenshotPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Platform getPlatform()
|
|
||||||
{
|
|
||||||
return mPlatform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle()
|
|
||||||
{
|
|
||||||
return mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription()
|
|
||||||
{
|
|
||||||
return mDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCompany()
|
|
||||||
{
|
|
||||||
return mCompany;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCountry()
|
|
||||||
{
|
|
||||||
return mCountry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPath()
|
|
||||||
{
|
|
||||||
return mPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGameId()
|
|
||||||
{
|
|
||||||
return mGameId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getScreenshotPath()
|
|
||||||
{
|
|
||||||
return mScreenshotPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ContentValues asContentValues(Platform platform, String title, String description, int country, String path, String gameId, String company)
|
|
||||||
{
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
|
|
||||||
String screenPath = PATH_SCREENSHOT_FOLDER + gameId + "/" + gameId + "-1.png";
|
|
||||||
|
|
||||||
values.put(GameDatabase.KEY_GAME_PLATFORM, platform.toInt());
|
|
||||||
values.put(GameDatabase.KEY_GAME_TITLE, title);
|
|
||||||
values.put(GameDatabase.KEY_GAME_DESCRIPTION, description);
|
|
||||||
values.put(GameDatabase.KEY_GAME_COUNTRY, company);
|
|
||||||
values.put(GameDatabase.KEY_GAME_PATH, path);
|
|
||||||
values.put(GameDatabase.KEY_GAME_ID, gameId);
|
|
||||||
values.put(GameDatabase.KEY_GAME_COMPANY, company);
|
|
||||||
values.put(GameDatabase.KEY_GAME_SCREENSHOT_PATH, screenPath);
|
|
||||||
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Game fromCursor(Cursor cursor)
|
|
||||||
{
|
|
||||||
return new Game(Platform.fromInt(cursor.getInt(GameDatabase.GAME_COLUMN_PLATFORM)),
|
|
||||||
cursor.getString(GameDatabase.GAME_COLUMN_TITLE),
|
|
||||||
cursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION),
|
|
||||||
cursor.getInt(GameDatabase.GAME_COLUMN_COUNTRY),
|
|
||||||
cursor.getString(GameDatabase.GAME_COLUMN_PATH),
|
|
||||||
cursor.getString(GameDatabase.GAME_COLUMN_GAME_ID),
|
|
||||||
cursor.getString(GameDatabase.GAME_COLUMN_COMPANY),
|
|
||||||
cursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,299 +0,0 @@
|
||||||
package org.dolphinemu.dolphinemu.model;
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.NativeLibrary;
|
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.Subscriber;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper class that provides several utilities simplifying interaction with
|
|
||||||
* the SQLite database.
|
|
||||||
*/
|
|
||||||
public final class GameDatabase extends SQLiteOpenHelper
|
|
||||||
{
|
|
||||||
private static final int DB_VERSION = 1;
|
|
||||||
|
|
||||||
public static final int COLUMN_DB_ID = 0;
|
|
||||||
|
|
||||||
public static final int GAME_COLUMN_PATH = 1;
|
|
||||||
public static final int GAME_COLUMN_PLATFORM = 2;
|
|
||||||
public static final int GAME_COLUMN_TITLE = 3;
|
|
||||||
public static final int GAME_COLUMN_DESCRIPTION = 4;
|
|
||||||
public static final int GAME_COLUMN_COUNTRY = 5;
|
|
||||||
public static final int GAME_COLUMN_GAME_ID = 6;
|
|
||||||
public static final int GAME_COLUMN_COMPANY = 7;
|
|
||||||
public static final int GAME_COLUMN_SCREENSHOT_PATH = 8;
|
|
||||||
|
|
||||||
public static final String KEY_DB_ID = "_id";
|
|
||||||
|
|
||||||
public static final String KEY_GAME_PATH = "path";
|
|
||||||
public static final String KEY_GAME_PLATFORM = "platform";
|
|
||||||
public static final String KEY_GAME_TITLE = "title";
|
|
||||||
public static final String KEY_GAME_DESCRIPTION = "description";
|
|
||||||
public static final String KEY_GAME_COUNTRY = "country";
|
|
||||||
public static final String KEY_GAME_ID = "game_id";
|
|
||||||
public static final String KEY_GAME_COMPANY = "company";
|
|
||||||
public static final String KEY_GAME_SCREENSHOT_PATH = "screenshot_path";
|
|
||||||
public static final String TABLE_NAME_GAMES = "games";
|
|
||||||
|
|
||||||
private static final String TYPE_PRIMARY = " INTEGER PRIMARY KEY";
|
|
||||||
private static final String TYPE_INTEGER = " INTEGER";
|
|
||||||
private static final String TYPE_STRING = " TEXT";
|
|
||||||
|
|
||||||
private static final String SEPARATOR = ", ";
|
|
||||||
|
|
||||||
private static final String SQL_CREATE_GAMES = "CREATE TABLE " + TABLE_NAME_GAMES + "("
|
|
||||||
+ KEY_DB_ID + TYPE_PRIMARY + SEPARATOR
|
|
||||||
+ KEY_GAME_PATH + TYPE_STRING + SEPARATOR
|
|
||||||
+ KEY_GAME_PLATFORM + TYPE_STRING + SEPARATOR
|
|
||||||
+ KEY_GAME_TITLE + TYPE_STRING + SEPARATOR
|
|
||||||
+ KEY_GAME_DESCRIPTION + TYPE_STRING + SEPARATOR
|
|
||||||
+ KEY_GAME_COUNTRY + TYPE_INTEGER + SEPARATOR
|
|
||||||
+ KEY_GAME_ID + TYPE_STRING + SEPARATOR
|
|
||||||
+ KEY_GAME_COMPANY + TYPE_STRING + SEPARATOR
|
|
||||||
+ KEY_GAME_SCREENSHOT_PATH + TYPE_STRING + ")";
|
|
||||||
|
|
||||||
private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES;
|
|
||||||
|
|
||||||
private static final String GAME_FOLDER_PATHS_PREFERENCE = "gameFolderPaths";
|
|
||||||
|
|
||||||
private static final Set<String> EMPTY_SET = new HashSet<>();
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
public GameDatabase(Context context)
|
|
||||||
{
|
|
||||||
// Superclass constructor builds a database or uses an existing one.
|
|
||||||
super(context, "games.db", null, DB_VERSION);
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(SQLiteDatabase database)
|
|
||||||
{
|
|
||||||
Log.debug("[GameDatabase] GameDatabase - Creating database...");
|
|
||||||
|
|
||||||
execSqlAndLog(database, SQL_CREATE_GAMES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDowngrade(SQLiteDatabase database, int oldVersion, int newVersion)
|
|
||||||
{
|
|
||||||
Log.verbose("[GameDatabase] Downgrades not supporting, clearing databases..");
|
|
||||||
execSqlAndLog(database, SQL_DELETE_GAMES);
|
|
||||||
execSqlAndLog(database, SQL_CREATE_GAMES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion)
|
|
||||||
{
|
|
||||||
Log.info("[GameDatabase] Upgrading database from schema version " + oldVersion + " to " + newVersion);
|
|
||||||
|
|
||||||
// Delete all the games
|
|
||||||
execSqlAndLog(database, SQL_DELETE_GAMES);
|
|
||||||
execSqlAndLog(database, SQL_CREATE_GAMES);
|
|
||||||
|
|
||||||
Log.verbose("[GameDatabase] Re-scanning library with new schema.");
|
|
||||||
scanLibrary(database);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addGameFolder(String path)
|
|
||||||
{
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
|
|
||||||
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
|
|
||||||
Set<String> newFolderPaths = new HashSet<>(folderPaths);
|
|
||||||
newFolderPaths.add(path);
|
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
|
||||||
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
scanLibrary(getWritableDatabase());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void scanLibrary(SQLiteDatabase database)
|
|
||||||
{
|
|
||||||
// Before scanning known folders, go through the game table and remove any entries for which the file itself is missing.
|
|
||||||
Cursor fileCursor = database.query(TABLE_NAME_GAMES,
|
|
||||||
null, // Get all columns.
|
|
||||||
null, // Get all rows.
|
|
||||||
null,
|
|
||||||
null, // No grouping.
|
|
||||||
null,
|
|
||||||
null); // Order of games is irrelevant.
|
|
||||||
|
|
||||||
// Possibly overly defensive, but ensures that moveToNext() does not skip a row.
|
|
||||||
fileCursor.moveToPosition(-1);
|
|
||||||
|
|
||||||
while (fileCursor.moveToNext())
|
|
||||||
{
|
|
||||||
String gamePath = fileCursor.getString(GAME_COLUMN_PATH);
|
|
||||||
File game = new File(gamePath);
|
|
||||||
|
|
||||||
if (!game.exists())
|
|
||||||
{
|
|
||||||
Log.error("[GameDatabase] Game file no longer exists. Removing from the library: " + gamePath);
|
|
||||||
database.delete(TABLE_NAME_GAMES,
|
|
||||||
KEY_DB_ID + " = ?",
|
|
||||||
new String[]{Long.toString(fileCursor.getLong(COLUMN_DB_ID))});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> allowedExtensions = new HashSet<String>(Arrays.asList(
|
|
||||||
".ciso", ".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".tgc", ".wad", ".wbfs"));
|
|
||||||
|
|
||||||
// Iterate through all results of the DB query (i.e. all folders in the library.)
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
|
|
||||||
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
|
|
||||||
Set<String> newFolderPaths = new HashSet<>();
|
|
||||||
for (String folderPath : folderPaths)
|
|
||||||
{
|
|
||||||
File folder = new File(folderPath);
|
|
||||||
boolean deleteFolder = false;
|
|
||||||
|
|
||||||
Log.info("[GameDatabase] Reading files from library folder: " + folderPath);
|
|
||||||
|
|
||||||
// Iterate through every file in the folder.
|
|
||||||
File[] children = folder.listFiles();
|
|
||||||
|
|
||||||
if (children != null)
|
|
||||||
{
|
|
||||||
for (File file : children)
|
|
||||||
{
|
|
||||||
if (!file.isHidden() && !file.isDirectory())
|
|
||||||
{
|
|
||||||
String filePath = file.getPath();
|
|
||||||
|
|
||||||
int extensionStart = filePath.lastIndexOf('.');
|
|
||||||
if (extensionStart > 0)
|
|
||||||
{
|
|
||||||
String fileExtension = filePath.substring(extensionStart);
|
|
||||||
|
|
||||||
// Check that the file has an extension we care about before trying to read out of it.
|
|
||||||
if (allowedExtensions.contains(fileExtension.toLowerCase()))
|
|
||||||
{
|
|
||||||
String name = NativeLibrary.GetTitle(filePath);
|
|
||||||
|
|
||||||
// If the game's title field is empty, use the filename.
|
|
||||||
if (name.isEmpty())
|
|
||||||
{
|
|
||||||
name = filePath.substring(filePath.lastIndexOf("/") + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
String gameId = NativeLibrary.GetGameId(filePath);
|
|
||||||
|
|
||||||
// If the game's ID field is empty, use the filename without extension.
|
|
||||||
if (gameId.isEmpty())
|
|
||||||
{
|
|
||||||
gameId = filePath.substring(filePath.lastIndexOf("/") + 1, filePath.lastIndexOf("."));
|
|
||||||
}
|
|
||||||
|
|
||||||
Platform platform = Platform.fromNativeInt(NativeLibrary.GetPlatform(filePath));
|
|
||||||
|
|
||||||
ContentValues game = Game.asContentValues(platform,
|
|
||||||
name,
|
|
||||||
NativeLibrary.GetDescription(filePath).replace("\n", " "),
|
|
||||||
NativeLibrary.GetCountry(filePath),
|
|
||||||
filePath,
|
|
||||||
gameId,
|
|
||||||
NativeLibrary.GetCompany(filePath));
|
|
||||||
|
|
||||||
// Try to update an existing game first.
|
|
||||||
int rowsMatched = database.update(TABLE_NAME_GAMES, // Which table to update.
|
|
||||||
game, // The values to fill the row with.
|
|
||||||
KEY_GAME_ID + " = ?", // The WHERE clause used to find the right row.
|
|
||||||
new String[]{game.getAsString(KEY_GAME_ID)}); // The ? in WHERE clause is replaced with this,
|
|
||||||
// which is provided as an array because there
|
|
||||||
// could potentially be more than one argument.
|
|
||||||
|
|
||||||
// If update fails, insert a new game instead.
|
|
||||||
if (rowsMatched == 0)
|
|
||||||
{
|
|
||||||
Log.verbose("[GameDatabase] Adding game: " + game.getAsString(KEY_GAME_TITLE));
|
|
||||||
database.insert(TABLE_NAME_GAMES, null, game);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.verbose("[GameDatabase] Updated game: " + game.getAsString(KEY_GAME_TITLE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the folder is empty because it no longer exists, remove it from the library.
|
|
||||||
else if (!folder.exists())
|
|
||||||
{
|
|
||||||
Log.error("[GameDatabase] Folder no longer exists. Removing from the library: " + folderPath);
|
|
||||||
deleteFolder = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.error("[GameDatabase] Folder contains no games: " + folderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!deleteFolder)
|
|
||||||
{
|
|
||||||
newFolderPaths.add(folderPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileCursor.close();
|
|
||||||
database.close();
|
|
||||||
|
|
||||||
if (folderPaths.size() != newFolderPaths.size())
|
|
||||||
{
|
|
||||||
// One or more folders are being deleted
|
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
|
||||||
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Observable<Cursor> getGamesForPlatform(final Platform platform)
|
|
||||||
{
|
|
||||||
return Observable.create(subscriber ->
|
|
||||||
{
|
|
||||||
Log.info("[GameDatabase] Reading games list...");
|
|
||||||
|
|
||||||
String[] whereArgs = new String[]{Integer.toString(platform.toInt())};
|
|
||||||
|
|
||||||
SQLiteDatabase database = getReadableDatabase();
|
|
||||||
Cursor resultCursor = database.query(
|
|
||||||
TABLE_NAME_GAMES,
|
|
||||||
null,
|
|
||||||
KEY_GAME_PLATFORM + " = ?",
|
|
||||||
whereArgs,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
KEY_GAME_TITLE + " ASC"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Pass the result cursor to the consumer.
|
|
||||||
subscriber.onNext(resultCursor);
|
|
||||||
|
|
||||||
// Tell the consumer we're done; it will unsubscribe implicitly.
|
|
||||||
subscriber.onCompleted();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void execSqlAndLog(SQLiteDatabase database, String sql)
|
|
||||||
{
|
|
||||||
Log.verbose("[GameDatabase] Executing SQL: " + sql);
|
|
||||||
database.execSQL(sql);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.dolphinemu.dolphinemu.model;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
|
public class GameFile
|
||||||
|
{
|
||||||
|
private long mPointer; // Do not rename or move without editing the native code
|
||||||
|
|
||||||
|
private GameFile(long pointer)
|
||||||
|
{
|
||||||
|
mPointer = pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native void finalize();
|
||||||
|
|
||||||
|
public native int getPlatform();
|
||||||
|
public native String getTitle();
|
||||||
|
public native String getDescription();
|
||||||
|
public native String getCompany();
|
||||||
|
public native int getCountry();
|
||||||
|
public native String getPath();
|
||||||
|
public native String getGameId();
|
||||||
|
public native int[] getBanner();
|
||||||
|
public native int getBannerWidth();
|
||||||
|
public native int getBannerHeight();
|
||||||
|
|
||||||
|
public String getScreenshotPath()
|
||||||
|
{
|
||||||
|
String gameId = getGameId();
|
||||||
|
return "file://" + Environment.getExternalStorageDirectory().getPath() +
|
||||||
|
"/dolphin-emu/ScreenShots/" + gameId + "/" + gameId + "-1.png";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
package org.dolphinemu.dolphinemu.model;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class GameFileCache
|
||||||
|
{
|
||||||
|
private static final String GAME_FOLDER_PATHS_PREFERENCE = "gameFolderPaths";
|
||||||
|
private static final Set<String> EMPTY_SET = new HashSet<>();
|
||||||
|
|
||||||
|
private long mPointer; // Do not rename or move without editing the native code
|
||||||
|
|
||||||
|
public GameFileCache(String path)
|
||||||
|
{
|
||||||
|
mPointer = newGameFileCache(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native long newGameFileCache(String path);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public native void finalize();
|
||||||
|
|
||||||
|
public static void addGameFolder(String path, Context context)
|
||||||
|
{
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
|
||||||
|
Set<String> newFolderPaths = new HashSet<>(folderPaths);
|
||||||
|
newFolderPaths.add(path);
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeNonExistentGameFolders(Context context)
|
||||||
|
{
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
|
||||||
|
Set<String> newFolderPaths = new HashSet<>();
|
||||||
|
for (String folderPath : folderPaths)
|
||||||
|
{
|
||||||
|
File folder = new File(folderPath);
|
||||||
|
if (folder.exists())
|
||||||
|
{
|
||||||
|
newFolderPaths.add(folderPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folderPaths.size() != newFolderPaths.size())
|
||||||
|
{
|
||||||
|
// One or more folders are being deleted
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans through the file system and updates the cache to match.
|
||||||
|
* @return true if the cache was modified
|
||||||
|
*/
|
||||||
|
public boolean scanLibrary(Context context)
|
||||||
|
{
|
||||||
|
removeNonExistentGameFolders(context);
|
||||||
|
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
Set<String> folderPathsSet = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
|
||||||
|
String[] folderPaths = folderPathsSet.toArray(new String[folderPathsSet.size()]);
|
||||||
|
|
||||||
|
boolean cacheChanged = update(folderPaths);
|
||||||
|
cacheChanged |= updateAdditionalMetadata();
|
||||||
|
if (cacheChanged)
|
||||||
|
{
|
||||||
|
save();
|
||||||
|
}
|
||||||
|
return cacheChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public native GameFile[] getAllGames();
|
||||||
|
public native GameFile addOrGet(String gamePath);
|
||||||
|
private native boolean update(String[] folderPaths);
|
||||||
|
private native boolean updateAdditionalMetadata();
|
||||||
|
public native boolean load();
|
||||||
|
private native boolean save();
|
||||||
|
}
|
|
@ -1,139 +0,0 @@
|
||||||
package org.dolphinemu.dolphinemu.model;
|
|
||||||
|
|
||||||
import android.content.ContentProvider;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.BuildConfig;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides an interface allowing Activities to interact with the SQLite database.
|
|
||||||
* CRUD methods in this class can be called by Activities using getContentResolver().
|
|
||||||
*/
|
|
||||||
public final class GameProvider extends ContentProvider
|
|
||||||
{
|
|
||||||
public static final String REFRESH_LIBRARY = "refresh";
|
|
||||||
|
|
||||||
public static final String AUTHORITY = "content://" + BuildConfig.APPLICATION_ID + ".provider";
|
|
||||||
public static final Uri URI_GAME = Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_GAMES + "/");
|
|
||||||
public static final Uri URI_REFRESH = Uri.parse(AUTHORITY + "/" + REFRESH_LIBRARY + "/");
|
|
||||||
|
|
||||||
public static final String MIME_TYPE_FOLDER = "vnd.android.cursor.item/vnd.dolphin.folder";
|
|
||||||
public static final String MIME_TYPE_GAME = "vnd.android.cursor.item/vnd.dolphin.game";
|
|
||||||
|
|
||||||
|
|
||||||
private GameDatabase mDbHelper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate()
|
|
||||||
{
|
|
||||||
Log.info("[GameProvider] Creating Content Provider...");
|
|
||||||
|
|
||||||
mDbHelper = new GameDatabase(getContext());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
|
|
||||||
{
|
|
||||||
Log.info("[GameProvider] Querying URI: " + uri);
|
|
||||||
|
|
||||||
SQLiteDatabase db = mDbHelper.getReadableDatabase();
|
|
||||||
|
|
||||||
String table = uri.getLastPathSegment();
|
|
||||||
|
|
||||||
if (table == null)
|
|
||||||
{
|
|
||||||
Log.error("[GameProvider] Badly formatted URI: " + uri);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cursor cursor = db.query(table, projection, selection, selectionArgs, null, null, sortOrder);
|
|
||||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
|
||||||
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getType(@NonNull Uri uri)
|
|
||||||
{
|
|
||||||
Log.verbose("[GameProvider] Getting MIME type for URI: " + uri);
|
|
||||||
String lastSegment = uri.getLastPathSegment();
|
|
||||||
|
|
||||||
if (lastSegment == null)
|
|
||||||
{
|
|
||||||
Log.error("[GameProvider] Badly formatted URI: " + uri);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastSegment.equals(GameDatabase.TABLE_NAME_GAMES))
|
|
||||||
{
|
|
||||||
return MIME_TYPE_GAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.error("[GameProvider] Unknown MIME type for URI: " + uri);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues values)
|
|
||||||
{
|
|
||||||
Log.info("[GameProvider] Inserting row at URI: " + uri);
|
|
||||||
|
|
||||||
SQLiteDatabase database = mDbHelper.getWritableDatabase();
|
|
||||||
String table = uri.getLastPathSegment();
|
|
||||||
|
|
||||||
long id = -1;
|
|
||||||
|
|
||||||
if (table != null)
|
|
||||||
{
|
|
||||||
if (table.equals(REFRESH_LIBRARY))
|
|
||||||
{
|
|
||||||
Log.info("[GameProvider] URI specified table REFRESH_LIBRARY. No insertion necessary; refreshing library contents...");
|
|
||||||
mDbHelper.scanLibrary(database);
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
id = database.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
|
||||||
|
|
||||||
// If insertion was successful...
|
|
||||||
if (id > 0)
|
|
||||||
{
|
|
||||||
// Notify the UI that its contents should be refreshed.
|
|
||||||
getContext().getContentResolver().notifyChange(uri, null);
|
|
||||||
uri = Uri.withAppendedPath(uri, Long.toString(id));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.error("[GameProvider] Row already exists: " + uri + " id: " + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.error("[GameProvider] Badly formatted URI: " + uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
database.close();
|
|
||||||
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs)
|
|
||||||
{
|
|
||||||
Log.error("[GameProvider] Delete operations unsupported. URI: " + uri);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs)
|
|
||||||
{
|
|
||||||
Log.error("[GameProvider] Update operations unsupported. URI: " + uri);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -32,7 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
*/
|
*/
|
||||||
public final class DirectoryInitializationService extends IntentService
|
public final class DirectoryInitializationService extends IntentService
|
||||||
{
|
{
|
||||||
public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.BROADCAST";
|
public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.DIRECTORY_INITIALIZATION";
|
||||||
|
|
||||||
public static final String EXTRA_STATE = "directoryState";
|
public static final String EXTRA_STATE = "directoryState";
|
||||||
private static volatile DirectoryInitializationState directoryState = null;
|
private static volatile DirectoryInitializationState directoryState = null;
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package org.dolphinemu.dolphinemu.services;
|
||||||
|
|
||||||
|
import android.app.IntentService;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFileCache;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service that loads game list data on a separate thread.
|
||||||
|
*/
|
||||||
|
public final class GameFileCacheService extends IntentService
|
||||||
|
{
|
||||||
|
public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_UPDATED";
|
||||||
|
|
||||||
|
private static final String ACTION_LOAD = "org.dolphinemu.dolphinemu.LOAD_GAME_FILE_CACHE";
|
||||||
|
private static final String ACTION_RESCAN = "org.dolphinemu.dolphinemu.RESCAN_GAME_FILE_CACHE";
|
||||||
|
|
||||||
|
private static GameFileCache gameFileCache = null;
|
||||||
|
private static AtomicReference<GameFile[]> gameFiles = new AtomicReference<>(new GameFile[]{});
|
||||||
|
|
||||||
|
public GameFileCacheService()
|
||||||
|
{
|
||||||
|
// Superclass constructor is called to name the thread on which this service executes.
|
||||||
|
super("GameFileCacheService");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<GameFile> getGameFilesForPlatform(Platform platform)
|
||||||
|
{
|
||||||
|
GameFile[] allGames = gameFiles.get();
|
||||||
|
ArrayList<GameFile> platformGames = new ArrayList<>();
|
||||||
|
for (GameFile game : allGames)
|
||||||
|
{
|
||||||
|
if (Platform.fromNativeInt(game.getPlatform()) == platform)
|
||||||
|
{
|
||||||
|
platformGames.add(game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return platformGames;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void startService(Context context, String action)
|
||||||
|
{
|
||||||
|
Intent intent = new Intent(context, GameFileCacheService.class);
|
||||||
|
intent.setAction(action);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously loads the game file cache from disk without checking
|
||||||
|
* which games are present on the file system.
|
||||||
|
*/
|
||||||
|
public static void startLoad(Context context)
|
||||||
|
{
|
||||||
|
startService(context, ACTION_LOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronously scans for games in the user's configured folders,
|
||||||
|
* updating the game file cache with the results.
|
||||||
|
* If startLoad hasn't been called before this, this has no effect.
|
||||||
|
*/
|
||||||
|
public static void startRescan(Context context)
|
||||||
|
{
|
||||||
|
startService(context, ACTION_RESCAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameFile addOrGet(String gamePath)
|
||||||
|
{
|
||||||
|
// The existence of this one function, which is called from one
|
||||||
|
// single place, forces us to use synchronization in onHandleIntent...
|
||||||
|
// A bit annoying, but should be good enough for now
|
||||||
|
synchronized (gameFileCache)
|
||||||
|
{
|
||||||
|
return gameFileCache.addOrGet(gamePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent)
|
||||||
|
{
|
||||||
|
// Load the game list cache if it isn't already loaded, otherwise do nothing
|
||||||
|
if (ACTION_LOAD.equals(intent.getAction()) && gameFileCache == null)
|
||||||
|
{
|
||||||
|
GameFileCache temp = new GameFileCache(getCacheDir() + File.separator + "gamelist.cache");
|
||||||
|
synchronized (temp)
|
||||||
|
{
|
||||||
|
gameFileCache = temp;
|
||||||
|
gameFileCache.load();
|
||||||
|
updateGameFileArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rescan the file system and update the game list cache with the results
|
||||||
|
if (ACTION_RESCAN.equals(intent.getAction()) && gameFileCache != null)
|
||||||
|
{
|
||||||
|
synchronized (gameFileCache)
|
||||||
|
{
|
||||||
|
if (gameFileCache.scanLibrary(this))
|
||||||
|
{
|
||||||
|
updateGameFileArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateGameFileArray()
|
||||||
|
{
|
||||||
|
GameFile[] gameFilesTemp = gameFileCache.getAllGames();
|
||||||
|
Arrays.sort(gameFilesTemp, (lhs, rhs) -> lhs.getTitle().compareTo(rhs.getTitle()));
|
||||||
|
gameFiles.set(gameFilesTemp);
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_ACTION));
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ package org.dolphinemu.dolphinemu.ui.main;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
@ -18,12 +17,11 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
|
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
|
||||||
import org.dolphinemu.dolphinemu.model.GameProvider;
|
|
||||||
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
||||||
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
|
||||||
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
||||||
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||||
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
||||||
|
@ -39,7 +37,7 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
private TabLayout mTabLayout;
|
private TabLayout mTabLayout;
|
||||||
private FloatingActionButton mFab;
|
private FloatingActionButton mFab;
|
||||||
|
|
||||||
private MainPresenter mPresenter = new MainPresenter(this);
|
private MainPresenter mPresenter = new MainPresenter(this, this);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
|
@ -67,7 +65,11 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
||||||
getSupportFragmentManager(), this);
|
getSupportFragmentManager(), this);
|
||||||
mViewPager.setAdapter(platformPagerAdapter);
|
mViewPager.setAdapter(platformPagerAdapter);
|
||||||
} else {
|
showGames();
|
||||||
|
GameFileCacheService.startLoad(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
mViewPager.setVisibility(View.INVISIBLE);
|
mViewPager.setVisibility(View.INVISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +78,14 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
protected void onResume()
|
protected void onResume()
|
||||||
{
|
{
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));
|
mPresenter.addDirIfNeeded(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy()
|
||||||
|
{
|
||||||
|
super.onDestroy();
|
||||||
|
mPresenter.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Replace with a ButterKnife injection.
|
// TODO: Replace with a ButterKnife injection.
|
||||||
|
@ -106,13 +115,6 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
mToolbar.setSubtitle(version);
|
mToolbar.setSubtitle(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh()
|
|
||||||
{
|
|
||||||
getContentResolver().insert(GameProvider.URI_REFRESH, null);
|
|
||||||
refreshAllFragments();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void refreshFragmentScreenshot(int fragmentPosition)
|
public void refreshFragmentScreenshot(int fragmentPosition)
|
||||||
{
|
{
|
||||||
|
@ -138,12 +140,6 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
FileBrowserHelper.openDirectoryPicker(this);
|
FileBrowserHelper.openDirectoryPicker(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void showGames(Platform platform, Cursor games)
|
|
||||||
{
|
|
||||||
// no-op. Handled by PlatformGamesFragment.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
* @param requestCode An int describing whether the Activity that is returning did so successfully.
|
||||||
* @param resultCode An int describing what Activity is giving us this callback.
|
* @param resultCode An int describing what Activity is giving us this callback.
|
||||||
|
@ -174,12 +170,12 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
|
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
DirectoryInitializationService.startService(this);
|
DirectoryInitializationService.startService(this);
|
||||||
|
|
||||||
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
|
||||||
getSupportFragmentManager(), this);
|
getSupportFragmentManager(), this);
|
||||||
mViewPager.setAdapter(platformPagerAdapter);
|
mViewPager.setAdapter(platformPagerAdapter);
|
||||||
mTabLayout.setupWithViewPager(mViewPager);
|
mTabLayout.setupWithViewPager(mViewPager);
|
||||||
mViewPager.setVisibility(View.VISIBLE);
|
mViewPager.setVisibility(View.VISIBLE);
|
||||||
|
GameFileCacheService.startLoad(this);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
||||||
.show();
|
.show();
|
||||||
|
@ -200,17 +196,17 @@ public final class MainActivity extends AppCompatActivity implements MainView
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item)
|
public boolean onOptionsItemSelected(MenuItem item)
|
||||||
{
|
{
|
||||||
return mPresenter.handleOptionSelection(item.getItemId());
|
return mPresenter.handleOptionSelection(item.getItemId(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshAllFragments()
|
public void showGames()
|
||||||
{
|
{
|
||||||
for (Platform platform : Platform.values())
|
for (Platform platform : Platform.values())
|
||||||
{
|
{
|
||||||
PlatformGamesView fragment = getPlatformGamesView(platform);
|
PlatformGamesView fragment = getPlatformGamesView(platform);
|
||||||
if (fragment != null)
|
if (fragment != null)
|
||||||
{
|
{
|
||||||
fragment.refresh();
|
fragment.showGames();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,57 @@
|
||||||
package org.dolphinemu.dolphinemu.ui.main;
|
package org.dolphinemu.dolphinemu.ui.main;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.BuildConfig;
|
import org.dolphinemu.dolphinemu.BuildConfig;
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication;
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
import org.dolphinemu.dolphinemu.model.GameFileCache;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
import org.dolphinemu.dolphinemu.utils.SettingsFile;
|
||||||
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public final class MainPresenter
|
public final class MainPresenter
|
||||||
{
|
{
|
||||||
public static final int REQUEST_ADD_DIRECTORY = 1;
|
public static final int REQUEST_ADD_DIRECTORY = 1;
|
||||||
public static final int REQUEST_EMULATE_GAME = 2;
|
public static final int REQUEST_EMULATE_GAME = 2;
|
||||||
|
|
||||||
private final MainView mView;
|
private final MainView mView;
|
||||||
|
private final Context mContext;
|
||||||
|
private BroadcastReceiver mBroadcastReceiver = null;
|
||||||
private String mDirToAdd;
|
private String mDirToAdd;
|
||||||
|
|
||||||
public MainPresenter(MainView view)
|
public MainPresenter(MainView view, Context context)
|
||||||
{
|
{
|
||||||
mView = view;
|
mView = view;
|
||||||
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCreate()
|
public void onCreate()
|
||||||
{
|
{
|
||||||
String versionName = BuildConfig.VERSION_NAME;
|
String versionName = BuildConfig.VERSION_NAME;
|
||||||
mView.setVersionString(versionName);
|
mView.setVersionString(versionName);
|
||||||
|
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(GameFileCacheService.BROADCAST_ACTION);
|
||||||
|
mBroadcastReceiver = new BroadcastReceiver()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent)
|
||||||
|
{
|
||||||
|
mView.showGames();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDestroy()
|
||||||
|
{
|
||||||
|
if (mBroadcastReceiver != null)
|
||||||
|
{
|
||||||
|
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mBroadcastReceiver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFabClick()
|
public void onFabClick()
|
||||||
|
@ -36,7 +59,7 @@ public final class MainPresenter
|
||||||
mView.launchFileListActivity();
|
mView.launchFileListActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean handleOptionSelection(int itemId)
|
public boolean handleOptionSelection(int itemId, Context context)
|
||||||
{
|
{
|
||||||
switch (itemId)
|
switch (itemId)
|
||||||
{
|
{
|
||||||
|
@ -57,9 +80,7 @@ public final class MainPresenter
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.menu_refresh:
|
case R.id.menu_refresh:
|
||||||
GameDatabase databaseHelper = DolphinApplication.databaseHelper;
|
GameFileCacheService.startRescan(context);
|
||||||
databaseHelper.scanLibrary(databaseHelper.getWritableDatabase());
|
|
||||||
mView.refresh();
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case R.id.button_add_directory:
|
case R.id.button_add_directory:
|
||||||
|
@ -70,13 +91,13 @@ public final class MainPresenter
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDirIfNeeded(AddDirectoryHelper helper)
|
public void addDirIfNeeded(Context context)
|
||||||
{
|
{
|
||||||
if (mDirToAdd != null)
|
if (mDirToAdd != null)
|
||||||
{
|
{
|
||||||
helper.addDirectory(mDirToAdd, mView::refresh);
|
GameFileCache.addGameFolder(mDirToAdd, context);
|
||||||
|
|
||||||
mDirToAdd = null;
|
mDirToAdd = null;
|
||||||
|
GameFileCacheService.startRescan(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,15 +110,4 @@ public final class MainPresenter
|
||||||
{
|
{
|
||||||
mView.refreshFragmentScreenshot(resultCode);
|
mView.refreshFragmentScreenshot(resultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void loadGames(final Platform platform)
|
|
||||||
{
|
|
||||||
GameDatabase databaseHelper = DolphinApplication.databaseHelper;
|
|
||||||
|
|
||||||
databaseHelper.getGamesForPlatform(platform)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(games -> mView.showGames(platform, games));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.dolphinemu.dolphinemu.ui.main;
|
||||||
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -19,11 +20,6 @@ public interface MainView
|
||||||
*/
|
*/
|
||||||
void setVersionString(String version);
|
void setVersionString(String version);
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the view to refresh its contents.
|
|
||||||
*/
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell the view to tell the currently displayed {@link android.support.v4.app.Fragment}
|
* Tell the view to tell the currently displayed {@link android.support.v4.app.Fragment}
|
||||||
* to refresh the screenshot at the given position in its list of games.
|
* to refresh the screenshot at the given position in its list of games.
|
||||||
|
@ -38,11 +34,7 @@ public interface MainView
|
||||||
void launchFileListActivity();
|
void launchFileListActivity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called when an asynchronous database read completes. Passes the
|
* To be called when the game file cache is updated.
|
||||||
* result, in this case a {@link Cursor} to the view.
|
|
||||||
*
|
|
||||||
* @param platform Which platform to show games for.
|
|
||||||
* @param games A Cursor containing the games read from the database.
|
|
||||||
*/
|
*/
|
||||||
void showGames(Platform platform, Cursor games);
|
void showGames();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,10 @@ package org.dolphinemu.dolphinemu.ui.main;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v17.leanback.app.BrowseFragment;
|
import android.support.v17.leanback.app.BrowseFragment;
|
||||||
import android.support.v17.leanback.app.BrowseSupportFragment;
|
import android.support.v17.leanback.app.BrowseSupportFragment;
|
||||||
import android.support.v17.leanback.database.CursorMapper;
|
|
||||||
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
import android.support.v17.leanback.widget.ArrayObjectAdapter;
|
||||||
import android.support.v17.leanback.widget.CursorObjectAdapter;
|
|
||||||
import android.support.v17.leanback.widget.HeaderItem;
|
import android.support.v17.leanback.widget.HeaderItem;
|
||||||
import android.support.v17.leanback.widget.ListRow;
|
import android.support.v17.leanback.widget.ListRow;
|
||||||
import android.support.v17.leanback.widget.ListRowPresenter;
|
import android.support.v17.leanback.widget.ListRowPresenter;
|
||||||
|
@ -21,20 +18,22 @@ import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
|
||||||
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
|
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
|
||||||
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
|
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
|
||||||
import org.dolphinemu.dolphinemu.model.Game;
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
|
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
|
||||||
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
|
||||||
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
import org.dolphinemu.dolphinemu.ui.platform.Platform;
|
||||||
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
|
||||||
import org.dolphinemu.dolphinemu.utils.AddDirectoryHelper;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
|
||||||
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
|
||||||
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
import org.dolphinemu.dolphinemu.utils.StartupHandler;
|
||||||
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
public final class TvMainActivity extends FragmentActivity implements MainView
|
public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
{
|
{
|
||||||
private MainPresenter mPresenter = new MainPresenter(this);
|
private MainPresenter mPresenter = new MainPresenter(this, this);
|
||||||
|
|
||||||
private BrowseSupportFragment mBrowseFragment;
|
private BrowseSupportFragment mBrowseFragment;
|
||||||
|
|
||||||
|
@ -59,7 +58,14 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
protected void onResume()
|
protected void onResume()
|
||||||
{
|
{
|
||||||
super.onResume();
|
super.onResume();
|
||||||
mPresenter.addDirIfNeeded(new AddDirectoryHelper(this));
|
mPresenter.addDirIfNeeded(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy()
|
||||||
|
{
|
||||||
|
super.onDestroy();
|
||||||
|
mPresenter.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupUI() {
|
void setupUI() {
|
||||||
|
@ -82,7 +88,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
if (item instanceof TvSettingsItem)
|
if (item instanceof TvSettingsItem)
|
||||||
{
|
{
|
||||||
TvSettingsItem settingsItem = (TvSettingsItem) item;
|
TvSettingsItem settingsItem = (TvSettingsItem) item;
|
||||||
mPresenter.handleOptionSelection(settingsItem.getItemId());
|
mPresenter.handleOptionSelection(settingsItem.getItemId(), this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -90,9 +96,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
|
|
||||||
// Start the emulation activity and send the path of the clicked ISO to it.
|
// Start the emulation activity and send the path of the clicked ISO to it.
|
||||||
EmulationActivity.launch(TvMainActivity.this,
|
EmulationActivity.launch(TvMainActivity.this,
|
||||||
holder.path,
|
holder.gameFile,
|
||||||
holder.title,
|
|
||||||
holder.screenshotPath,
|
|
||||||
-1,
|
-1,
|
||||||
holder.imageScreenshot);
|
holder.imageScreenshot);
|
||||||
}
|
}
|
||||||
|
@ -108,12 +112,6 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
mBrowseFragment.setTitle(version);
|
mBrowseFragment.setTitle(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh()
|
|
||||||
{
|
|
||||||
recreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void refreshFragmentScreenshot(int fragmentPosition)
|
public void refreshFragmentScreenshot(int fragmentPosition)
|
||||||
{
|
{
|
||||||
|
@ -133,15 +131,9 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showGames(Platform platform, Cursor games)
|
public void showGames()
|
||||||
{
|
{
|
||||||
ListRow row = buildGamesRow(platform, games);
|
recreate();
|
||||||
|
|
||||||
// Add row to the adapter only if it is not empty.
|
|
||||||
if (row != null)
|
|
||||||
{
|
|
||||||
mRowsAdapter.add(row);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -176,7 +168,7 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
|
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
|
||||||
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
DirectoryInitializationService.startService(this);
|
DirectoryInitializationService.startService(this);
|
||||||
loadGames();
|
GameFileCacheService.startLoad(this);
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
|
||||||
.show();
|
.show();
|
||||||
|
@ -194,48 +186,36 @@ public final class TvMainActivity extends FragmentActivity implements MainView
|
||||||
|
|
||||||
if (PermissionsHandler.hasWriteAccess(this))
|
if (PermissionsHandler.hasWriteAccess(this))
|
||||||
{
|
{
|
||||||
loadGames();
|
GameFileCacheService.startLoad(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
mRowsAdapter.add(buildSettingsRow());
|
mRowsAdapter.add(buildSettingsRow());
|
||||||
|
|
||||||
|
for (Platform platform : Platform.values())
|
||||||
|
{
|
||||||
|
ListRow row = buildGamesRow(platform, GameFileCacheService.getGameFilesForPlatform(platform));
|
||||||
|
|
||||||
|
// Add row to the adapter only if it is not empty.
|
||||||
|
if (row != null)
|
||||||
|
{
|
||||||
|
mRowsAdapter.add(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mBrowseFragment.setAdapter(mRowsAdapter);
|
mBrowseFragment.setAdapter(mRowsAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadGames() {
|
private ListRow buildGamesRow(Platform platform, Collection<GameFile> gameFiles)
|
||||||
for (Platform platform : Platform.values()) {
|
|
||||||
mPresenter.loadGames(platform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ListRow buildGamesRow(Platform platform, Cursor games)
|
|
||||||
{
|
{
|
||||||
// Create an adapter for this row.
|
// If there are no games, don't return a Row.
|
||||||
CursorObjectAdapter row = new CursorObjectAdapter(new GameRowPresenter());
|
if (gameFiles.size() == 0)
|
||||||
|
|
||||||
// If cursor is empty, don't return a Row.
|
|
||||||
if (!games.moveToFirst())
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
row.changeCursor(games);
|
// Create an adapter for this row.
|
||||||
row.setMapper(new CursorMapper()
|
ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter());
|
||||||
{
|
row.addAll(0, gameFiles);
|
||||||
@Override
|
|
||||||
protected void bindColumns(Cursor cursor)
|
|
||||||
{
|
|
||||||
// No-op? Not sure what this does.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object bind(Cursor cursor)
|
|
||||||
{
|
|
||||||
return Game.fromCursor(cursor);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
String headerName = platform.getHeaderName();
|
|
||||||
|
|
||||||
// Create a header for this row.
|
// Create a header for this row.
|
||||||
HeaderItem header = new HeaderItem(platform.toInt(), platform.getHeaderName());
|
HeaderItem header = new HeaderItem(platform.toInt(), platform.getHeaderName());
|
||||||
|
|
|
@ -23,11 +23,9 @@ public enum Platform
|
||||||
|
|
||||||
public static Platform fromNativeInt(int i)
|
public static Platform fromNativeInt(int i)
|
||||||
{
|
{
|
||||||
// If the game's platform field is empty, file under Wiiware. // TODO Something less dum
|
// TODO: Proper support for DOL and ELF files
|
||||||
if (i == -1) {
|
boolean in_range = i >= 0 && i < values().length;
|
||||||
return Platform.WIIWARE;
|
return values()[in_range ? i : WIIWARE.value];
|
||||||
}
|
|
||||||
return values()[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Platform fromPosition(int position)
|
public static Platform fromPosition(int position)
|
||||||
|
@ -44,4 +42,4 @@ public enum Platform
|
||||||
{
|
{
|
||||||
return headerName;
|
return headerName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.dolphinemu.dolphinemu.ui.platform;
|
package org.dolphinemu.dolphinemu.ui.platform;
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
@ -12,13 +11,15 @@ import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
|
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class PlatformGamesFragment extends Fragment implements PlatformGamesView
|
public final class PlatformGamesFragment extends Fragment implements PlatformGamesView
|
||||||
{
|
{
|
||||||
private static final String ARG_PLATFORM = "platform";
|
private static final String ARG_PLATFORM = "platform";
|
||||||
|
|
||||||
private PlatformGamesPresenter mPresenter = new PlatformGamesPresenter(this);
|
|
||||||
|
|
||||||
private GameAdapter mAdapter;
|
private GameAdapter mAdapter;
|
||||||
private RecyclerView mRecyclerView;
|
private RecyclerView mRecyclerView;
|
||||||
|
|
||||||
|
@ -37,8 +38,6 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
public void onCreate(Bundle savedInstanceState)
|
public void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
mPresenter.onCreate((Platform) getArguments().getSerializable(ARG_PLATFORM));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -49,8 +48,6 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
|
|
||||||
findViews(rootView);
|
findViews(rootView);
|
||||||
|
|
||||||
mPresenter.onCreateView();
|
|
||||||
|
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +62,8 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
mRecyclerView.setAdapter(mAdapter);
|
mRecyclerView.setAdapter(mAdapter);
|
||||||
|
|
||||||
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
|
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
|
||||||
|
|
||||||
|
showGames();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,12 +72,6 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
mAdapter.notifyItemChanged(position);
|
mAdapter.notifyItemChanged(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void refresh()
|
|
||||||
{
|
|
||||||
mPresenter.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(String gameId)
|
public void onItemClick(String gameId)
|
||||||
{
|
{
|
||||||
|
@ -86,11 +79,12 @@ public final class PlatformGamesFragment extends Fragment implements PlatformGam
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showGames(Cursor games)
|
public void showGames()
|
||||||
{
|
{
|
||||||
if (mAdapter != null)
|
if (mAdapter != null)
|
||||||
{
|
{
|
||||||
mAdapter.swapCursor(games);
|
Platform platform = (Platform) getArguments().getSerializable(ARG_PLATFORM);
|
||||||
|
mAdapter.swapDataSet(GameFileCacheService.getGameFilesForPlatform(platform));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package org.dolphinemu.dolphinemu.ui.platform;
|
|
||||||
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication;
|
|
||||||
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
|
||||||
import org.dolphinemu.dolphinemu.utils.Log;
|
|
||||||
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
|
||||||
import rx.functions.Action1;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public final class PlatformGamesPresenter
|
|
||||||
{
|
|
||||||
private final PlatformGamesView mView;
|
|
||||||
|
|
||||||
private Platform mPlatform;
|
|
||||||
|
|
||||||
public PlatformGamesPresenter(PlatformGamesView view)
|
|
||||||
{
|
|
||||||
mView = view;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCreate(Platform platform)
|
|
||||||
{
|
|
||||||
mPlatform = platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCreateView()
|
|
||||||
{
|
|
||||||
loadGames();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh()
|
|
||||||
{
|
|
||||||
Log.debug("[PlatformGamesPresenter] " + mPlatform + ": Refreshing...");
|
|
||||||
loadGames();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadGames()
|
|
||||||
{
|
|
||||||
Log.debug("[PlatformGamesPresenter] " + mPlatform + ": Loading games...");
|
|
||||||
|
|
||||||
GameDatabase databaseHelper = DolphinApplication.databaseHelper;
|
|
||||||
|
|
||||||
databaseHelper.getGamesForPlatform(mPlatform)
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(games ->
|
|
||||||
{
|
|
||||||
Log.debug("[PlatformGamesPresenter] " + mPlatform + ": Load finished, swapping cursor...");
|
|
||||||
|
|
||||||
mView.showGames(games);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,14 @@
|
||||||
package org.dolphinemu.dolphinemu.ui.platform;
|
package org.dolphinemu.dolphinemu.ui.platform;
|
||||||
|
|
||||||
import android.database.Cursor;
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstraction for a screen representing a single platform's games.
|
* Abstraction for a screen representing a single platform's games.
|
||||||
*/
|
*/
|
||||||
public interface PlatformGamesView
|
public interface PlatformGamesView
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Tell the view to refresh its contents.
|
|
||||||
*/
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tell the view that a certain game's screenshot has been updated,
|
* Tell the view that a certain game's screenshot has been updated,
|
||||||
* and should be redrawn on-screen.
|
* and should be redrawn on-screen.
|
||||||
|
@ -29,10 +26,7 @@ public interface PlatformGamesView
|
||||||
void onItemClick(String gameId);
|
void onItemClick(String gameId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be called when an asynchronous database read completes. Passes the
|
* To be called when the game file cache is updated.
|
||||||
* result, in this case a {@link Cursor}, to the view.
|
|
||||||
*
|
|
||||||
* @param games A Cursor containing the games read from the database.
|
|
||||||
*/
|
*/
|
||||||
void showGames(Cursor games);
|
void showGames();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
package org.dolphinemu.dolphinemu.utils;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.DolphinApplication;
|
|
||||||
import org.dolphinemu.dolphinemu.model.GameDatabase;
|
|
||||||
|
|
||||||
public class AddDirectoryHelper
|
|
||||||
{
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
public interface AddDirectoryListener
|
|
||||||
{
|
|
||||||
void onDirectoryAdded();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddDirectoryHelper(Context context)
|
|
||||||
{
|
|
||||||
this.mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDirectory(String dir, AddDirectoryListener addDirectoryListener)
|
|
||||||
{
|
|
||||||
new AsyncTask<String, Void, Void>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(String... params)
|
|
||||||
{
|
|
||||||
for (String path : params)
|
|
||||||
{
|
|
||||||
DolphinApplication.databaseHelper.addGameFolder(path);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void result)
|
|
||||||
{
|
|
||||||
addDirectoryListener.onDirectoryAdded();
|
|
||||||
}
|
|
||||||
}.execute(dir);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,22 +7,28 @@ import com.squareup.picasso.Request;
|
||||||
import com.squareup.picasso.RequestHandler;
|
import com.squareup.picasso.RequestHandler;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.NativeLibrary;
|
import org.dolphinemu.dolphinemu.NativeLibrary;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
|
|
||||||
public class GameBannerRequestHandler extends RequestHandler {
|
public class GameBannerRequestHandler extends RequestHandler {
|
||||||
|
GameFile mGameFile;
|
||||||
|
|
||||||
|
public GameBannerRequestHandler(GameFile gameFile)
|
||||||
|
{
|
||||||
|
mGameFile = gameFile;
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean canHandleRequest(Request data) {
|
public boolean canHandleRequest(Request data) {
|
||||||
return "iso".equals(data.uri.getScheme());
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result load(Request request, int networkPolicy) throws IOException {
|
public Result load(Request request, int networkPolicy) {
|
||||||
String url = request.uri.getHost() + request.uri.getPath();
|
int[] vector = mGameFile.getBanner();
|
||||||
int[] vector = NativeLibrary.GetBanner(url);
|
int width = mGameFile.getBannerWidth();
|
||||||
int width = 96;
|
int height = mGameFile.getBannerHeight();
|
||||||
int height = 32;
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
bitmap.setPixels(vector, 0, width, 0, 0, width, height);
|
bitmap.setPixels(vector, 0, width, 0, 0, width, height);
|
||||||
return new Result(bitmap, Picasso.LoadedFrom.DISK);
|
return new Result(bitmap, Picasso.LoadedFrom.DISK);
|
||||||
|
|
|
@ -7,17 +7,18 @@ import android.widget.ImageView;
|
||||||
import com.squareup.picasso.Picasso;
|
import com.squareup.picasso.Picasso;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
public class PicassoUtils {
|
public class PicassoUtils {
|
||||||
public static void loadGameBanner(ImageView imageView, String screenshotPath, String gamePath) {
|
public static void loadGameBanner(ImageView imageView, GameFile gameFile) {
|
||||||
File file = new File(URI.create(screenshotPath));
|
File screenshotFile = new File(URI.create(gameFile.getScreenshotPath()));
|
||||||
if (file.exists()) {
|
if (screenshotFile.exists()) {
|
||||||
// Fill in the view contents.
|
// Fill in the view contents.
|
||||||
Picasso.with(imageView.getContext())
|
Picasso.with(imageView.getContext())
|
||||||
.load(screenshotPath)
|
.load(gameFile.getScreenshotPath())
|
||||||
.fit()
|
.fit()
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
.noFade()
|
.noFade()
|
||||||
|
@ -27,11 +28,11 @@ public class PicassoUtils {
|
||||||
.into(imageView);
|
.into(imageView);
|
||||||
} else {
|
} else {
|
||||||
Picasso picassoInstance = new Picasso.Builder(imageView.getContext())
|
Picasso picassoInstance = new Picasso.Builder(imageView.getContext())
|
||||||
.addRequestHandler(new GameBannerRequestHandler())
|
.addRequestHandler(new GameBannerRequestHandler(gameFile))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
picassoInstance
|
picassoInstance
|
||||||
.load(Uri.parse("iso:/" + gamePath))
|
.load(Uri.parse("iso:/" + gameFile.getPath()))
|
||||||
.fit()
|
.fit()
|
||||||
.noFade()
|
.noFade()
|
||||||
.noPlaceholder()
|
.noPlaceholder()
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.dolphinemu.dolphinemu.R;
|
import org.dolphinemu.dolphinemu.R;
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple class that stores references to views so that the GameAdapter doesn't need to
|
* A simple class that stores references to views so that the GameAdapter doesn't need to
|
||||||
|
@ -17,15 +18,7 @@ public class GameViewHolder extends RecyclerView.ViewHolder
|
||||||
public TextView textGameTitle;
|
public TextView textGameTitle;
|
||||||
public TextView textCompany;
|
public TextView textCompany;
|
||||||
|
|
||||||
public String gameId;
|
public GameFile gameFile;
|
||||||
|
|
||||||
// TODO Not need any of this stuff. Currently only the properties dialog needs it.
|
|
||||||
public String path;
|
|
||||||
public String title;
|
|
||||||
public String description;
|
|
||||||
public int country;
|
|
||||||
public String company;
|
|
||||||
public String screenshotPath;
|
|
||||||
|
|
||||||
public GameViewHolder(View itemView)
|
public GameViewHolder(View itemView)
|
||||||
{
|
{
|
||||||
|
@ -33,8 +26,8 @@ public class GameViewHolder extends RecyclerView.ViewHolder
|
||||||
|
|
||||||
itemView.setTag(this);
|
itemView.setTag(this);
|
||||||
|
|
||||||
imageScreenshot = (ImageView) itemView.findViewById(R.id.image_game_screen);
|
imageScreenshot = itemView.findViewById(R.id.image_game_screen);
|
||||||
textGameTitle = (TextView) itemView.findViewById(R.id.text_game_title);
|
textGameTitle = itemView.findViewById(R.id.text_game_title);
|
||||||
textCompany = (TextView) itemView.findViewById(R.id.text_company);
|
textCompany = itemView.findViewById(R.id.text_company);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ import android.support.v17.leanback.widget.Presenter;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import org.dolphinemu.dolphinemu.model.GameFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple class that stores references to views so that the GameAdapter doesn't need to
|
* A simple class that stores references to views so that the GameAdapter doesn't need to
|
||||||
* keep calling findViewById(), which is expensive.
|
* keep calling findViewById(), which is expensive.
|
||||||
|
@ -15,15 +17,7 @@ public final class TvGameViewHolder extends Presenter.ViewHolder
|
||||||
|
|
||||||
public ImageView imageScreenshot;
|
public ImageView imageScreenshot;
|
||||||
|
|
||||||
public String gameId;
|
public GameFile gameFile;
|
||||||
|
|
||||||
// TODO Not need any of this stuff. Currently only the properties dialog needs it.
|
|
||||||
public String path;
|
|
||||||
public String title;
|
|
||||||
public String description;
|
|
||||||
public int country;
|
|
||||||
public String company;
|
|
||||||
public String screenshotPath;
|
|
||||||
|
|
||||||
public TvGameViewHolder(View itemView)
|
public TvGameViewHolder(View itemView)
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
tools:text="Rhythm Heaven Fever"/>
|
tools:text="Rhythm Heaven Fever"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_company"
|
android:id="@+id/text_description"
|
||||||
style="@android:style/TextAppearance.Material.Caption"
|
style="@android:style/TextAppearance.Material.Caption"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
android:layout_height="1dp"
|
android:layout_height="1dp"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_below="@+id/text_company"
|
android:layout_below="@+id/text_description"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:background="#1F000000"/>
|
android:background="#1F000000"/>
|
||||||
|
|
||||||
|
@ -96,13 +96,13 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBottom="@+id/icon_country"
|
android:layout_alignBottom="@+id/icon_country"
|
||||||
android:layout_alignStart="@+id/text_company"
|
android:layout_alignStart="@+id/text_description"
|
||||||
android:layout_alignTop="@+id/icon_country"
|
android:layout_alignTop="@+id/icon_country"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
tools:text="United States"/>
|
tools:text="United States"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_date"
|
android:id="@+id/text_company"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBottom="@+id/icon_company"
|
android:layout_alignBottom="@+id/icon_company"
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
std::string GetJString(JNIEnv* env, jstring jstr)
|
||||||
|
{
|
||||||
|
std::string result = "";
|
||||||
|
if (!jstr)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
const char* s = env->GetStringUTFChars(jstr, nullptr);
|
||||||
|
result = s;
|
||||||
|
env->ReleaseStringUTFChars(jstr, s);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
jstring ToJString(JNIEnv* env, const std::string& str)
|
||||||
|
{
|
||||||
|
return env->NewStringUTF(str.c_str());
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
std::string GetJString(JNIEnv* env, jstring jstr);
|
||||||
|
jstring ToJString(JNIEnv* env, const std::string& str);
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "jni/AndroidCommon/IDCache.h"
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
static constexpr jint JNI_VERSION = JNI_VERSION_1_6;
|
||||||
|
|
||||||
|
static JavaVM* s_java_vm;
|
||||||
|
|
||||||
|
static jclass s_native_library_class;
|
||||||
|
static jmethodID s_display_alert_msg;
|
||||||
|
|
||||||
|
static jclass s_game_file_class;
|
||||||
|
static jfieldID s_game_file_pointer;
|
||||||
|
static jmethodID s_game_file_constructor;
|
||||||
|
|
||||||
|
static jclass s_game_file_cache_class;
|
||||||
|
static jfieldID s_game_file_cache_pointer;
|
||||||
|
|
||||||
|
namespace IDCache
|
||||||
|
{
|
||||||
|
JavaVM* GetJavaVM()
|
||||||
|
{
|
||||||
|
return s_java_vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetNativeLibraryClass()
|
||||||
|
{
|
||||||
|
return s_native_library_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetDisplayAlertMsg()
|
||||||
|
{
|
||||||
|
return s_display_alert_msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetGameFileClass()
|
||||||
|
{
|
||||||
|
return s_game_file_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetGameFilePointer()
|
||||||
|
{
|
||||||
|
return s_game_file_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
jmethodID GetGameFileConstructor()
|
||||||
|
{
|
||||||
|
return s_game_file_constructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
jclass GetGameFileCacheClass()
|
||||||
|
{
|
||||||
|
return s_game_file_cache_class;
|
||||||
|
}
|
||||||
|
|
||||||
|
jfieldID GetGameFileCachePointer()
|
||||||
|
{
|
||||||
|
return s_game_file_cache_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace IDCache
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
|
{
|
||||||
|
s_java_vm = vm;
|
||||||
|
|
||||||
|
JNIEnv* env;
|
||||||
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK)
|
||||||
|
return JNI_ERR;
|
||||||
|
|
||||||
|
const jclass native_library_class = env->FindClass("org/dolphinemu/dolphinemu/NativeLibrary");
|
||||||
|
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
|
||||||
|
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;Z)Z");
|
||||||
|
|
||||||
|
const jclass game_file_class = env->FindClass("org/dolphinemu/dolphinemu/model/GameFile");
|
||||||
|
s_game_file_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_class));
|
||||||
|
s_game_file_pointer = env->GetFieldID(game_file_class, "mPointer", "J");
|
||||||
|
s_game_file_constructor = env->GetMethodID(game_file_class, "<init>", "(J)V");
|
||||||
|
|
||||||
|
const jclass game_file_cache_class =
|
||||||
|
env->FindClass("org/dolphinemu/dolphinemu/model/GameFileCache");
|
||||||
|
s_game_file_cache_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_cache_class));
|
||||||
|
s_game_file_cache_pointer = env->GetFieldID(game_file_cache_class, "mPointer", "J");
|
||||||
|
|
||||||
|
return JNI_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JNI_OnUnload(JavaVM* vm, void* reserved)
|
||||||
|
{
|
||||||
|
JNIEnv* env;
|
||||||
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
env->DeleteGlobalRef(s_native_library_class);
|
||||||
|
env->DeleteGlobalRef(s_game_file_class);
|
||||||
|
env->DeleteGlobalRef(s_game_file_cache_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
namespace IDCache
|
||||||
|
{
|
||||||
|
JavaVM* GetJavaVM();
|
||||||
|
|
||||||
|
jclass GetNativeLibraryClass();
|
||||||
|
jmethodID GetDisplayAlertMsg();
|
||||||
|
|
||||||
|
jclass GetGameFileClass();
|
||||||
|
jfieldID GetGameFilePointer();
|
||||||
|
jmethodID GetGameFileConstructor();
|
||||||
|
|
||||||
|
jclass GetGameFileCacheClass();
|
||||||
|
jfieldID GetGameFileCachePointer();
|
||||||
|
|
||||||
|
} // namespace IDCache
|
|
@ -1,4 +1,8 @@
|
||||||
add_library(main SHARED
|
add_library(main SHARED
|
||||||
|
AndroidCommon/AndroidCommon.cpp
|
||||||
|
AndroidCommon/IDCache.cpp
|
||||||
|
GameList/GameFile.cpp
|
||||||
|
GameList/GameFileCache.cpp
|
||||||
ButtonManager.cpp
|
ButtonManager.cpp
|
||||||
MainAndroid.cpp
|
MainAndroid.cpp
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "jni/GameList/GameFile.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "DiscIO/Enums.h"
|
||||||
|
#include "UICommon/GameFile.h"
|
||||||
|
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||||
|
#include "jni/AndroidCommon/IDCache.h"
|
||||||
|
|
||||||
|
static std::shared_ptr<const UICommon::GameFile>* GetPointer(JNIEnv* env, jobject obj)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<std::shared_ptr<const UICommon::GameFile>*>(
|
||||||
|
env->GetLongField(obj, IDCache::GetGameFilePointer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<const UICommon::GameFile>& GetRef(JNIEnv* env, jobject obj)
|
||||||
|
{
|
||||||
|
return *GetPointer(env, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject GameFileToJava(JNIEnv* env, std::shared_ptr<const UICommon::GameFile> game_file)
|
||||||
|
{
|
||||||
|
if (!game_file)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return env->NewObject(
|
||||||
|
IDCache::GetGameFileClass(), IDCache::GetGameFileConstructor(),
|
||||||
|
reinterpret_cast<jlong>(new std::shared_ptr<const UICommon::GameFile>(std::move(game_file))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_finalize(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getPlatform(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getTitle(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getDescription(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCompany(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCountry(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,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jintArray JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBanner(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBannerWidth(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBannerHeight(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_finalize(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
delete GetPointer(env, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getPlatform(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return static_cast<jint>(GetRef(env, obj)->GetPlatform());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getTitle(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return ToJString(env, GetRef(env, obj)->GetName());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getDescription(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return ToJString(env, GetRef(env, obj)->GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCompany(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return ToJString(env, DiscIO::GetCompanyFromID(GetRef(env, obj)->GetMakerID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getCountry(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return static_cast<jint>(GetRef(env, obj)->GetCountry());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getPath(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return ToJString(env, GetRef(env, obj)->GetFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getGameId(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return ToJString(env, GetRef(env, obj)->GetGameID());
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jintArray JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBanner(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
const std::vector<u32>& buffer = GetRef(env, obj)->GetBannerImage().buffer;
|
||||||
|
const jsize size = static_cast<jsize>(buffer.size());
|
||||||
|
const jintArray out_array = env->NewIntArray(size);
|
||||||
|
if (!out_array)
|
||||||
|
return nullptr;
|
||||||
|
env->SetIntArrayRegion(out_array, 0, size, reinterpret_cast<const jint*>(buffer.data()));
|
||||||
|
return out_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBannerWidth(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return static_cast<jint>(GetRef(env, obj)->GetBannerImage().width);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBannerHeight(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return static_cast<jint>(GetRef(env, obj)->GetBannerImage().height);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,17 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
namespace UICommon
|
||||||
|
{
|
||||||
|
class GameFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject GameFileToJava(JNIEnv* env, std::shared_ptr<const UICommon::GameFile> game_file);
|
|
@ -0,0 +1,121 @@
|
||||||
|
// Copyright 2018 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <jni.h>
|
||||||
|
|
||||||
|
#include "UICommon/GameFileCache.h"
|
||||||
|
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||||
|
#include "jni/AndroidCommon/IDCache.h"
|
||||||
|
#include "jni/GameList/GameFile.h"
|
||||||
|
|
||||||
|
namespace UICommon
|
||||||
|
{
|
||||||
|
class GameFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
static UICommon::GameFileCache* GetPointer(JNIEnv* env, jobject obj)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<UICommon::GameFileCache*>(
|
||||||
|
env->GetLongField(obj, IDCache::GetGameFileCachePointer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_newGameFileCache(
|
||||||
|
JNIEnv* env, jobject obj, jstring path);
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_finalize(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_model_GameFileCache_getAllGames(JNIEnv* env, jobject obj);
|
||||||
|
JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_addOrGet(JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring path);
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_update(
|
||||||
|
JNIEnv* env, jobject obj, jobjectArray folder_paths);
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_model_GameFileCache_updateAdditionalMetadata(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_load(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_save(JNIEnv* env,
|
||||||
|
jobject obj);
|
||||||
|
|
||||||
|
JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_newGameFileCache(
|
||||||
|
JNIEnv* env, jobject obj, jstring path)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<jlong>(new UICommon::GameFileCache(GetJString(env, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_finalize(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
delete GetPointer(env, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jobjectArray JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_model_GameFileCache_getAllGames(JNIEnv* env, jobject obj)
|
||||||
|
{
|
||||||
|
const UICommon::GameFileCache* ptr = GetPointer(env, obj);
|
||||||
|
const jobjectArray array =
|
||||||
|
env->NewObjectArray(static_cast<jsize>(ptr->GetSize()), IDCache::GetGameFileClass(), nullptr);
|
||||||
|
jsize i = 0;
|
||||||
|
GetPointer(env, obj)->ForEach([env, array, &i](const auto& game_file) {
|
||||||
|
env->SetObjectArrayElement(array, i++, GameFileToJava(env, game_file));
|
||||||
|
});
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_addOrGet(JNIEnv* env,
|
||||||
|
jobject obj,
|
||||||
|
jstring path)
|
||||||
|
{
|
||||||
|
bool cache_changed = false;
|
||||||
|
return GameFileToJava(env, GetPointer(env, obj)->AddOrGet(GetJString(env, path), &cache_changed));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_update(
|
||||||
|
JNIEnv* env, jobject obj, jobjectArray folder_paths)
|
||||||
|
{
|
||||||
|
jsize size = env->GetArrayLength(folder_paths);
|
||||||
|
|
||||||
|
std::vector<std::string> folder_paths_vector;
|
||||||
|
folder_paths_vector.reserve(size);
|
||||||
|
|
||||||
|
for (jsize i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
const jstring path = reinterpret_cast<jstring>(env->GetObjectArrayElement(folder_paths, i));
|
||||||
|
folder_paths_vector.push_back(GetJString(env, path));
|
||||||
|
env->DeleteLocalRef(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetPointer(env, obj)->Update(UICommon::FindAllGamePaths(folder_paths_vector, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL
|
||||||
|
Java_org_dolphinemu_dolphinemu_model_GameFileCache_updateAdditionalMetadata(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return GetPointer(env, obj)->UpdateAdditionalMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_load(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return GetPointer(env, obj)->Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_model_GameFileCache_save(JNIEnv* env,
|
||||||
|
jobject obj)
|
||||||
|
{
|
||||||
|
return GetPointer(env, obj)->Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -16,8 +16,6 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "ButtonManager.h"
|
|
||||||
|
|
||||||
#include "Common/CPUDetect.h"
|
#include "Common/CPUDetect.h"
|
||||||
#include "Common/CommonPaths.h"
|
#include "Common/CommonPaths.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
@ -51,16 +49,15 @@
|
||||||
#include "VideoCommon/RenderBase.h"
|
#include "VideoCommon/RenderBase.h"
|
||||||
#include "VideoCommon/VideoBackendBase.h"
|
#include "VideoCommon/VideoBackendBase.h"
|
||||||
|
|
||||||
#define DOLPHIN_TAG "DolphinEmuNative"
|
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||||
|
#include "jni/AndroidCommon/IDCache.h"
|
||||||
JavaVM* g_java_vm;
|
#include "jni/ButtonManager.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
ANativeWindow* s_surf;
|
static constexpr char DOLPHIN_TAG[] = "DolphinEmuNative";
|
||||||
|
|
||||||
jclass s_jni_class;
|
ANativeWindow* s_surf;
|
||||||
jmethodID s_jni_method_alert;
|
|
||||||
|
|
||||||
// The Core only supports using a single Host thread.
|
// The Core only supports using a single Host thread.
|
||||||
// If multiple threads want to call host functions then they need to queue
|
// If multiple threads want to call host functions then they need to queue
|
||||||
|
@ -70,16 +67,6 @@ Common::Event s_update_main_frame_event;
|
||||||
bool s_have_wm_user_stop = false;
|
bool s_have_wm_user_stop = false;
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
/*
|
|
||||||
* Cache the JavaVM so that we can call into it later.
|
|
||||||
*/
|
|
||||||
jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|
||||||
{
|
|
||||||
g_java_vm = vm;
|
|
||||||
|
|
||||||
return JNI_VERSION_1_6;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Host_NotifyMapLoaded()
|
void Host_NotifyMapLoaded()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -156,229 +143,19 @@ static bool MsgAlert(const char* caption, const char* text, bool yes_no, MsgType
|
||||||
|
|
||||||
// Associate the current Thread with the Java VM.
|
// Associate the current Thread with the Java VM.
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
g_java_vm->AttachCurrentThread(&env, NULL);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, nullptr);
|
||||||
|
|
||||||
// Execute the Java method.
|
// Execute the Java method.
|
||||||
jboolean result =
|
jboolean result = env->CallStaticBooleanMethod(
|
||||||
env->CallStaticBooleanMethod(s_jni_class, s_jni_method_alert, env->NewStringUTF(caption),
|
IDCache::GetNativeLibraryClass(), IDCache::GetDisplayAlertMsg(), ToJString(env, caption),
|
||||||
env->NewStringUTF(text), yes_no ? JNI_TRUE : JNI_FALSE);
|
ToJString(env, text), yes_no ? JNI_TRUE : JNI_FALSE);
|
||||||
|
|
||||||
// Must be called before the current thread exits; might as well do it here.
|
// Must be called before the current thread exits; might as well do it here.
|
||||||
g_java_vm->DetachCurrentThread();
|
IDCache::GetJavaVM()->DetachCurrentThread();
|
||||||
|
|
||||||
return result != JNI_FALSE;
|
return result != JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DVD_BANNER_WIDTH 96
|
|
||||||
#define DVD_BANNER_HEIGHT 32
|
|
||||||
|
|
||||||
static inline u32 Average32(u32 a, u32 b)
|
|
||||||
{
|
|
||||||
return ((a >> 1) & 0x7f7f7f7f) + ((b >> 1) & 0x7f7f7f7f);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 GetPixel(u32* buffer, unsigned int x, unsigned int y)
|
|
||||||
{
|
|
||||||
// thanks to unsignedness, these also check for <0 automatically.
|
|
||||||
if (x > 191)
|
|
||||||
return 0;
|
|
||||||
if (y > 63)
|
|
||||||
return 0;
|
|
||||||
return buffer[y * 192 + x];
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool LoadBanner(std::string filename, u32* Banner)
|
|
||||||
{
|
|
||||||
std::unique_ptr<DiscIO::Volume> pVolume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
|
|
||||||
if (pVolume != nullptr)
|
|
||||||
{
|
|
||||||
u32 Width, Height;
|
|
||||||
std::vector<u32> BannerVec = pVolume->GetBanner(&Width, &Height);
|
|
||||||
// This code (along with above inlines) is moved from
|
|
||||||
// elsewhere. Someone who knows anything about Android
|
|
||||||
// please get rid of it and use proper high-resolution
|
|
||||||
// images.
|
|
||||||
if (Height == 64 && Width == 192)
|
|
||||||
{
|
|
||||||
u32* Buffer = &BannerVec[0];
|
|
||||||
for (int y = 0; y < 32; y++)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < 96; x++)
|
|
||||||
{
|
|
||||||
// simplified plus-shaped "gaussian"
|
|
||||||
u32 surround = Average32(
|
|
||||||
Average32(GetPixel(Buffer, x * 2 - 1, y * 2), GetPixel(Buffer, x * 2 + 1, y * 2)),
|
|
||||||
Average32(GetPixel(Buffer, x * 2, y * 2 - 1), GetPixel(Buffer, x * 2, y * 2 + 1)));
|
|
||||||
Banner[y * 96 + x] = Average32(GetPixel(Buffer, x * 2, y * 2), surround);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (Height == 32 && Width == 96)
|
|
||||||
{
|
|
||||||
memcpy(Banner, &BannerVec[0], 96 * 32 * 4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GetCountry(std::string filename)
|
|
||||||
{
|
|
||||||
std::unique_ptr<DiscIO::Volume> pVolume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
|
|
||||||
if (pVolume != nullptr)
|
|
||||||
{
|
|
||||||
int country = static_cast<int>(pVolume->GetCountry());
|
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Country Code: %i", country);
|
|
||||||
|
|
||||||
return country;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<int>(DiscIO::Country::Unknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GetPlatform(std::string filename)
|
|
||||||
{
|
|
||||||
std::unique_ptr<DiscIO::Volume> pVolume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
|
|
||||||
if (pVolume != nullptr)
|
|
||||||
{
|
|
||||||
switch (pVolume->GetVolumeType())
|
|
||||||
{
|
|
||||||
case DiscIO::Platform::GameCubeDisc:
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Volume is a GameCube disc.");
|
|
||||||
return 0;
|
|
||||||
case DiscIO::Platform::WiiDisc:
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Volume is a Wii disc.");
|
|
||||||
return 1;
|
|
||||||
case DiscIO::Platform::WiiWAD:
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Volume is a Wii WAD.");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetTitle(std::string filename)
|
|
||||||
{
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting Title for file: %s",
|
|
||||||
filename.c_str());
|
|
||||||
|
|
||||||
std::unique_ptr<DiscIO::Volume> pVolume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
|
|
||||||
if (pVolume != nullptr)
|
|
||||||
{
|
|
||||||
std::map<DiscIO::Language, std::string> titles = pVolume->GetLongNames();
|
|
||||||
if (titles.empty())
|
|
||||||
titles = pVolume->GetShortNames();
|
|
||||||
|
|
||||||
auto end = titles.end();
|
|
||||||
|
|
||||||
// English tends to be a good fallback when the requested language isn't available
|
|
||||||
// if (language != DiscIO::Language::English) {
|
|
||||||
auto it = titles.find(DiscIO::Language::English);
|
|
||||||
if (it != end)
|
|
||||||
return it->second;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// If English isn't available either, just pick something
|
|
||||||
if (!titles.empty())
|
|
||||||
return titles.cbegin()->second;
|
|
||||||
|
|
||||||
// No usable name, return filename (better than nothing)
|
|
||||||
std::string name;
|
|
||||||
SplitPath(filename, nullptr, &name, nullptr);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string("");
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetDescription(std::string filename)
|
|
||||||
{
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting Description for file: %s",
|
|
||||||
filename.c_str());
|
|
||||||
|
|
||||||
std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
|
|
||||||
if (volume != nullptr)
|
|
||||||
{
|
|
||||||
std::map<DiscIO::Language, std::string> descriptions = volume->GetDescriptions();
|
|
||||||
|
|
||||||
auto end = descriptions.end();
|
|
||||||
|
|
||||||
// English tends to be a good fallback when the requested language isn't available
|
|
||||||
// if (language != DiscIO::Language::English) {
|
|
||||||
auto it = descriptions.find(DiscIO::Language::English);
|
|
||||||
if (it != end)
|
|
||||||
return it->second;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// If English isn't available either, just pick something
|
|
||||||
if (!descriptions.empty())
|
|
||||||
return descriptions.cbegin()->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetGameId(std::string filename)
|
|
||||||
{
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting ID for file: %s", filename.c_str());
|
|
||||||
|
|
||||||
std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
if (volume == nullptr)
|
|
||||||
return std::string();
|
|
||||||
|
|
||||||
std::string id = volume->GetGameID();
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Game ID: %s", id.c_str());
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetCompany(std::string filename)
|
|
||||||
{
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting Company for file: %s",
|
|
||||||
filename.c_str());
|
|
||||||
|
|
||||||
std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
if (volume == nullptr)
|
|
||||||
return std::string();
|
|
||||||
|
|
||||||
std::string company = DiscIO::GetCompanyFromID(volume->GetMakerID());
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Company: %s", company.c_str());
|
|
||||||
return company;
|
|
||||||
}
|
|
||||||
|
|
||||||
static u64 GetFileSize(std::string filename)
|
|
||||||
{
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting size of file: %s", filename.c_str());
|
|
||||||
|
|
||||||
std::unique_ptr<DiscIO::Volume> volume(DiscIO::CreateVolumeFromFilename(filename));
|
|
||||||
if (volume == nullptr)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
u64 size = volume->GetSize();
|
|
||||||
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Size: %" PRIu64, size);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string GetJString(JNIEnv* env, jstring jstr)
|
|
||||||
{
|
|
||||||
std::string result = "";
|
|
||||||
if (!jstr)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
const char* s = env->GetStringUTFChars(jstr, nullptr);
|
|
||||||
result = s;
|
|
||||||
env->ReleaseStringUTFChars(jstr, s);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
@ -395,28 +172,6 @@ JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePa
|
||||||
JNIEnv* env, jobject obj, jstring jDevice, jint Button, jint Action);
|
JNIEnv* env, jobject obj, jstring jDevice, jint Button, jint Action);
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
|
||||||
JNIEnv* env, jobject obj, jstring jDevice, jint Axis, jfloat Value);
|
JNIEnv* env, jobject obj, jstring jDevice, jint Axis, jfloat Value);
|
||||||
JNIEXPORT jintArray JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetBanner(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFile);
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetTitle(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename);
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetDescription(
|
|
||||||
JNIEnv* env, jobject obj, jstring jFilename);
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameId(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename);
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetCountry(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename);
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetCompany(
|
|
||||||
JNIEnv* env, jobject obj, jstring jFilename);
|
|
||||||
JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetFilesize(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename);
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetPlatform(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename);
|
|
||||||
JNIEXPORT jstring JNICALL
|
JNIEXPORT jstring JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env, jobject obj);
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env, jobject obj);
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
||||||
|
@ -461,8 +216,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling
|
||||||
jboolean enable);
|
jboolean enable);
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj);
|
Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv* env, jobject obj);
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv* env, jobject obj);
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2(
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run__Ljava_lang_String_2(
|
||||||
JNIEnv* env, jobject obj, jstring jFile);
|
JNIEnv* env, jobject obj, jstring jFile);
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
|
@ -518,93 +271,16 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMov
|
||||||
ButtonManager::GamepadAxisEvent(GetJString(env, jDevice), Axis, Value);
|
ButtonManager::GamepadAxisEvent(GetJString(env, jDevice), Axis, Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jintArray JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetBanner(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFile)
|
|
||||||
{
|
|
||||||
std::string file = GetJString(env, jFile);
|
|
||||||
u32 uBanner[DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT];
|
|
||||||
jintArray Banner = env->NewIntArray(DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT);
|
|
||||||
|
|
||||||
if (LoadBanner(file, uBanner))
|
|
||||||
{
|
|
||||||
env->SetIntArrayRegion(Banner, 0, DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT, (jint*)uBanner);
|
|
||||||
}
|
|
||||||
return Banner;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetTitle(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
std::string name = GetTitle(filename);
|
|
||||||
return env->NewStringUTF(name.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetDescription(
|
|
||||||
JNIEnv* env, jobject obj, jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
std::string description = GetDescription(filename);
|
|
||||||
return env->NewStringUTF(description.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameId(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
std::string id = GetGameId(filename);
|
|
||||||
return env->NewStringUTF(id.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetCompany(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
std::string company = GetCompany(filename);
|
|
||||||
return env->NewStringUTF(company.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetCountry(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
int country = GetCountry(filename);
|
|
||||||
return country;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetFilesize(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
u64 size = GetFileSize(filename);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetPlatform(JNIEnv* env,
|
|
||||||
jobject obj,
|
|
||||||
jstring jFilename)
|
|
||||||
{
|
|
||||||
std::string filename = GetJString(env, jFilename);
|
|
||||||
int platform = GetPlatform(filename);
|
|
||||||
return platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env,
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env,
|
||||||
jobject obj)
|
jobject obj)
|
||||||
{
|
{
|
||||||
return env->NewStringUTF(Common::scm_rev_str.c_str());
|
return ToJString(env, Common::scm_rev_str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGitRevision(JNIEnv* env,
|
||||||
jobject obj)
|
jobject obj)
|
||||||
{
|
{
|
||||||
return env->NewStringUTF(Common::scm_rev_git_str.c_str());
|
return ToJString(env, Common::scm_rev_git_str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv* env,
|
||||||
|
@ -646,7 +322,7 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserSe
|
||||||
|
|
||||||
ini.GetOrCreateSection(section)->Get(key, &value, "-1");
|
ini.GetOrCreateSection(section)->Get(key, &value, "-1");
|
||||||
|
|
||||||
return env->NewStringUTF(value.c_str());
|
return ToJString(env, value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserSetting(
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserSetting(
|
||||||
|
@ -686,8 +362,9 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetConfig
|
||||||
|
|
||||||
ini.GetOrCreateSection(section)->Get(key, &value, defaultValue);
|
ini.GetOrCreateSection(section)->Get(key, &value, defaultValue);
|
||||||
|
|
||||||
return env->NewStringUTF(value.c_str());
|
return ToJString(env, value.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig(
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetConfig(
|
||||||
JNIEnv* env, jobject obj, jstring jFile, jstring jSection, jstring jKey, jstring jValue)
|
JNIEnv* env, jobject obj, jstring jFile, jstring jSection, jstring jKey, jstring jValue)
|
||||||
{
|
{
|
||||||
|
@ -762,7 +439,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirec
|
||||||
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserDirectory(JNIEnv* env,
|
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetUserDirectory(JNIEnv* env,
|
||||||
jobject obj)
|
jobject obj)
|
||||||
{
|
{
|
||||||
return env->NewStringUTF(File::GetUserPath(D_USER_IDX).c_str());
|
return ToJString(env, File::GetUserPath(D_USER_IDX).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
|
||||||
|
@ -791,25 +468,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfile
|
||||||
JitInterface::WriteProfileResults(filename);
|
JitInterface::WriteProfileResults(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_org_dolphinemu_dolphinemu_NativeLibrary_CacheClassesAndMethods(JNIEnv* env, jobject obj)
|
|
||||||
{
|
|
||||||
// This class reference is only valid for the lifetime of this method.
|
|
||||||
jclass localClass = env->FindClass("org/dolphinemu/dolphinemu/NativeLibrary");
|
|
||||||
|
|
||||||
// This reference, however, is valid until we delete it.
|
|
||||||
s_jni_class = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
|
|
||||||
|
|
||||||
// TODO Find a place for this.
|
|
||||||
// So we don't leak a reference to NativeLibrary.class.
|
|
||||||
// env->DeleteGlobalRef(s_jni_class);
|
|
||||||
|
|
||||||
// Method signature taken from javap -s
|
|
||||||
// Source/Android/app/build/intermediates/classes/arm/debug/org/dolphinemu/dolphinemu/NativeLibrary.class
|
|
||||||
s_jni_method_alert = env->GetStaticMethodID(s_jni_class, "displayAlertMsg",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Z)Z");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Surface Handling
|
// Surface Handling
|
||||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceChanged(JNIEnv* env,
|
||||||
jobject obj,
|
jobject obj,
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
|
|
||||||
#include "Core/HW/WiimoteReal/IOAndroid.h"
|
#include "Core/HW/WiimoteReal/IOAndroid.h"
|
||||||
|
|
||||||
// Global java_vm class
|
#include "jni/AndroidCommon/IDCache.h"
|
||||||
extern JavaVM* g_java_vm;
|
|
||||||
|
|
||||||
namespace WiimoteReal
|
namespace WiimoteReal
|
||||||
{
|
{
|
||||||
|
@ -31,10 +30,11 @@ void WiimoteScannerAndroid::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||||
NOTICE_LOG(WIIMOTE, "Finding Wiimotes");
|
NOTICE_LOG(WIIMOTE, "Finding Wiimotes");
|
||||||
|
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
int get_env_status = g_java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
int get_env_status =
|
||||||
|
IDCache::GetJavaVM()->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
||||||
|
|
||||||
if (get_env_status == JNI_EDETACHED)
|
if (get_env_status == JNI_EDETACHED)
|
||||||
g_java_vm->AttachCurrentThread(&env, nullptr);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, nullptr);
|
||||||
|
|
||||||
jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "OpenAdapter", "()Z");
|
jmethodID openadapter_func = env->GetStaticMethodID(s_adapter_class, "OpenAdapter", "()Z");
|
||||||
jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z");
|
jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z");
|
||||||
|
@ -47,7 +47,7 @@ void WiimoteScannerAndroid::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_env_status == JNI_EDETACHED)
|
if (get_env_status == JNI_EDETACHED)
|
||||||
g_java_vm->DetachCurrentThread();
|
IDCache::GetJavaVM()->DetachCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
WiimoteAndroid::WiimoteAndroid(int index) : Wiimote(), m_mayflash_index(index)
|
WiimoteAndroid::WiimoteAndroid(int index) : Wiimote(), m_mayflash_index(index)
|
||||||
|
@ -62,7 +62,7 @@ WiimoteAndroid::~WiimoteAndroid()
|
||||||
// Connect to a Wiimote with a known address.
|
// Connect to a Wiimote with a known address.
|
||||||
bool WiimoteAndroid::ConnectInternal()
|
bool WiimoteAndroid::ConnectInternal()
|
||||||
{
|
{
|
||||||
g_java_vm->AttachCurrentThread(&m_env, nullptr);
|
IDCache::GetJavaVM()->AttachCurrentThread(&m_env, nullptr);
|
||||||
|
|
||||||
jfieldID payload_field = m_env->GetStaticFieldID(s_adapter_class, "wiimote_payload", "[[B");
|
jfieldID payload_field = m_env->GetStaticFieldID(s_adapter_class, "wiimote_payload", "[[B");
|
||||||
jobjectArray payload_object =
|
jobjectArray payload_object =
|
||||||
|
@ -81,7 +81,7 @@ bool WiimoteAndroid::ConnectInternal()
|
||||||
|
|
||||||
void WiimoteAndroid::DisconnectInternal()
|
void WiimoteAndroid::DisconnectInternal()
|
||||||
{
|
{
|
||||||
g_java_vm->DetachCurrentThread();
|
IDCache::GetJavaVM()->DetachCurrentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WiimoteAndroid::IsConnected() const
|
bool WiimoteAndroid::IsConnected() const
|
||||||
|
@ -117,7 +117,7 @@ int WiimoteAndroid::IOWrite(u8 const* buf, size_t len)
|
||||||
void InitAdapterClass()
|
void InitAdapterClass()
|
||||||
{
|
{
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
g_java_vm->AttachCurrentThread(&env, nullptr);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, nullptr);
|
||||||
|
|
||||||
jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter");
|
jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter");
|
||||||
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
|
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
|
||||||
|
|
|
@ -20,8 +20,7 @@
|
||||||
#include "InputCommon/GCAdapter.h"
|
#include "InputCommon/GCAdapter.h"
|
||||||
#include "InputCommon/GCPadStatus.h"
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|
||||||
// Global java_vm class
|
#include "jni/AndroidCommon/IDCache.h"
|
||||||
extern JavaVM* g_java_vm;
|
|
||||||
|
|
||||||
namespace GCAdapter
|
namespace GCAdapter
|
||||||
{
|
{
|
||||||
|
@ -67,7 +66,7 @@ static void ScanThreadFunc()
|
||||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started");
|
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread started");
|
||||||
|
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
g_java_vm->AttachCurrentThread(&env, NULL);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, NULL);
|
||||||
|
|
||||||
jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z");
|
jmethodID queryadapter_func = env->GetStaticMethodID(s_adapter_class, "QueryAdapter", "()Z");
|
||||||
|
|
||||||
|
@ -78,7 +77,7 @@ static void ScanThreadFunc()
|
||||||
Setup();
|
Setup();
|
||||||
Common::SleepCurrentThread(1000);
|
Common::SleepCurrentThread(1000);
|
||||||
}
|
}
|
||||||
g_java_vm->DetachCurrentThread();
|
IDCache::GetJavaVM()->DetachCurrentThread();
|
||||||
|
|
||||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped");
|
NOTICE_LOG(SERIALINTERFACE, "GC Adapter scanning thread stopped");
|
||||||
}
|
}
|
||||||
|
@ -89,7 +88,7 @@ static void Write()
|
||||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread started");
|
NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread started");
|
||||||
|
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
g_java_vm->AttachCurrentThread(&env, NULL);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, NULL);
|
||||||
jmethodID output_func = env->GetStaticMethodID(s_adapter_class, "Output", "([B)I");
|
jmethodID output_func = env->GetStaticMethodID(s_adapter_class, "Output", "([B)I");
|
||||||
|
|
||||||
while (s_write_adapter_thread_running.IsSet())
|
while (s_write_adapter_thread_running.IsSet())
|
||||||
|
@ -119,7 +118,7 @@ static void Write()
|
||||||
Common::YieldCPU();
|
Common::YieldCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_java_vm->DetachCurrentThread();
|
IDCache::GetJavaVM()->DetachCurrentThread();
|
||||||
|
|
||||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread stopped");
|
NOTICE_LOG(SERIALINTERFACE, "GC Adapter write thread stopped");
|
||||||
}
|
}
|
||||||
|
@ -131,7 +130,7 @@ static void Read()
|
||||||
|
|
||||||
bool first_read = true;
|
bool first_read = true;
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
g_java_vm->AttachCurrentThread(&env, NULL);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, NULL);
|
||||||
|
|
||||||
jfieldID payload_field = env->GetStaticFieldID(s_adapter_class, "controller_payload", "[B");
|
jfieldID payload_field = env->GetStaticFieldID(s_adapter_class, "controller_payload", "[B");
|
||||||
jobject payload_object = env->GetStaticObjectField(s_adapter_class, payload_field);
|
jobject payload_object = env->GetStaticObjectField(s_adapter_class, payload_field);
|
||||||
|
@ -185,7 +184,7 @@ static void Read()
|
||||||
s_fd = 0;
|
s_fd = 0;
|
||||||
s_detected = false;
|
s_detected = false;
|
||||||
|
|
||||||
g_java_vm->DetachCurrentThread();
|
IDCache::GetJavaVM()->DetachCurrentThread();
|
||||||
|
|
||||||
NOTICE_LOG(SERIALINTERFACE, "GC Adapter read thread stopped");
|
NOTICE_LOG(SERIALINTERFACE, "GC Adapter read thread stopped");
|
||||||
}
|
}
|
||||||
|
@ -204,7 +203,7 @@ void Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv* env;
|
JNIEnv* env;
|
||||||
g_java_vm->AttachCurrentThread(&env, NULL);
|
IDCache::GetJavaVM()->AttachCurrentThread(&env, NULL);
|
||||||
|
|
||||||
jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_GCAdapter");
|
jclass adapter_class = env->FindClass("org/dolphinemu/dolphinemu/utils/Java_GCAdapter");
|
||||||
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
|
s_adapter_class = reinterpret_cast<jclass>(env->NewGlobalRef(adapter_class));
|
||||||
|
|
|
@ -78,8 +78,15 @@ const std::string& GameFile::Lookup(DiscIO::Language language,
|
||||||
const std::string&
|
const std::string&
|
||||||
GameFile::LookupUsingConfigLanguage(const std::map<DiscIO::Language, std::string>& strings) const
|
GameFile::LookupUsingConfigLanguage(const std::map<DiscIO::Language, std::string>& strings) const
|
||||||
{
|
{
|
||||||
|
#ifdef ANDROID
|
||||||
|
// TODO: Make the Android app load the config at app start instead of emulation start
|
||||||
|
// so that we can access the user's preference here
|
||||||
|
const DiscIO::Language language = DiscIO::Language::English;
|
||||||
|
#else
|
||||||
const bool wii = DiscIO::IsWii(m_platform);
|
const bool wii = DiscIO::IsWii(m_platform);
|
||||||
return Lookup(SConfig::GetInstance().GetCurrentLanguage(wii), strings);
|
const DiscIO::Language language = SConfig::GetInstance().GetCurrentLanguage(wii);
|
||||||
|
#endif
|
||||||
|
return Lookup(language, strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameFile::GameFile(const std::string& path)
|
GameFile::GameFile(const std::string& path)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
|
@ -38,12 +39,25 @@ std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& direct
|
||||||
return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);
|
return Common::DoFileSearch(directories_to_scan, search_extensions, recursive_scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GameFileCache::GameFileCache() : m_path(File::GetUserPath(D_CACHE_IDX) + "gamelist.cache")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GameFileCache::GameFileCache(std::string path) : m_path(std::move(path))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void GameFileCache::ForEach(std::function<void(const std::shared_ptr<const GameFile>&)> f) const
|
void GameFileCache::ForEach(std::function<void(const std::shared_ptr<const GameFile>&)> f) const
|
||||||
{
|
{
|
||||||
for (const std::shared_ptr<const GameFile>& item : m_cached_files)
|
for (const std::shared_ptr<const GameFile>& item : m_cached_files)
|
||||||
f(item);
|
f(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t GameFileCache::GetSize() const
|
||||||
|
{
|
||||||
|
return m_cached_files.size();
|
||||||
|
}
|
||||||
|
|
||||||
void GameFileCache::Clear()
|
void GameFileCache::Clear()
|
||||||
{
|
{
|
||||||
m_cached_files.clear();
|
m_cached_files.clear();
|
||||||
|
@ -179,9 +193,8 @@ bool GameFileCache::Save()
|
||||||
|
|
||||||
bool GameFileCache::SyncCacheFile(bool save)
|
bool GameFileCache::SyncCacheFile(bool save)
|
||||||
{
|
{
|
||||||
std::string filename(File::GetUserPath(D_CACHE_IDX) + "gamelist.cache");
|
|
||||||
const char* open_mode = save ? "wb" : "rb";
|
const char* open_mode = save ? "wb" : "rb";
|
||||||
File::IOFile f(filename, open_mode);
|
File::IOFile f(m_path, open_mode);
|
||||||
if (!f)
|
if (!f)
|
||||||
return false;
|
return false;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
@ -217,7 +230,7 @@ bool GameFileCache::SyncCacheFile(bool save)
|
||||||
{
|
{
|
||||||
// If some file operation failed, try to delete the probably-corrupted cache
|
// If some file operation failed, try to delete the probably-corrupted cache
|
||||||
f.Close();
|
f.Close();
|
||||||
File::Delete(filename);
|
File::Delete(m_path);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,7 @@
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -26,8 +24,12 @@ std::vector<std::string> FindAllGamePaths(const std::vector<std::string>& direct
|
||||||
class GameFileCache
|
class GameFileCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
GameFileCache(); // Uses the default path
|
||||||
|
explicit GameFileCache(std::string path);
|
||||||
|
|
||||||
void ForEach(std::function<void(const std::shared_ptr<const GameFile>&)> f) const;
|
void ForEach(std::function<void(const std::shared_ptr<const GameFile>&)> f) const;
|
||||||
|
|
||||||
|
size_t GetSize() const;
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
// Returns nullptr if the file is invalid.
|
// Returns nullptr if the file is invalid.
|
||||||
|
@ -49,6 +51,7 @@ private:
|
||||||
bool SyncCacheFile(bool save);
|
bool SyncCacheFile(bool save);
|
||||||
void DoState(PointerWrap* p, u64 size = 0);
|
void DoState(PointerWrap* p, u64 size = 0);
|
||||||
|
|
||||||
|
std::string m_path;
|
||||||
std::vector<std::shared_ptr<GameFile>> m_cached_files;
|
std::vector<std::shared_ptr<GameFile>> m_cached_files;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue