From f58a8561f856bdf7f83019d74c485f69e953eb76 Mon Sep 17 00:00:00 2001 From: sigmabeta Date: Sat, 9 Jan 2016 12:28:29 -0500 Subject: [PATCH] Refactor DB read code to use RxJava --- Source/Android/app/build.gradle | 3 + .../Android/app/src/main/AndroidManifest.xml | 1 + .../dolphinemu/DolphinApplication.java | 19 +++ .../dolphinemu/activities/MainActivity.java | 140 +----------------- .../fragments/PlatformGamesFragment.java | 59 ++++---- .../dolphinemu/model/GameDatabase.java | 43 ++++++ 6 files changed, 99 insertions(+), 166 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java diff --git a/Source/Android/app/build.gradle b/Source/Android/app/build.gradle index 62588307e0..dacc7d6ff5 100644 --- a/Source/Android/app/build.gradle +++ b/Source/Android/app/build.gradle @@ -89,6 +89,9 @@ dependencies { // For loading huge screenshots from the disk. compile 'com.squareup.picasso:picasso:2.5.2' + + // Allows FRP-style asynchronous operations in Android. + compile 'io.reactivex:rxandroid:1.1.0' } task setupCMake(type: Exec) { diff --git a/Source/Android/app/src/main/AndroidManifest.xml b/Source/Android/app/src/main/AndroidManifest.xml index e08b2dd0a8..adfda3d0ce 100644 --- a/Source/Android/app/src/main/AndroidManifest.xml +++ b/Source/Android/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ +public final class MainActivity extends AppCompatActivity { public static final int REQUEST_ADD_DIRECTORY = 1; public static final int REQUEST_EMULATE_GAME = 2; - /** - * 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 ViewPager mViewPager; private PlatformPagerAdapter mPlatformPagerAdapter; @@ -175,132 +163,6 @@ public final class MainActivity extends AppCompatActivity implements LoaderManag } } - - /** - * 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) - { - Log.d("DolphinEmu", "Creating loader with id: " + id); - - // Take action based on the ID of the Loader that's being created. - switch (id) - { - case LOADER_ID_ALL: - // 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 - null, // No selection clause - null, // No selection arguments - 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 - GameDatabase.KEY_GAME_TITLE + " asc" // Sort by game name, ascending order - ); - - default: - Log.e("DolphinEmu", "Bad ID passed in."); - return null; - } - } - - /** - * 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); - - 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.onLoadFinished(loader, data); - } - } - - @Override - public void onLoaderReset(Loader loader) - { - int id = loader.getId(); - Log.e("DolphinEmu", "Loader resetting with id: " + id); - - 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) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/PlatformGamesFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/PlatformGamesFragment.java index c1d1b1c8e7..8fff63bbcd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/PlatformGamesFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/PlatformGamesFragment.java @@ -1,8 +1,6 @@ package org.dolphinemu.dolphinemu.fragments; 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; @@ -15,8 +13,14 @@ import android.view.View; import android.view.ViewGroup; import org.dolphinemu.dolphinemu.BuildConfig; +import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.adapters.GameAdapter; +import org.dolphinemu.dolphinemu.model.GameDatabase; + +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; +import rx.schedulers.Schedulers; public class PlatformGamesFragment extends Fragment { @@ -69,9 +73,7 @@ public class PlatformGamesFragment extends Fragment 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) getActivity()); + loadGames(); mAdapter = new GameAdapter(); recyclerView.setAdapter(mAdapter); @@ -87,32 +89,35 @@ public class PlatformGamesFragment extends Fragment public void refresh() { Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Refreshing..."); - // +1 because of LOADER_ID_ALL - getLoaderManager().restartLoader(mPlatform, null, (LoaderManager.LoaderCallbacks) getActivity()); + loadGames(); } - public void onLoadFinished(Loader loader, Cursor data) + public void loadGames() { - // TODO Play some sort of load-finished animation; maybe fade the list in. + Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Loading games..."); - 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."); - } - } + GameDatabase databaseHelper = DolphinApplication.databaseHelper; - public void onLoaderReset() - { - Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Loader reset; clearing data from view."); - if (mAdapter != null) - { - mAdapter.swapCursor(null); - } + databaseHelper.getGamesForPlatform(mPlatform) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(new Action1() + { + @Override + public void call(Cursor data) + { + Log.d("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": Load finished, swapping cursor..."); + + if (mAdapter != null) + { + mAdapter.swapCursor(data); + } + else + { + Log.e("DolphinEmu", "[PlatformGamesFragment] " + mPlatform + ": No adapter available."); + } + } + } + ); } } 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 index 6aaee3ae16..ae674c921f 100644 --- 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 @@ -14,6 +14,9 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Set; +import rx.Observable; +import rx.Subscriber; + /** * A helper class that provides several utilities simplifying interaction with * the SQLite database. @@ -252,4 +255,44 @@ public final class GameDatabase extends SQLiteOpenHelper folderCursor.close(); database.close(); } + + public Observable getGamesForPlatform(final int platform) + { + return Observable.create(new Observable.OnSubscribe() + { + @Override + public void call(Subscriber subscriber) + { + Log.i("DolphinEmu", "[GameDatabase] Reading games list..."); + + String whereClause = null; + String[] whereArgs = null; + + // If -1 passed in, return all games. Else, return games for one platform only. + if (platform >= 0) + { + whereClause = KEY_GAME_PLATFORM + " = ?"; + whereArgs = new String[]{Integer.toString(platform)}; + } + + SQLiteDatabase database = getReadableDatabase(); + + Cursor resultCursor = database.query( + TABLE_NAME_GAMES, + null, + whereClause, + whereArgs, + null, + null, + KEY_GAME_TITLE + " ASC" + ); + + // Pass the result cursor to the consumer. + subscriber.onNext(resultCursor); + + // Tell the consumer we're done; it will unsubscribe implicitly. + subscriber.onCompleted(); + } + }); + } }