mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 21:33:53 +01:00
Fixes 4544 : Language selection: history (#4880)
* Xml changes * Content provider created * Database setup done * Database setup revised * Database setup revised * SettingsFragment finished * SettingsFragment finished * UploadMediaDetailFragment updated * UploadMediaDetailFragment updated * Java docs * Test fixed * Test added * Test added * Test updated * More tests added
This commit is contained in:
parent
85bdcd5a7a
commit
4194409cd2
20 changed files with 1312 additions and 9 deletions
|
|
@ -0,0 +1,3 @@
|
|||
package fr.free.nrw.commons.recentlanguages
|
||||
|
||||
data class Language(val languageName: String, val languageCode: String)
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package fr.free.nrw.commons.recentlanguages
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.utils.LangCodeUtils
|
||||
import kotlinx.android.synthetic.main.row_item_languages_spinner.view.*
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.util.HashMap
|
||||
|
||||
/**
|
||||
* Array adapter for recent languages
|
||||
*/
|
||||
class RecentLanguagesAdapter constructor(
|
||||
context: Context,
|
||||
var recentLanguages: List<Language>,
|
||||
private val selectedLanguages: HashMap<*, String>
|
||||
) : ArrayAdapter<String?>(context, R.layout.row_item_languages_spinner) {
|
||||
|
||||
/**
|
||||
* Selected language code in UploadMediaDetailAdapter
|
||||
* Used for marking selected ones
|
||||
*/
|
||||
var selectedLangCode = ""
|
||||
|
||||
override fun isEnabled(position: Int) = recentLanguages[position].languageCode.let {
|
||||
it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode
|
||||
}
|
||||
|
||||
override fun getCount() = recentLanguages.size
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val rowView: View = convertView
|
||||
?: LayoutInflater.from(context)
|
||||
.inflate(R.layout.row_item_languages_spinner, parent, false)
|
||||
val languageCode = recentLanguages[position].languageCode
|
||||
val languageName = recentLanguages[position].languageName
|
||||
rowView.tv_language.let {
|
||||
it.isEnabled = isEnabled(position)
|
||||
if (languageCode.isEmpty()) {
|
||||
it.text = StringUtils.capitalize(languageName)
|
||||
it.textAlignment = View.TEXT_ALIGNMENT_CENTER
|
||||
} else {
|
||||
it.text =
|
||||
"${StringUtils.capitalize(languageName)}" +
|
||||
" [${LangCodeUtils.fixLanguageCode(languageCode)}]"
|
||||
}
|
||||
}
|
||||
return rowView
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides code of a language from recent languages for a specific position
|
||||
*/
|
||||
fun getLanguageCode(position: Int): String {
|
||||
return recentLanguages[position].languageCode
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides name of a language from recent languages for a specific position
|
||||
*/
|
||||
fun getLanguageName(position: Int): String {
|
||||
return recentLanguages[position].languageName
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
package fr.free.nrw.commons.recentlanguages;
|
||||
|
||||
import static fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.COLUMN_NAME;
|
||||
import static fr.free.nrw.commons.recentlanguages.RecentLanguagesDao.Table.TABLE_NAME;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteQueryBuilder;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerContentProvider;
|
||||
import javax.inject.Inject;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Content provider of recently used languages
|
||||
*/
|
||||
public class RecentLanguagesContentProvider extends CommonsDaggerContentProvider {
|
||||
|
||||
private static final String BASE_PATH = "recent_languages";
|
||||
public static final Uri BASE_URI =
|
||||
Uri.parse("content://" + BuildConfig.RECENT_LANGUAGE_AUTHORITY + "/" + BASE_PATH);
|
||||
|
||||
|
||||
/**
|
||||
* Append language code to the base uri
|
||||
* @param languageCode Code of a language
|
||||
*/
|
||||
public static Uri uriForCode(final String languageCode) {
|
||||
return Uri.parse(BASE_URI + "/" + languageCode);
|
||||
}
|
||||
|
||||
@Inject
|
||||
DBOpenHelper dbOpenHelper;
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull final Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the SQLite database for the recently used languages
|
||||
* @param uri : contains the uri for recently used languages
|
||||
* @param projection : contains the all fields of the table
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
* @param sortOrder : ascending or descending
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(@NonNull final Uri uri, final String[] projection, final String selection,
|
||||
final String[] selectionArgs, final String sortOrder) {
|
||||
final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
queryBuilder.setTables(TABLE_NAME);
|
||||
final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
final Cursor cursor = queryBuilder.query(db, projection, selection,
|
||||
selectionArgs, null, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), uri);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the update query of local SQLite Database
|
||||
* @param uri : contains the uri for recently used languages
|
||||
* @param contentValues : new values to be entered to db
|
||||
* @param selection : handles Where
|
||||
* @param selectionArgs : the condition of Where clause
|
||||
*/
|
||||
@Override
|
||||
public int update(@NonNull final Uri uri, final ContentValues contentValues,
|
||||
final String selection, final String[] selectionArgs) {
|
||||
final SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
final int rowsUpdated;
|
||||
if (TextUtils.isEmpty(selection)) {
|
||||
final int id = Integer.parseInt(uri.getLastPathSegment());
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
contentValues,
|
||||
COLUMN_NAME + " = ?",
|
||||
new String[]{String.valueOf(id)});
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Parameter `selection` should be empty when updating an ID");
|
||||
}
|
||||
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rowsUpdated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the insertion of new recently used languages record to local SQLite Database
|
||||
* @param uri : contains the uri for recently used languages
|
||||
* @param contentValues : new values to be entered to db
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(@NonNull final Uri uri, final ContentValues contentValues) {
|
||||
final SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
final long id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return Uri.parse(BASE_URI + "/" + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the deletion of new recently used languages record to local SQLite Database
|
||||
* @param uri : contains the uri for recently used languages
|
||||
*/
|
||||
@Override
|
||||
public int delete(@NonNull final Uri uri, final String s, final String[] strings) {
|
||||
final int rows;
|
||||
final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
|
||||
Timber.d("Deleting recently used language %s", uri.getLastPathSegment());
|
||||
rows = db.delete(
|
||||
TABLE_NAME,
|
||||
"language_code = ?",
|
||||
new String[]{uri.getLastPathSegment()}
|
||||
);
|
||||
getContext().getContentResolver().notifyChange(uri, null);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
package fr.free.nrw.commons.recentlanguages;
|
||||
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.RemoteException;
|
||||
import androidx.annotation.NonNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Provider;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Handles database operations for recently used languages
|
||||
*/
|
||||
@Singleton
|
||||
public class RecentLanguagesDao {
|
||||
|
||||
private final Provider<ContentProviderClient> clientProvider;
|
||||
|
||||
@Inject
|
||||
public RecentLanguagesDao
|
||||
(@Named("recent_languages") final Provider<ContentProviderClient> clientProvider) {
|
||||
this.clientProvider = clientProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all persisted recently used languages on database
|
||||
* @return list of recently used languages
|
||||
*/
|
||||
public List<Language> getRecentLanguages() {
|
||||
final List<Language> languages = new ArrayList<>();
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try (final Cursor cursor = db.query(
|
||||
RecentLanguagesContentProvider.BASE_URI,
|
||||
RecentLanguagesDao.Table.ALL_FIELDS,
|
||||
null,
|
||||
new String[]{},
|
||||
null)) {
|
||||
if(cursor != null && cursor.moveToLast()) {
|
||||
do {
|
||||
languages.add(fromCursor(cursor));
|
||||
} while (cursor.moveToPrevious());
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
return languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Language to database
|
||||
* @param language : Language to add
|
||||
*/
|
||||
public void addRecentLanguage(final Language language) {
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.insert(RecentLanguagesContentProvider.BASE_URI, toContentValues(language));
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a language from database
|
||||
* @param languageCode : code of the Language to delete
|
||||
*/
|
||||
public void deleteRecentLanguage(final String languageCode) {
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try {
|
||||
db.delete(RecentLanguagesContentProvider.uriForCode(languageCode), null, null);
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a language from database based on its name
|
||||
* @param languageCode : code of the Language to find
|
||||
* @return boolean : is language in database ?
|
||||
*/
|
||||
public boolean findRecentLanguage(final String languageCode) {
|
||||
if (languageCode == null) { //Avoiding NPE's
|
||||
return false;
|
||||
}
|
||||
final ContentProviderClient db = clientProvider.get();
|
||||
try (final Cursor cursor = db.query(
|
||||
RecentLanguagesContentProvider.BASE_URI,
|
||||
RecentLanguagesDao.Table.ALL_FIELDS,
|
||||
Table.COLUMN_CODE + "=?",
|
||||
new String[]{languageCode},
|
||||
null
|
||||
)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return true;
|
||||
}
|
||||
} catch (final RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
db.release();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* It creates an Recent Language object from data stored in the SQLite DB by using cursor
|
||||
* @param cursor cursor
|
||||
* @return Language object
|
||||
*/
|
||||
@NonNull
|
||||
Language fromCursor(final Cursor cursor) {
|
||||
// Hardcoding column positions!
|
||||
final String languageName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME));
|
||||
final String languageCode = cursor.getString(cursor.getColumnIndex(Table.COLUMN_CODE));
|
||||
return new Language(languageName, languageCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes data from Language and create a content value object
|
||||
* @param recentLanguage recently used language
|
||||
* @return ContentValues
|
||||
*/
|
||||
private ContentValues toContentValues(final Language recentLanguage) {
|
||||
final ContentValues cv = new ContentValues();
|
||||
cv.put(Table.COLUMN_NAME, recentLanguage.getLanguageName());
|
||||
cv.put(Table.COLUMN_CODE, recentLanguage.getLanguageCode());
|
||||
return cv;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains the database table architecture for recently used languages,
|
||||
* It also contains queries and logic necessary to the create, update, delete this table.
|
||||
*/
|
||||
public static final class Table {
|
||||
public static final String TABLE_NAME = "recent_languages";
|
||||
static final String COLUMN_NAME = "language_name";
|
||||
static final String COLUMN_CODE = "language_code";
|
||||
|
||||
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
|
||||
public static final String[] ALL_FIELDS = {
|
||||
COLUMN_NAME,
|
||||
COLUMN_CODE
|
||||
};
|
||||
|
||||
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
|
||||
static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
|
||||
+ COLUMN_NAME + " STRING,"
|
||||
+ COLUMN_CODE + " STRING PRIMARY KEY"
|
||||
+ ");";
|
||||
|
||||
/**
|
||||
* This method creates a LanguagesTable in SQLiteDatabase
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
public static void onCreate(final SQLiteDatabase db) {
|
||||
db.execSQL(CREATE_TABLE_STATEMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method deletes LanguagesTable from SQLiteDatabase
|
||||
* @param db SQLiteDatabase
|
||||
*/
|
||||
public static void onDelete(final SQLiteDatabase db) {
|
||||
db.execSQL(DROP_TABLE_STATEMENT);
|
||||
onCreate(db);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called on migrating from a older version to a newer version
|
||||
* @param db SQLiteDatabase
|
||||
* @param from Version from which we are migrating
|
||||
* @param to Version to which we are migrating
|
||||
*/
|
||||
public static void onUpdate(final SQLiteDatabase db, int from, final int to) {
|
||||
if (from == to) {
|
||||
return;
|
||||
}
|
||||
if (from < 6) {
|
||||
// doesn't exist yet
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
if (from == 6) {
|
||||
// table added in version 7
|
||||
onCreate(db);
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
if (from == 7) {
|
||||
from++;
|
||||
onUpdate(db, from, to);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue