Skip to content

Commit

Permalink
#155: Fix crash if missing file permissions
Browse files Browse the repository at this point in the history
  • Loading branch information
k3b committed Dec 11, 2020
1 parent 461b889 commit 04cef1a
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 38 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Expand Up @@ -19,7 +19,7 @@ android {

minSdkVersion 14 // Android 4.0 Ice Cream Sandwich (API 14); Android 4.4 KitKat (API 19); Android 5.0 Lollipop (API 21);
// Android 6.0 Marshmallow (API 23); Android 7.0 Nougat (API 24)
maxSdkVersion 28 // #155: android-10=api29
// maxSdkVersion 28 // #155: android-10=api29

targetSdkVersion 28

Expand Down
Expand Up @@ -48,6 +48,7 @@
import de.k3b.android.androFotoFinder.queries.MediaContentproviderRepository;
import de.k3b.android.androFotoFinder.queries.MediaContentproviderRepositoryImpl;
import de.k3b.android.androFotoFinder.queries.MediaDBRepository;
import de.k3b.android.androFotoFinder.queries.MediaRepositoryApiWrapper;
import de.k3b.android.androFotoFinder.queries.MergedMediaRepository;
import de.k3b.android.io.AndroidFileFacade;
import de.k3b.android.io.DocumentFileTranslator;
Expand Down Expand Up @@ -106,31 +107,77 @@ public static void setMediaImageDbReplacement(Context context, boolean useMediaI

Global.useAo10MediaImageDbReplacement = useMediaImageDbReplacement;

final MediaContentproviderRepository mediaContentproviderRepository = new MediaContentproviderRepository(context);

if (Global.useAo10MediaImageDbReplacement) {
final SQLiteDatabase writableDatabase = DatabaseHelper.getWritableDatabase(context);
final MediaDBRepository mediaDBRepository = new MediaDBRepository(writableDatabase);
FotoSql.setMediaDBApi(new MergedMediaRepository(mediaDBRepository, mediaContentproviderRepository));
registerAo10MediaImageDbReplacement(context);
} else {
registerMediaContentProvider(context, oldMediaDBApi);
}
}
}

MediaContent2DBUpdateService.instance = new MediaContent2DBUpdateService(context, writableDatabase);
private static void registerMediaContentProvider(Context context, IMediaRepositoryApi oldMediaDBApi) {
final MediaContentproviderRepository mediaContentproviderRepository = new MediaContentproviderRepository(context);
PhotoChangeNotifyer.unregisterContentObserver(context, GlobalMediaContentObserver.getInstance(context));
if ((oldMediaDBApi != null) && (MediaContent2DBUpdateService.instance != null)) {
// switching from mediaImageDbReplacement to Contentprovider
MediaContent2DBUpdateService.instance.clearMediaCopy();
}
FotoSql.setMediaDBApi(mediaContentproviderRepository);
MediaContent2DBUpdateService.instance = null;
}

if (FotoSql.getCount(new QueryParameter().addWhere("1 = 1")) == 0) {
// database is empty; reload from Contentprovider
MediaContent2DBUpdateService.instance.rebuild(context, null);
}
/**
* Android-10-ff use copy of media database for reading to circumvent android-10-media-contentprovider-restrictions
*/
private static IMediaRepositoryApi registerAo10MediaImageDbReplacement(Context context) {
File databaseFile = DatabaseHelper.getDatabasePath(context);
try {
final SQLiteDatabase writableDatabase = DatabaseHelper.getWritableDatabase(context);
//!!! throws SQLiteCantOpenDatabaseException("Failed to open database '/storage/emulated/0/databases/APhotoManager.db'") if no permission

PhotoChangeNotifyer.registerContentObserver(context, GlobalMediaContentObserver.getInstance(context));
final MediaDBRepository mediaDBRepository = new MediaDBRepository(writableDatabase);
final MediaContentproviderRepository mediaContentproviderRepository = new MediaContentproviderRepository(context);

} else {
PhotoChangeNotifyer.unregisterContentObserver(context, GlobalMediaContentObserver.getInstance(context));
if ((oldMediaDBApi != null) && (MediaContent2DBUpdateService.instance != null)) {
// switching from mediaImageDbReplacement to Contentprovider
MediaContent2DBUpdateService.instance.clearMediaCopy();
}
FotoSql.setMediaDBApi(mediaContentproviderRepository);
MediaContent2DBUpdateService.instance = null;
// read from copy database, write to both: copy-database and content-provider
final MergedMediaRepository mediaDBApi = new MergedMediaRepository(mediaDBRepository, mediaContentproviderRepository);
FotoSql.setMediaDBApi(mediaDBApi);

MediaContent2DBUpdateService.instance = new MediaContent2DBUpdateService(context, writableDatabase);

if (FotoSql.getCount(new QueryParameter().addWhere("1 = 1")) == 0) {
// database is empty; reload from Contentprovider
MediaContent2DBUpdateService.instance.rebuild(context, null);
}

PhotoChangeNotifyer.registerContentObserver(context, GlobalMediaContentObserver.getInstance(context));
return mediaDBApi;
} catch (RuntimeException ignore) {
Log.w(Global.LOG_CONTEXT,
"Cannot open Database (missing permissions) "
+ DatabaseHelper.getDatabasePath(context) + " "
+ ignore.getMessage(), ignore);
FotoSql.setMediaDBApi(new MediaDBRepositoryLoadOnDemand(context));
}
return null;
}

/**
* if Open Database failes because of missing File permissions
* postpone opening database until permission is granted
*/
private static class MediaDBRepositoryLoadOnDemand extends MediaRepositoryApiWrapper {

private final Context context;

public MediaDBRepositoryLoadOnDemand(Context context) {
super(null);
this.context = context;
}

@Override
protected IMediaRepositoryApi getReadChild() {
return registerAo10MediaImageDbReplacement(context);
}
}

Expand Down
Expand Up @@ -24,6 +24,8 @@
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.io.File;

import de.k3b.android.androFotoFinder.transactionlog.TransactionLogSql;
import de.k3b.android.util.DatabaseContext;

Expand All @@ -39,20 +41,33 @@ public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION_2_MEDIA_DB_COPY = 2;

public static final int DATABASE_VERSION = DatabaseHelper.DATABASE_VERSION_2_MEDIA_DB_COPY;
public static final String DATABASE_NAME = "APhotoManager";

private static DatabaseHelper instance = null;
private static DatabaseContext databaseContext = null;

public DatabaseHelper(final Context context, final String databaseName) {
super(context, databaseName, null, DatabaseHelper.DATABASE_VERSION);
}

public static SQLiteDatabase getWritableDatabase(Context context) {
return getInstance(context).getWritableDatabase();
}

public static File getDatabasePath(Context context) {
getInstance(context);
return databaseContext.getDatabasePath(DATABASE_NAME);
}

private static DatabaseHelper getInstance(Context context) {
if (instance == null) {
instance = new DatabaseHelper(new DatabaseContext(context), "APhotoManager");
databaseContext = new DatabaseContext(context);
instance = new DatabaseHelper(databaseContext, DATABASE_NAME);
}
return instance.getWritableDatabase();
return instance;
}


public static void version2Upgrade_RecreateMediDbCopy(final SQLiteDatabase db) {
for (String sql : MediaDBRepository.Impl.DDL) {
db.execSQL(sql);
Expand Down
Expand Up @@ -33,9 +33,9 @@
* Created by k3b on 30.11.2019.
*/
public class MediaRepositoryApiWrapper implements IMediaRepositoryApi {
protected final IMediaRepositoryApi readChild;
protected final IMediaRepositoryApi writeChild;
protected final IMediaRepositoryApi transactionChild;
private final IMediaRepositoryApi readChild;
private final IMediaRepositoryApi writeChild;
private final IMediaRepositoryApi transactionChild;

/**
* count the non path write calls
Expand All @@ -54,27 +54,27 @@ public MediaRepositoryApiWrapper(IMediaRepositoryApi readChild, IMediaRepository

@Override
public Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, QueryParameter parameters, VISIBILITY visibility, CancellationSignal cancellationSignal) {
return readChild.createCursorForQuery(out_debugMessage, dbgContext, parameters, visibility, cancellationSignal);
return getReadChild().createCursorForQuery(out_debugMessage, dbgContext, parameters, visibility, cancellationSignal);
}

@Override
public Cursor createCursorForQuery(StringBuilder out_debugMessage, String dbgContext, String from, String sqlWhereStatement, String[] sqlWhereParameters, String sqlSortOrder, CancellationSignal cancellationSignal, String... sqlSelectColums) {
return readChild.createCursorForQuery(out_debugMessage, dbgContext, from, sqlWhereStatement, sqlWhereParameters, sqlSortOrder, cancellationSignal, sqlSelectColums);
return getReadChild().createCursorForQuery(out_debugMessage, dbgContext, from, sqlWhereStatement, sqlWhereParameters, sqlSortOrder, cancellationSignal, sqlSelectColums);
}

@Override
public int execUpdate(String dbgContext, long id, ContentValues values) {
return writeChild.execUpdate(dbgContext, id, values);
return getWriteChild().execUpdate(dbgContext, id, values);
}

@Override
public int execUpdate(String dbgContext, String path, ContentValues values, VISIBILITY visibility) {
return writeChild.execUpdate(dbgContext, path, values, visibility);
return getWriteChild().execUpdate(dbgContext, path, values, visibility);
}

@Override
public int exexUpdateImpl(String dbgContext, ContentValues values, String sqlWhere, String[] selectionArgs) {
return writeChild.exexUpdateImpl(dbgContext, values, sqlWhere, selectionArgs);
return getWriteChild().exexUpdateImpl(dbgContext, values, sqlWhere, selectionArgs);
}

/**
Expand All @@ -88,7 +88,7 @@ public int exexUpdateImpl(String dbgContext, ContentValues values, String sqlWhe
*/
@Override
public Long insertOrUpdateMediaDatabase(String dbgContext, String dbUpdateFilterJpgFullPathName, ContentValues values, VISIBILITY visibility, Long updateSuccessValue) {
return writeChild.insertOrUpdateMediaDatabase(dbgContext, dbUpdateFilterJpgFullPathName, values, visibility, updateSuccessValue);
return getWriteChild().insertOrUpdateMediaDatabase(dbgContext, dbUpdateFilterJpgFullPathName, values, visibility, updateSuccessValue);
}

/**
Expand All @@ -99,44 +99,56 @@ public Long insertOrUpdateMediaDatabase(String dbgContext, String dbUpdateFilter
*/
@Override
public Uri execInsert(String dbgContext, ContentValues values) {
return writeChild.execInsert(dbgContext, values);
return getWriteChild().execInsert(dbgContext, values);
}

/**
* Deletes media items specified by where with the option to prevent cascade delete of the image.
*/
@Override
public int deleteMedia(String dbgContext, String where, String[] selectionArgs, boolean preventDeleteImageFile) {
return writeChild.deleteMedia(dbgContext, where, selectionArgs, preventDeleteImageFile);
return getWriteChild().deleteMedia(dbgContext, where, selectionArgs, preventDeleteImageFile);
}

@Override
public ContentValues getDbContent(long id) {
return readChild.getDbContent(id);
return getReadChild().getDbContent(id);
}

@Override
public long getCurrentUpdateId() {
return transactionChild.getCurrentUpdateId();
return getTransactionChild().getCurrentUpdateId();
}

@Override
public boolean mustRequery(long updateId) {
return transactionChild.mustRequery(updateId);
return getTransactionChild().mustRequery(updateId);
}

@Override
public void beginTransaction() {
transactionChild.beginTransaction();
getTransactionChild().beginTransaction();
}

@Override
public void setTransactionSuccessful() {
transactionChild.setTransactionSuccessful();
getTransactionChild().setTransactionSuccessful();
}

@Override
public void endTransaction() {
transactionChild.endTransaction();
getTransactionChild().endTransaction();
}

protected IMediaRepositoryApi getReadChild() {
return readChild;
}

protected IMediaRepositoryApi getWriteChild() {
return writeChild;
}

protected IMediaRepositoryApi getTransactionChild() {
return transactionChild;
}
}

0 comments on commit 04cef1a

Please sign in to comment.