/*
* Copyright (C) 2016 Blue Jay Wireless
* Copyright (C) 2015-2016 Daniel Martà <mvdan@mvdan.cc>
* Copyright (C) 2015 Christian Morgner
* Copyright (C) 2014-2016 Hans-Christoph Steiner <hans@eds.org>
* Copyright (C) 2013-2016 Peter Serwylo <peter@serwylo.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.fdroid.fdroid.data;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.ApkTable;
import org.fdroid.fdroid.data.Schema.CatJoinTable;
import org.fdroid.fdroid.data.Schema.PackageTable;
import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.AppMetadataTable;
import org.fdroid.fdroid.data.Schema.InstalledAppTable;
import org.fdroid.fdroid.data.Schema.RepoTable;
import java.util.ArrayList;
import java.util.List;
class DBHelper extends SQLiteOpenHelper {
private static final String TAG = "DBHelper";
public static final int REPO_XML_ARG_COUNT = 8;
private static final String DATABASE_NAME = "fdroid";
private static final String CREATE_TABLE_PACKAGE = "CREATE TABLE " + PackageTable.NAME
+ " ( "
+ PackageTable.Cols.PACKAGE_NAME + " text not null, "
+ PackageTable.Cols.PREFERRED_METADATA + " integer"
+ ");";
private static final String CREATE_TABLE_REPO = "create table "
+ RepoTable.NAME + " ("
+ RepoTable.Cols._ID + " integer primary key, "
+ RepoTable.Cols.ADDRESS + " text not null, "
+ RepoTable.Cols.NAME + " text, "
+ RepoTable.Cols.DESCRIPTION + " text, "
+ RepoTable.Cols.IN_USE + " integer not null, "
+ RepoTable.Cols.PRIORITY + " integer not null, "
+ RepoTable.Cols.SIGNING_CERT + " text, "
+ RepoTable.Cols.FINGERPRINT + " text, "
+ RepoTable.Cols.MAX_AGE + " integer not null default 0, "
+ RepoTable.Cols.VERSION + " integer not null default 0, "
+ RepoTable.Cols.LAST_ETAG + " text, "
+ RepoTable.Cols.LAST_UPDATED + " string,"
+ RepoTable.Cols.IS_SWAP + " integer boolean default 0,"
+ RepoTable.Cols.USERNAME + " string, "
+ RepoTable.Cols.PASSWORD + " string,"
+ RepoTable.Cols.TIMESTAMP + " integer not null default 0, "
+ RepoTable.Cols.PUSH_REQUESTS + " integer not null default " + Repo.PUSH_REQUEST_IGNORE
+ ");";
static final String CREATE_TABLE_APK =
"CREATE TABLE " + ApkTable.NAME + " ( "
+ ApkTable.Cols.APP_ID + " integer not null, "
+ ApkTable.Cols.VERSION_NAME + " text not null, "
+ ApkTable.Cols.REPO_ID + " integer not null, "
+ ApkTable.Cols.HASH + " text not null, "
+ ApkTable.Cols.VERSION_CODE + " int not null,"
+ ApkTable.Cols.NAME + " text not null, "
+ ApkTable.Cols.SIZE + " int not null, "
+ ApkTable.Cols.SIGNATURE + " string, "
+ ApkTable.Cols.SOURCE_NAME + " string, "
+ ApkTable.Cols.MIN_SDK_VERSION + " integer, "
+ ApkTable.Cols.TARGET_SDK_VERSION + " integer, "
+ ApkTable.Cols.MAX_SDK_VERSION + " integer, "
+ ApkTable.Cols.OBB_MAIN_FILE + " string, "
+ ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string, "
+ ApkTable.Cols.OBB_PATCH_FILE + " string, "
+ ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string, "
+ ApkTable.Cols.REQUESTED_PERMISSIONS + " string, "
+ ApkTable.Cols.FEATURES + " string, "
+ ApkTable.Cols.NATIVE_CODE + " string, "
+ ApkTable.Cols.HASH_TYPE + " string, "
+ ApkTable.Cols.ADDED_DATE + " string, "
+ ApkTable.Cols.IS_COMPATIBLE + " int not null, "
+ ApkTable.Cols.INCOMPATIBLE_REASONS + " text, "
+ "PRIMARY KEY (" + ApkTable.Cols.APP_ID + ", " + ApkTable.Cols.VERSION_CODE + ", " + ApkTable.Cols.REPO_ID + ")"
+ ");";
static final String CREATE_TABLE_APP_METADATA = "CREATE TABLE " + AppMetadataTable.NAME
+ " ( "
+ AppMetadataTable.Cols.PACKAGE_ID + " integer not null, "
+ AppMetadataTable.Cols.REPO_ID + " integer not null, "
+ AppMetadataTable.Cols.NAME + " text not null, "
+ AppMetadataTable.Cols.SUMMARY + " text not null, "
+ AppMetadataTable.Cols.ICON + " text, "
+ AppMetadataTable.Cols.DESCRIPTION + " text not null, "
+ AppMetadataTable.Cols.LICENSE + " text not null, "
+ AppMetadataTable.Cols.AUTHOR + " text, "
+ AppMetadataTable.Cols.EMAIL + " text, "
+ AppMetadataTable.Cols.WEB_URL + " text, "
+ AppMetadataTable.Cols.TRACKER_URL + " text, "
+ AppMetadataTable.Cols.SOURCE_URL + " text, "
+ AppMetadataTable.Cols.CHANGELOG_URL + " text, "
+ AppMetadataTable.Cols.SUGGESTED_VERSION_CODE + " text,"
+ AppMetadataTable.Cols.UPSTREAM_VERSION_NAME + " text,"
+ AppMetadataTable.Cols.UPSTREAM_VERSION_CODE + " integer,"
+ AppMetadataTable.Cols.ANTI_FEATURES + " string,"
+ AppMetadataTable.Cols.DONATE_URL + " string,"
+ AppMetadataTable.Cols.BITCOIN_ADDR + " string,"
+ AppMetadataTable.Cols.LITECOIN_ADDR + " string,"
+ AppMetadataTable.Cols.FLATTR_ID + " string,"
+ AppMetadataTable.Cols.REQUIREMENTS + " string,"
+ AppMetadataTable.Cols.ADDED + " string,"
+ AppMetadataTable.Cols.LAST_UPDATED + " string,"
+ AppMetadataTable.Cols.IS_COMPATIBLE + " int not null,"
+ AppMetadataTable.Cols.ICON_URL + " text, "
+ AppMetadataTable.Cols.ICON_URL_LARGE + " text, "
+ "primary key(" + AppMetadataTable.Cols.PACKAGE_ID + ", " + AppMetadataTable.Cols.REPO_ID + "));";
private static final String CREATE_TABLE_APP_PREFS = "CREATE TABLE " + AppPrefsTable.NAME
+ " ( "
+ AppPrefsTable.Cols.PACKAGE_NAME + " TEXT, "
+ AppPrefsTable.Cols.IGNORE_THIS_UPDATE + " INT BOOLEAN NOT NULL, "
+ AppPrefsTable.Cols.IGNORE_ALL_UPDATES + " INT NOT NULL "
+ " );";
private static final String CREATE_TABLE_CATEGORY = "CREATE TABLE " + Schema.CategoryTable.NAME
+ " ( "
+ Schema.CategoryTable.Cols.NAME + " TEXT NOT NULL "
+ " );";
/**
* The order of the two columns in the primary key matters for this table. The index that is
* built for sqlite to quickly search the primary key will be sorted by app metadata id first,
* and category id second. This means that we don't need a separate individual index on the
* app metadata id, because it can instead look through the primary key index. This can be
* observed by flipping the order of the primary key columns, and noting the resulting sqlite
* logs along the lines of:
* E/SQLiteLog(14164): (284) automatic index on fdroid_categoryAppMetadataJoin(appMetadataId)
*/
static final String CREATE_TABLE_CAT_JOIN = "CREATE TABLE " + CatJoinTable.NAME
+ " ( "
+ CatJoinTable.Cols.APP_METADATA_ID + " INT NOT NULL, "
+ CatJoinTable.Cols.CATEGORY_ID + " INT NOT NULL, "
+ "primary key(" + CatJoinTable.Cols.APP_METADATA_ID + ", " + CatJoinTable.Cols.CATEGORY_ID + ") "
+ " );";
private static final String CREATE_TABLE_INSTALLED_APP = "CREATE TABLE " + InstalledAppTable.NAME
+ " ( "
+ InstalledAppTable.Cols.PACKAGE_NAME + " TEXT NOT NULL PRIMARY KEY, "
+ InstalledAppTable.Cols.VERSION_CODE + " INT NOT NULL, "
+ InstalledAppTable.Cols.VERSION_NAME + " TEXT NOT NULL, "
+ InstalledAppTable.Cols.APPLICATION_LABEL + " TEXT NOT NULL, "
+ InstalledAppTable.Cols.SIGNATURE + " TEXT NOT NULL, "
+ InstalledAppTable.Cols.LAST_UPDATE_TIME + " INTEGER NOT NULL DEFAULT 0, "
+ InstalledAppTable.Cols.HASH_TYPE + " TEXT NOT NULL, "
+ InstalledAppTable.Cols.HASH + " TEXT NOT NULL"
+ " );";
protected static final int DB_VERSION = 65;
private final Context context;
DBHelper(Context context) {
super(context, DATABASE_NAME, null, DB_VERSION);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_PACKAGE);
db.execSQL(CREATE_TABLE_APP_METADATA);
db.execSQL(CREATE_TABLE_APK);
db.execSQL(CREATE_TABLE_CATEGORY);
db.execSQL(CREATE_TABLE_CAT_JOIN);
db.execSQL(CREATE_TABLE_INSTALLED_APP);
db.execSQL(CREATE_TABLE_REPO);
db.execSQL(CREATE_TABLE_APP_PREFS);
ensureIndexes(db);
String[] defaultRepos = context.getResources().getStringArray(R.array.default_repos);
if (defaultRepos.length % REPO_XML_ARG_COUNT != 0) {
throw new IllegalArgumentException(
"default_repo.xml array does not have the right number of elements");
}
for (int i = 0; i < defaultRepos.length / REPO_XML_ARG_COUNT; i++) {
int offset = i * REPO_XML_ARG_COUNT;
insertRepo(
db,
defaultRepos[offset], // name
defaultRepos[offset + 1], // address
defaultRepos[offset + 2], // description
defaultRepos[offset + 3], // version
defaultRepos[offset + 4], // enabled
defaultRepos[offset + 5], // priority
defaultRepos[offset + 6], // pushRequests
defaultRepos[offset + 7] // pubkey
);
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Utils.debugLog(TAG, "Upgrading database from v" + oldVersion + " v" + newVersion);
migrateRepoTable(db, oldVersion);
// The other tables are transient and can just be reset. Do this after
// the repo table changes though, because it also clears the lastetag
// fields which didn't always exist.
resetTransientPre42(db, oldVersion);
addNameAndDescriptionToRepo(db, oldVersion);
addFingerprintToRepo(db, oldVersion);
addMaxAgeToRepo(db, oldVersion);
addVersionToRepo(db, oldVersion);
addLastUpdatedToRepo(db, oldVersion);
renameRepoId(db, oldVersion);
populateRepoNames(db, oldVersion);
addIsSwapToRepo(db, oldVersion);
addChangelogToApp(db, oldVersion);
addIconUrlLargeToApp(db, oldVersion);
updateIconUrlLarge(db, oldVersion);
addCredentialsToRepo(db, oldVersion);
addAuthorToApp(db, oldVersion);
useMaxValueInMaxSdkVersion(db, oldVersion);
requireTimestampInRepos(db, oldVersion);
recreateInstalledAppTable(db, oldVersion);
addTargetSdkVersionToApk(db, oldVersion);
migrateAppPrimaryKeyToRowId(db, oldVersion);
removeApkPackageNameColumn(db, oldVersion);
addAppPrefsTable(db, oldVersion);
lowerCaseApkHashes(db, oldVersion);
supportRepoPushRequests(db, oldVersion);
migrateToPackageTable(db, oldVersion);
addObbFiles(db, oldVersion);
addCategoryTables(db, oldVersion);
}
/**
* It is possible to correctly migrate categories from the previous `categories` column in
* app metadata to the new join table without destroying any data and requiring a repo update.
* However, in practice other code since the previous stable has already reset the transient
* tables and forced a repo update, so it is much easier to do the same here. It wont have any
* negative impact on those upgrading from the previous stable. If there was a number of solid
* alpha releases before this, then a proper migration would've be in order.
*/
private void addCategoryTables(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 65) {
return;
}
resetTransient(db);
}
private void addObbFiles(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 64) {
return;
}
Utils.debugLog(TAG, "Ensuring " + ApkTable.Cols.OBB_MAIN_FILE + ", " +
ApkTable.Cols.OBB_PATCH_FILE + ", and hash columns exist on " + ApkTable.NAME);
if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.OBB_MAIN_FILE)) {
db.execSQL("alter table " + ApkTable.NAME + " add column "
+ ApkTable.Cols.OBB_MAIN_FILE + " string");
}
if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.OBB_MAIN_FILE_SHA256)) {
db.execSQL("alter table " + ApkTable.NAME + " add column "
+ ApkTable.Cols.OBB_MAIN_FILE_SHA256 + " string");
}
if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.OBB_PATCH_FILE)) {
db.execSQL("alter table " + ApkTable.NAME + " add column "
+ ApkTable.Cols.OBB_PATCH_FILE + " string");
}
if (!columnExists(db, ApkTable.NAME, ApkTable.Cols.OBB_PATCH_FILE_SHA256)) {
db.execSQL("alter table " + ApkTable.NAME + " add column "
+ ApkTable.Cols.OBB_PATCH_FILE_SHA256 + " string");
}
}
private void migrateToPackageTable(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 63) {
return;
}
resetTransient(db);
// By pushing _ALL_ repositories to a priority of 10, it makes it slightly easier
// to query for the non-default repositories later on in this method.
ContentValues highPriority = new ContentValues(1);
highPriority.put(RepoTable.Cols.PRIORITY, 10);
db.update(RepoTable.NAME, highPriority, null, null);
String[] defaultRepos = context.getResources().getStringArray(R.array.default_repos);
String fdroidPubKey = defaultRepos[7];
String fdroidAddress = defaultRepos[1];
String fdroidArchiveAddress = defaultRepos[REPO_XML_ARG_COUNT + 1];
String gpPubKey = defaultRepos[REPO_XML_ARG_COUNT * 2 + 7];
String gpAddress = defaultRepos[REPO_XML_ARG_COUNT * 2 + 1];
String gpArchiveAddress = defaultRepos[REPO_XML_ARG_COUNT * 3 + 1];
updateRepoPriority(db, fdroidPubKey, fdroidAddress, 1);
updateRepoPriority(db, fdroidPubKey, fdroidArchiveAddress, 2);
updateRepoPriority(db, gpPubKey, gpAddress, 3);
updateRepoPriority(db, gpPubKey, gpArchiveAddress, 4);
int priority = 5;
String[] projection = new String[] {RepoTable.Cols.SIGNING_CERT, RepoTable.Cols.ADDRESS};
// Order by ID, because that is a good analogy for the order in which they were added.
// The order in which they were added is likely the order they present in the ManageRepos activity.
Cursor cursor = db.query(RepoTable.NAME, projection, RepoTable.Cols.PRIORITY + " > 4", null, null, null, RepoTable.Cols._ID);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String signingCert = cursor.getString(cursor.getColumnIndex(RepoTable.Cols.SIGNING_CERT));
String address = cursor.getString(cursor.getColumnIndex(RepoTable.Cols.ADDRESS));
updateRepoPriority(db, signingCert, address, priority);
cursor.moveToNext();
priority++;
}
cursor.close();
}
private void updateRepoPriority(SQLiteDatabase db, String signingCert, String address, int priority) {
ContentValues values = new ContentValues(1);
values.put(RepoTable.Cols.PRIORITY, Integer.toString(priority));
Utils.debugLog(TAG, "Setting priority of repo " + address + " to " + priority);
db.update(
RepoTable.NAME,
values,
RepoTable.Cols.SIGNING_CERT + " = ? AND " + RepoTable.Cols.ADDRESS + " = ?",
new String[] {signingCert, address}
);
}
private void lowerCaseApkHashes(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 61) {
return;
}
Utils.debugLog(TAG, "Lowercasing all APK hashes");
db.execSQL("UPDATE " + InstalledAppTable.NAME + " SET " + InstalledAppTable.Cols.HASH
+ " = lower(" + InstalledAppTable.Cols.HASH + ")");
}
private void addAppPrefsTable(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 60) {
return;
}
Utils.debugLog(TAG, "Creating app preferences table");
db.execSQL(CREATE_TABLE_APP_PREFS);
Utils.debugLog(TAG, "Migrating app preferences to separate table");
db.execSQL(
"INSERT INTO " + AppPrefsTable.NAME + " ("
+ AppPrefsTable.Cols.PACKAGE_NAME + ", "
+ AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ", "
+ AppPrefsTable.Cols.IGNORE_ALL_UPDATES
+ ") SELECT "
+ "id, "
+ "ignoreThisUpdate, "
+ "ignoreAllUpdates "
+ "FROM " + AppMetadataTable.NAME + " "
+ "WHERE ignoreThisUpdate > 0 OR ignoreAllUpdates > 0"
);
resetTransient(db);
}
/**
* Ordinarily, if a column is no longer used, we'd err on the side of just leaving it in the
* database but stop referring to it in Java. However because it forms part of the primary
* key of this table, we need to change the primary key to something which _is_ used. Thus,
* this function will rename the old table, create the new table, and then insert all of the
* data from the old into the new with the new primary key.
*/
private void removeApkPackageNameColumn(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 59) {
Utils.debugLog(TAG, "Changing primary key of " + ApkTable.NAME + " from package + vercode to app + vercode + repo");
db.beginTransaction();
try {
// http://stackoverflow.com/questions/805363/how-do-i-rename-a-column-in-a-sqlite-database-table#805508
String tempTableName = ApkTable.NAME + "__temp__";
db.execSQL("ALTER TABLE " + ApkTable.NAME + " RENAME TO " + tempTableName + ";");
String createTableDdl = "CREATE TABLE " + ApkTable.NAME + " ( "
+ ApkTable.Cols.APP_ID + " integer not null, "
+ ApkTable.Cols.VERSION_NAME + " text not null, "
+ ApkTable.Cols.REPO_ID + " integer not null, "
+ ApkTable.Cols.HASH + " text not null, "
+ ApkTable.Cols.VERSION_CODE + " int not null,"
+ ApkTable.Cols.NAME + " text not null, "
+ ApkTable.Cols.SIZE + " int not null, "
+ ApkTable.Cols.SIGNATURE + " string, "
+ ApkTable.Cols.SOURCE_NAME + " string, "
+ ApkTable.Cols.MIN_SDK_VERSION + " integer, "
+ ApkTable.Cols.TARGET_SDK_VERSION + " integer, "
+ ApkTable.Cols.MAX_SDK_VERSION + " integer, "
+ ApkTable.Cols.REQUESTED_PERMISSIONS + " string, "
+ ApkTable.Cols.FEATURES + " string, "
+ ApkTable.Cols.NATIVE_CODE + " string, "
+ ApkTable.Cols.HASH_TYPE + " string, "
+ ApkTable.Cols.ADDED_DATE + " string, "
+ ApkTable.Cols.IS_COMPATIBLE + " int not null, "
+ ApkTable.Cols.INCOMPATIBLE_REASONS + " text, "
+ "PRIMARY KEY (" + ApkTable.Cols.APP_ID + ", " + ApkTable.Cols.VERSION_CODE + ", " + ApkTable.Cols.REPO_ID + ")"
+ ");";
db.execSQL(createTableDdl);
String nonPackageNameFields = TextUtils.join(", ", new String[] {
ApkTable.Cols.APP_ID,
ApkTable.Cols.VERSION_NAME,
ApkTable.Cols.REPO_ID,
ApkTable.Cols.HASH,
ApkTable.Cols.VERSION_CODE,
ApkTable.Cols.NAME,
ApkTable.Cols.SIZE,
ApkTable.Cols.SIGNATURE,
ApkTable.Cols.SOURCE_NAME,
ApkTable.Cols.MIN_SDK_VERSION,
ApkTable.Cols.TARGET_SDK_VERSION,
ApkTable.Cols.MAX_SDK_VERSION,
ApkTable.Cols.REQUESTED_PERMISSIONS,
ApkTable.Cols.FEATURES,
ApkTable.Cols.NATIVE_CODE,
ApkTable.Cols.HASH_TYPE,
ApkTable.Cols.ADDED_DATE,
ApkTable.Cols.IS_COMPATIBLE,
ApkTable.Cols.INCOMPATIBLE_REASONS,
});
String insertSql = "INSERT INTO " + ApkTable.NAME +
"(" + nonPackageNameFields + " ) " +
"SELECT " + nonPackageNameFields + " FROM " + tempTableName + ";";
db.execSQL(insertSql);
db.execSQL("DROP TABLE " + tempTableName + ";");
// Now that the old table has been dropped, we can create indexes again.
// Attempting this before dropping the old table will not work, because the
// indexes exist on the _old_ table, and so are unable to be added (with the
// same name) to the _new_ table.
ensureIndexes(db);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
private void migrateAppPrimaryKeyToRowId(SQLiteDatabase db, int oldVersion) {
if (oldVersion < 58 && !columnExists(db, ApkTable.NAME, ApkTable.Cols.APP_ID)) {
db.beginTransaction();
try {
final String alter = "ALTER TABLE " + ApkTable.NAME + " ADD COLUMN " + ApkTable.Cols.APP_ID + " NUMERIC";
Log.i(TAG, "Adding appId foreign key to " + ApkTable.NAME);
Utils.debugLog(TAG, alter);
db.execSQL(alter);
// Hard coded the string literal ".id" as ApkTable.Cols.PACKAGE_NAME was removed in
// the subsequent migration (DB_VERSION 59)
final String update = "UPDATE " + ApkTable.NAME + " SET " + ApkTable.Cols.APP_ID + " = ( " +
"SELECT app." + AppMetadataTable.Cols.ROW_ID + " " +
"FROM " + AppMetadataTable.NAME + " AS app " +
"WHERE " + ApkTable.NAME + ".id = app.id)";
Log.i(TAG, "Updating foreign key from " + ApkTable.NAME + " to " + AppMetadataTable.NAME + " to use numeric foreign key.");
Utils.debugLog(TAG, update);
db.execSQL(update);
ensureIndexes(db);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
/**
* Migrate repo list to new structure. (No way to change primary
* key in sqlite - table must be recreated).
*/
private void migrateRepoTable(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 20) {
return;
}
List<Repo> oldrepos = new ArrayList<>();
Cursor cursor = db.query(RepoTable.NAME,
new String[] {RepoTable.Cols.ADDRESS, RepoTable.Cols.IN_USE, RepoTable.Cols.SIGNING_CERT},
null, null, null, null, null);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Repo repo = new Repo();
repo.address = cursor.getString(0);
repo.inuse = cursor.getInt(1) == 1;
repo.signingCertificate = cursor.getString(2);
oldrepos.add(repo);
cursor.moveToNext();
}
}
cursor.close();
}
db.execSQL("drop table " + RepoTable.NAME);
db.execSQL(CREATE_TABLE_REPO);
for (final Repo repo : oldrepos) {
ContentValues values = new ContentValues();
values.put(RepoTable.Cols.ADDRESS, repo.address);
values.put(RepoTable.Cols.IN_USE, repo.inuse);
values.put(RepoTable.Cols.PRIORITY, 10);
values.put(RepoTable.Cols.SIGNING_CERT, repo.signingCertificate);
values.put(RepoTable.Cols.LAST_ETAG, (String) null);
db.insert(RepoTable.NAME, null, values);
}
}
private void insertNameAndDescription(SQLiteDatabase db,
String name, String address, String description) {
ContentValues values = new ContentValues();
values.clear();
values.put(RepoTable.Cols.NAME, name);
values.put(RepoTable.Cols.DESCRIPTION, description);
db.update(RepoTable.NAME, values, RepoTable.Cols.ADDRESS + " = ?", new String[]{
address,
});
}
/**
* Add a name and description to the repo table, and updates the two
* default repos with values from strings.xml.
*/
private void addNameAndDescriptionToRepo(SQLiteDatabase db, int oldVersion) {
boolean nameExists = columnExists(db, RepoTable.NAME, RepoTable.Cols.NAME);
boolean descriptionExists = columnExists(db, RepoTable.NAME, RepoTable.Cols.DESCRIPTION);
if (oldVersion >= 21 || (nameExists && descriptionExists)) {
return;
}
if (!nameExists) {
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.NAME + " text");
}
if (!descriptionExists) {
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.DESCRIPTION + " text");
}
String[] defaultRepos = context.getResources().getStringArray(R.array.default_repos);
for (int i = 0; i < defaultRepos.length / REPO_XML_ARG_COUNT; i++) {
int offset = i * REPO_XML_ARG_COUNT;
insertNameAndDescription(db,
defaultRepos[offset], // name
defaultRepos[offset + 1], // address
defaultRepos[offset + 2] // description
);
}
}
/**
* Add a fingerprint field to repos. For any field with a public key,
* calculate its fingerprint and save it to the database.
*/
private void addFingerprintToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 44) {
return;
}
if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.FINGERPRINT)) {
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.FINGERPRINT + " text");
}
List<Repo> oldrepos = new ArrayList<>();
Cursor cursor = db.query(RepoTable.NAME,
new String[] {RepoTable.Cols.ADDRESS, RepoTable.Cols.SIGNING_CERT},
null, null, null, null, null);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Repo repo = new Repo();
repo.address = cursor.getString(0);
repo.signingCertificate = cursor.getString(1);
oldrepos.add(repo);
cursor.moveToNext();
}
}
cursor.close();
}
for (final Repo repo : oldrepos) {
ContentValues values = new ContentValues();
values.put(RepoTable.Cols.FINGERPRINT, Utils.calcFingerprint(repo.signingCertificate));
db.update(RepoTable.NAME, values, RepoTable.Cols.ADDRESS + " = ?", new String[] {repo.address});
}
}
private void addMaxAgeToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 30 || columnExists(db, RepoTable.NAME, RepoTable.Cols.MAX_AGE)) {
return;
}
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.MAX_AGE + " integer not null default 0");
}
private void addVersionToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 33 || columnExists(db, RepoTable.NAME, RepoTable.Cols.VERSION)) {
return;
}
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.VERSION + " integer not null default 0");
}
private void addLastUpdatedToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 35 || columnExists(db, RepoTable.NAME, RepoTable.Cols.LAST_UPDATED)) {
return;
}
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.LAST_UPDATED + " column to " + RepoTable.NAME);
db.execSQL("Alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.LAST_UPDATED + " string");
}
private void populateRepoNames(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 37) {
return;
}
Utils.debugLog(TAG, "Populating repo names from the url");
final String[] columns = {RepoTable.Cols.ADDRESS, RepoTable.Cols._ID};
Cursor cursor = db.query(RepoTable.NAME, columns,
RepoTable.Cols.NAME + " IS NULL OR " + RepoTable.Cols.NAME + " = ''", null, null, null, null);
if (cursor != null) {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String address = cursor.getString(0);
long id = cursor.getInt(1);
ContentValues values = new ContentValues(1);
String name = Repo.addressToName(address);
values.put(RepoTable.Cols.NAME, name);
final String[] args = {Long.toString(id)};
Utils.debugLog(TAG, "Setting repo name to '" + name + "' for repo " + address);
db.update(RepoTable.NAME, values, RepoTable.Cols._ID + " = ?", args);
cursor.moveToNext();
}
}
cursor.close();
}
}
private void renameRepoId(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 36 || columnExists(db, RepoTable.NAME, RepoTable.Cols._ID)) {
return;
}
Utils.debugLog(TAG, "Renaming " + RepoTable.NAME + ".id to " + RepoTable.Cols._ID);
db.beginTransaction();
try {
// http://stackoverflow.com/questions/805363/how-do-i-rename-a-column-in-a-sqlite-database-table#805508
String tempTableName = RepoTable.NAME + "__temp__";
db.execSQL("ALTER TABLE " + RepoTable.NAME + " RENAME TO " + tempTableName + ";");
// I realise this is available in the CREATE_TABLE_REPO above,
// however I have a feeling that it will need to be the same as the
// current structure of the table as of DBVersion 36, or else we may
// get into strife. For example, if there was a field that
// got removed, then it will break the "insert select"
// statement. Therefore, I've put a copy of CREATE_TABLE_REPO
// here that is the same as it was at DBVersion 36.
String createTableDdl = "create table " + RepoTable.NAME + " ("
+ RepoTable.Cols._ID + " integer not null primary key, "
+ RepoTable.Cols.ADDRESS + " text not null, "
+ RepoTable.Cols.NAME + " text, "
+ RepoTable.Cols.DESCRIPTION + " text, "
+ RepoTable.Cols.IN_USE + " integer not null, "
+ RepoTable.Cols.PRIORITY + " integer not null, "
+ RepoTable.Cols.SIGNING_CERT + " text, "
+ RepoTable.Cols.FINGERPRINT + " text, "
+ RepoTable.Cols.MAX_AGE + " integer not null default 0, "
+ RepoTable.Cols.VERSION + " integer not null default 0, "
+ RepoTable.Cols.LAST_ETAG + " text, "
+ RepoTable.Cols.LAST_UPDATED + " string);";
db.execSQL(createTableDdl);
String nonIdFields = TextUtils.join(", ", new String[] {
RepoTable.Cols.ADDRESS,
RepoTable.Cols.NAME,
RepoTable.Cols.DESCRIPTION,
RepoTable.Cols.IN_USE,
RepoTable.Cols.PRIORITY,
RepoTable.Cols.SIGNING_CERT,
RepoTable.Cols.FINGERPRINT,
RepoTable.Cols.MAX_AGE,
RepoTable.Cols.VERSION,
RepoTable.Cols.LAST_ETAG,
RepoTable.Cols.LAST_UPDATED,
});
String insertSql = "INSERT INTO " + RepoTable.NAME +
"(" + RepoTable.Cols._ID + ", " + nonIdFields + " ) " +
"SELECT id, " + nonIdFields + " FROM " + tempTableName + ";";
db.execSQL(insertSql);
db.execSQL("DROP TABLE " + tempTableName + ";");
db.setTransactionSuccessful();
} catch (Exception e) {
Log.e(TAG, "Error renaming id to " + RepoTable.Cols._ID, e);
}
db.endTransaction();
}
private void addIsSwapToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 47 || columnExists(db, RepoTable.NAME, RepoTable.Cols.IS_SWAP)) {
return;
}
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.IS_SWAP + " field to " + RepoTable.NAME + " table in db.");
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.IS_SWAP + " boolean default 0;");
}
private void addCredentialsToRepo(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 52) {
return;
}
if (!columnExists(db, Schema.RepoTable.NAME, RepoTable.Cols.USERNAME)) {
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.USERNAME + " field to " + RepoTable.NAME + " table in db.");
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.USERNAME + " string;");
}
if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.PASSWORD)) {
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.PASSWORD + " field to " + RepoTable.NAME + " table in db.");
db.execSQL("alter table " + RepoTable.NAME + " add column " + RepoTable.Cols.PASSWORD + " string;");
}
}
private void addChangelogToApp(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 48 || columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.CHANGELOG_URL)) {
return;
}
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.CHANGELOG_URL + " column to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.CHANGELOG_URL + " text");
}
private void addIconUrlLargeToApp(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 49 || columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.ICON_URL_LARGE)) {
return;
}
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.ICON_URL_LARGE + " columns to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.ICON_URL_LARGE + " text");
}
private void updateIconUrlLarge(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 50) {
return;
}
Utils.debugLog(TAG, "Recalculating app icon URLs so that the newly added large icons will get updated.");
String query = "UPDATE fdroid_app "
+ "SET iconUrl = ("
+ " SELECT (fdroid_repo.address || CASE WHEN fdroid_repo.version >= ? THEN ? ELSE ? END || fdroid_app.icon) "
+ " FROM fdroid_apk "
+ " JOIN fdroid_repo ON (fdroid_repo._id = fdroid_apk.repo) "
+ " WHERE fdroid_app.id = fdroid_apk.id AND fdroid_apk.vercode = fdroid_app.suggestedVercode "
+ "), iconUrlLarge = ("
+ " SELECT (fdroid_repo.address || CASE WHEN fdroid_repo.version >= ? THEN ? ELSE ? END || fdroid_app.icon) "
+ " FROM fdroid_apk "
+ " JOIN fdroid_repo ON (fdroid_repo._id = fdroid_apk.repo) "
+ " WHERE fdroid_app.id = fdroid_apk.id AND fdroid_apk.vercode = fdroid_app.suggestedVercode"
+ ")";
String iconsDir = Utils.getIconsDir(context, 1.0);
String iconsDirLarge = Utils.getIconsDir(context, 1.5);
String repoVersion = Integer.toString(Repo.VERSION_DENSITY_SPECIFIC_ICONS);
Utils.debugLog(TAG, "Using icons dir '" + iconsDir + "'");
Utils.debugLog(TAG, "Using large icons dir '" + iconsDirLarge + "'");
String[] args = {
repoVersion, iconsDir, Utils.FALLBACK_ICONS_DIR,
repoVersion, iconsDirLarge, Utils.FALLBACK_ICONS_DIR,
};
db.rawQuery(query, args);
clearRepoEtags(db);
}
private void addAuthorToApp(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 53) {
return;
}
if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.AUTHOR)) {
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.AUTHOR + " column to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.AUTHOR + " text");
}
if (!columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.EMAIL)) {
Utils.debugLog(TAG, "Adding " + AppMetadataTable.Cols.EMAIL + " column to " + AppMetadataTable.NAME);
db.execSQL("alter table " + AppMetadataTable.NAME + " add column " + AppMetadataTable.Cols.EMAIL + " text");
}
}
private void useMaxValueInMaxSdkVersion(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 54) {
return;
}
Utils.debugLog(TAG, "Converting " + ApkTable.Cols.MAX_SDK_VERSION + " value 0 to " + Byte.MAX_VALUE);
ContentValues values = new ContentValues();
values.put(ApkTable.Cols.MAX_SDK_VERSION, Byte.MAX_VALUE);
db.update(ApkTable.NAME, values, ApkTable.Cols.MAX_SDK_VERSION + " < 1", null);
}
/**
* The {@code <repo timestamp="">} value was in the metadata for a long time,
* but it was not being used in the client until now.
*/
private void requireTimestampInRepos(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 55) {
return;
}
if (!columnExists(db, RepoTable.NAME, RepoTable.Cols.TIMESTAMP)) {
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.TIMESTAMP + " column to " + RepoTable.NAME);
db.execSQL("alter table " + RepoTable.NAME + " add column "
+ RepoTable.Cols.TIMESTAMP + " integer not null default 0");
}
}
/**
* By clearing the etags stored in the repo table, it means that next time the user updates
* their repos (either manually or on a scheduled task), they will update regardless of whether
* they have changed since last update or not.
*/
private static void clearRepoEtags(SQLiteDatabase db) {
Utils.debugLog(TAG, "Clearing repo etags, so next update will not be skipped with \"Repos up to date\".");
db.execSQL("update " + RepoTable.NAME + " set " + RepoTable.Cols.LAST_ETAG + " = NULL");
}
private void resetTransient(SQLiteDatabase db) {
Utils.debugLog(TAG, "Removing app + apk tables so they can be recreated. Next time F-Droid updates it should trigger an index update.");
context.getSharedPreferences("FDroid", Context.MODE_PRIVATE)
.edit()
.putBoolean("triedEmptyUpdate", false)
.apply();
db.beginTransaction();
try {
if (tableExists(db, Schema.CategoryTable.NAME)) {
db.execSQL("DROP TABLE " + Schema.CategoryTable.NAME);
}
if (tableExists(db, CatJoinTable.NAME)) {
db.execSQL("DROP TABLE " + CatJoinTable.NAME);
}
if (tableExists(db, PackageTable.NAME)) {
db.execSQL("DROP TABLE " + PackageTable.NAME);
}
db.execSQL("DROP TABLE " + AppMetadataTable.NAME);
db.execSQL("DROP TABLE " + ApkTable.NAME);
db.execSQL(CREATE_TABLE_PACKAGE);
db.execSQL(CREATE_TABLE_APP_METADATA);
db.execSQL(CREATE_TABLE_APK);
db.execSQL(CREATE_TABLE_CATEGORY);
db.execSQL(CREATE_TABLE_CAT_JOIN);
clearRepoEtags(db);
ensureIndexes(db);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void resetTransientPre42(SQLiteDatabase db, int oldVersion) {
// Before version 42, only transient info was stored in here. As of some time
// just before 42 (F-Droid 0.60ish) it now has "ignore this version" info which
// was is specified by the user. We don't want to weely-neely nuke that data.
// and the new way to deal with changes to the table structure is to add a
// if (oldVersion < x && !columnExists(...) and then alter the table as required.
if (oldVersion >= 42) {
return;
}
context.getSharedPreferences("FDroid", Context.MODE_PRIVATE).edit()
.putBoolean("triedEmptyUpdate", false).apply();
db.execSQL("drop table " + AppMetadataTable.NAME);
db.execSQL("drop table " + ApkTable.NAME);
clearRepoEtags(db);
db.execSQL(CREATE_TABLE_APP_METADATA);
db.execSQL(CREATE_TABLE_APK);
ensureIndexes(db);
}
private static void ensureIndexes(SQLiteDatabase db) {
if (tableExists(db, PackageTable.NAME)) {
Utils.debugLog(TAG, "Ensuring indexes exist for " + PackageTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS package_packageName on " + PackageTable.NAME + " (" + PackageTable.Cols.PACKAGE_NAME + ");");
db.execSQL("CREATE INDEX IF NOT EXISTS package_preferredMetadata on " + PackageTable.NAME + " (" + PackageTable.Cols.PREFERRED_METADATA + ");");
}
Utils.debugLog(TAG, "Ensuring indexes exist for " + AppMetadataTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS name on " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.NAME + ");"); // Used for sorting most lists
db.execSQL("CREATE INDEX IF NOT EXISTS added on " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.ADDED + ");"); // Used for sorting "newly added"
if (columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.PACKAGE_ID)) {
db.execSQL("CREATE INDEX IF NOT EXISTS metadata_packageId ON " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.PACKAGE_ID + ");");
}
if (columnExists(db, AppMetadataTable.NAME, AppMetadataTable.Cols.REPO_ID)) {
db.execSQL("CREATE INDEX IF NOT EXISTS metadata_repoId ON " + AppMetadataTable.NAME + " (" + AppMetadataTable.Cols.REPO_ID + ");");
}
Utils.debugLog(TAG, "Ensuring indexes exist for " + ApkTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS apk_vercode on " + ApkTable.NAME + " (" + ApkTable.Cols.VERSION_CODE + ");");
db.execSQL("CREATE INDEX IF NOT EXISTS apk_appId on " + ApkTable.NAME + " (" + ApkTable.Cols.APP_ID + ");");
db.execSQL("CREATE INDEX IF NOT EXISTS repoId ON " + ApkTable.NAME + " (" + ApkTable.Cols.REPO_ID + ");");
if (tableExists(db, AppPrefsTable.NAME)) {
Utils.debugLog(TAG, "Ensuring indexes exist for " + AppPrefsTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS appPrefs_packageName on " + AppPrefsTable.NAME + " (" + AppPrefsTable.Cols.PACKAGE_NAME + ");");
db.execSQL("CREATE INDEX IF NOT EXISTS appPrefs_packageName_ignoreAll_ignoreThis on " + AppPrefsTable.NAME + " (" +
AppPrefsTable.Cols.PACKAGE_NAME + ", " +
AppPrefsTable.Cols.IGNORE_ALL_UPDATES + ", " +
AppPrefsTable.Cols.IGNORE_THIS_UPDATE + ");");
}
Utils.debugLog(TAG, "Ensuring indexes exist for " + InstalledAppTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS installedApp_appId_vercode on " + InstalledAppTable.NAME + " (" +
InstalledAppTable.Cols.PACKAGE_NAME + ", " + InstalledAppTable.Cols.VERSION_CODE + ");");
Utils.debugLog(TAG, "Ensuring indexes exist for " + RepoTable.NAME);
db.execSQL("CREATE INDEX IF NOT EXISTS repo_id_isSwap on " + RepoTable.NAME + " (" +
RepoTable.Cols._ID + ", " + RepoTable.Cols.IS_SWAP + ");");
}
/**
* If any column was added or removed, just drop the table, create it again
* and let the cache be filled from scratch by {@link InstalledAppProviderService}
* For DB versions older than 43, this will create the {@link InstalledAppProvider}
* table for the first time.
*/
private void recreateInstalledAppTable(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 56) {
return;
}
Utils.debugLog(TAG, "(re)creating 'installed app' database table.");
if (tableExists(db, "fdroid_installedApp")) {
db.execSQL("DROP TABLE fdroid_installedApp;");
}
db.execSQL(CREATE_TABLE_INSTALLED_APP);
}
private void addTargetSdkVersionToApk(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 57) {
return;
}
Utils.debugLog(TAG, "Adding " + ApkTable.Cols.TARGET_SDK_VERSION
+ " columns to " + ApkTable.NAME);
db.execSQL("alter table " + ApkTable.NAME + " add column "
+ ApkTable.Cols.TARGET_SDK_VERSION + " integer");
}
private void supportRepoPushRequests(SQLiteDatabase db, int oldVersion) {
if (oldVersion >= 62) {
return;
}
Utils.debugLog(TAG, "Adding " + RepoTable.Cols.PUSH_REQUESTS
+ " columns to " + RepoTable.NAME);
db.execSQL("alter table " + RepoTable.NAME + " add column "
+ RepoTable.Cols.PUSH_REQUESTS + " integer not null default "
+ Repo.PUSH_REQUEST_IGNORE);
}
private static boolean columnExists(SQLiteDatabase db, String table, String field) {
boolean found = false;
Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
if (name.equalsIgnoreCase(field)) {
found = true;
break;
}
cursor.moveToNext();
}
cursor.close();
return found;
}
private static boolean tableExists(SQLiteDatabase db, String table) {
Cursor cursor = db.query("sqlite_master", new String[] {"name"},
"type = 'table' AND name = ?", new String[] {table}, null, null, null);
boolean exists = cursor.getCount() > 0;
cursor.close();
return exists;
}
private void insertRepo(SQLiteDatabase db, String name, String address,
String description, String version, String enabled,
String priority, String pushRequests, String pubKey) {
ContentValues values = new ContentValues();
values.put(RepoTable.Cols.ADDRESS, address);
values.put(RepoTable.Cols.NAME, name);
values.put(RepoTable.Cols.DESCRIPTION, description);
values.put(RepoTable.Cols.SIGNING_CERT, pubKey);
values.put(RepoTable.Cols.FINGERPRINT, Utils.calcFingerprint(pubKey));
values.put(RepoTable.Cols.MAX_AGE, 0);
values.put(RepoTable.Cols.VERSION, Utils.parseInt(version, 0));
values.put(RepoTable.Cols.IN_USE, Utils.parseInt(enabled, 0));
values.put(RepoTable.Cols.PRIORITY, Utils.parseInt(priority, Integer.MAX_VALUE));
values.put(RepoTable.Cols.LAST_ETAG, (String) null);
values.put(RepoTable.Cols.TIMESTAMP, 0);
switch (pushRequests) {
case "ignore":
values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_IGNORE);
break;
case "prompt":
values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_PROMPT);
break;
case "always":
values.put(RepoTable.Cols.PUSH_REQUESTS, Repo.PUSH_REQUEST_ACCEPT_ALWAYS);
break;
default:
throw new IllegalArgumentException(pushRequests + " is not a supported option!");
}
Utils.debugLog(TAG, "Adding repository " + name + " with push requests as " + pushRequests);
db.insert(RepoTable.NAME, null, values);
}
}