Android: MainActivity now contains a tab switcher separating games by platform.

This commit is contained in:
sigmabeta
2015-06-21 19:22:12 -04:00
committed by Eder Bastos
parent 8b5b83c29a
commit 6b24b604e5
7 changed files with 326 additions and 43 deletions

View File

@ -8,10 +8,11 @@ import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@ -21,7 +22,9 @@ import android.view.View;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.GameAdapter; import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
import org.dolphinemu.dolphinemu.fragments.PlatformGamesFragment;
import org.dolphinemu.dolphinemu.model.Game;
import org.dolphinemu.dolphinemu.model.GameDatabase; import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.model.GameProvider; import org.dolphinemu.dolphinemu.model.GameProvider;
import org.dolphinemu.dolphinemu.services.AssetCopyService; import org.dolphinemu.dolphinemu.services.AssetCopyService;
@ -34,10 +37,17 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
{ {
private static final int REQUEST_ADD_DIRECTORY = 1; 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. * It is important to keep track of loader ID separately from platform ID (see Game.java)
* because we could potentially have Loaders that load things other than Games.
*/
public static final int LOADER_ID_ALL = 100; // TODO
public static final int LOADER_ID_GAMECUBE = 0;
public static final int LOADER_ID_WII = 1;
public static final int LOADER_ID_WIIWARE = 2;
private GameAdapter mAdapter; private ViewPager mViewPager;
private PlatformPagerAdapter mPlatformPagerAdapter;
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
@ -45,29 +55,26 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// Set up the Toolbar.
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
FloatingActionButton buttonAddDirectory = (FloatingActionButton) findViewById(R.id.button_add_directory);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.grid_games);
// TODO Rather than calling into native code, this should use the commented line below. // TODO Rather than calling into native code, this should use the commented line below.
// String versionName = BuildConfig.VERSION_NAME; // String versionName = BuildConfig.VERSION_NAME;
String versionName = NativeLibrary.GetVersionString(); String versionName = NativeLibrary.GetVersionString();
toolbar.setSubtitle(versionName); toolbar.setSubtitle(versionName);
// Specifying the LayoutManager determines how the RecyclerView arranges views. // Set up the Tab bar.
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, mViewPager = (ViewPager) findViewById(R.id.pager_platforms);
getResources().getInteger(R.integer.game_grid_columns));
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); mPlatformPagerAdapter = new PlatformPagerAdapter(getFragmentManager(), this);
mViewPager.setAdapter(mPlatformPagerAdapter);
// Create an adapter that will relate the dataset to the views on-screen. TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs_platforms);
getLoaderManager().initLoader(LOADER_ID_GAMES, null, this); tabLayout.setupWithViewPager(mViewPager);
mAdapter = new GameAdapter();
recyclerView.setAdapter(mAdapter);
// Set up the FAB.
FloatingActionButton buttonAddDirectory = (FloatingActionButton) findViewById(R.id.button_add_directory);
buttonAddDirectory.setOnClickListener(new View.OnClickListener() buttonAddDirectory.setOnClickListener(new View.OnClickListener()
{ {
@Override @Override
@ -115,7 +122,7 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
// other activities might use this callback in the future (don't forget to change Javadoc!) // other activities might use this callback in the future (don't forget to change Javadoc!)
if (requestCode == REQUEST_ADD_DIRECTORY) if (requestCode == REQUEST_ADD_DIRECTORY)
{ {
getLoaderManager().restartLoader(LOADER_ID_GAMES, null, this); refreshFragment();
} }
} }
} }
@ -147,7 +154,7 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
case R.id.menu_refresh: case R.id.menu_refresh:
getContentResolver().insert(GameProvider.URI_REFRESH, null); getContentResolver().insert(GameProvider.URI_REFRESH, null);
getLoaderManager().restartLoader(LOADER_ID_GAMES, null, this); refreshFragment();
return true; return true;
} }
@ -155,6 +162,15 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
return false; return false;
} }
public void refreshFragment()
{
PlatformGamesFragment fragment = getPlatformFragment(mViewPager.getCurrentItem());
if (fragment != null)
{
fragment.refresh();
}
}
/** /**
* Callback that's invoked when the system has initialized the Loader and * Callback that's invoked when the system has initialized the Loader and
@ -173,7 +189,7 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
// Take action based on the ID of the Loader that's being created. // Take action based on the ID of the Loader that's being created.
switch (id) switch (id)
{ {
case LOADER_ID_GAMES: case LOADER_ID_ALL:
// TODO Play some sort of load-starting animation; maybe fade the list out. // TODO Play some sort of load-starting animation; maybe fade the list out.
return new CursorLoader( return new CursorLoader(
@ -185,6 +201,20 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order
); );
case LOADER_ID_GAMECUBE:
case LOADER_ID_WII:
case LOADER_ID_WIIWARE:
// TODO Play some sort of load-starting animation; maybe fade the list out.
return new CursorLoader(
this, // Parent activity context
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
GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order
);
default: default:
Log.e("DolphinEmu", "Bad ID passed in."); Log.e("DolphinEmu", "Bad ID passed in.");
return null; return null;
@ -205,25 +235,73 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag
int id = loader.getId(); int id = loader.getId();
Log.d("DolphinEmu", "Loader finished with id: " + id); Log.d("DolphinEmu", "Loader finished with id: " + id);
// TODO When each platform has its own tab, this should just call into those tabs instead. PlatformGamesFragment fragment = null;
switch (id) switch (id)
{ {
case LOADER_ID_GAMES: case LOADER_ID_GAMECUBE:
mAdapter.swapCursor(data); fragment = getPlatformFragment(Game.PLATFORM_GC);
// TODO Play some sort of load-finished animation; maybe fade the list in.
break; break;
case LOADER_ID_WII:
fragment = getPlatformFragment(Game.PLATFORM_WII);
break;
case LOADER_ID_WIIWARE:
fragment = getPlatformFragment(Game.PLATFORM_WII_WARE);
break;
// TODO case LOADER_ID_ALL:
default: default:
Log.e("DolphinEmu", "Bad ID passed in."); Log.e("DolphinEmu", "Bad ID passed in.");
break;
} }
if (fragment != null)
{
fragment.onLoadFinished(loader, data);
}
} }
@Override @Override
public void onLoaderReset(Loader<Cursor> loader) public void onLoaderReset(Loader<Cursor> loader)
{ {
Log.d("DolphinEmu", "Loader resetting."); int id = loader.getId();
Log.e("DolphinEmu", "Loader resetting with id: " + id);
// TODO ¯\_(ツ)_/¯ PlatformGamesFragment fragment = null;
switch (id)
{
case LOADER_ID_GAMECUBE:
fragment = getPlatformFragment(Game.PLATFORM_GC);
break;
case LOADER_ID_WII:
fragment = getPlatformFragment(Game.PLATFORM_WII);
break;
case LOADER_ID_WIIWARE:
fragment = getPlatformFragment(Game.PLATFORM_WII_WARE);
break;
// TODO case LOADER_ID_ALL:
default:
Log.e("DolphinEmu", "Bad ID passed in.");
break;
}
if (fragment != null)
{
fragment.onLoaderReset();
}
}
@Nullable
public PlatformGamesFragment getPlatformFragment(int platform)
{
String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform;
return (PlatformGamesFragment) getFragmentManager().findFragmentByTag(fragmentTag);
} }
} }

View File

@ -0,0 +1,61 @@
package org.dolphinemu.dolphinemu.adapters;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v13.app.FragmentPagerAdapter;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.fragments.PlatformGamesFragment;
public class PlatformPagerAdapter extends FragmentPagerAdapter
{
private Context mContext;
private final static int[] TAB_ICONS = {
R.drawable.ic_gamecube,
R.drawable.ic_wii,
R.drawable.ic_folder// wiiware TODO Have an icon here.
};
public PlatformPagerAdapter(FragmentManager fm, Context context)
{
super(fm);
mContext = context;
}
@Override
public Fragment getItem(int position)
{
return PlatformGamesFragment.newInstance(position);
}
@Override
public int getCount()
{
return TAB_ICONS.length;
}
@Override
public CharSequence getPageTitle(int position)
{
// Hax from https://guides.codepath.com/android/Google-Play-Style-Tabs-using-TabLayout#design-support-library
// Apparently a workaround for TabLayout not supporting icons.
// TODO This workaround will eventually not be necessary; switch to more legit methods when that is the case
// TODO Also remove additional hax from styles.xml
Drawable drawable = mContext.getResources().getDrawable(TAB_ICONS[position]);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
SpannableString sb = new SpannableString(" ");
sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb;
}
}

View File

@ -0,0 +1,110 @@
package org.dolphinemu.dolphinemu.fragments;
import android.app.Activity;
import android.app.Fragment;
import android.app.LoaderManager;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.GameAdapter;
public class PlatformGamesFragment extends Fragment
{
private static final String ARG_PLATFORM = BuildConfig.APPLICATION_ID + ".PLATFORM";
private int mPlatform;
private GameAdapter mAdapter;
public static PlatformGamesFragment newInstance(int platform)
{
PlatformGamesFragment fragment = new PlatformGamesFragment();
Bundle args = new Bundle();
args.putInt(ARG_PLATFORM, platform);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mPlatform = getArguments().getInt(ARG_PLATFORM);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_grid, container, false);
RecyclerView recyclerView = (RecyclerView) rootView.findViewById(R.id.grid_games);
// Specifying the LayoutManager determines how the RecyclerView arranges views.
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(),
getResources().getInteger(R.integer.game_grid_columns));
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
// Create an adapter that will relate the dataset to the views on-screen. +1 because of LOADER_ID_ALL
getLoaderManager().initLoader(mPlatform, null,
(LoaderManager.LoaderCallbacks<Cursor>) getActivity());
mAdapter = new GameAdapter();
recyclerView.setAdapter(mAdapter);
return rootView;
}
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
}
public void refresh()
{
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Refreshing...");
// +1 because of LOADER_ID_ALL
getLoaderManager().restartLoader(mPlatform, null, (LoaderManager.LoaderCallbacks<Cursor>) getActivity());
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
// TODO Play some sort of load-finished animation; maybe fade the list in.
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Load finished, swapping cursor...");
Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Cursor size: " + data.getCount());
if (mAdapter != null)
{
mAdapter.swapCursor(data);
}
else
{
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": No adapter available.");
}
}
public void onLoaderReset()
{
Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Loader reset; clearing data from view.");
if (mAdapter != null)
{
mAdapter.swapCursor(null);
}
}
}

View File

@ -196,7 +196,14 @@ public final class GameDatabase extends SQLiteOpenHelper
gameId = filePath.substring(filePath.lastIndexOf("/") + 1, filePath.lastIndexOf(".")); gameId = filePath.substring(filePath.lastIndexOf("/") + 1, filePath.lastIndexOf("."));
} }
ContentValues game = Game.asContentValues(NativeLibrary.GetPlatform(filePath), // If the game's platform field is empty, file under Wiiware. // TODO Something less dum
int platform = NativeLibrary.GetPlatform(filePath);
if (platform == -1)
{
platform = Game.PLATFORM_WII_WARE;
}
ContentValues game = Game.asContentValues(platform,
name, name,
NativeLibrary.GetDescription(filePath).replace("\n", " "), NativeLibrary.GetDescription(filePath).replace("\n", " "),
NativeLibrary.GetCountry(filePath), NativeLibrary.GetCountry(filePath),

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_main" android:id="@+id/coordinator_main"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -10,7 +9,7 @@
<android.support.design.widget.AppBarLayout <android.support.design.widget.AppBarLayout
android:id="@+id/appbar" android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/main_appbar_height" android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar <android.support.v7.widget.Toolbar
@ -20,25 +19,31 @@
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_scrollFlags="scroll|enterAlways"/> app:layout_scrollFlags="scroll|enterAlways"/>
<android.support.design.widget.TabLayout
android:id="@+id/tabs_platforms"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextAppearance="@style/MyCustomTextAppearance"
app:tabMode="fixed"
app:tabGravity="fill"/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView <android.support.v4.view.ViewPager
android:id="@+id/grid_games" android:id="@+id/pager_platforms"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:listitem="@layout/grid_card_game"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<android.support.design.widget.FloatingActionButton <android.support.design.widget.FloatingActionButton
android:id="@+id/button_add_directory" android:id="@+id/button_add_directory"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_margin="16dp" android:layout_margin="32dp"
android:src="@drawable/ic_add" android:src="@drawable/ic_add"
app:borderWidth="0dp" app:borderWidth="0dp"
app:rippleColor="?android:colorPrimaryDark" app:rippleColor="?android:colorPrimaryDark"
app:layout_anchor="@+id/grid_games" app:layout_anchor="@+id/pager_platforms"
app:layout_anchorGravity="bottom|right|end"/> app:layout_anchorGravity="bottom|right|end"/>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/grid_games"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
tools:listitem="@layout/grid_card_game"/>
</FrameLayout>

View File

@ -80,4 +80,11 @@
<style name="DolphinEmulationWiiware" parent="DolphinEmulationBase"> <style name="DolphinEmulationWiiware" parent="DolphinEmulationBase">
<item name="colorAccent">@color/dolphin_accent_wiiware</item> <item name="colorAccent">@color/dolphin_accent_wiiware</item>
</style> </style>
<!-- Hax to make Tablayout render icons -->
<style name="MyCustomTextAppearance" parent="TextAppearance.Design.Tab">
<item name="textAllCaps">false</item>
</style>
</resources> </resources>