Skip to content

Commit

Permalink
Android: Store folders to scan in SharedPreferences instead of SQLite
Browse files Browse the repository at this point in the history
Needed in order for the next commit to get rid of the SQLite database.
  • Loading branch information
JosJuice committed Jun 4, 2018
1 parent 5f29e89 commit daee5a4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 66 deletions.
Expand Up @@ -2,9 +2,11 @@

import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.preference.PreferenceManager;

import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
Expand Down Expand Up @@ -37,8 +39,6 @@ public final class GameDatabase extends SQLiteOpenHelper
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";
Expand All @@ -49,18 +49,12 @@ public final class GameDatabase extends SQLiteOpenHelper
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 + "("
Expand All @@ -74,17 +68,19 @@ public final class GameDatabase extends SQLiteOpenHelper
+ 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_FOLDERS = "DROP TABLE IF EXISTS " + TABLE_NAME_FOLDERS;
private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES;

private static final String GAME_FOLDER_PATHS_PREFERENCE = "gameFolderPaths";

private static final Set<String> EMPTY_SET = new HashSet<>();

private Context mContext;

public GameDatabase(Context context)
{
// Superclass constructor builds a database or uses an existing one.
super(context, "games.db", null, DB_VERSION);
mContext = context;
}

@Override
Expand All @@ -93,16 +89,12 @@ public void onCreate(SQLiteDatabase database)
Log.debug("[GameDatabase] GameDatabase - Creating database...");

execSqlAndLog(database, SQL_CREATE_GAMES);
execSqlAndLog(database, SQL_CREATE_FOLDERS);
}

@Override
public void onDowngrade(SQLiteDatabase database, int oldVersion, int newVersion)
{
Log.verbose("[GameDatabase] Downgrades not supporting, clearing databases..");
execSqlAndLog(database, SQL_DELETE_FOLDERS);
execSqlAndLog(database, SQL_CREATE_FOLDERS);

execSqlAndLog(database, SQL_DELETE_GAMES);
execSqlAndLog(database, SQL_CREATE_GAMES);
}
Expand All @@ -120,6 +112,19 @@ public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion)
scanLibrary(database);
}

public void addGameFolder(String path)
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
Set<String> newFolderPaths = new HashSet<>(folderPaths);
newFolderPaths.add(path);
SharedPreferences.Editor editor = preferences.edit();
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
editor.apply();

scanLibrary(getWritableDatabase());
}

public void scanLibrary(SQLiteDatabase database)
{
// Before scanning known folders, go through the game table and remove any entries for which the file itself is missing.
Expand Down Expand Up @@ -148,28 +153,17 @@ public void scanLibrary(SQLiteDatabase database)
}
}


// Get a cursor listing all the folders the user has added to the library.
Cursor folderCursor = 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<String> allowedExtensions = new HashSet<String>(Arrays.asList(
".ciso", ".dff", ".dol", ".elf", ".gcm", ".gcz", ".iso", ".tgc", ".wad", ".wbfs"));

// Possibly overly defensive, but ensures that moveToNext() does not skip a row.
folderCursor.moveToPosition(-1);

// Iterate through all results of the DB query (i.e. all folders in the library.)
while (folderCursor.moveToNext())
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext);
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
Set<String> newFolderPaths = new HashSet<>();
for (String folderPath : folderPaths)
{

String folderPath = folderCursor.getString(FOLDER_COLUMN_PATH);
File folder = new File(folderPath);
boolean deleteFolder = false;

Log.info("[GameDatabase] Reading files from library folder: " + folderPath);

Expand Down Expand Up @@ -245,19 +239,29 @@ public void scanLibrary(SQLiteDatabase database)
else if (!folder.exists())
{
Log.error("[GameDatabase] Folder no longer exists. Removing from the library: " + folderPath);
database.delete(TABLE_NAME_FOLDERS,
KEY_DB_ID + " = ?",
new String[]{Long.toString(folderCursor.getLong(COLUMN_DB_ID))});
deleteFolder = true;
}
else
{
Log.error("[GameDatabase] Folder contains no games: " + folderPath);
}

if (!deleteFolder)
{
newFolderPaths.add(folderPath);
}
}

fileCursor.close();
folderCursor.close();
database.close();

if (folderPaths.size() != newFolderPaths.size())
{
// One or more folders are being deleted
SharedPreferences.Editor editor = preferences.edit();
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
editor.apply();
}
}

public Observable<Cursor> getGamesForPlatform(final Platform platform)
Expand Down
Expand Up @@ -19,7 +19,6 @@ public final class GameProvider extends ContentProvider
public static final String REFRESH_LIBRARY = "refresh";

public static final String AUTHORITY = "content://" + BuildConfig.APPLICATION_ID + ".provider";
public static final Uri URI_FOLDER = Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_FOLDERS + "/");
public static final Uri URI_GAME = Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_GAMES + "/");
public static final Uri URI_REFRESH = Uri.parse(AUTHORITY + "/" + REFRESH_LIBRARY + "/");

Expand Down Expand Up @@ -72,11 +71,7 @@ public String getType(@NonNull Uri uri)
return null;
}

if (lastSegment.equals(GameDatabase.TABLE_NAME_FOLDERS))
{
return MIME_TYPE_FOLDER;
}
else if (lastSegment.equals(GameDatabase.TABLE_NAME_GAMES))
if (lastSegment.equals(GameDatabase.TABLE_NAME_GAMES))
{
return MIME_TYPE_GAME;
}
Expand Down Expand Up @@ -109,12 +104,6 @@ public Uri insert(@NonNull Uri uri, ContentValues values)
// 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));
Expand Down
@@ -1,12 +1,10 @@
package org.dolphinemu.dolphinemu.utils;

import android.content.AsyncQueryHandler;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;

import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.model.GameProvider;

public class AddDirectoryHelper
{
Expand All @@ -24,21 +22,23 @@ public AddDirectoryHelper(Context context)

public void addDirectory(String dir, AddDirectoryListener addDirectoryListener)
{
AsyncQueryHandler handler = new AsyncQueryHandler(mContext.getContentResolver())
new AsyncTask<String, Void, Void>()
{
@Override
protected void onInsertComplete(int token, Object cookie, Uri uri)
protected Void doInBackground(String... params)
{
addDirectoryListener.onDirectoryAdded();
}
};

ContentValues file = new ContentValues();
file.put(GameDatabase.KEY_FOLDER_PATH, dir);

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);
for (String path : params)
{
DolphinApplication.databaseHelper.addGameFolder(path);
}
return null;
}

@Override
protected void onPostExecute(Void result)
{
addDirectoryListener.onDirectoryAdded();
}
}.execute(dir);
}
}

0 comments on commit daee5a4

Please sign in to comment.