From 7c14996e3e24b0a4a1064ece1c656bcbe6c923c7 Mon Sep 17 00:00:00 2001 From: sigmabeta Date: Tue, 14 Jul 2015 22:35:52 -0400 Subject: Android TV: Implement game selector activity in new Android TV UI --- .../dolphinemu/activities/MainActivity.java | 2 +- .../dolphinemu/activities/TvMainActivity.java | 141 +++++++++++++++++++++ .../dolphinemu/adapters/GamePresenter.java | 122 ++++++++++++++++++ .../java/org/dolphinemu/dolphinemu/model/Game.java | 33 +---- .../dolphinemu/viewholders/TvGameViewHolder.java | 40 ++++++ 5 files changed, 311 insertions(+), 27 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java index 33cd0b0c15..a32b65d566 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java @@ -226,7 +226,7 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag GameProvider.URI_GAME, // URI of table to query null, // Return all columns GameDatabase.KEY_GAME_PLATFORM + " = ?", // Select by platform - new String[]{Integer.toString(id)}, // Platform id is Loader id minus 1 + new String[]{Integer.toString(id)}, // Platform id is Loader id GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order ); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java new file mode 100644 index 0000000000..2c191bdaf0 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java @@ -0,0 +1,141 @@ +package org.dolphinemu.dolphinemu.activities; + +import android.app.Activity; +import android.app.ActivityOptions; +import android.app.FragmentManager; +import android.content.Intent; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v17.leanback.app.BrowseFragment; +import android.support.v17.leanback.database.CursorMapper; +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.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.adapters.GamePresenter; +import org.dolphinemu.dolphinemu.model.Game; +import org.dolphinemu.dolphinemu.model.GameDatabase; +import org.dolphinemu.dolphinemu.model.GameProvider; +import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; + +public final class TvMainActivity extends Activity +{ + protected BrowseFragment mBrowseFragment; + + private ArrayObjectAdapter mRowsAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_tv_main); + + final FragmentManager fragmentManager = getFragmentManager(); + mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById( + R.id.fragment_game_list); + + // Set display parameters for the BrowseFragment + mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED); + mBrowseFragment.setTitle(getString(R.string.app_name)); + mBrowseFragment.setBadgeDrawable(getResources().getDrawable( + R.drawable.ic_launcher, null)); + mBrowseFragment.setBrandColor(getResources().getColor(R.color.dolphin_blue_dark)); + + buildRowsAdapter(); + + mBrowseFragment.setOnItemViewClickedListener( + new OnItemViewClickedListener() + { + @Override + public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) + { + TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; + // Start the emulation activity and send the path of the clicked ISO to it. + Intent intent = new Intent(TvMainActivity.this, EmulationActivity.class); + + intent.putExtra("SelectedGame", holder.path); + intent.putExtra("SelectedTitle", holder.title); + intent.putExtra("ScreenPath", holder.screenshotPath); + + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( + TvMainActivity.this, + holder.imageScreenshot, + "image_game_screenshot"); + + startActivity(intent, options.toBundle()); + } + }); + } + + private void buildRowsAdapter() + { + mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); + + // For each row + for (int platformIndex = 0; platformIndex <= Game.PLATFORM_WII_WARE; ++platformIndex) + { + // Create an adapter for this row. + CursorObjectAdapter row = new CursorObjectAdapter(new GamePresenter(platformIndex)); + + // Add items to the adapter. + Cursor games = getContentResolver().query( + GameProvider.URI_GAME, // URI of table to query + null, // Return all columns + GameDatabase.KEY_GAME_PLATFORM + " = ?", // Select by platform + new String[]{Integer.toString(platformIndex)}, // Platform id + GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order + ); + + row.swapCursor(games); + row.setMapper(new CursorMapper() + { + @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; + switch (platformIndex) + { + case Game.PLATFORM_GC: + headerName = "GameCube Games"; + break; + + case Game.PLATFORM_WII: + headerName = "Wii Games"; + break; + + case Game.PLATFORM_WII_WARE: + headerName = "WiiWare"; + break; + + default: + headerName = "Error"; + break; + } + + // Create a header for this row. + HeaderItem header = new HeaderItem(platformIndex, headerName); + + // Create the row, passing it the filled adapter and the header, and give it to the master adapter. + mRowsAdapter.add(new ListRow(header, row)); + } + + mBrowseFragment.setAdapter(mRowsAdapter); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java new file mode 100644 index 0000000000..d9b4487278 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java @@ -0,0 +1,122 @@ +package org.dolphinemu.dolphinemu.adapters; + +import android.graphics.Bitmap; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.squareup.picasso.Picasso; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.model.Game; +import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; + +/** + * The Leanback library / docs call this a Presenter, but it works very + * similarly to a RecyclerView.ViewHolder. + */ +public final class GamePresenter extends Presenter +{ + private int mPlatform; + + public GamePresenter(int platform) + { + mPlatform = platform; + } + + public ViewHolder onCreateViewHolder(ViewGroup parent) + { + // Create a new view. + ImageCardView gameCard = new ImageCardView(parent.getContext()) + { + @Override + public void setSelected(boolean selected) + { + setCardBackground(this, selected); + super.setSelected(selected); + } + }; + + gameCard.setMainImageAdjustViewBounds(true); + gameCard.setMainImageDimensions(480, 320); + gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP); + + gameCard.setFocusable(true); + gameCard.setFocusableInTouchMode(true); + + setCardBackground(gameCard, false); + + // Use that view to create a ViewHolder. + return new TvGameViewHolder(gameCard); + } + + public void onBindViewHolder(ViewHolder viewHolder, Object item) + { + TvGameViewHolder holder = (TvGameViewHolder) viewHolder; + Game game = (Game) item; + + String screenPath = game.getScreenshotPath(); + + // Fill in the view contents. + Picasso.with(holder.imageScreenshot.getContext()) + .load(screenPath) + .fit() + .centerCrop() + .noFade() + .noPlaceholder() + .config(Bitmap.Config.RGB_565) + .error(R.drawable.no_banner) + .into(holder.imageScreenshot); + + holder.cardParent.setTitleText(game.getTitle()); + holder.cardParent.setContentText(game.getCompany()); + + // TODO These shouldn't be necessary once the move to a DB-based model is complete. + 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(); + } + + public void onUnbindViewHolder(ViewHolder viewHolder) + { + // no op + } + + public void setCardBackground(ImageCardView view, boolean selected) + { + int backgroundColor; + + if (selected) + { + switch (mPlatform) + { + case Game.PLATFORM_GC: + backgroundColor = R.color.dolphin_accent_gamecube; + break; + + case Game.PLATFORM_WII: + backgroundColor = R.color.dolphin_accent_wii; + break; + + case Game.PLATFORM_WII_WARE: + backgroundColor = R.color.dolphin_accent_wiiware; + break; + + default: + backgroundColor = android.R.color.holo_red_dark; + break; + } + } + else + { + backgroundColor = R.color.tv_card_unselected; + } + + view.setInfoAreaBackgroundColor(view.getResources().getColor(backgroundColor)); + } +} 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 f3ef6f7514..2068f01f08 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 @@ -3,8 +3,6 @@ package org.dolphinemu.dolphinemu.model; import android.content.ContentValues; import android.database.Cursor; -import java.io.File; - public final class Game { public static final int PLATFORM_GC = 0; @@ -33,13 +31,13 @@ public final class Game private String mDescription; private String mPath; private String mGameId; - private String mScreenshotFolderPath; + private String mScreenshotPath; private String mCompany; private int mPlatform; private int mCountry; - public Game(int platform, String title, String description, int country, String path, String gameId, String company) + public Game(int platform, String title, String description, int country, String path, String gameId, String company, String screenshotPath) { mPlatform = platform; mTitle = title; @@ -48,7 +46,7 @@ public final class Game mPath = path; mGameId = gameId; mCompany = company; - mScreenshotFolderPath = PATH_SCREENSHOT_FOLDER + getGameId() + "/"; + mScreenshotPath = screenshotPath; } public int getPlatform() @@ -86,27 +84,9 @@ public final class Game return mGameId; } - public String getScreenshotFolderPath() - { - return mScreenshotFolderPath; - } - - public String getScreenPath() + public String getScreenshotPath() { - // 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; + return mScreenshotPath; } public static ContentValues asContentValues(int platform, String title, String description, int country, String path, String gameId, String company) @@ -135,6 +115,7 @@ public final class Game 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_COMPANY), + cursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH)); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java new file mode 100644 index 0000000000..dd996ccbe7 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java @@ -0,0 +1,40 @@ +package org.dolphinemu.dolphinemu.viewholders; + +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +/** + * A simple class that stores references to views so that the GameAdapter doesn't need to + * keep calling findViewById(), which is expensive. + */ +public class TvGameViewHolder extends Presenter.ViewHolder +{ + public ImageCardView cardParent; + + public ImageView imageScreenshot; + public TextView textGameTitle; + public TextView textCompany; + + 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 TvGameViewHolder(View itemView) + { + super(itemView); + + itemView.setTag(this); + + cardParent = (ImageCardView) itemView; + imageScreenshot = cardParent.getMainImageView(); + } +} -- cgit v1.2.3 From 0b1212b77db43376012756c4219156708316810e Mon Sep 17 00:00:00 2001 From: sigmabeta Date: Mon, 20 Jul 2015 20:15:26 -0400 Subject: Android TV: Add row listing all games --- .../dolphinemu/activities/TvMainActivity.java | 123 ++++++++++++++------- .../java/org/dolphinemu/dolphinemu/model/Game.java | 1 + 2 files changed, 83 insertions(+), 41 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java index 2c191bdaf0..0e716d6503 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java @@ -78,64 +78,105 @@ public final class TvMainActivity extends Activity { mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); - // For each row - for (int platformIndex = 0; platformIndex <= Game.PLATFORM_WII_WARE; ++platformIndex) + // For each platform + for (int platformIndex = 0; platformIndex <= Game.PLATFORM_ALL; ++platformIndex) { - // Create an adapter for this row. - CursorObjectAdapter row = new CursorObjectAdapter(new GamePresenter(platformIndex)); + ListRow row = buildGamesRow(platformIndex); - // Add items to the adapter. - Cursor games = getContentResolver().query( + // Add row to the adapter only if it is not empty. + if (row != null) + { + mRowsAdapter.add(row); + } + } + + mBrowseFragment.setAdapter(mRowsAdapter); + } + + private ListRow buildGamesRow(int platform) + { + // Create an adapter for this row. + CursorObjectAdapter row = new CursorObjectAdapter(new GamePresenter(platform)); + + Cursor games; + if (platform == Game.PLATFORM_ALL) + { + // Get all games. + games = getContentResolver().query( + GameProvider.URI_GAME, // URI of table to query + null, // Return all columns + null, // Return all games + null, // Return all games + GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order + ); + } + else + { + // Get games for this particular platform. + games = getContentResolver().query( GameProvider.URI_GAME, // URI of table to query null, // Return all columns GameDatabase.KEY_GAME_PLATFORM + " = ?", // Select by platform - new String[]{Integer.toString(platformIndex)}, // Platform id + new String[]{Integer.toString(platform)}, // Platform id GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order ); + } - row.swapCursor(games); - row.setMapper(new CursorMapper() - { - @Override - protected void bindColumns(Cursor cursor) - { - // No-op? Not sure what this does. - } + // If cursor is empty, don't return a Row. + if (!games.moveToFirst()) + { + return null; + } - @Override - protected Object bind(Cursor cursor) - { - return Game.fromCursor(cursor); - } - }); + row.changeCursor(games); + row.setMapper(new CursorMapper() + { + @Override + protected void bindColumns(Cursor cursor) + { + // No-op? Not sure what this does. + } - String headerName; - switch (platformIndex) + @Override + protected Object bind(Cursor cursor) { - case Game.PLATFORM_GC: - headerName = "GameCube Games"; - break; + return Game.fromCursor(cursor); + } + }); - case Game.PLATFORM_WII: - headerName = "Wii Games"; - break; + String headerName; + switch (platform) + { + case Game.PLATFORM_GC: + headerName = "GameCube Games"; + break; - case Game.PLATFORM_WII_WARE: - headerName = "WiiWare"; - break; + case Game.PLATFORM_WII: + headerName = "Wii Games"; + break; - default: - headerName = "Error"; - break; - } + case Game.PLATFORM_WII_WARE: + headerName = "WiiWare"; + break; - // Create a header for this row. - HeaderItem header = new HeaderItem(platformIndex, headerName); + case Game.PLATFORM_ALL: + headerName = "All Games"; + break; - // Create the row, passing it the filled adapter and the header, and give it to the master adapter. - mRowsAdapter.add(new ListRow(header, row)); + default: + headerName = "Error"; + break; } - mBrowseFragment.setAdapter(mRowsAdapter); + // Create a header for this row. + HeaderItem header = new HeaderItem(platform, headerName); + + // Create the row, passing it the filled adapter and the header, and give it to the master adapter. + return new ListRow(header, row); } + + /*private ListRow buildSettingsRow() + { + + }*/ } 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 2068f01f08..33a9f27c54 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 @@ -8,6 +8,7 @@ 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 static final int PLATFORM_ALL = 3; // Copied from IVolume::ECountry. Update these if that is ever modified. public static final int COUNTRY_EUROPE = 0; -- cgit v1.2.3 From 245b58124eeca6c6a72f072c10ad5d8dfb9e0d93 Mon Sep 17 00:00:00 2001 From: sigmabeta Date: Mon, 20 Jul 2015 22:46:12 -0400 Subject: Android TV: Add settings row, enabling access to other screens. --- .../dolphinemu/activities/EmulationActivity.java | 4 +- .../dolphinemu/activities/MainActivity.java | 3 +- .../dolphinemu/activities/TvMainActivity.java | 117 +++++++++++++++++--- .../dolphinemu/adapters/GamePresenter.java | 122 --------------------- .../dolphinemu/adapters/GameRowPresenter.java | 118 ++++++++++++++++++++ .../dolphinemu/adapters/SettingsRowPresenter.java | 47 ++++++++ .../dolphinemu/model/TvSettingsItem.java | 31 ++++++ .../dolphinemu/viewholders/TvGameViewHolder.java | 7 +- .../viewholders/TvSettingsViewHolder.java | 23 ++++ 9 files changed, 328 insertions(+), 144 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java (limited to 'Source/Android/app/src/main/java') 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 index c4cc9e1f3a..04b5149a0e 100644 --- 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 @@ -50,8 +50,8 @@ public final class EmulationActivity extends AppCompatActivity private boolean mSystemUiVisible; private boolean mMenuVisible; - private static Interpolator sDecelerator = new DecelerateInterpolator(); - private static Interpolator sAccelerator = new AccelerateInterpolator(); + private static final Interpolator sDecelerator = new DecelerateInterpolator(); + private static final Interpolator sAccelerator = new AccelerateInterpolator(); /** * Handlers are a way to pass a message to an Activity telling it to do something diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java index a32b65d566..9ffa651cc2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/MainActivity.java @@ -35,7 +35,7 @@ import org.dolphinemu.dolphinemu.services.AssetCopyService; */ public final class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks { - private static final int REQUEST_ADD_DIRECTORY = 1; + public static final int REQUEST_ADD_DIRECTORY = 1; public static final int REQUEST_EMULATE_GAME = 2; /** @@ -139,6 +139,7 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag { fragment.refreshScreenshotAtPosition(resultCode); } + break; } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java index 0e716d6503..1f58698a7e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java @@ -17,12 +17,15 @@ import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; +import android.widget.Toast; import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.adapters.GamePresenter; +import org.dolphinemu.dolphinemu.adapters.GameRowPresenter; +import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter; import org.dolphinemu.dolphinemu.model.Game; import org.dolphinemu.dolphinemu.model.GameDatabase; import org.dolphinemu.dolphinemu.model.GameProvider; +import org.dolphinemu.dolphinemu.model.TvSettingsItem; import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; public final class TvMainActivity extends Activity @@ -56,24 +59,88 @@ public final class TvMainActivity extends Activity @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { - TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; - // Start the emulation activity and send the path of the clicked ISO to it. - Intent intent = new Intent(TvMainActivity.this, EmulationActivity.class); + // Special case: user clicked on a settings row item. + if (item instanceof TvSettingsItem) + { + TvSettingsItem settingsItem = (TvSettingsItem) item; - intent.putExtra("SelectedGame", holder.path); - intent.putExtra("SelectedTitle", holder.title); - intent.putExtra("ScreenPath", holder.screenshotPath); + switch (settingsItem.getItemId()) + { + case R.id.menu_refresh: + getContentResolver().insert(GameProvider.URI_REFRESH, null); - ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( - TvMainActivity.this, - holder.imageScreenshot, - "image_game_screenshot"); + // TODO Let the Activity know the data is refreshed in some other, better way. + recreate(); + break; - startActivity(intent, options.toBundle()); + case R.id.menu_settings: + // Launch the Settings Actvity. + Intent settings = new Intent(TvMainActivity.this, SettingsActivity.class); + startActivity(settings); + break; + + case R.id.button_add_directory: + Intent fileChooser = new Intent(TvMainActivity.this, AddDirectoryActivity.class); + + // The second argument to this method is read below in onActivityResult(). + startActivityForResult(fileChooser, MainActivity.REQUEST_ADD_DIRECTORY); + + break; + + default: + Toast.makeText(TvMainActivity.this, "Unimplemented menu option.", Toast.LENGTH_SHORT).show(); + break; + } + } + else + { + TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; + // Start the emulation activity and send the path of the clicked ISO to it. + Intent intent = new Intent(TvMainActivity.this, EmulationActivity.class); + + intent.putExtra("SelectedGame", holder.path); + intent.putExtra("SelectedTitle", holder.title); + intent.putExtra("ScreenPath", holder.screenshotPath); + + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( + TvMainActivity.this, + holder.imageScreenshot, + "image_game_screenshot"); + + startActivity(intent, options.toBundle()); + } } }); } + /** + * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. + * + * @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 result The information the returning Activity is providing us. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent result) + { + switch (requestCode) + { + case MainActivity.REQUEST_ADD_DIRECTORY: + // If the user picked a file, as opposed to just backing out. + if (resultCode == RESULT_OK) + { + // Sanity check to make sure the Activity that just returned was the AddDirectoryActivity; + // other activities might use this callback in the future (don't forget to change Javadoc!) + if (requestCode == MainActivity.REQUEST_ADD_DIRECTORY) + { + // TODO Let the Activity know the data is refreshed in some other, better way. + recreate(); + } + } + break; + } + } + private void buildRowsAdapter() { mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); @@ -90,13 +157,16 @@ public final class TvMainActivity extends Activity } } + ListRow settingsRow = buildSettingsRow(); + mRowsAdapter.add(settingsRow); + mBrowseFragment.setAdapter(mRowsAdapter); } private ListRow buildGamesRow(int platform) { // Create an adapter for this row. - CursorObjectAdapter row = new CursorObjectAdapter(new GamePresenter(platform)); + CursorObjectAdapter row = new CursorObjectAdapter(new GameRowPresenter()); Cursor games; if (platform == Game.PLATFORM_ALL) @@ -175,8 +245,25 @@ public final class TvMainActivity extends Activity return new ListRow(header, row); } - /*private ListRow buildSettingsRow() + private ListRow buildSettingsRow() { + ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter()); - }*/ + rowItems.add(new TvSettingsItem(R.id.menu_refresh, + R.drawable.ic_refresh_tv, + R.string.grid_menu_refresh)); + + rowItems.add(new TvSettingsItem(R.id.menu_settings, + R.drawable.ic_settings_tv, + R.string.grid_menu_settings)); + + rowItems.add(new TvSettingsItem(R.id.button_add_directory, + R.drawable.ic_add_tv, + R.string.add_directory_title)); + + // Create a header for this row. + HeaderItem header = new HeaderItem(R.string.settings, getString(R.string.settings)); + + return new ListRow(header, rowItems); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java deleted file mode 100644 index d9b4487278..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GamePresenter.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.dolphinemu.dolphinemu.adapters; - -import android.graphics.Bitmap; -import android.support.v17.leanback.widget.ImageCardView; -import android.support.v17.leanback.widget.Presenter; -import android.view.ViewGroup; -import android.widget.ImageView; - -import com.squareup.picasso.Picasso; - -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.model.Game; -import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; - -/** - * The Leanback library / docs call this a Presenter, but it works very - * similarly to a RecyclerView.ViewHolder. - */ -public final class GamePresenter extends Presenter -{ - private int mPlatform; - - public GamePresenter(int platform) - { - mPlatform = platform; - } - - public ViewHolder onCreateViewHolder(ViewGroup parent) - { - // Create a new view. - ImageCardView gameCard = new ImageCardView(parent.getContext()) - { - @Override - public void setSelected(boolean selected) - { - setCardBackground(this, selected); - super.setSelected(selected); - } - }; - - gameCard.setMainImageAdjustViewBounds(true); - gameCard.setMainImageDimensions(480, 320); - gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP); - - gameCard.setFocusable(true); - gameCard.setFocusableInTouchMode(true); - - setCardBackground(gameCard, false); - - // Use that view to create a ViewHolder. - return new TvGameViewHolder(gameCard); - } - - public void onBindViewHolder(ViewHolder viewHolder, Object item) - { - TvGameViewHolder holder = (TvGameViewHolder) viewHolder; - Game game = (Game) item; - - String screenPath = game.getScreenshotPath(); - - // Fill in the view contents. - Picasso.with(holder.imageScreenshot.getContext()) - .load(screenPath) - .fit() - .centerCrop() - .noFade() - .noPlaceholder() - .config(Bitmap.Config.RGB_565) - .error(R.drawable.no_banner) - .into(holder.imageScreenshot); - - holder.cardParent.setTitleText(game.getTitle()); - holder.cardParent.setContentText(game.getCompany()); - - // TODO These shouldn't be necessary once the move to a DB-based model is complete. - 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(); - } - - public void onUnbindViewHolder(ViewHolder viewHolder) - { - // no op - } - - public void setCardBackground(ImageCardView view, boolean selected) - { - int backgroundColor; - - if (selected) - { - switch (mPlatform) - { - case Game.PLATFORM_GC: - backgroundColor = R.color.dolphin_accent_gamecube; - break; - - case Game.PLATFORM_WII: - backgroundColor = R.color.dolphin_accent_wii; - break; - - case Game.PLATFORM_WII_WARE: - backgroundColor = R.color.dolphin_accent_wiiware; - break; - - default: - backgroundColor = android.R.color.holo_red_dark; - break; - } - } - else - { - backgroundColor = R.color.tv_card_unselected; - } - - view.setInfoAreaBackgroundColor(view.getResources().getColor(backgroundColor)); - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java new file mode 100644 index 0000000000..3b0b8095ce --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.java @@ -0,0 +1,118 @@ +package org.dolphinemu.dolphinemu.adapters; + +import android.graphics.Bitmap; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.squareup.picasso.Picasso; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.model.Game; +import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; + +/** + * The Leanback library / docs call this a Presenter, but it works very + * similarly to a RecyclerView.ViewHolder. + */ +public final class GameRowPresenter extends Presenter +{ + public ViewHolder onCreateViewHolder(ViewGroup parent) + { + // Create a new view. + ImageCardView gameCard = new ImageCardView(parent.getContext()) + { + @Override + public void setSelected(boolean selected) + { + setCardBackground(this, selected); + super.setSelected(selected); + } + }; + + gameCard.setMainImageAdjustViewBounds(true); + gameCard.setMainImageDimensions(480, 320); + gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP); + + gameCard.setFocusable(true); + gameCard.setFocusableInTouchMode(true); + + setCardBackground(gameCard, false); + + // Use that view to create a ViewHolder. + return new TvGameViewHolder(gameCard); + } + + public void onBindViewHolder(ViewHolder viewHolder, Object item) + { + TvGameViewHolder holder = (TvGameViewHolder) viewHolder; + Game game = (Game) item; + + String screenPath = game.getScreenshotPath(); + + // Fill in the view contents. + Picasso.with(holder.imageScreenshot.getContext()) + .load(screenPath) + .fit() + .centerCrop() + .noFade() + .noPlaceholder() + .config(Bitmap.Config.RGB_565) + .error(R.drawable.no_banner) + .into(holder.imageScreenshot); + + holder.cardParent.setTitleText(game.getTitle()); + holder.cardParent.setContentText(game.getCompany()); + + // TODO These shouldn't be necessary once the move to a DB-based model is complete. + 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(); + + switch (game.getPlatform()) + { + case Game.PLATFORM_GC: + holder.cardParent.setTag(R.color.dolphin_accent_gamecube); + break; + + case Game.PLATFORM_WII: + holder.cardParent.setTag(R.color.dolphin_accent_wii); + break; + + case Game.PLATFORM_WII_WARE: + holder.cardParent.setTag(R.color.dolphin_accent_wiiware); + break; + + default: + holder.cardParent.setTag(android.R.color.holo_red_dark); + break; + } + } + + public void onUnbindViewHolder(ViewHolder viewHolder) + { + // no op + } + + public void setCardBackground(ImageCardView view, boolean selected) + { + int backgroundColor; + + if (selected) + { + // TODO: 7/20/15 Try using view tag to set color + backgroundColor = (int) view.getTag(); + } + else + { + backgroundColor = R.color.tv_card_unselected; + } + + view.setInfoAreaBackgroundColor(view.getResources().getColor(backgroundColor)); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java new file mode 100644 index 0000000000..beef06a218 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/SettingsRowPresenter.java @@ -0,0 +1,47 @@ +package org.dolphinemu.dolphinemu.adapters; + + +import android.content.res.Resources; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.ViewGroup; + +import org.dolphinemu.dolphinemu.model.TvSettingsItem; +import org.dolphinemu.dolphinemu.viewholders.TvSettingsViewHolder; + +public final class SettingsRowPresenter extends Presenter +{ + public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) + { + // Create a new view. + ImageCardView settingsCard = new ImageCardView(parent.getContext()); + + settingsCard.setMainImageAdjustViewBounds(true); + settingsCard.setMainImageDimensions(192, 160); + + + settingsCard.setFocusable(true); + settingsCard.setFocusableInTouchMode(true); + + // Use that view to create a ViewHolder. + return new TvSettingsViewHolder(settingsCard); + } + + public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) + { + TvSettingsViewHolder holder = (TvSettingsViewHolder) viewHolder; + TvSettingsItem settingsItem = (TvSettingsItem) item; + + Resources resources = holder.cardParent.getResources(); + + holder.itemId = settingsItem.getItemId(); + + holder.cardParent.setTitleText(resources.getString(settingsItem.getLabelId())); + holder.cardParent.setMainImage(resources.getDrawable(settingsItem.getIconId(), null)); + } + + public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) + { + // no op + } +} \ No newline at end of file diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java new file mode 100644 index 0000000000..ccd87bfa4c --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/TvSettingsItem.java @@ -0,0 +1,31 @@ +package org.dolphinemu.dolphinemu.model; + + +public final class TvSettingsItem +{ + private final int mItemId; + private final int mIconId; + private final int mLabelId; + + public TvSettingsItem(int itemId, int iconId, int labelId) + { + mItemId = itemId; + mIconId = iconId; + mLabelId = labelId; + } + + public int getItemId() + { + return mItemId; + } + + public int getIconId() + { + return mIconId; + } + + public int getLabelId() + { + return mLabelId; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java index dd996ccbe7..d27a671c2f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvGameViewHolder.java @@ -4,19 +4,16 @@ import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.Presenter; import android.view.View; import android.widget.ImageView; -import android.widget.TextView; /** * A simple class that stores references to views so that the GameAdapter doesn't need to * keep calling findViewById(), which is expensive. */ -public class TvGameViewHolder extends Presenter.ViewHolder +public final class TvGameViewHolder extends Presenter.ViewHolder { public ImageCardView cardParent; public ImageView imageScreenshot; - public TextView textGameTitle; - public TextView textCompany; public String gameId; @@ -28,6 +25,8 @@ public class TvGameViewHolder extends Presenter.ViewHolder public String company; public String screenshotPath; + public int backgroundColor; + public TvGameViewHolder(View itemView) { super(itemView); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java new file mode 100644 index 0000000000..3264e93f5b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/viewholders/TvSettingsViewHolder.java @@ -0,0 +1,23 @@ +package org.dolphinemu.dolphinemu.viewholders; + + +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.Presenter; +import android.view.View; + +public final class TvSettingsViewHolder extends Presenter.ViewHolder +{ + public ImageCardView cardParent; + + // Determines what action to take when this item is clicked. + public int itemId; + + public TvSettingsViewHolder(View itemView) + { + super(itemView); + + itemView.setTag(this); + + cardParent = (ImageCardView) itemView; + } +} -- cgit v1.2.3 From e7f98c7f959e01cdbf6e09805b9f17719a4efd5f Mon Sep 17 00:00:00 2001 From: sigmabeta Date: Sat, 25 Jul 2015 14:09:58 -0400 Subject: Android TV: Add first-run copy operations to TvMainActivity. --- .../dolphinemu/activities/TvMainActivity.java | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java index 1f58698a7e..3f3b8c168d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/TvMainActivity.java @@ -4,8 +4,10 @@ import android.app.Activity; import android.app.ActivityOptions; import android.app.FragmentManager; import android.content.Intent; +import android.content.SharedPreferences; import android.database.Cursor; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.v17.leanback.app.BrowseFragment; import android.support.v17.leanback.database.CursorMapper; import android.support.v17.leanback.widget.ArrayObjectAdapter; @@ -19,6 +21,7 @@ import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.widget.Toast; +import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.adapters.GameRowPresenter; import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter; @@ -26,6 +29,7 @@ import org.dolphinemu.dolphinemu.model.Game; import org.dolphinemu.dolphinemu.model.GameDatabase; import org.dolphinemu.dolphinemu.model.GameProvider; import org.dolphinemu.dolphinemu.model.TvSettingsItem; +import org.dolphinemu.dolphinemu.services.AssetCopyService; import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; public final class TvMainActivity extends Activity @@ -111,6 +115,23 @@ public final class TvMainActivity extends Activity } } }); + + // Stuff in this block only happens when this activity is newly created (i.e. not a rotation) + if (savedInstanceState == null) + { + NativeLibrary.SetUserDirectory(""); // Auto-Detect + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + boolean assetsCopied = preferences.getBoolean("assetsCopied", false); + + // Only perform these extensive copy operations once. + if (!assetsCopied) + { + // Copy assets into appropriate locations. + Intent copyAssets = new Intent(this, AssetCopyService.class); + startService(copyAssets); + } + } } /** -- cgit v1.2.3