android: Convert GameDatabase to Kotlin
This commit is contained in:
		| @@ -1,275 +0,0 @@ | |||||||
| package org.yuzu.yuzu_emu.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.net.Uri; |  | ||||||
|  |  | ||||||
| import org.yuzu.yuzu_emu.NativeLibrary; |  | ||||||
| import org.yuzu.yuzu_emu.utils.FileUtil; |  | ||||||
| import org.yuzu.yuzu_emu.utils.Log; |  | ||||||
|  |  | ||||||
| import java.io.File; |  | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.HashSet; |  | ||||||
| import java.util.Set; |  | ||||||
|  |  | ||||||
| import rx.Observable; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * A helper class that provides several utilities simplifying interaction with |  | ||||||
|  * the SQLite database. |  | ||||||
|  */ |  | ||||||
| public final class GameDatabase extends SQLiteOpenHelper { |  | ||||||
|     public static final int COLUMN_DB_ID = 0; |  | ||||||
|     public static final int GAME_COLUMN_PATH = 1; |  | ||||||
|     public static final int GAME_COLUMN_TITLE = 2; |  | ||||||
|     public static final int GAME_COLUMN_DESCRIPTION = 3; |  | ||||||
|     public static final int GAME_COLUMN_REGIONS = 4; |  | ||||||
|     public static final int GAME_COLUMN_GAME_ID = 5; |  | ||||||
|     public static final int GAME_COLUMN_CAPTION = 6; |  | ||||||
|     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_TITLE = "title"; |  | ||||||
|     public static final String KEY_GAME_DESCRIPTION = "description"; |  | ||||||
|     public static final String KEY_GAME_REGIONS = "regions"; |  | ||||||
|     public static final String KEY_GAME_ID = "game_id"; |  | ||||||
|     public static final String KEY_GAME_COMPANY = "company"; |  | ||||||
|     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 int DB_VERSION = 2; |  | ||||||
|     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_TITLE + TYPE_STRING + SEPARATOR |  | ||||||
|             + KEY_GAME_DESCRIPTION + TYPE_STRING + SEPARATOR |  | ||||||
|             + KEY_GAME_REGIONS + TYPE_STRING + SEPARATOR |  | ||||||
|             + KEY_GAME_ID + TYPE_STRING + SEPARATOR |  | ||||||
|             + KEY_GAME_COMPANY + 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 final Context context; |  | ||||||
|  |  | ||||||
|     public GameDatabase(Context context) { |  | ||||||
|         // Superclass constructor builds a database or uses an existing one. |  | ||||||
|         super(context, "games.db", null, DB_VERSION); |  | ||||||
|         this.context = context; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     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); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) { |  | ||||||
|         Log.info("[GameDatabase] Upgrading database from schema version " + oldVersion + " to " + |  | ||||||
|                 newVersion); |  | ||||||
|  |  | ||||||
|         // Delete all the games |  | ||||||
|         execSqlAndLog(database, SQL_DELETE_GAMES); |  | ||||||
|         execSqlAndLog(database, SQL_CREATE_GAMES); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void resetDatabase(SQLiteDatabase database) { |  | ||||||
|         execSqlAndLog(database, SQL_DELETE_FOLDERS); |  | ||||||
|         execSqlAndLog(database, SQL_CREATE_FOLDERS); |  | ||||||
|  |  | ||||||
|         execSqlAndLog(database, SQL_DELETE_GAMES); |  | ||||||
|         execSqlAndLog(database, SQL_CREATE_GAMES); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     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. |  | ||||||
|         Cursor fileCursor = database.query(TABLE_NAME_GAMES, |  | ||||||
|                 null,    // Get all columns. |  | ||||||
|                 null,    // Get all rows. |  | ||||||
|                 null, |  | ||||||
|                 null,    // No grouping. |  | ||||||
|                 null, |  | ||||||
|                 null);    // Order of games is irrelevant. |  | ||||||
|  |  | ||||||
|         // Possibly overly defensive, but ensures that moveToNext() does not skip a row. |  | ||||||
|         fileCursor.moveToPosition(-1); |  | ||||||
|  |  | ||||||
|         while (fileCursor.moveToNext()) { |  | ||||||
|             String gamePath = fileCursor.getString(GAME_COLUMN_PATH); |  | ||||||
|             File game = new File(gamePath); |  | ||||||
|  |  | ||||||
|             if (!game.exists()) { |  | ||||||
|                 database.delete(TABLE_NAME_GAMES, |  | ||||||
|                         KEY_DB_ID + " = ?", |  | ||||||
|                         new String[]{Long.toString(fileCursor.getLong(COLUMN_DB_ID))}); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // 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( |  | ||||||
|                 ".xci", ".nsp", ".nca", ".nro")); |  | ||||||
|  |  | ||||||
|         // 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()) { |  | ||||||
|             String folderPath = folderCursor.getString(FOLDER_COLUMN_PATH); |  | ||||||
|  |  | ||||||
|             Uri folderUri = Uri.parse(folderPath); |  | ||||||
|             // If the folder is empty because it no longer exists, remove it from the library. |  | ||||||
|             if (FileUtil.listFiles(context, folderUri).length == 0) { |  | ||||||
|                 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))}); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             this.addGamesRecursive(database, folderUri, allowedExtensions, 3); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         fileCursor.close(); |  | ||||||
|         folderCursor.close(); |  | ||||||
|  |  | ||||||
|         database.close(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void addGamesRecursive(SQLiteDatabase database, Uri parent, Set<String> allowedExtensions, int depth) { |  | ||||||
|         if (depth <= 0) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Ensure keys are loaded so that ROM metadata can be decrypted. |  | ||||||
|         NativeLibrary.ReloadKeys(); |  | ||||||
|  |  | ||||||
|         MinimalDocumentFile[] children = FileUtil.listFiles(context, parent); |  | ||||||
|         for (MinimalDocumentFile file : children) { |  | ||||||
|             if (file.isDirectory()) { |  | ||||||
|                 Set<String> newExtensions = new HashSet<>(Arrays.asList( |  | ||||||
|                         ".xci", ".nsp", ".nca", ".nro")); |  | ||||||
|                 this.addGamesRecursive(database, file.getUri(), newExtensions, depth - 1); |  | ||||||
|             } else { |  | ||||||
|                 String filename = file.getUri().toString(); |  | ||||||
|  |  | ||||||
|                 int extensionStart = filename.lastIndexOf('.'); |  | ||||||
|                 if (extensionStart > 0) { |  | ||||||
|                     String fileExtension = filename.substring(extensionStart); |  | ||||||
|  |  | ||||||
|                     // Check that the file has an extension we care about before trying to read out of it. |  | ||||||
|                     if (allowedExtensions.contains(fileExtension.toLowerCase())) { |  | ||||||
|                         attemptToAddGame(database, filename); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static void attemptToAddGame(SQLiteDatabase database, String filePath) { |  | ||||||
|         String name = NativeLibrary.GetTitle(filePath); |  | ||||||
|  |  | ||||||
|         // If the game's title field is empty, use the filename. |  | ||||||
|         if (name.isEmpty()) { |  | ||||||
|             name = filePath.substring(filePath.lastIndexOf("/") + 1); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         String gameId = NativeLibrary.GetGameId(filePath); |  | ||||||
|  |  | ||||||
|         // If the game's ID field is empty, use the filename without extension. |  | ||||||
|         if (gameId.isEmpty()) { |  | ||||||
|             gameId = filePath.substring(filePath.lastIndexOf("/") + 1, |  | ||||||
|                     filePath.lastIndexOf(".")); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         ContentValues game = Game.asContentValues(name, |  | ||||||
|                 NativeLibrary.GetDescription(filePath).replace("\n", " "), |  | ||||||
|                 NativeLibrary.GetRegions(filePath), |  | ||||||
|                 filePath, |  | ||||||
|                 gameId, |  | ||||||
|                 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.verbose("[GameDatabase] Adding game: " + game.getAsString(KEY_GAME_TITLE)); |  | ||||||
|             database.insert(TABLE_NAME_GAMES, null, game); |  | ||||||
|         } else { |  | ||||||
|             Log.verbose("[GameDatabase] Updated game: " + game.getAsString(KEY_GAME_TITLE)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public Observable<Cursor> getGames() { |  | ||||||
|         return Observable.create(subscriber -> |  | ||||||
|         { |  | ||||||
|             Log.info("[GameDatabase] Reading games list..."); |  | ||||||
|  |  | ||||||
|             SQLiteDatabase database = getReadableDatabase(); |  | ||||||
|             Cursor resultCursor = database.query( |  | ||||||
|                     TABLE_NAME_GAMES, |  | ||||||
|                     null, |  | ||||||
|                     null, |  | ||||||
|                     null, |  | ||||||
|                     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(); |  | ||||||
|         }); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void execSqlAndLog(SQLiteDatabase database, String sql) { |  | ||||||
|         Log.verbose("[GameDatabase] Executing SQL: " + sql); |  | ||||||
|         database.execSQL(sql); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,260 @@ | |||||||
|  | package org.yuzu.yuzu_emu.model | ||||||
|  |  | ||||||
|  | import android.content.Context | ||||||
|  | import android.database.Cursor | ||||||
|  | import android.database.sqlite.SQLiteDatabase | ||||||
|  | import android.database.sqlite.SQLiteOpenHelper | ||||||
|  | import android.net.Uri | ||||||
|  | import org.yuzu.yuzu_emu.NativeLibrary | ||||||
|  | import org.yuzu.yuzu_emu.utils.FileUtil | ||||||
|  | import org.yuzu.yuzu_emu.utils.Log | ||||||
|  | import rx.Observable | ||||||
|  | import rx.Subscriber | ||||||
|  | import java.io.File | ||||||
|  | import java.util.* | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * A helper class that provides several utilities simplifying interaction with | ||||||
|  |  * the SQLite database. | ||||||
|  |  */ | ||||||
|  | class GameDatabase(private val context: Context) : | ||||||
|  |     SQLiteOpenHelper(context, "games.db", null, DB_VERSION) { | ||||||
|  |     override fun onCreate(database: SQLiteDatabase) { | ||||||
|  |         Log.debug("[GameDatabase] GameDatabase - Creating database...") | ||||||
|  |         execSqlAndLog(database, SQL_CREATE_GAMES) | ||||||
|  |         execSqlAndLog(database, SQL_CREATE_FOLDERS) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun onDowngrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) { | ||||||
|  |         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) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun onUpgrade(database: SQLiteDatabase, oldVersion: Int, newVersion: Int) { | ||||||
|  |         Log.info( | ||||||
|  |             "[GameDatabase] Upgrading database from schema version $oldVersion to $newVersion" | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         // Delete all the games | ||||||
|  |         execSqlAndLog(database, SQL_DELETE_GAMES) | ||||||
|  |         execSqlAndLog(database, SQL_CREATE_GAMES) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun resetDatabase(database: SQLiteDatabase) { | ||||||
|  |         execSqlAndLog(database, SQL_DELETE_FOLDERS) | ||||||
|  |         execSqlAndLog(database, SQL_CREATE_FOLDERS) | ||||||
|  |         execSqlAndLog(database, SQL_DELETE_GAMES) | ||||||
|  |         execSqlAndLog(database, SQL_CREATE_GAMES) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun scanLibrary(database: SQLiteDatabase) { | ||||||
|  |         // Before scanning known folders, go through the game table and remove any entries for which the file itself is missing. | ||||||
|  |         val fileCursor = database.query( | ||||||
|  |             TABLE_NAME_GAMES, | ||||||
|  |             null,  // Get all columns. | ||||||
|  |             null,  // Get all rows. | ||||||
|  |             null, | ||||||
|  |             null,  // No grouping. | ||||||
|  |             null, | ||||||
|  |             null | ||||||
|  |         ) // Order of games is irrelevant. | ||||||
|  |  | ||||||
|  |         // Possibly overly defensive, but ensures that moveToNext() does not skip a row. | ||||||
|  |         fileCursor.moveToPosition(-1) | ||||||
|  |         while (fileCursor.moveToNext()) { | ||||||
|  |             val gamePath = fileCursor.getString(GAME_COLUMN_PATH) | ||||||
|  |             val game = File(gamePath) | ||||||
|  |             if (!game.exists()) { | ||||||
|  |                 database.delete( | ||||||
|  |                     TABLE_NAME_GAMES, | ||||||
|  |                     "$KEY_DB_ID = ?", | ||||||
|  |                     arrayOf(fileCursor.getLong(COLUMN_DB_ID).toString()) | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Get a cursor listing all the folders the user has added to the library. | ||||||
|  |         val 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. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         // 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()) { | ||||||
|  |             val folderPath = folderCursor.getString(FOLDER_COLUMN_PATH) | ||||||
|  |             val folderUri = Uri.parse(folderPath) | ||||||
|  |             // If the folder is empty because it no longer exists, remove it from the library. | ||||||
|  |             if (FileUtil.listFiles(context, folderUri).isEmpty()) { | ||||||
|  |                 Log.error( | ||||||
|  |                     "[GameDatabase] Folder no longer exists. Removing from the library: $folderPath" | ||||||
|  |                 ) | ||||||
|  |                 database.delete( | ||||||
|  |                     TABLE_NAME_FOLDERS, | ||||||
|  |                     "$KEY_DB_ID = ?", | ||||||
|  |                     arrayOf(folderCursor.getLong(COLUMN_DB_ID).toString()) | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             addGamesRecursive(database, folderUri, Game.extensions, 3) | ||||||
|  |         } | ||||||
|  |         fileCursor.close() | ||||||
|  |         folderCursor.close() | ||||||
|  |         database.close() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun addGamesRecursive( | ||||||
|  |         database: SQLiteDatabase, | ||||||
|  |         parent: Uri, | ||||||
|  |         allowedExtensions: Set<String>, | ||||||
|  |         depth: Int | ||||||
|  |     ) { | ||||||
|  |         if (depth <= 0) | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         // Ensure keys are loaded so that ROM metadata can be decrypted. | ||||||
|  |         NativeLibrary.ReloadKeys() | ||||||
|  |         val children = FileUtil.listFiles(context, parent) | ||||||
|  |         for (file in children) { | ||||||
|  |             if (file.isDirectory) { | ||||||
|  |                 addGamesRecursive(database, file.uri, Game.extensions, depth - 1) | ||||||
|  |             } else { | ||||||
|  |                 val filename = file.uri.toString() | ||||||
|  |                 val extensionStart = filename.lastIndexOf('.') | ||||||
|  |                 if (extensionStart > 0) { | ||||||
|  |                     val fileExtension = filename.substring(extensionStart) | ||||||
|  |  | ||||||
|  |                     // Check that the file has an extension we care about before trying to read out of it. | ||||||
|  |                     if (allowedExtensions.contains(fileExtension.lowercase(Locale.getDefault()))) { | ||||||
|  |                         attemptToAddGame(database, filename) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     // Pass the result cursor to the consumer. | ||||||
|  |  | ||||||
|  |     // Tell the consumer we're done; it will unsubscribe implicitly. | ||||||
|  |     val games: Observable<Cursor?> | ||||||
|  |         get() = Observable.create { subscriber: Subscriber<in Cursor?> -> | ||||||
|  |             Log.info("[GameDatabase] Reading games list...") | ||||||
|  |             val database = readableDatabase | ||||||
|  |             val resultCursor = database.query( | ||||||
|  |                 TABLE_NAME_GAMES, | ||||||
|  |                 null, | ||||||
|  |                 null, | ||||||
|  |                 null, | ||||||
|  |                 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() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private fun execSqlAndLog(database: SQLiteDatabase, sql: String) { | ||||||
|  |         Log.verbose("[GameDatabase] Executing SQL: $sql") | ||||||
|  |         database.execSQL(sql) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     companion object { | ||||||
|  |         const val COLUMN_DB_ID = 0 | ||||||
|  |         const val GAME_COLUMN_PATH = 1 | ||||||
|  |         const val GAME_COLUMN_TITLE = 2 | ||||||
|  |         const val GAME_COLUMN_DESCRIPTION = 3 | ||||||
|  |         const val GAME_COLUMN_REGIONS = 4 | ||||||
|  |         const val GAME_COLUMN_GAME_ID = 5 | ||||||
|  |         const val GAME_COLUMN_CAPTION = 6 | ||||||
|  |         const val FOLDER_COLUMN_PATH = 1 | ||||||
|  |         const val KEY_DB_ID = "_id" | ||||||
|  |         const val KEY_GAME_PATH = "path" | ||||||
|  |         const val KEY_GAME_TITLE = "title" | ||||||
|  |         const val KEY_GAME_DESCRIPTION = "description" | ||||||
|  |         const val KEY_GAME_REGIONS = "regions" | ||||||
|  |         const val KEY_GAME_ID = "game_id" | ||||||
|  |         const val KEY_GAME_COMPANY = "company" | ||||||
|  |         const val KEY_FOLDER_PATH = "path" | ||||||
|  |         const val TABLE_NAME_FOLDERS = "folders" | ||||||
|  |         const val TABLE_NAME_GAMES = "games" | ||||||
|  |         private const val DB_VERSION = 2 | ||||||
|  |         private const val TYPE_PRIMARY = " INTEGER PRIMARY KEY" | ||||||
|  |         private const val TYPE_INTEGER = " INTEGER" | ||||||
|  |         private const val TYPE_STRING = " TEXT" | ||||||
|  |         private const val CONSTRAINT_UNIQUE = " UNIQUE" | ||||||
|  |         private const val SEPARATOR = ", " | ||||||
|  |         private const val SQL_CREATE_GAMES = ("CREATE TABLE " + TABLE_NAME_GAMES + "(" | ||||||
|  |                 + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR | ||||||
|  |                 + KEY_GAME_PATH + TYPE_STRING + SEPARATOR | ||||||
|  |                 + KEY_GAME_TITLE + TYPE_STRING + SEPARATOR | ||||||
|  |                 + KEY_GAME_DESCRIPTION + TYPE_STRING + SEPARATOR | ||||||
|  |                 + KEY_GAME_REGIONS + TYPE_STRING + SEPARATOR | ||||||
|  |                 + KEY_GAME_ID + TYPE_STRING + SEPARATOR | ||||||
|  |                 + KEY_GAME_COMPANY + TYPE_STRING + ")") | ||||||
|  |         private const val SQL_CREATE_FOLDERS = ("CREATE TABLE " + TABLE_NAME_FOLDERS + "(" | ||||||
|  |                 + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR | ||||||
|  |                 + KEY_FOLDER_PATH + TYPE_STRING + CONSTRAINT_UNIQUE + ")") | ||||||
|  |         private const val SQL_DELETE_FOLDERS = "DROP TABLE IF EXISTS $TABLE_NAME_FOLDERS" | ||||||
|  |         private const val SQL_DELETE_GAMES = "DROP TABLE IF EXISTS $TABLE_NAME_GAMES" | ||||||
|  |         private fun attemptToAddGame(database: SQLiteDatabase, filePath: String) { | ||||||
|  |             var name = NativeLibrary.GetTitle(filePath) | ||||||
|  |  | ||||||
|  |             // If the game's title field is empty, use the filename. | ||||||
|  |             if (name.isEmpty()) { | ||||||
|  |                 name = filePath.substring(filePath.lastIndexOf("/") + 1) | ||||||
|  |             } | ||||||
|  |             var gameId = NativeLibrary.GetGameId(filePath) | ||||||
|  |  | ||||||
|  |             // If the game's ID field is empty, use the filename without extension. | ||||||
|  |             if (gameId.isEmpty()) { | ||||||
|  |                 gameId = filePath.substring( | ||||||
|  |                     filePath.lastIndexOf("/") + 1, | ||||||
|  |                     filePath.lastIndexOf(".") | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |             val game = Game.asContentValues( | ||||||
|  |                 name, | ||||||
|  |                 NativeLibrary.GetDescription(filePath).replace("\n", " "), | ||||||
|  |                 NativeLibrary.GetRegions(filePath), | ||||||
|  |                 filePath, | ||||||
|  |                 gameId, | ||||||
|  |                 NativeLibrary.GetCompany(filePath) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             // Try to update an existing game first. | ||||||
|  |             val rowsMatched = database.update( | ||||||
|  |                 TABLE_NAME_GAMES,  // Which table to update. | ||||||
|  |                 game,  // The values to fill the row with. | ||||||
|  |                 "$KEY_GAME_ID = ?", arrayOf( | ||||||
|  |                     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.verbose("[GameDatabase] Adding game: " + game.getAsString(KEY_GAME_TITLE)) | ||||||
|  |                 database.insert(TABLE_NAME_GAMES, null, game) | ||||||
|  |             } else { | ||||||
|  |                 Log.verbose("[GameDatabase] Updated game: " + game.getAsString(KEY_GAME_TITLE)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 Charles Lombardo
					Charles Lombardo