Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Support scoped storage when sharing images #148 #149

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ apply plugin: 'com.android.application'
apply plugin: 'org.ajoberstar.grgit'

android {
compileSdk 34
compileSdk 35
namespace 'ai.elimu.content_provider'

defaultConfig {
applicationId "ai.elimu.content_provider"
minSdkVersion 24
targetSdkVersion 34
targetSdkVersion 35
versionCode 1002028
versionName "1.2.28-SNAPSHOT"
setProperty("archivesBaseName", "${applicationId}-${versionCode}")
Expand Down
7 changes: 6 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@
android:name=".provider.ImageContentProvider"
android:authorities="${applicationId}.provider.image_provider"
android:enabled="true"
android:exported="true" />
android:exported="true"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/image_file_path"/>
</provider>
<provider
android:name=".provider.AudioContentProvider"
android:authorities="${applicationId}.provider.audio_provider"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.List;

import ai.elimu.content_provider.BuildConfig;
import ai.elimu.content_provider.room.dao.ImageDao;
import ai.elimu.content_provider.room.db.RoomDb;
import ai.elimu.content_provider.room.entity.Image;
import ai.elimu.content_provider.util.FileHelper;

public class ImageContentProvider extends ContentProvider {

Expand All @@ -24,6 +29,7 @@ public class ImageContentProvider extends ContentProvider {
private static final int CODE_IMAGES_BY_WORD_LABEL_ID = 3;

private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
private String TAG = ImageContentProvider.class.getName();

static {
MATCHER.addURI(AUTHORITY, TABLE_IMAGES, CODE_IMAGES);
Expand Down Expand Up @@ -150,4 +156,41 @@ public int delete(Uri uri, String selection, String[] selectionArgs) {

throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
List<String> segments = uri.getPathSegments();
if (segments.size() < 2) {
throw new FileNotFoundException("Invalid URI: " + uri);
}
String fileId = segments.get(1);

RoomDb roomDb = RoomDb.getDatabase(getContext());
ImageDao imageDao = roomDb.imageDao();

long imageId;
try {
imageId = Long.parseLong(fileId);
} catch (NumberFormatException e) {
Log.e(TAG, "Failed to parse image ID: " + fileId, e);
throw new FileNotFoundException("Invalid image ID format: " + fileId);
}

Image image = imageDao.load(imageId);

if (image == null) {
throw new FileNotFoundException("File not found with id: " + imageId);
}

File imageFile = FileHelper.getImageFile(image, getContext());
if (imageFile == null) {
throw new FileNotFoundException("imageFile not found with id: " + imageId);
}
if (!imageFile.exists()) {
Log.e(TAG, "imageFile doesn't exist: " + imageFile.getAbsolutePath());
throw new FileNotFoundException("File not found: " + imageFile.getAbsolutePath());
}
return ParcelFileDescriptor.open(imageFile, ParcelFileDescriptor.MODE_READ_ONLY);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
import android.content.Context;
import android.os.Environment;

import androidx.annotation.RestrictTo;

import java.io.File;

import ai.elimu.content_provider.room.entity.Image;
import ai.elimu.model.v2.gson.content.AudioGson;
import ai.elimu.model.v2.gson.content.ImageGson;
import ai.elimu.model.v2.gson.content.VideoGson;
Expand All @@ -19,8 +22,20 @@ public static File getImageFile(ImageGson imageGson, Context context) {
return null;
}
File imagesDirectory = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File file = new File(imagesDirectory, imageGson.getId() + "_r" + imageGson.getRevisionNumber() + "." + imageGson.getImageFormat().toString().toLowerCase());
return file;
return new File(imagesDirectory, imageGson.getId()
+ "_r" + imageGson.getRevisionNumber() + "."
+ imageGson.getImageFormat().toString().toLowerCase());
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public static File getImageFile(Image imageGson, Context context) {
if ((imageGson.getId() == null) || (imageGson.getRevisionNumber() == null)) {
return null;
}
File imagesDirectory = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return new File(imagesDirectory, imageGson.getId()
+ "_r" + imageGson.getRevisionNumber() + "."
+ imageGson.getImageFormat().toString().toLowerCase());
}

public static File getAudioFile(AudioGson audioGson, Context context) {
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/res/xml/image_file_path.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="pictures" path="Pictures/" />
</paths>