package org.fdroid.fdroid.data;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import org.fdroid.fdroid.data.Schema.ApkTable;
import java.util.List;
/**
* This class does all of its operations in a temporary sqlite table.
*/
public class TempApkProvider extends ApkProvider {
private static final String PROVIDER_NAME = "TempApkProvider";
static final String TABLE_TEMP_APK = "temp_" + ApkTable.NAME;
private static final String PATH_INIT = "init";
private static final int CODE_INIT = 10000;
private static final UriMatcher MATCHER = new UriMatcher(-1);
static {
MATCHER.addURI(getAuthority(), PATH_INIT, CODE_INIT);
MATCHER.addURI(getAuthority(), PATH_APK_FROM_ANY_REPO + "/#/*", CODE_APK_FROM_ANY_REPO);
MATCHER.addURI(getAuthority(), PATH_APK_FROM_REPO + "/#/#", CODE_APK_FROM_REPO);
MATCHER.addURI(getAuthority(), PATH_REPO_APK + "/#/*", CODE_REPO_APK);
}
@Override
protected String getTableName() {
return TABLE_TEMP_APK;
}
@Override
protected String getAppTableName() {
return TempAppProvider.TABLE_TEMP_APP;
}
public static String getAuthority() {
return AUTHORITY + "." + PROVIDER_NAME;
}
public static Uri getContentUri() {
return Uri.parse("content://" + getAuthority());
}
public static Uri getApkUri(Apk apk) {
return getContentUri()
.buildUpon()
.appendPath(PATH_APK_FROM_REPO)
.appendPath(Long.toString(apk.appId))
.appendPath(Integer.toString(apk.versionCode))
.build();
}
public static Uri getApksUri(Repo repo, List<Apk> apks) {
return getContentUri()
.buildUpon()
.appendPath(PATH_REPO_APK)
.appendPath(Long.toString(repo.id))
.appendPath(buildApkString(apks))
.build();
}
public static class Helper {
/**
* Deletes the old temporary table (if it exists). Then creates a new temporary apk provider
* table and populates it with all the data from the real apk provider table.
*
* This is package local because it must be invoked after
* {@link org.fdroid.fdroid.data.TempAppProvider.Helper#init(Context)}. Due to this
* dependence, that method invokes this one itself, rather than leaving it to the
* {@link RepoPersister}.
*/
static void init(Context context) {
Uri uri = Uri.withAppendedPath(getContentUri(), PATH_INIT);
context.getContentResolver().insert(uri, new ContentValues());
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
if (MATCHER.match(uri) == CODE_INIT) {
initTable();
return null;
}
return super.insert(uri, values);
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
if (MATCHER.match(uri) != CODE_APK_FROM_REPO) {
throw new UnsupportedOperationException("Cannot update anything other than a single apk.");
}
return performUpdateUnchecked(uri, values, where, whereArgs);
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
if (MATCHER.match(uri) != CODE_REPO_APK) {
throw new UnsupportedOperationException("Invalid URI for apk content provider: " + uri);
}
List<String> pathSegments = uri.getPathSegments();
QuerySelection query = new QuerySelection(where, whereArgs)
.add(queryRepo(Long.parseLong(pathSegments.get(1)), false))
.add(queryApks(pathSegments.get(2), false));
int rowsAffected = db().delete(getTableName(), query.getSelection(), query.getArgs());
if (!isApplyingBatch()) {
getContext().getContentResolver().notifyChange(uri, null);
}
return rowsAffected;
}
private void initTable() {
final SQLiteDatabase db = db();
final String memoryDbName = TempAppProvider.DB;
db.execSQL(DBHelper.CREATE_TABLE_APK.replaceFirst(Schema.ApkTable.NAME, memoryDbName + "." + getTableName()));
db.execSQL(TempAppProvider.copyData(Schema.ApkTable.Cols.ALL_COLS, Schema.ApkTable.NAME, memoryDbName + "." + getTableName()));
db.execSQL("CREATE INDEX IF NOT EXISTS " + memoryDbName + ".apk_vercode on " + getTableName() + " (" + ApkTable.Cols.VERSION_CODE + ");");
db.execSQL("CREATE INDEX IF NOT EXISTS " + memoryDbName + ".apk_compatible ON " + getTableName() + " (" + ApkTable.Cols.IS_COMPATIBLE + ");");
}
}