diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml
index 7976a1cc8d..78548b4c6c 100644
--- a/Source/Android/app/src/main/AndroidManifest.xml
+++ b/Source/Android/app/src/main/AndroidManifest.xml
@@ -48,20 +48,24 @@
+
+
@@ -69,6 +73,13 @@
+
+
+
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
index e7d2e2b328..a47ff9c8ed 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java
@@ -132,9 +132,13 @@ public final class NativeLibrary
public static native String GetDescription(String filename);
public static native String GetGameId(String filename);
- public static native String GetDate(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 boolean IsWiiTitle(String filename);
+
+ public static native int GetPlatform(String filename);
/**
* Gets the Dolphin version string.
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java
index 1c536d8f91..1cbc30e7e5 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/AddDirectoryActivity.java
@@ -1,7 +1,10 @@
package org.dolphinemu.dolphinemu.activities;
import android.app.Activity;
+import android.content.AsyncQueryHandler;
+import android.content.ContentValues;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.widget.LinearLayoutManager;
@@ -14,6 +17,8 @@ import android.widget.Toolbar;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.FileAdapter;
+import org.dolphinemu.dolphinemu.model.GameDatabase;
+import org.dolphinemu.dolphinemu.model.GameProvider;
/**
* An Activity that shows a list of files and folders, allowing the user to tell the app which folder(s)
@@ -91,17 +96,36 @@ public class AddDirectoryActivity extends Activity implements FileAdapter.FileCl
}
/**
- * Tell the GameGridActivity that launched this Activity that the user picked a folder.
+ * Add a directory to the library, and if successful, end the activity.
+ *
+ * @param path The target directory's path.
*/
@Override
- public void finishSuccessfully()
+ public void addDirectory()
{
- Intent resultData = new Intent();
+ // Set up a callback for when the addition is complete
+ // TODO This has a nasty warning on it; find a cleaner way to do this Insert asynchronously
+ AsyncQueryHandler handler = new AsyncQueryHandler(getContentResolver())
+ {
+ @Override
+ protected void onInsertComplete(int token, Object cookie, Uri uri)
+ {
+ Intent resultData = new Intent();
- resultData.putExtra(KEY_CURRENT_PATH, mAdapter.getPath());
- setResult(RESULT_OK, resultData);
+ resultData.putExtra(KEY_CURRENT_PATH, mAdapter.getPath());
+ setResult(RESULT_OK, resultData);
- finish();
+ finish();
+ }
+ };
+
+ ContentValues file = new ContentValues();
+ file.put(GameDatabase.KEY_FOLDER_PATH, mAdapter.getPath());
+
+ handler.startInsert(0, // We don't need to identify this call to the handler
+ null, // We don't need to pass additional data to the handler
+ GameProvider.URI_FOLDER, // Tell the GameProvider we are adding a folder
+ file); // Tell the GameProvider what folder we are adding
}
@Override
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
new file mode 100644
index 0000000000..de7ebb17d3
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
@@ -0,0 +1,292 @@
+package org.dolphinemu.dolphinemu.activities;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.dolphinemu.dolphinemu.NativeLibrary;
+import org.dolphinemu.dolphinemu.R;
+import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
+import org.dolphinemu.dolphinemu.settings.input.InputConfigFragment;
+
+import java.util.List;
+
+public final class EmulationActivity extends Activity
+{
+ private View mDecorView;
+
+ /**
+ * Handlers are a way to pass a message to an Activity telling it to do something
+ * on the UI thread. This Handler responds to any message, even blank ones, by
+ * hiding the system UI.
+ */
+ private Handler mSystemUiHider = new Handler()
+ {
+ @Override
+ public void handleMessage(Message msg)
+ {
+ hideSystemUI();
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ // Get a handle to the Window containing the UI.
+ mDecorView = getWindow().getDecorView();
+
+ // Set these options now so that the SurfaceView the game renders into is the right size.
+ mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+
+ // Set the ActionBar to follow the navigation/status bar's visibility changes.
+ mDecorView.setOnSystemUiVisibilityChangeListener(
+ new View.OnSystemUiVisibilityChangeListener()
+ {
+ @Override
+ public void onSystemUiVisibilityChange(int flags)
+ {
+ boolean visible = (flags & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+
+ if (visible)
+ {
+ getActionBar().show();
+ hideSystemUiAfterDelay();
+ }
+ else
+ {
+ getActionBar().hide();
+ }
+ }
+ }
+ );
+
+ setContentView(R.layout.activity_emulation);
+
+ Intent gameToEmulate = getIntent();
+ String path = gameToEmulate.getStringExtra("SelectedGame");
+ String title = gameToEmulate.getStringExtra("SelectedTitle");
+
+ setTitle(title);
+
+ // Instantiate an EmulationFragment.
+ EmulationFragment emulationFragment = EmulationFragment.newInstance(path);
+
+ // Add fragment to the activity - this triggers all its lifecycle callbacks.
+ getFragmentManager().beginTransaction()
+ .add(R.id.frame_content, emulationFragment, EmulationFragment.FRAGMENT_TAG)
+ .commit();
+ }
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState)
+ {
+ super.onPostCreate(savedInstanceState);
+
+ // Give the user a few seconds to see what the controls look like, then hide them.
+ hideSystemUiAfterDelay();
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus)
+ {
+ super.onWindowFocusChanged(hasFocus);
+
+ if (hasFocus)
+ {
+ hideSystemUiAfterDelay();
+ }
+ else
+ {
+ // If the window loses focus (i.e. a dialog box, or a popup menu is on screen
+ // stop hiding the UI.
+ mSystemUiHider.removeMessages(0);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_emulation, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ // Enable/Disable input overlay.
+ case R.id.enableInputOverlay:
+ {
+ EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager()
+ .findFragmentByTag(EmulationFragment.FRAGMENT_TAG);
+
+ emulationFragment.toggleInputOverlayVisibility();
+
+ return true;
+ }
+
+ // Screenshot capturing
+ case R.id.takeScreenshot:
+ NativeLibrary.SaveScreenShot();
+ return true;
+
+ // Save state slots
+ case R.id.saveSlot1:
+ NativeLibrary.SaveState(0);
+ return true;
+
+ case R.id.saveSlot2:
+ NativeLibrary.SaveState(1);
+ return true;
+
+ case R.id.saveSlot3:
+ NativeLibrary.SaveState(2);
+ return true;
+
+ case R.id.saveSlot4:
+ NativeLibrary.SaveState(3);
+ return true;
+
+ case R.id.saveSlot5:
+ NativeLibrary.SaveState(4);
+ return true;
+
+ // Load state slots
+ case R.id.loadSlot1:
+ NativeLibrary.LoadState(0);
+ return true;
+
+ case R.id.loadSlot2:
+ NativeLibrary.LoadState(1);
+ return true;
+
+ case R.id.loadSlot3:
+ NativeLibrary.LoadState(2);
+ return true;
+
+ case R.id.loadSlot4:
+ NativeLibrary.LoadState(3);
+ return true;
+
+ case R.id.loadSlot5:
+ NativeLibrary.LoadState(4);
+ return true;
+
+ case R.id.exitEmulation:
+ {
+ // Create a confirmation method for quitting the current emulation instance.
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.overlay_exit_emulation);
+ builder.setMessage(R.string.overlay_exit_emulation_confirm);
+ builder.setNegativeButton(R.string.no, null);
+ builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int which)
+ {
+ onDestroy();
+ }
+ });
+ builder.show();
+ return true;
+ }
+
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ // Gets button presses
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event)
+ {
+ int action = 0;
+
+ switch (event.getAction())
+ {
+ case KeyEvent.ACTION_DOWN:
+ // Handling the case where the back button is pressed.
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK)
+ {
+ onBackPressed();
+ return true;
+ }
+
+ // Normal key events.
+ action = NativeLibrary.ButtonState.PRESSED;
+ break;
+ case KeyEvent.ACTION_UP:
+ action = NativeLibrary.ButtonState.RELEASED;
+ break;
+ default:
+ return false;
+ }
+ InputDevice input = event.getDevice();
+ boolean handled = NativeLibrary.onGamePadEvent(InputConfigFragment.getInputDesc(input), event.getKeyCode(), action);
+ return handled;
+ }
+
+ @Override
+ public boolean dispatchGenericMotionEvent(MotionEvent event)
+ {
+ if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0))
+ {
+ return super.dispatchGenericMotionEvent(event);
+ }
+
+ // Don't attempt to do anything if we are disconnecting a device.
+ if (event.getActionMasked() == MotionEvent.ACTION_CANCEL)
+ return true;
+
+ InputDevice input = event.getDevice();
+ List motions = input.getMotionRanges();
+
+ for (InputDevice.MotionRange range : motions)
+ {
+ NativeLibrary.onGamePadMoveEvent(InputConfigFragment.getInputDesc(input), range.getAxis(), event.getAxisValue(range.getAxis()));
+ }
+
+ return true;
+ }
+
+ private void hideSystemUiAfterDelay()
+ {
+ // Clear any pending hide events.
+ mSystemUiHider.removeMessages(0);
+
+ // Add a new hide event, to occur 3 seconds from now.
+ mSystemUiHider.sendEmptyMessageDelayed(0, 3000);
+ }
+
+ private void hideSystemUI()
+ {
+ mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_IMMERSIVE);
+ }
+
+ private void showSystemUI()
+ {
+ mDecorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java
index c0ba984023..619e9c647e 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/GameGridActivity.java
@@ -1,13 +1,15 @@
package org.dolphinemu.dolphinemu.activities;
import android.app.Activity;
+import android.app.LoaderManager;
+import android.content.CursorLoader;
import android.content.Intent;
-import android.content.SharedPreferences;
+import android.content.Loader;
+import android.database.Cursor;
import android.os.Bundle;
-import android.os.Environment;
-import android.preference.PreferenceManager;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -15,27 +17,23 @@ import android.view.View;
import android.widget.ImageButton;
import android.widget.Toolbar;
-import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
-import org.dolphinemu.dolphinemu.model.Game;
-import org.dolphinemu.dolphinemu.model.GcGame;
+import org.dolphinemu.dolphinemu.model.GameDatabase;
+import org.dolphinemu.dolphinemu.model.GameProvider;
import org.dolphinemu.dolphinemu.services.AssetCopyService;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
/**
* The main Activity of the Lollipop style UI. Shows a grid of games on tablets & landscape phones,
* shows a list of games on portrait phones.
*/
-public final class GameGridActivity extends Activity
+public final class GameGridActivity extends Activity implements LoaderManager.LoaderCallbacks
{
private static final int REQUEST_ADD_DIRECTORY = 1;
+ private static final int LOADER_ID_GAMES = 1;
+ // TODO When each platform has its own tab, there should be a LOADER_ID for each platform.
+
private GameAdapter mAdapter;
@Override
@@ -62,7 +60,8 @@ public final class GameGridActivity extends Activity
recyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
// Create an adapter that will relate the dataset to the views on-screen.
- mAdapter = new GameAdapter(getGameList());
+ getLoaderManager().initLoader(LOADER_ID_GAMES, null, this);
+ mAdapter = new GameAdapter();
recyclerView.setAdapter(mAdapter);
buttonAddDirectory.setOnClickListener(new View.OnClickListener()
@@ -103,20 +102,7 @@ public final class GameGridActivity extends Activity
// other activities might use this callback in the future (don't forget to change Javadoc!)
if (requestCode == REQUEST_ADD_DIRECTORY)
{
- // Get the path the user selected in AddDirectoryActivity.
- String path = result.getStringExtra(AddDirectoryActivity.KEY_CURRENT_PATH);
-
- // Store this path as a preference.
- // TODO Use SQLite instead.
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
- SharedPreferences.Editor editor = prefs.edit();
-
- editor.putString(AddDirectoryActivity.KEY_CURRENT_PATH, path);
-
- // Using commit, not apply, in order to block so the next method has the correct data to load.
- editor.commit();
-
- mAdapter.setGameList(getGameList());
+ getLoaderManager().restartLoader(LOADER_ID_GAMES, null, this);
}
}
}
@@ -150,55 +136,75 @@ public final class GameGridActivity extends Activity
return false;
}
- // TODO Replace all of this with a SQLite database
- private ArrayList getGameList()
+
+ /**
+ * Callback that's invoked when the system has initialized the Loader and
+ * is ready to start the query. This usually happens when initLoader() is
+ * called. Here, we use it to make a DB query for games.
+ *
+ * @param id The ID value passed to the initLoader() call that triggered this.
+ * @param args The args bundle supplied by the caller.
+ * @return A new Loader instance that is ready to start loading.
+ */
+ @Override
+ public Loader onCreateLoader(int id, Bundle args)
{
- ArrayList gameList = new ArrayList();
+ Log.d("DolphinEmu", "Creating loader with id: " + id);
- final String DefaultDir = Environment.getExternalStorageDirectory() + File.separator + "dolphin-emu";
-
- NativeLibrary.SetUserDirectory(DefaultDir);
-
- // Extensions to filter by.
- Set exts = new HashSet(Arrays.asList(".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs"));
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
-
- String path = prefs.getString(AddDirectoryActivity.KEY_CURRENT_PATH, "/");
-
- File currentDir = new File(path);
- File[] dirs = currentDir.listFiles();
- try
+ // Take action based on the ID of the Loader that's being created.
+ switch (id)
{
- for (File entry : dirs)
- {
- if (!entry.isHidden() && !entry.isDirectory())
- {
- String entryName = entry.getName();
+ case LOADER_ID_GAMES:
+ // TODO Play some sort of load-starting animation; maybe fade the list out.
- // Check that the file has an appropriate extension before trying to read out of it.
- if (exts.contains(entryName.toLowerCase().substring(entryName.lastIndexOf('.'))))
- {
- GcGame game = new GcGame(NativeLibrary.GetTitle(entry.getAbsolutePath()),
- NativeLibrary.GetDescription(entry.getAbsolutePath()).replace("\n", " "),
- // TODO Some games might actually not be from this region, believe it or not.
- "United States",
- entry.getAbsolutePath(),
- NativeLibrary.GetGameId(entry.getAbsolutePath()),
- NativeLibrary.GetDate(entry.getAbsolutePath()));
+ return new CursorLoader(
+ this, // Parent activity context
+ GameProvider.URI_GAME, // URI of table to query
+ null, // Return all columns
+ null, // No selection clause
+ null, // No selection arguments
+ GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order
+ );
- gameList.add(game);
- }
-
- }
-
- }
+ default:
+ Log.e("DolphinEmu", "Bad ID passed in.");
+ return null;
}
- catch (Exception ignored)
- {
+ }
+ /**
+ * Callback that's invoked when the Loader returned in onCreateLoader is finished
+ * with its task. In this case, the game DB query is finished, so we should put the results
+ * on screen.
+ *
+ * @param loader The loader that finished.
+ * @param data The data the Loader loaded.
+ */
+ @Override
+ public void onLoadFinished(Loader loader, Cursor data)
+ {
+ int id = loader.getId();
+ Log.d("DolphinEmu", "Loader finished with id: " + id);
+
+ // TODO When each platform has its own tab, this should just call into those tabs instead.
+ switch (id)
+ {
+ case LOADER_ID_GAMES:
+ mAdapter.swapCursor(data);
+ // TODO Play some sort of load-finished animation; maybe fade the list in.
+ break;
+
+ default:
+ Log.e("DolphinEmu", "Bad ID passed in.");
}
- return gameList;
+ }
+
+ @Override
+ public void onLoaderReset(Loader loader)
+ {
+ Log.d("DolphinEmu", "Loader resetting.");
+
+ // TODO ¯\_(ツ)_/¯
}
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java
index 7ce0047b08..8d07b6c101 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/FileAdapter.java
@@ -14,7 +14,7 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
-public class FileAdapter extends RecyclerView.Adapter implements View.OnClickListener
+public final class FileAdapter extends RecyclerView.Adapter implements View.OnClickListener
{
private ArrayList mFileList;
@@ -146,7 +146,7 @@ public class FileAdapter extends RecyclerView.Adapter implements
else
{
// Pass the activity the path of the parent directory of the clicked file.
- mListener.finishSuccessfully();
+ mListener.addDirectory();
}
}
@@ -154,7 +154,7 @@ public class FileAdapter extends RecyclerView.Adapter implements
* For a given directory, return a list of Files it contains.
*
* @param directory A File representing the directory that should have its contents displayed.
- * @return
+ * @return The list of files contained in the directory.
*/
private ArrayList generateFileList(File directory)
{
@@ -205,7 +205,7 @@ public class FileAdapter extends RecyclerView.Adapter implements
*/
public interface FileClickListener
{
- void finishSuccessfully();
+ void addDirectory();
void updateSubtitle(String path);
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java
index 883107a410..d296c5e023 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.java
@@ -2,8 +2,11 @@ package org.dolphinemu.dolphinemu.adapters;
import android.app.Activity;
import android.content.Intent;
+import android.database.Cursor;
+import android.database.DataSetObserver;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,27 +14,33 @@ import android.view.ViewGroup;
import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.R;
+import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.dialogs.GameDetailsDialog;
-import org.dolphinemu.dolphinemu.emulation.EmulationActivity;
-import org.dolphinemu.dolphinemu.model.Game;
+import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
-import java.util.ArrayList;
-
-public class GameAdapter extends RecyclerView.Adapter implements
+/**
+ * This adapter, unlike {@link FileAdapter} which is backed by an ArrayList, 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 implements
View.OnClickListener,
View.OnLongClickListener
{
- private ArrayList mGameList;
+ private Cursor mCursor;
+ private GameDataSetObserver mObserver;
+
+ private boolean mDatasetValid;
/**
- * Mostly just initializes the dataset to be displayed.
- *
- * @param gameList
+ * 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.
*/
- public GameAdapter(ArrayList gameList)
+ public GameAdapter()
{
- mGameList = gameList;
+ mDatasetValid = false;
+ mObserver = new GameDataSetObserver();
}
/**
@@ -52,8 +61,7 @@ public class GameAdapter extends RecyclerView.Adapter implements
gameCard.setOnLongClickListener(this);
// Use that view to create a ViewHolder.
- GameViewHolder holder = new GameViewHolder(gameCard);
- return holder;
+ return new GameViewHolder(gameCard);
}
/**
@@ -67,26 +75,41 @@ public class GameAdapter extends RecyclerView.Adapter implements
@Override
public void onBindViewHolder(GameViewHolder holder, int position)
{
- // Get a reference to the item from the dataset; we'll use this to fill in the view contents.
- final Game game = mGameList.get(position);
-
- // Fill in the view contents.
- Picasso.with(holder.imageScreenshot.getContext())
- .load(game.getScreenPath())
- .fit()
- .centerCrop()
- .error(R.drawable.no_banner)
- .into(holder.imageScreenshot);
-
- holder.textGameTitle.setText(game.getTitle());
- if (game.getDescription() != null)
+ if (mDatasetValid)
{
- holder.textDescription.setText(game.getDescription());
+ if (mCursor.moveToPosition(position))
+ {
+ // Fill in the view contents.
+ Picasso.with(holder.imageScreenshot.getContext())
+ .load(mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH))
+ .fit()
+ .centerCrop()
+ .error(R.drawable.no_banner)
+ .into(holder.imageScreenshot);
+
+ holder.textGameTitle.setText(mCursor.getString(GameDatabase.GAME_COLUMN_TITLE));
+ holder.textCompany.setText(mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY));
+
+ // TODO These shouldn't be necessary once the move to a DB-based model is complete.
+ 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.e("DolphinEmu", "Can't bind view; Cursor is not valid.");
+ }
+ }
+ else
+ {
+ Log.e("DolphinEmu", "Can't bind view; dataset is not valid.");
}
- holder.path = game.getPath();
- holder.screenshotPath = game.getScreenPath();
- holder.game = game;
+
}
/**
@@ -97,7 +120,85 @@ public class GameAdapter extends RecyclerView.Adapter implements
@Override
public int getItemCount()
{
- return mGameList.size();
+ if (mDatasetValid && mCursor != null)
+ {
+ return mCursor.getCount();
+ }
+ Log.e("DolphinEmu", "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.e("DolphinEmu", "Dataset is not valid.");
+ return 0;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public void setHasStableIds(boolean hasStableIds)
+ {
+ super.setHasStableIds(true);
+ }
+
+ /**
+ * When a load is finished, call this to replace the existing data with the newly-loaded
+ * data.
+ *
+ * @param cursor The newly-loaded Cursor.
+ */
+ public void swapCursor(Cursor cursor)
+ {
+ // Sanity check.
+ 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();
}
/**
@@ -114,6 +215,7 @@ public class GameAdapter extends RecyclerView.Adapter implements
Intent intent = new Intent(view.getContext(), EmulationActivity.class);
intent.putExtra("SelectedGame", holder.path);
+ intent.putExtra("SelectedTitle", holder.title);
view.getContext().startActivity(intent);
}
@@ -134,7 +236,12 @@ public class GameAdapter extends RecyclerView.Adapter implements
// String gameId = (String) holder.gameId;
Activity activity = (Activity) view.getContext();
- GameDetailsDialog.newInstance(holder.game).show(activity.getFragmentManager(), "game_details");
+ GameDetailsDialog.newInstance(holder.title,
+ holder.description,
+ holder.country,
+ holder.company,
+ holder.path,
+ holder.screenshotPath).show(activity.getFragmentManager(), "game_details");
return true;
}
@@ -158,9 +265,24 @@ public class GameAdapter extends RecyclerView.Adapter implements
}
}
- public void setGameList(ArrayList gameList)
+ private final class GameDataSetObserver extends DataSetObserver
{
- mGameList = gameList;
- notifyDataSetChanged();
+ @Override
+ public void onChanged()
+ {
+ super.onChanged();
+
+ mDatasetValid = true;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onInvalidated()
+ {
+ super.onInvalidated();
+
+ mDatasetValid = false;
+ notifyDataSetChanged();
+ }
}
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java
index e22b30ea18..04c7253cb5 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.java
@@ -16,12 +16,11 @@ import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.emulation.EmulationActivity;
-import org.dolphinemu.dolphinemu.model.Game;
+import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import de.hdodenhof.circleimageview.CircleImageView;
-public class GameDetailsDialog extends DialogFragment
+public final class GameDetailsDialog extends DialogFragment
{
public static final String ARGUMENT_GAME_TITLE = BuildConfig.APPLICATION_ID + ".game_title";
public static final String ARGUMENT_GAME_DESCRIPTION = BuildConfig.APPLICATION_ID + ".game_description";
@@ -30,18 +29,18 @@ public class GameDetailsDialog extends DialogFragment
public static final String ARGUMENT_GAME_PATH = BuildConfig.APPLICATION_ID + ".game_path";
public static final String ARGUMENT_GAME_SCREENSHOT_PATH = BuildConfig.APPLICATION_ID + ".game_screenshot_path";
-
- public static GameDetailsDialog newInstance(Game game)
+ // TODO Add all of this to the Loader in GameActivity.java
+ public static GameDetailsDialog newInstance(String title, String description, int country, String company, String path, String screenshotPath)
{
GameDetailsDialog fragment = new GameDetailsDialog();
Bundle arguments = new Bundle();
- arguments.putString(ARGUMENT_GAME_TITLE, game.getTitle());
- arguments.putString(ARGUMENT_GAME_DESCRIPTION, game.getDescription());
- arguments.putString(ARGUMENT_GAME_COUNTRY, game.getCountry());
- arguments.putString(ARGUMENT_GAME_DATE, game.getDate());
- arguments.putString(ARGUMENT_GAME_PATH, game.getPath());
- arguments.putString(ARGUMENT_GAME_SCREENSHOT_PATH, game.getScreenPath());
+ arguments.putString(ARGUMENT_GAME_TITLE, title);
+ arguments.putString(ARGUMENT_GAME_DESCRIPTION, description);
+ arguments.putInt(ARGUMENT_GAME_COUNTRY, country);
+ arguments.putString(ARGUMENT_GAME_DATE, company);
+ arguments.putString(ARGUMENT_GAME_PATH, path);
+ arguments.putString(ARGUMENT_GAME_SCREENSHOT_PATH, screenshotPath);
fragment.setArguments(arguments);
return fragment;
@@ -57,16 +56,19 @@ public class GameDetailsDialog extends DialogFragment
CircleImageView circleBanner = (CircleImageView) contents.findViewById(R.id.circle_banner);
TextView textTitle = (TextView) contents.findViewById(R.id.text_game_title);
- TextView textDescription = (TextView) contents.findViewById(R.id.text_game_description);
+ TextView textDescription = (TextView) contents.findViewById(R.id.text_company);
TextView textCountry = (TextView) contents.findViewById(R.id.text_country);
TextView textDate = (TextView) contents.findViewById(R.id.text_date);
ImageButton buttonLaunch = (ImageButton) contents.findViewById(R.id.button_launch);
+ int countryIndex = getArguments().getInt(ARGUMENT_GAME_COUNTRY);
+ String country = getResources().getStringArray(R.array.country_names)[countryIndex];
+
textTitle.setText(getArguments().getString(ARGUMENT_GAME_TITLE));
textDescription.setText(getArguments().getString(ARGUMENT_GAME_DESCRIPTION));
- textCountry.setText(getArguments().getString(ARGUMENT_GAME_COUNTRY));
+ textCountry.setText(country);
textDate.setText(getArguments().getString(ARGUMENT_GAME_DATE));
buttonLaunch.setOnClickListener(new View.OnClickListener()
{
@@ -77,6 +79,8 @@ public class GameDetailsDialog extends DialogFragment
Intent intent = new Intent(view.getContext(), EmulationActivity.class);
intent.putExtra("SelectedGame", getArguments().getString(ARGUMENT_GAME_PATH));
+ intent.putExtra("SelectedTitle", getArguments().getString(ARGUMENT_GAME_TITLE));
+
startActivity(intent);
}
});
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java
new file mode 100644
index 0000000000..7c83c9d977
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java
@@ -0,0 +1,114 @@
+package org.dolphinemu.dolphinemu.fragments;
+
+import android.app.Fragment;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.dolphinemu.dolphinemu.BuildConfig;
+import org.dolphinemu.dolphinemu.NativeLibrary;
+import org.dolphinemu.dolphinemu.R;
+import org.dolphinemu.dolphinemu.emulation.overlay.InputOverlay;
+
+
+public final class EmulationFragment extends Fragment
+{
+ public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".emulation_fragment";
+
+ private static final String ARGUMENT_GAME_PATH = BuildConfig.APPLICATION_ID + ".game_path";
+
+ private SharedPreferences mPreferences;
+
+ private InputOverlay mInputOverlay;
+
+ public static EmulationFragment newInstance(String path)
+ {
+ EmulationFragment fragment = new EmulationFragment();
+
+ Bundle arguments = new Bundle();
+ arguments.putString(ARGUMENT_GAME_PATH, path);
+ fragment.setArguments(arguments);
+
+ return fragment;
+ }
+
+ /**
+ * Initialize anything that doesn't depend on the layout / views in here.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ }
+
+ /**
+ * Initialize the UI and start emulation in here.
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
+ {
+ String path = getArguments().getString(ARGUMENT_GAME_PATH);
+
+ View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
+
+ mInputOverlay = (InputOverlay) contents.findViewById(R.id.surface_input_overlay);
+
+ NativeLibrary.SetFilename(path);
+
+
+ // If the input overlay was previously disabled, then don't show it.
+ if (!mPreferences.getBoolean("showInputOverlay", true))
+ {
+ mInputOverlay.setVisibility(View.GONE);
+ }
+
+ return contents;
+ }
+
+ @Override
+ public void onStart()
+ {
+ super.onStart();
+ NativeLibrary.UnPauseEmulation();
+ }
+
+ @Override
+ public void onStop()
+ {
+ super.onStop();
+ NativeLibrary.PauseEmulation();
+ }
+
+ @Override
+ public void onDestroyView()
+ {
+ super.onDestroyView();
+ NativeLibrary.StopEmulation();
+ }
+
+ public void toggleInputOverlayVisibility()
+ {
+ SharedPreferences.Editor editor = mPreferences.edit();
+
+ // If the overlay is currently set to INVISIBLE
+ if (!mPreferences.getBoolean("showInputOverlay", false))
+ {
+ // Set it to VISIBLE
+ mInputOverlay.setVisibility(View.VISIBLE);
+ editor.putBoolean("showInputOverlay", true);
+ }
+ else
+ {
+ // Set it to INVISIBLE
+ mInputOverlay.setVisibility(View.GONE);
+ editor.putBoolean("showInputOverlay", false);
+ }
+
+ editor.apply();
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java
index b9c8a571cf..e6fdacc110 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/FileListItem.java
@@ -13,7 +13,8 @@ public class FileListItem implements Comparable
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_OTHER = 3;
+ public static final int TYPE_WII_WARE = 3;
+ public static final int TYPE_OTHER = 4;
private int mType;
private String mFilename;
@@ -30,8 +31,6 @@ public class FileListItem implements Comparable
}
else
{
- String fileExtension = null;
-
int extensionStart = mPath.lastIndexOf('.');
if (extensionStart < 1)
{
@@ -40,7 +39,7 @@ public class FileListItem implements Comparable
}
else
{
- fileExtension = mPath.substring(extensionStart);
+ String fileExtension = mPath.substring(extensionStart);
// The extensions we care about.
Set allowedExtensions = new HashSet(Arrays.asList(".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs"));
@@ -48,7 +47,8 @@ public class FileListItem implements Comparable
// Check that the file has an extension we care about before trying to read out of it.
if (allowedExtensions.contains(fileExtension))
{
- mType = NativeLibrary.IsWiiTitle(mPath) ? TYPE_WII : TYPE_GC;
+ // Add 1 because 0 = TYPE_FOLDER
+ mType = NativeLibrary.GetPlatform(mPath) + 1;
}
else
{
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/Game.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/Game.java
index 9d3e736841..ae52261b45 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/Game.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/Game.java
@@ -1,23 +1,154 @@
package org.dolphinemu.dolphinemu.model;
-public interface Game
+import android.content.ContentValues;
+import android.database.Cursor;
+
+import java.io.File;
+
+public final class Game
{
public static final int PLATFORM_GC = 0;
public static final int PLATFORM_WII = 1;
+ public static final int PLATFORM_WII_WARE = 2;
- public int getPlatform();
+ // 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;
- public String getDate();
+ private static final String PATH_SCREENSHOT_FOLDER = "file:///sdcard/dolphin-emu/ScreenShots/";
- public String getTitle();
+ private String mTitle;
+ private String mDescription;
+ private String mPath;
+ private String mGameId;
+ private String mScreenshotFolderPath;
+ private String mCompany;
- public String getDescription();
+ private int mPlatform;
+ private int mCountry;
- public String getCountry();
+ public Game(int platform, String title, String description, int country, String path, String gameId, String company)
+ {
+ mPlatform = platform;
+ mTitle = title;
+ mDescription = description;
+ mCountry = country;
+ mPath = path;
+ mGameId = gameId;
+ mCompany = company;
+ mScreenshotFolderPath = PATH_SCREENSHOT_FOLDER + getGameId() + "/";
+ }
- public String getPath();
+ public int getPlatform()
+ {
+ return mPlatform;
+ }
- public String getGameId();
+ public String getTitle()
+ {
+ return mTitle;
+ }
- public String getScreenPath();
+ 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 getScreenshotFolderPath()
+ {
+ return mScreenshotFolderPath;
+ }
+
+ public String getScreenPath()
+ {
+ // Count how many screenshots are available, so we can use the most recent one.
+ File screenshotFolder = new File(mScreenshotFolderPath.substring(mScreenshotFolderPath.indexOf('s') - 1));
+ int screenCount = 0;
+
+ if (screenshotFolder.isDirectory())
+ {
+ screenCount = screenshotFolder.list().length;
+ }
+
+ String screenPath = mScreenshotFolderPath
+ + getGameId() + "-"
+ + screenCount + ".png";
+
+ return screenPath;
+ }
+
+ public static ContentValues asContentValues(int platform, String title, String description, int country, String path, String gameId, String company)
+ {
+ ContentValues values = new ContentValues();
+
+ // TODO Come up with a way of finding the most recent screenshot that doesn't involve counting files
+ String screenshotFolderPath = PATH_SCREENSHOT_FOLDER + gameId + "/";
+
+ // Count how many screenshots are available, so we can use the most recent one.
+ File screenshotFolder = new File(screenshotFolderPath.substring(screenshotFolderPath.indexOf('s') - 1));
+ int screenCount = 0;
+
+ if (screenshotFolder.isDirectory())
+ {
+ screenCount = screenshotFolder.list().length;
+ }
+
+ String screenPath = screenshotFolderPath
+ + gameId + "-"
+ + screenCount + ".png";
+
+ values.put(GameDatabase.KEY_GAME_PLATFORM, platform);
+ 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(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));
+ }
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameDatabase.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameDatabase.java
new file mode 100644
index 0000000000..3d597b7ac3
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameDatabase.java
@@ -0,0 +1,190 @@
+package org.dolphinemu.dolphinemu.model;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import org.dolphinemu.dolphinemu.NativeLibrary;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 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 int FOLDER_COLUMN_PATH = 1;
+
+ 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 KEY_FOLDER_PATH = "path";
+
+ public static final String TABLE_NAME_FOLDERS = "folders";
+ 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 CONSTRAINT_UNIQUE = " UNIQUE";
+
+ 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_CREATE_FOLDERS = "CREATE TABLE " + TABLE_NAME_FOLDERS + "("
+ + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR
+ + KEY_FOLDER_PATH + TYPE_STRING + CONSTRAINT_UNIQUE + ")";
+
+ private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES;
+
+ public GameDatabase(Context context)
+ {
+ // Superclass constructor builds a database or uses an existing one.
+ super(context, "games.db", null, DB_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase database)
+ {
+ Log.d("DolphinEmu", "GameDatabase - Creating database...");
+
+ Log.v("DolphinEmu", "Executing SQL: " + SQL_CREATE_GAMES);
+ database.execSQL(SQL_CREATE_GAMES);
+
+ Log.v("DolphinEmu", "Executing SQL: " + SQL_CREATE_FOLDERS);
+ database.execSQL(SQL_CREATE_FOLDERS);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion)
+ {
+ Log.i("DolphinEmu", "Upgrading database from schema version " + oldVersion + " to " + newVersion);
+
+ Log.v("DolphinEmu", "Executing SQL: " + SQL_DELETE_GAMES);
+ database.execSQL(SQL_DELETE_GAMES);
+
+ Log.v("DolphinEmu", "Executing SQL: " + SQL_CREATE_GAMES);
+ database.execSQL(SQL_CREATE_GAMES);
+
+ Log.v("DolphinEmu", "Re-scanning library with new schema.");
+ scanLibrary(database);
+ }
+
+ public void scanLibrary(SQLiteDatabase database)
+ {
+ // TODO Before scanning known folders, go through the game table and remove any entries for which the file itself is missing.
+
+ // Get a cursor listing all the folders the user has added to the library.
+ Cursor cursor = database.query(TABLE_NAME_FOLDERS,
+ null, // Get all columns.
+ null, // Get all rows.
+ null,
+ null, // No grouping.
+ null,
+ null); // Order of folders is irrelevant.
+
+ Set allowedExtensions = new HashSet(Arrays.asList(".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".wad", ".wbfs"));
+
+ // Possibly overly defensive, but ensures that moveToNext() does not skip a row.
+ cursor.moveToPosition(-1);
+
+ // Iterate through all results of the DB query (i.e. all folders in the library.)
+ while (cursor.moveToNext())
+ {
+
+ String folderPath = cursor.getString(FOLDER_COLUMN_PATH);
+ File folder = new File(folderPath);
+
+ Log.i("DolphinEmu", "Reading files from library folder: " + folderPath);
+
+ // Iterate through every file in the folder.
+ File[] children = folder.listFiles();
+ 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))
+ {
+ ContentValues game = Game.asContentValues(NativeLibrary.GetPlatform(filePath),
+ NativeLibrary.GetTitle(filePath),
+ NativeLibrary.GetDescription(filePath).replace("\n", " "),
+ NativeLibrary.GetCountry(filePath),
+ filePath,
+ NativeLibrary.GetGameId(filePath),
+ 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.v("DolphinEmu", "Adding game: " + game.getAsString(KEY_GAME_TITLE));
+ database.insert(TABLE_NAME_GAMES, null, game);
+ }
+ else
+ {
+ Log.v("DolphinEmu", "Updated game: " + game.getAsString(KEY_GAME_TITLE));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ cursor.close();
+ database.close();
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameProvider.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameProvider.java
new file mode 100644
index 0000000000..702c180309
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameProvider.java
@@ -0,0 +1,138 @@
+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.util.Log;
+
+import org.dolphinemu.dolphinemu.BuildConfig;
+
+/**
+ * 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 AUTHORITY = "content://" + BuildConfig.APPLICATION_ID + ".provider";
+ public static final Uri URI_FOLDER = Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_FOLDERS + "/");
+ public static final Uri URI_GAME = Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_GAMES + "/");
+
+ 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.i("DolphinEmu", "Creating Content Provider...");
+
+ mDbHelper = new GameDatabase(getContext());
+
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
+ {
+ Log.i("DolphinEmu", "Querying URI: " + uri);
+
+ SQLiteDatabase db = mDbHelper.getReadableDatabase();
+
+ String table = uri.getLastPathSegment();
+
+ if (table == null)
+ {
+ Log.e("DolphinEmu", "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(Uri uri)
+ {
+ Log.v("DolphinEmu", "Getting MIME type for URI: " + uri);
+ String lastSegment = uri.getLastPathSegment();
+
+ if (lastSegment == null)
+ {
+ Log.e("DolphinEmu", "Badly formatted URI: " + uri);
+ return null;
+ }
+
+ if (lastSegment.equals(GameDatabase.TABLE_NAME_FOLDERS))
+ {
+ return MIME_TYPE_FOLDER;
+ }
+ else if (lastSegment.equals(GameDatabase.TABLE_NAME_GAMES))
+ {
+ return MIME_TYPE_GAME;
+ }
+
+ Log.e("DolphinEmu", "Unknown MIME type for URI: " + uri);
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values)
+ {
+ Log.i("DolphinEmu", "Inserting row at URI: " + uri);
+
+ SQLiteDatabase database = mDbHelper.getWritableDatabase();
+ String table = uri.getLastPathSegment();
+
+ long id = -1;
+
+ if (table != null)
+ {
+ id = database.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE);
+
+ // If insertion was successful...
+ if (id > 0)
+ {
+ // If we just added a folder, add its contents to the game list.
+ if (table.equals(GameDatabase.TABLE_NAME_FOLDERS))
+ {
+ mDbHelper.scanLibrary(database);
+ }
+
+ // Notify the UI that its contents should be refreshed.
+ getContext().getContentResolver().notifyChange(uri, null);
+ uri = Uri.withAppendedPath(uri, Long.toString(id));
+ }
+ else
+ {
+ Log.e("DolphinEmu", "Row already exists: " + uri + " id: " + id);
+ }
+ }
+ else
+ {
+ Log.e("DolphinEmu", "Badly formatted URI: " + uri);
+ }
+
+ database.close();
+
+ return uri;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs)
+ {
+ Log.e("DolphinEmu", "Delete operations unsupported. URI: " + uri);
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
+ {
+ Log.e("DolphinEmu", "Update operations unsupported. URI: " + uri);
+ return 0;
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GcGame.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GcGame.java
deleted file mode 100644
index d682bee0b6..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GcGame.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.dolphinemu.dolphinemu.model;
-
-
-import java.io.File;
-
-public final class GcGame implements Game
-{
- private String mTitle;
- private String mDescription;
- private String mCountry;
- private String mPath;
- private String mGameId;
-
- private String mScreenshotFolderPath;
-
- private String mDate;
- private int mPlatform = PLATFORM_GC;
-
- private static final String PATH_SCREENSHOT_FOLDER = "file:///sdcard/dolphin-emu/ScreenShots/";
-
- public GcGame(String title, String description, String country, String path, String gameId, String date)
- {
- mTitle = title;
- mDescription = description;
- mCountry = country;
- mPath = path;
- mGameId = gameId;
- mDate = date;
- mScreenshotFolderPath = PATH_SCREENSHOT_FOLDER + getGameId() + "/";
- }
-
- @Override
- public int getPlatform()
- {
- return mPlatform;
- }
-
- @Override
- public String getTitle()
- {
- return mTitle;
- }
-
- @Override
- public String getDescription()
- {
- return mDescription;
- }
-
- @Override
- public String getDate()
- {
- return mDate;
- }
-
- @Override
- public String getCountry()
- {
- return mCountry;
- }
-
- @Override
- public String getPath()
- {
- return mPath;
- }
-
- public String getGameId()
- {
- return mGameId;
- }
-
- public String getScreenshotFolderPath()
- {
- return mScreenshotFolderPath;
- }
-
- @Override
- public String getScreenPath()
- {
- // Count how many screenshots are available, so we can use the most recent one.
- File screenshotFolder = new File(mScreenshotFolderPath.substring(mScreenshotFolderPath.indexOf('s') - 1));
- int screenCount = 0;
-
- if (screenshotFolder.isDirectory())
- {
- screenCount = screenshotFolder.list().length;
- }
-
- String screenPath = mScreenshotFolderPath
- + getGameId() + "-"
- + screenCount + ".png";
-
- return screenPath;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/WiiGame.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/WiiGame.java
deleted file mode 100644
index c64de8d582..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/WiiGame.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package org.dolphinemu.dolphinemu.model;
-
-
-public final class WiiGame implements Game
-{
- @Override
- public int getPlatform()
- {
- return 0;
- }
-
- @Override
- public String getDate()
- {
- return null;
- }
-
- @Override
- public String getTitle()
- {
- return null;
- }
-
- @Override
- public String getDescription()
- {
- return null;
- }
-
- @Override
- public String getCountry()
- {
- return null;
- }
-
- @Override
- public String getPath()
- {
- return null;
- }
-
- @Override
- public String getGameId()
- {
- return null;
- }
-
- @Override
- public String getScreenPath()
- {
- return null;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java
index 79acc8400b..902e5299f4 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/FileViewHolder.java
@@ -7,7 +7,10 @@ import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
-
+/**
+ * A simple class that stores references to views so that the FileAdapter doesn't need to
+ * keep calling findViewById(), which is expensive.
+ */
public class FileViewHolder extends RecyclerView.ViewHolder
{
public View itemView;
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/GameViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/GameViewHolder.java
index 18e271e0f5..592a2ecf32 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/GameViewHolder.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/GameViewHolder.java
@@ -1,29 +1,31 @@
package org.dolphinemu.dolphinemu.viewholders;
-import android.app.Activity;
-import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.View;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.dialogs.GameDetailsDialog;
-import org.dolphinemu.dolphinemu.emulation.EmulationActivity;
-import org.dolphinemu.dolphinemu.model.Game;
-
+/**
+ * A simple class that stores references to views so that the GameAdapter doesn't need to
+ * keep calling findViewById(), which is expensive.
+ */
public class GameViewHolder extends RecyclerView.ViewHolder
{
public ImageView imageScreenshot;
public TextView textGameTitle;
- public TextView textDescription;
+ public TextView textCompany;
- // Used to handle onClick(). Set this in onBindViewHolder().
+ public String gameId;
+
+ // 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 Game game;
public GameViewHolder(View itemView)
{
@@ -33,6 +35,6 @@ public class GameViewHolder extends RecyclerView.ViewHolder
imageScreenshot = (ImageView) itemView.findViewById(R.id.image_game_screen);
textGameTitle = (TextView) itemView.findViewById(R.id.text_game_title);
- textDescription = (TextView) itemView.findViewById(R.id.text_game_description);
+ textCompany = (TextView) itemView.findViewById(R.id.text_company);
}
}
diff --git a/Source/Android/app/src/main/res/drawable-hdpi/ic_company.png b/Source/Android/app/src/main/res/drawable-hdpi/ic_company.png
new file mode 100644
index 0000000000..69dba7ac8f
Binary files /dev/null and b/Source/Android/app/src/main/res/drawable-hdpi/ic_company.png differ
diff --git a/Source/Android/app/src/main/res/drawable-mdpi/ic_company.png b/Source/Android/app/src/main/res/drawable-mdpi/ic_company.png
new file mode 100644
index 0000000000..481de5052d
Binary files /dev/null and b/Source/Android/app/src/main/res/drawable-mdpi/ic_company.png differ
diff --git a/Source/Android/app/src/main/res/drawable-xhdpi/ic_company.png b/Source/Android/app/src/main/res/drawable-xhdpi/ic_company.png
new file mode 100644
index 0000000000..750d7ff388
Binary files /dev/null and b/Source/Android/app/src/main/res/drawable-xhdpi/ic_company.png differ
diff --git a/Source/Android/app/src/main/res/drawable-xxhdpi/ic_company.png b/Source/Android/app/src/main/res/drawable-xxhdpi/ic_company.png
new file mode 100644
index 0000000000..96fa6ac8f0
Binary files /dev/null and b/Source/Android/app/src/main/res/drawable-xxhdpi/ic_company.png differ
diff --git a/Source/Android/app/src/main/res/drawable-xxxhdpi/ic_company.png b/Source/Android/app/src/main/res/drawable-xxxhdpi/ic_company.png
new file mode 100644
index 0000000000..967aa1fbe2
Binary files /dev/null and b/Source/Android/app/src/main/res/drawable-xxxhdpi/ic_company.png differ
diff --git a/Source/Android/app/src/main/res/drawable/oval_ripple_wii.xml b/Source/Android/app/src/main/res/drawable/oval_ripple_accent.xml
similarity index 73%
rename from Source/Android/app/src/main/res/drawable/oval_ripple_wii.xml
rename to Source/Android/app/src/main/res/drawable/oval_ripple_accent.xml
index 644876eda3..fbdfec87f5 100644
--- a/Source/Android/app/src/main/res/drawable/oval_ripple_wii.xml
+++ b/Source/Android/app/src/main/res/drawable/oval_ripple_accent.xml
@@ -2,7 +2,7 @@
android:color="?android:colorControlHighlight">
-
-
+
\ No newline at end of file
diff --git a/Source/Android/app/src/main/res/drawable/oval_ripple_gc.xml b/Source/Android/app/src/main/res/drawable/oval_ripple_gc.xml
deleted file mode 100644
index 167d3ef13a..0000000000
--- a/Source/Android/app/src/main/res/drawable/oval_ripple_gc.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
- -
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Android/app/src/main/res/layout/activity_emulation.xml b/Source/Android/app/src/main/res/layout/activity_emulation.xml
new file mode 100644
index 0000000000..63ea99ba83
--- /dev/null
+++ b/Source/Android/app/src/main/res/layout/activity_emulation.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Source/Android/app/src/main/res/layout/activity_game_grid.xml b/Source/Android/app/src/main/res/layout/activity_game_grid.xml
index 2dc6416a94..53ac5d614e 100644
--- a/Source/Android/app/src/main/res/layout/activity_game_grid.xml
+++ b/Source/Android/app/src/main/res/layout/activity_game_grid.xml
@@ -9,7 +9,7 @@
android:id="@+id/toolbar_game_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/dolphin_blue"
+ android:background="?android:colorPrimary"
android:minHeight="?android:attr/actionBarSize"
android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar"
android:elevation="6dp"/>
@@ -33,7 +33,7 @@
android:layout_alignBottom="@+id/image_game_screen"
android:layout_alignEnd="@+id/text_game_title"
android:layout_marginBottom="28dp"
- android:background="@drawable/oval_ripple_gc"
+ android:background="@drawable/oval_ripple_accent"
android:src="@drawable/ic_add"
android:stateListAnimator="@anim/button_elevation"
android:elevation="4dp"
diff --git a/Source/Android/app/src/main/res/layout/card_game.xml b/Source/Android/app/src/main/res/layout/card_game.xml
index dc8321f8f8..b18bc9e7ee 100644
--- a/Source/Android/app/src/main/res/layout/card_game.xml
+++ b/Source/Android/app/src/main/res/layout/card_game.xml
@@ -40,7 +40,7 @@
tools:text="The Legend of Zelda: The Wind Waker"/>
+ tools:text="Nintendo"/>
diff --git a/Source/Android/app/src/main/res/layout/dialog_game_details.xml b/Source/Android/app/src/main/res/layout/dialog_game_details.xml
index b431991baf..1d7b63468b 100644
--- a/Source/Android/app/src/main/res/layout/dialog_game_details.xml
+++ b/Source/Android/app/src/main/res/layout/dialog_game_details.xml
@@ -18,7 +18,7 @@
android:layout_marginLeft="16dp"
android:layout_marginTop="24dp"
tools:src="@drawable/placeholder_banner"
- app:border_color="#ffcccccc"
+ app:border_color="?android:colorAccent"
app:border_width="2dp"
/>
@@ -32,6 +32,7 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:transitionName="image_game_screen"
+ tools:scaleType="centerCrop"
tools:src="@drawable/placeholder_screenshot"/>
@@ -69,7 +67,7 @@
android:layout_height="1dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
- android:layout_below="@+id/text_game_description"
+ android:layout_below="@+id/text_company"
android:layout_marginTop="16dp"
android:background="#1F000000"/>
@@ -85,14 +83,14 @@
android:src="@drawable/ic_country"/>
+ tools:text="Nintendo"/>
diff --git a/Source/Android/app/src/main/res/layout/fragment_emulation.xml b/Source/Android/app/src/main/res/layout/fragment_emulation.xml
new file mode 100644
index 0000000000..6a936dae64
--- /dev/null
+++ b/Source/Android/app/src/main/res/layout/fragment_emulation.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/Source/Android/app/src/main/res/menu/menu_emulation.xml b/Source/Android/app/src/main/res/menu/menu_emulation.xml
new file mode 100644
index 0000000000..627d1bc9e3
--- /dev/null
+++ b/Source/Android/app/src/main/res/menu/menu_emulation.xml
@@ -0,0 +1,77 @@
+
diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml
index 4ada90cb1f..9cdf07d876 100644
--- a/Source/Android/app/src/main/res/values/arrays.xml
+++ b/Source/Android/app/src/main/res/values/arrays.xml
@@ -190,4 +190,20 @@
- 2
- 3
+
+
+ - Europe
+ - Japan
+ - USA
+ - Australia
+ - France
+ - Germany
+ - Italy
+ - Korea
+ - Netherlands
+ - Russia
+ - Spain
+ - Taiwan
+ - Unknown
+
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml
index 07e5ab3690..47fefbaf20 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -236,4 +236,7 @@
CPU Settings
Input Settings
Video Settings
+ Emulation Activity
+
+ Toggle Input Overlay
diff --git a/Source/Android/app/src/main/res/values/styles.xml b/Source/Android/app/src/main/res/values/styles.xml
index efc39de62c..bec7fc0035 100644
--- a/Source/Android/app/src/main/res/values/styles.xml
+++ b/Source/Android/app/src/main/res/values/styles.xml
@@ -22,23 +22,19 @@
@@ -46,4 +42,42 @@
- @color/dolphin_accent_wiiware
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Source/Core/DolphinWX/MainAndroid.cpp b/Source/Core/DolphinWX/MainAndroid.cpp
index 50aab79f45..c274deb5fb 100644
--- a/Source/Core/DolphinWX/MainAndroid.cpp
+++ b/Source/Core/DolphinWX/MainAndroid.cpp
@@ -21,7 +21,7 @@
#include
#include
#include
-
+#include "../DiscIO/Volume.h"
#include "Android/ButtonManager.h"
#include "Common/CommonPaths.h"
#include "Common/CommonTypes.h"
@@ -161,21 +161,52 @@ static bool LoadBanner(std::string filename, u32 *Banner)
return false;
}
-static bool IsWiiTitle(std::string filename)
+static int GetCountry(std::string filename)
{
std::unique_ptr pVolume(DiscIO::CreateVolumeFromFilename(filename));
if (pVolume != nullptr)
{
- bool is_wii_title = pVolume->IsWiiDisc() || pVolume->IsWadFile();
+ DiscIO::IVolume::ECountry country = pVolume->GetCountry();
- __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Is %s a Wii Disc: %s", filename.c_str(), is_wii_title ? "Yes" : "No" );
+ __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Country Code: %i", country);
- return is_wii_title;
+ return country;
}
- // Technically correct.
- return false;
+ // Return UNKNOWN
+ return 13;
+}
+
+static int GetPlatform(std::string filename)
+{
+ std::unique_ptr pVolume(DiscIO::CreateVolumeFromFilename(filename));
+
+ if (pVolume != nullptr)
+ {
+ bool is_wii_disc = pVolume->IsWiiDisc();
+ bool is_wii_wad = pVolume->IsWadFile();
+
+ if (is_wii_disc)
+ {
+ __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Volume is a Wii disc.");
+
+ // See Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/Game.java
+ return 1;
+ }
+ else if (is_wii_wad)
+ {
+ __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Volume is a Wii WAD.");
+ return 2;
+ }
+ else
+ {
+ __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Volume is a Gamecube disc.");
+ return 0;
+ }
+ }
+
+ return -1;
}
static std::string GetTitle(std::string filename)
@@ -274,15 +305,15 @@ static std::string GetGameId(std::string filename)
return std::string ("");
}
-static std::string GetApploaderDate(std::string filename)
+static std::string GetCompany(std::string filename)
{
- __android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting Date for file: %s", filename.c_str());
+ __android_log_print(ANDROID_LOG_WARN, DOLPHIN_TAG, "Getting Company for file: %s", filename.c_str());
DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(filename);
if (pVolume != nullptr)
{
- std::string date = pVolume->GetApploaderDate();
- __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Date: %s", date.c_str());
+ std::string date = pVolume->GetCompany();
+ __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Company: %s", date.c_str());
return date;
}
@@ -297,7 +328,8 @@ static u64 GetFileSize(std::string filename)
if (pVolume != nullptr)
{
u64 size = pVolume->GetSize();
- __android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Size: %lu", size);
+ // Causes a warning because size is u64, not 'long unsigned'
+ //__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Size: %lu", size);
return size;
}
@@ -330,9 +362,10 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMov
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 jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetDate(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 jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsWiiTitle(JNIEnv *env, jobject obj, jstring jFilename);
+JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetPlatform(JNIEnv *env, jobject obj, jstring jFilename);
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv *env, jobject obj);
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SupportsNEON(JNIEnv *env, jobject obj);
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SaveScreenShot(JNIEnv *env, jobject obj);
@@ -404,11 +437,18 @@ JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameId
return env->NewStringUTF(id.c_str());
}
-JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetDate(JNIEnv *env, jobject obj, jstring jFilename)
+JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetCompany(JNIEnv *env, jobject obj, jstring jFilename)
{
std::string filename = GetJString(env, jFilename);
- std::string date = GetApploaderDate(filename);
- return env->NewStringUTF(date.c_str());
+ 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)
@@ -418,11 +458,11 @@ JNIEXPORT jlong JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetFilesize
return size;
}
-JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_IsWiiTitle(JNIEnv *env, jobject obj, jstring jFilename)
+JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetPlatform(JNIEnv *env, jobject obj, jstring jFilename)
{
std::string filename = GetJString(env, jFilename);
- bool wiiDisc = IsWiiTitle(filename);
- return wiiDisc;
+ int platform = GetPlatform(filename);
+ return platform;
}
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv *env, jobject obj)