package com.novoda.downloadmanager.lib;
import android.content.ContentValues;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import com.novoda.downloadmanager.lib.logger.LLog;
/**
* Creates and updated database on demand when opening it.
* Helper class to create database the first time the provider is
* initialized and upgrade it when a new version of the provider needs
* an updated version of the database.
*/
final class DatabaseHelper extends SQLiteOpenHelper {
/**
* Current database version
*/
private static final int DB_VERSION = 2;
private static final String VERSION_ONE_TO_VERSION_TWO_MIGRATION_SCRIPT = "ALTER TABLE "
+ DownloadContract.Batches.BATCHES_TABLE_NAME
+ " ADD "
+ DownloadContract.Batches.COLUMN_HAS_STARTED + " BOOLEAN NOT NULL DEFAULT 0;";
/**
* columns to request from DownloadProvider.
*/
public static final String[] DOWNLOAD_BY_BATCH_VIEW_COLUMNS = new String[]{
DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads._ID + " AS _id ",
DownloadContract.Downloads.COLUMN_DATA,
DownloadContract.Downloads.COLUMN_MEDIAPROVIDER_URI,
DownloadContract.Downloads.COLUMN_DESTINATION,
DownloadContract.Downloads.COLUMN_URI,
DownloadContract.Downloads.COLUMN_STATUS,
DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_DELETED,
DownloadContract.Downloads.COLUMN_FILE_NAME_HINT,
DownloadContract.Downloads.COLUMN_MIME_TYPE,
DownloadContract.Downloads.COLUMN_TOTAL_BYTES,
DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_LAST_MODIFICATION,
DownloadContract.Downloads.COLUMN_CURRENT_BYTES,
DownloadContract.Downloads.COLUMN_NOTIFICATION_EXTRAS,
DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_EXTRA_DATA,
DownloadContract.Downloads.COLUMN_BATCH_ID,
DownloadContract.Batches.COLUMN_TITLE,
DownloadContract.Batches.COLUMN_DESCRIPTION,
DownloadContract.Batches.COLUMN_BIG_PICTURE,
DownloadContract.Batches.COLUMN_VISIBILITY,
DownloadContract.Batches.COLUMN_STATUS,
DownloadContract.BatchesWithSizes.VIEW_NAME_BATCHES_WITH_SIZES + "." + DownloadContract.Batches.COLUMN_DELETED,
DownloadContract.BatchesWithSizes.COLUMN_TOTAL_BYTES,
DownloadContract.BatchesWithSizes.COLUMN_CURRENT_BYTES
};
public static final String[] DOWNLOADS_WITHOUT_PROGRESS_VIEW_COLUMNS = new String[]{
DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads._ID + " AS _id ",
DownloadContract.Downloads.COLUMN_DATA,
DownloadContract.Downloads.COLUMN_MEDIAPROVIDER_URI,
DownloadContract.Downloads.COLUMN_DESTINATION,
DownloadContract.Downloads.COLUMN_URI,
DownloadContract.Downloads.COLUMN_STATUS,
DownloadContract.Downloads.COLUMN_FILE_NAME_HINT,
DownloadContract.Downloads.COLUMN_MIME_TYPE,
DownloadContract.Downloads.COLUMN_NOTIFICATION_EXTRAS,
DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_EXTRA_DATA,
DownloadContract.Downloads.COLUMN_BATCH_ID,
DownloadContract.Batches.COLUMN_TITLE,
DownloadContract.Batches.COLUMN_DESCRIPTION,
DownloadContract.Batches.COLUMN_BIG_PICTURE,
DownloadContract.Batches.COLUMN_STATUS
};
public static final String[] BATCHES_WITHOUT_PROGRESS_VIEW_COLUMNS = new String[]{
DownloadContract.Batches.COLUMN_TITLE,
DownloadContract.Batches.COLUMN_DESCRIPTION,
DownloadContract.Batches.COLUMN_BIG_PICTURE,
DownloadContract.Batches.COLUMN_STATUS,
DownloadContract.Batches.COLUMN_EXTRA_DATA,
DownloadContract.Batches.COLUMN_LAST_MODIFICATION,
DownloadContract.Batches.COLUMN_DELETED
};
public DatabaseHelper(Context context, String dbName) {
super(context, dbName, null, DB_VERSION);
}
/**
* Creates database the first time we try to open it.
*/
@Override
public void onCreate(@NonNull SQLiteDatabase db) {
LLog.v("populating new database");
createDownloadsTable(db);
createHeadersTable(db);
createBatchesTable(db);
createBatchesWithSizesView(db);
createDownloadsByBatchView(db);
createDownloadsWithoutProgressView(db);
createBatchesWithoutProgressView(db);
makeCacheDownloadsInvisible(db);
}
/**
* Updates the database format when a content provider is used
* with a database that was created with a different format.
* <p/>
* Note: to support downgrades, creating a table should always drop it first if it already
* exists.
*/
@Override
public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, final int newVersion) {
if (oldVersion == 1 && newVersion == 2) {
upgradeFromVersionOneToVersionTwo(db);
}
}
private void upgradeFromVersionOneToVersionTwo(@NonNull SQLiteDatabase db) {
try {
db.execSQL(VERSION_ONE_TO_VERSION_TWO_MIGRATION_SCRIPT);
} catch (SQLException ex) {
LLog.e("couldn't update table in downloads database to v2");
throw ex;
}
}
/**
* Creates the table that'll hold the download information.
*/
private void createDownloadsTable(SQLiteDatabase db) {
try {
db.execSQL("DROP TABLE IF EXISTS " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME);
db.execSQL(
"CREATE TABLE " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "("
+ DownloadContract.Downloads._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ DownloadContract.Downloads.COLUMN_URI + " TEXT, "
+ Constants.RETRY_AFTER_X_REDIRECT_COUNT + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_APP_DATA + " TEXT, "
+ DownloadContract.Downloads.COLUMN_NO_INTEGRITY + " BOOLEAN, "
+ DownloadContract.Downloads.COLUMN_FILE_NAME_HINT + " TEXT, "
+ DownloadContract.Downloads.COLUMN_DATA + " TEXT, "
+ DownloadContract.Downloads.COLUMN_MIME_TYPE + " TEXT, "
+ DownloadContract.Downloads.COLUMN_DESTINATION + " INTEGER, "
+ Constants.NO_SYSTEM_FILES + " BOOLEAN, "
+ DownloadContract.Downloads.COLUMN_CONTROL + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_STATUS + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_FAILED_CONNECTIONS + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_LAST_MODIFICATION + " BIGINT, "
+ DownloadContract.Downloads.COLUMN_NOTIFICATION_CLASS + " TEXT, "
+ DownloadContract.Downloads.COLUMN_NOTIFICATION_EXTRAS + " TEXT, "
+ DownloadContract.Downloads.COLUMN_COOKIE_DATA + " TEXT, "
+ DownloadContract.Downloads.COLUMN_USER_AGENT + " TEXT, "
+ DownloadContract.Downloads.COLUMN_REFERER + " TEXT, "
+ DownloadContract.Downloads.COLUMN_TOTAL_BYTES + " INTEGER NOT NULL DEFAULT -1, "
+ DownloadContract.Downloads.COLUMN_CURRENT_BYTES + " INTEGER NOT NULL DEFAULT 0, "
+ Constants.ETAG + " TEXT, "
+ Constants.UID + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_OTHER_UID + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_ALLOW_ROAMING + " INTEGER NOT NULL DEFAULT 0, "
+ DownloadContract.Downloads.COLUMN_ALLOWED_NETWORK_TYPES + " INTEGER NOT NULL DEFAULT 0, "
+ DownloadContract.Downloads.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI + " INTEGER NOT NULL DEFAULT 1, "
+ DownloadContract.Downloads.COLUMN_BYPASS_RECOMMENDED_SIZE_LIMIT + " INTEGER NOT NULL DEFAULT 0, "
+ DownloadContract.Downloads.COLUMN_MEDIAPROVIDER_URI + " TEXT, "
+ DownloadContract.Downloads.COLUMN_DELETED + " BOOLEAN NOT NULL DEFAULT 0, "
+ DownloadContract.Downloads.COLUMN_ERROR_MSG + " TEXT, "
+ DownloadContract.Downloads.COLUMN_ALLOW_METERED + " INTEGER NOT NULL DEFAULT 1, "
+ DownloadContract.Downloads.COLUMN_BATCH_ID + " INTEGER, "
+ DownloadContract.Downloads.COLUMN_EXTRA_DATA + " TEXT, "
+ DownloadContract.Downloads.COLUMN_ALWAYS_RESUME + " INTEGER NOT NULL DEFAULT 0, "
+ DownloadContract.Downloads.COLUMN_ALLOW_TAR_UPDATES + " INTEGER NOT NULL DEFAULT 0, "
+ Constants.MEDIA_SCANNED + " BOOLEAN);"
);
} catch (SQLException ex) {
LLog.e("couldn't create table in downloads database");
throw ex;
}
}
/**
* Set all existing downloads to the cache partition to be invisible in the downloads UI.
*/
private void makeCacheDownloadsInvisible(SQLiteDatabase db) {
ContentValues values = new ContentValues();
values.put(DownloadContract.Downloads.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI, false);
String cacheSelection = DownloadContract.Downloads.COLUMN_DESTINATION + " != " + DownloadsDestination.DESTINATION_EXTERNAL;
db.update(DownloadContract.Downloads.DOWNLOADS_TABLE_NAME, values, cacheSelection, null);
}
private void createHeadersTable(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + DownloadContract.RequestHeaders.HEADERS_DB_TABLE);
db.execSQL(
"CREATE TABLE " + DownloadContract.RequestHeaders.HEADERS_DB_TABLE + "("
+ DownloadContract.RequestHeaders._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ DownloadContract.RequestHeaders.COLUMN_DOWNLOAD_ID + " INTEGER NOT NULL,"
+ DownloadContract.RequestHeaders.COLUMN_HEADER + " TEXT NOT NULL,"
+ DownloadContract.RequestHeaders.COLUMN_VALUE + " TEXT NOT NULL"
+ ");"
);
}
private void createBatchesTable(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + DownloadContract.Batches.BATCHES_TABLE_NAME);
db.execSQL(
"CREATE TABLE " + DownloadContract.Batches.BATCHES_TABLE_NAME + "("
+ DownloadContract.Batches._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ DownloadContract.Batches.COLUMN_TITLE + " TEXT NOT NULL,"
+ DownloadContract.Batches.COLUMN_DESCRIPTION + " TEXT,"
+ DownloadContract.Batches.COLUMN_BIG_PICTURE + " TEXT,"
+ DownloadContract.Batches.COLUMN_STATUS + " INTEGER,"
+ DownloadContract.Batches.COLUMN_VISIBILITY + " INTEGER,"
+ DownloadContract.Batches.COLUMN_DELETED + " BOOLEAN NOT NULL DEFAULT 0,"
+ DownloadContract.Batches.COLUMN_EXTRA_DATA + " TEXT,"
+ DownloadContract.Batches.COLUMN_LAST_MODIFICATION + " TEXT,"
+ DownloadContract.Batches.COLUMN_HAS_STARTED + " BOOLEAN NOT NULL DEFAULT 0"
+ ");"
);
}
private void createDownloadsByBatchView(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + DownloadContract.DownloadsByBatch.VIEW_NAME_DOWNLOADS_BY_BATCH);
db.execSQL(
"CREATE VIEW " + DownloadContract.DownloadsByBatch.VIEW_NAME_DOWNLOADS_BY_BATCH
+ " AS SELECT DISTINCT "
+ projectionFrom(DOWNLOAD_BY_BATCH_VIEW_COLUMNS)
+ " FROM " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME
+ " INNER JOIN " + DownloadContract.BatchesWithSizes.VIEW_NAME_BATCHES_WITH_SIZES
+ " ON " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_BATCH_ID
+ " = " + DownloadContract.BatchesWithSizes.VIEW_NAME_BATCHES_WITH_SIZES + "." + DownloadContract.Batches._ID + ";"
);
}
private void createBatchesWithSizesView(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + DownloadContract.BatchesWithSizes.VIEW_NAME_BATCHES_WITH_SIZES);
db.execSQL(
"CREATE VIEW " + DownloadContract.BatchesWithSizes.VIEW_NAME_BATCHES_WITH_SIZES
+ " AS SELECT DISTINCT "
+ DownloadContract.Batches.BATCHES_TABLE_NAME + ".*, "
+ DownloadContract.BatchesWithSizes.COLUMN_CURRENT_BYTES + ", "
+ DownloadContract.BatchesWithSizes.COLUMN_TOTAL_BYTES
+ " FROM " + DownloadContract.Batches.BATCHES_TABLE_NAME
+ " INNER JOIN "
+ " (SELECT "
+ " " + DownloadContract.Downloads.COLUMN_BATCH_ID + ","
+ " SUM(" + DownloadContract.Downloads.COLUMN_CURRENT_BYTES + ") AS " + DownloadContract.BatchesWithSizes.COLUMN_CURRENT_BYTES + ","
+ " MAX(SUM(" + DownloadContract.Downloads.COLUMN_TOTAL_BYTES + "), -1) AS " + DownloadContract.BatchesWithSizes.COLUMN_TOTAL_BYTES
+ " FROM " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME
+ " GROUP BY " + DownloadContract.Downloads.COLUMN_BATCH_ID
+ " ) " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME
+ " ON " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_BATCH_ID
+ " = " + DownloadContract.Batches.BATCHES_TABLE_NAME + "." + DownloadContract.Batches._ID + ";"
);
}
private void createDownloadsWithoutProgressView(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + DownloadContract.DownloadsWithoutProgress.VIEW_NAME_DOWNLOADS_WITHOUT_PROGRESS);
db.execSQL(
"CREATE VIEW " + DownloadContract.DownloadsWithoutProgress.VIEW_NAME_DOWNLOADS_WITHOUT_PROGRESS
+ " AS SELECT DISTINCT "
+ projectionFrom(DOWNLOADS_WITHOUT_PROGRESS_VIEW_COLUMNS)
+ " FROM " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME
+ " INNER JOIN " + DownloadContract.Batches.BATCHES_TABLE_NAME
+ " ON " + DownloadContract.Downloads.DOWNLOADS_TABLE_NAME + "." + DownloadContract.Downloads.COLUMN_BATCH_ID
+ " = " + DownloadContract.Batches.BATCHES_TABLE_NAME + "." + DownloadContract.Batches._ID + ";"
);
}
private void createBatchesWithoutProgressView(SQLiteDatabase db) {
db.execSQL("DROP VIEW IF EXISTS " + DownloadContract.BatchesWithoutProgress.VIEW_NAME_BATCHES_WITHOUT_PROGRESS);
db.execSQL(
"CREATE VIEW " + DownloadContract.BatchesWithoutProgress.VIEW_NAME_BATCHES_WITHOUT_PROGRESS
+ " AS SELECT DISTINCT "
+ projectionFrom(BATCHES_WITHOUT_PROGRESS_VIEW_COLUMNS)
+ " FROM " + DownloadContract.Batches.BATCHES_TABLE_NAME
+ ";"
);
}
private String projectionFrom(String[] array) {
if (array == null) {
return "null";
}
if (array.length == 0) {
return "[]";
}
StringBuilder sb = new StringBuilder(array.length * 7);
sb.append(array[0]);
for (int i = 1; i < array.length; i++) {
sb.append(", ");
sb.append(array[i]);
}
return sb.toString();
}
}