Initial migrate SearchDatabase to ROOM

This commit is contained in:
tarsin committed 2023-05-27 19:27:01 +08:00
1 parent dc48363398
commit 5e14dad961
7 files changed
+158 -167

No files matched your search

@@ -0,0 +1,46 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "69230710633494b4b331e10dae436fb7",
"entities": [
{
"tableName": "suggestions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `date` LONG, `query` TEXT, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
},
{
"fieldPath": "date",
"columnName": "date",
"affinity": "LONG",
"notNull": false
},
{
"fieldPath": "query",
"columnName": "query",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '69230710633494b4b331e10dae436fb7')"
]
}
}
@@ -0,0 +1,46 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "bf4a5b5f3cb9bc7383488bae14f5d4bd",
"entities": [
{
"tableName": "suggestions",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`date` INTEGER NOT NULL, `query` TEXT NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))",
"fields": [
{
"fieldPath": "date",
"columnName": "date",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "query",
"columnName": "query",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "id",
"columnName": "_id",
"affinity": "INTEGER",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"_id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'bf4a5b5f3cb9bc7383488bae14f5d4bd')"
]
}
}
@@ -8,6 +8,7 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.AutoMigrationSpec
import androidx.sqlite.db.SupportSQLiteDatabase
import com.hippo.ehviewer.EhApplication
@DeleteTable(tableName = "BOOKMARKS")
class DropBookMarkDao : AutoMigrationSpec
@@ -86,3 +87,22 @@ fun buildMainDB(context: Context): EhDatabase {
fun buildCookiesDB(context: Context): CookiesDatabase {
return Room.databaseBuilder(context, CookiesDatabase::class.java, "okhttp3-cookie.db").build()
}
@Database(
entities = [Search::class],
version = 2,
autoMigrations = [
AutoMigration(
from = 1,
to = 2,
),
],
)
abstract class SearchDatabase : RoomDatabase() {
abstract fun searchDao(): SearchDao
}
val searchDatabase by lazy {
val context = EhApplication.application
Room.databaseBuilder(context, SearchDatabase::class.java, "search_database.db").build()
}
@@ -0,0 +1,16 @@
package com.hippo.ehviewer.dao
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "suggestions")
data class Search(
@ColumnInfo(name = "date")
val date: Long,
@ColumnInfo(name = "query")
val query: String,
@PrimaryKey
@ColumnInfo(name = "_id")
val id: Int? = null,
)
@@ -0,0 +1,25 @@
package com.hippo.ehviewer.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
@Dao
interface SearchDao {
@Query("DELETE FROM suggestions WHERE query = :query")
fun deleteQuery(query: String)
@Query("SELECT * FROM suggestions")
fun rawSuggestions(): Array<Search>
@Insert
fun insert(search: Search)
fun addQuery(query: String) {
deleteQuery(query)
val search = Search(System.currentTimeMillis(), query)
insert(search)
}
fun suggestions(prefix: String, limit: Int) = rawSuggestions().map { it.query }.toTypedArray()
}
@@ -1,162 +0,0 @@
/*
* Copyright (C) 2015 Hippo Seven
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hippo.ehviewer.ui.legacy;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import com.hippo.ehviewer.util.SqlUtils;
import java.util.LinkedList;
import java.util.List;
public final class SearchDatabase {
public static final String COLUMN_QUERY = "query";
public static final String COLUMN_DATE = "date";
private static final String TAG = SearchDatabase.class.getSimpleName();
private static final String DATABASE_NAME = "search_database.db";
private static final String TABLE_SUGGESTIONS = "suggestions";
private static final int MAX_HISTORY = 100;
private static SearchDatabase sInstance;
private final SQLiteDatabase mDatabase;
private SearchDatabase(Context context) {
DatabaseHelper databaseHelper = new DatabaseHelper(context);
mDatabase = databaseHelper.getWritableDatabase();
}
public static SearchDatabase getInstance(Context context) {
if (sInstance == null) {
sInstance = new SearchDatabase(context.getApplicationContext());
}
return sInstance;
}
public String[] getSuggestions(String prefix, int limit) {
List<String> queryList = new LinkedList<>();
limit = Math.max(0, limit);
StringBuilder sb = new StringBuilder();
sb.append("SELECT * FROM ").append(TABLE_SUGGESTIONS);
if (!TextUtils.isEmpty(prefix)) {
sb.append(" WHERE ").append(COLUMN_QUERY).append(" LIKE '")
.append(SqlUtils.sqlEscapeString(prefix)).append("%'");
}
sb.append(" ORDER BY ").append(COLUMN_DATE).append(" DESC")
.append(" LIMIT ").append(limit);
try {
Cursor cursor = mDatabase.rawQuery(sb.toString(), null);
int queryIndex = cursor.getColumnIndex(COLUMN_QUERY);
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
String suggestion = cursor.getString(queryIndex);
if (!prefix.equals(suggestion)) {
queryList.add(suggestion);
}
cursor.moveToNext();
}
}
cursor.close();
return queryList.toArray(new String[queryList.size()]);
} catch (SQLException e) {
return new String[0];
}
}
public void addQuery(final String query) {
if (!TextUtils.isEmpty(query)) {
// Delete old first
deleteQuery(query);
// Add it to database
ContentValues values = new ContentValues();
values.put(COLUMN_QUERY, query);
values.put(COLUMN_DATE, System.currentTimeMillis());
mDatabase.insert(TABLE_SUGGESTIONS, null, values);
// Remove history if more than max
truncateHistory(MAX_HISTORY);
}
}
public void deleteQuery(final String query) {
mDatabase.delete(TABLE_SUGGESTIONS, COLUMN_QUERY + "=?", new String[]{query});
}
public void clearQuery() {
truncateHistory(0);
}
/**
* Reduces the length of the history table, to prevent it from growing too large.
*
* @param maxEntries Max entries to leave in the table. 0 means remove all entries.
*/
private void truncateHistory(int maxEntries) {
if (maxEntries < 0) {
throw new IllegalArgumentException();
}
try {
// null means "delete all". otherwise "delete but leave n newest"
String selection = null;
if (maxEntries > 0) {
selection = "_id IN " +
"(SELECT _id FROM " + TABLE_SUGGESTIONS +
" ORDER BY " + COLUMN_DATE + " DESC" +
" LIMIT -1 OFFSET " + maxEntries + ")";
}
mDatabase.delete(TABLE_SUGGESTIONS, selection, null);
} catch (RuntimeException e) {
Log.e(TAG, "truncateHistory", e);
}
}
/**
* Builds the database. This version has extra support for using the version field
* as a mode flags field, and configures the database columns depending on the mode bits
* (features) requested by the extending class.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_SUGGESTIONS + " (" +
"_id INTEGER PRIMARY KEY" +
"," + COLUMN_QUERY + " TEXT" +
"," + COLUMN_DATE + " LONG" +
");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_SUGGESTIONS);
onCreate(db);
}
}
}
@@ -22,13 +22,13 @@ import com.google.android.material.shape.MaterialShapeDrawable
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhTagDatabase
import com.hippo.ehviewer.dao.searchDatabase
import com.hippo.ehviewer.databinding.SceneSearchbarBinding
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
import com.hippo.ehviewer.ui.legacy.SearchDatabase
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.withUIContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
abstract class SearchBarScene : BaseScene(), ToolBarScene {
private var _binding: SceneSearchbarBinding? = null
@@ -37,7 +37,7 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
private var mSuggestionAdapter: SuggestionAdapter? = null
private var mSuggestionProvider: SuggestionProvider? = null
private var mAllowEmptySearch = true
private val mSearchDatabase by lazy { SearchDatabase.getInstance(context) }
private val mSearchDatabase by lazy { searchDatabase.searchDao() }
private var onApplySearch: (String) -> Unit = {}
override fun onCreateView(
@@ -296,7 +296,7 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
@SuppressLint("NotifyDataSetChanged")
private fun updateSuggestions(scrollToTop: Boolean = true) {
_binding ?: return
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycleScope.launchIO {
val suggestions = mutableListOf<Suggestion>()
mergedSuggestionFlow().collect {
suggestions.add(it)
@@ -314,7 +314,7 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene {
private fun mergedSuggestionFlow(): Flow<Suggestion> = flow {
binding.searchview.editText.text?.toString()?.let { text ->
mSuggestionProvider?.run { providerSuggestions(text)?.forEach { emit(it) } }
mSearchDatabase.getSuggestions(text, 128).forEach { emit(KeywordSuggestion(it)) }
mSearchDatabase.suggestions(text, 128).forEach { emit(KeywordSuggestion(it)) }
EhTagDatabase.takeIf { it.isInitialized() }?.run {
if (text.isNotEmpty() && !text.endsWith(' ')) {
val keyword = text.substringAfterLast(' ')