Use string url or URI for image properties of object - android

I want to store object to data room. when i create an object like account with name, phone number, email and images taken from gallery. Should I use string or uri for images so that objects can be easily passed between activities?

You should copy images to the app's local storage and store that copied image path in the database.
For copying the image to the app's local storage after getting the selected image uri
val dir = requireActivity().getDir(FOLDER_TO_SAVE, Context.MODE_PRIVATE).path
val copiedImage = File(dir, UUID.randomUUID().toString() + ".jpg")
val copiedImageUri = Uri.fromFile(file)
receivedImageUri.path?.let {
File(it).copyTo(copiedImage)
}
Get path of the copied image, using its uri
copiedImageUri.path.?.let{
val copiedImagePath = "file://$it"
//now save this path to your database
}
Use this saved image path as string to exchange between activities.

I think better to store this image as object BLOB
otherwise if you removed this image from gallery it will not be able to get this, because Room, will stored only link not an object. I hope it will be useful
#Entity(tableName = "user")
public class User{
#ColumnInfo(typeAffinity = ColumnInfo.BLOB)
private byte[] image;
}

Related

How to load an image using a URI stored in Room as a String

I am making a practice application to load the inventory of a store, inside the screen I press a floating button that generates a dialog that asks for an image among several data, which the user selects from their gallery, later when pressing the save button in the dialog, the image and the rest of the data are saved in the ViewModel and ROOM, to then generate an item on the main screen that shows these data at the same time that they are printed with Log.d
When generating the item after saving, it is shown correctly, however, if I restart the application the image disappears. Both when generating the image and when restarting the application, the Log.d print shows the same URI in both cases.
My main goal is to save an image, its address, or URI in ROOM, and then load the item with the image. My research leads me to believe that it is correct to save the URI as a string, but I am unaware of the proper practices for saving an image and I am open to other ways to reach a solution if necessary.
First, in a dialog to create an item, I select an image from the gallery like this and save it in the ViewModel and later in ROOM:
val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri ->
selectedImageUri = uri
Log.d("URI RESULT", "$uri")
viewModel.onDialogChanged(
uri.toString()
)
}
)
I save it in ROOM when I press the 'Save' button:
DialogButton(
ButtonDefaults.buttonColors(
backgroundColor = verdeBillete,
contentColor = Color.White
), "Guardar", modifier = Modifier, onClick = {
viewModel.viewModelScope.launch {
viewModel.onAddSelected(
inventoryItem(
0,
uri,
)
)
}
onDismiss()
})
//add to ViewModel
fun onAddSelected(addItem: inventoryItem) {
viewModelScope.launch {
addItem(addItem)
getInventory()
}
}
//ROOM Table
#Entity(tableName = "inventory")
data class inventoryItem(
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "r_id")
var id: Int = 0,
#ColumnInfo(name = "r_uri")
val uri: String,
)
Then I currently try to load the image like this:
Log.d("Loading items", item.uri)
AsyncImage(
model = Uri.parse(item.uri),
contentDescription = null,
modifier = Modifier.fillMaxWidth(),
contentScale = ContentScale.Crop
)
Just after selecting the image from the gallery, the image is visible, however, after restarting the application the image disappears. In both cases the printed URI in Log.d is the same.
Also, I have permission for:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
Update: After reading both answers from CommonsWare and Gowtham K K (Thank you both!) and trying to implement them, I couldn't write the code myself, so I entered the content of the post (the question and both answers) into chatgpt and asked for a solution, which presented me with the following solution which worked for me.
To use takePersistableUriPermission, you must do the following:
First, you need to have permissions to read or write the URI that you
want to save persistently. You can do this by adding the following
line of code in your AndroidManifest.xml file:
or
Then, you need to obtain the URI that you want to save persistently.
For example, if you want to save the URI of an image selected from the
gallery, you can use the ActivityResultContracts.PickVisualMedia
method as follows:
val singlePhotoPickerLauncher =
rememberLauncherForActivityResult( contract =
ActivityResultContracts.PickVisualMedia(), onResult = { uri ->
selectedImageUri = uri } )
Once you have the URI, you can use takePersistableUriPermission to
save it persistently. The takePersistableUriPermission method should
be used on the ContentResolver and takes two parameters: the URI and
the access mode (read or write). For example:
contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION) or
contentResolver.takePersistableUriPermission(uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
Finally, you can save the URI in your ROOM database as a text string
and load it in your application when necessary. For example:
val inventoryItem = inventoryItem(0, uri.toString())
viewModel.onAddSelected(inventoryItem)
Putting everything together:
var selectedImageUri by remember {
mutableStateOf<Uri?>(null)
}
val singlePhotoPickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri ->
selectedImageUri = uri
Log.d("URI RESULT", "$uri")
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION//or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
val resolver = mContext.contentResolver
resolver.takePersistableUriPermission(uri!!, flags)
viewModel.onDialogChanged( //**save to database function**
uri.toString()
)
}
)
This is because the URI would get revoked when app process get killed.
For Storage access framework URIs you can get long term permission using takePersistableUriPermission .
But It might not work for ActivityResultContracts.PickVisualMedia() as far as I know.
In your case you can make your own copy of the image after getting first time and save it in the app specific storage and save that URI in your db.
This will be more flexible. Even if original file gets deleted / you will still can get access of your copied image.

getting image Uri in android

i am wanting to grab the image uri, upload it to firebase in order for it to be available there and other users r able to see this image when connecting to the firebase database. only issue is when an image is selected, firstImage is set as content://com.android.providers.media.documents/document/image%3A525 which doesn't have the extension, whether it's .png, etc.
the logic below is done in a composable function which is y something like gallery intent isn't used.
any insights?
var firstImage by remember { mutableStateOf<Uri?>(null) }
val firstLauncher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri ->
uri?.let { firstImage = it }
}

Saving media file path or URI to SQLite and getting it, best practice

My goal is to:
Save media file to External Storage (in my case it's photo).
Get file path or URI of saved data.
Save it to SQLite (either file path or content URI or smth else).
Be able to get correct URI to this content at any point in the future.
It's very similar to what other very popular application do - they create their directory in 'Pictures' folder and store there photos and use them in their applications while they're also available for viewing using gallery/file explorer etc.
As I understand recommended way to save media content (image, f.e.) is to use MediaStore API and as a result I get content URI, which I can use later.
But then I read that these content URIs might be changed after re-scan of Media happens, so it looks it's not reliable. (For example if SD card is used and it's taken out and inserted again)
At the same time usage of absolute file paths is not recommended and there's tendency to deprecate APIs which use absolute file paths to work with External Storage. So it doesn't look reliable either.
I can only imagine the following solution:
Use unique auto-generated file name while saving (like UUID).
When I need to get content URI (f.e. want to render photo in ImageView) - I can use ContentResolver and search for content URI using file name filter.
Problem with this approach is that I have a lot of photos (gallery) and querying it using ContentResolver can affect performance significantly.
I feel like I'm over complicating things and missing something.
You are indeed overcomplicating things.
Store file to the needed folder in the filesystem(it is better to name the folder under your app name)
Store this path or URI path - whatever you like. (Do not hardcode passes though in your app - device vendors may have different base paths in their devices)
As long as the folder is named the same and files in it named the same(as in your db) - you will be able to access them even if the sdcard was taken out and then put back in.
There are possible complications after reindexing - but for the eight years I work as Android dev I encountered it only once, thus you can easily ignore this stuff.
If you want to have more control over what you store and want to limit access to it - store data into the inner storage of your app - this way you will be 100% sure of where the data is and that it is not tampered with.
Starting from Android 10 you have scoped storage - it is like internal storage but it may be even on an external sdcard.
Here is a small overview of possible storage locations.
And don't overthink it too much - it is a default usecase of the phone and it works just as you would expect - pretty ok and pretty stable.
first, you have to apply for external storage permission in manifest and Runtime Permission Also.
after creating a directory for saving an image in this directory.
you have to also add file provider in XML and code side because it's required.
now it's time to code check my code for saving an image in the folder also these image in the gallery and get the path from a file path.
convert URI to bitmap
http://prntscr.com/10dpvjj
save image function from getting bitmap
private String save(Bitmap bitmap) {
File save_path = null;
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
try {
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + "/SaveDirectory");
dir.mkdirs();
File file = new File(dir, "DirName_" + new SimpleDateFormat("yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime()) + ".png");
save_path = file;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
FileOutputStream f = null;
f = new FileOutputStream(file);
MediaScannerConnection.scanFile(context, new String[]{file.getAbsolutePath()}, null, null);
if (f != null) {
f.write(baos.toByteArray());
f.flush();
f.close();
}
} catch (Exception e) {
// TODO: handle exception
}
Share(save_path); // call your Function Store into database
Log.e("PathOFExec----", "save: " + save_path);
}
get store image location into your database if you wish
private void Share(File savePath) {
if (savePath != null) {
Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", savePath);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/*");
share.putExtra(Intent.EXTRA_TEXT, "TextDetail");
share.putExtra(Intent.EXTRA_STREAM, uri);
context.startActivity(Intent.createChooser(share, "Share Image!"));
//after getting URI you can store the image into SQLite databse for get uri
}
}
I would recommend using Intent.ACTION_OPEN_DOCUMENT for your demand.
1. Create Photo Picking Intent:
val REQUEST_CODE_PICK_PHOTO = 1
fun pickAndSavePhoto(requestCode: Int) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "image/*"
startActivityForResult(intent, requestCode)
}
2. Handle Result:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_PICK_PHOTO && resultCode == RESULT_OK) {
val imageUri = data!!.data!!
//save this uri to your database as String -> imageUri.toString()
}
}
3. Get Image back and Display on ImageView:
fun getBitmapFromUri(context: Context, imageUri: Uri): Bitmap? { //uri is just an address, image may be deleted any time, if so returns null
val bitmap: Bitmap
return try {
val inputStream = context.contentResolver.openInputStream(imageUri)
inputStream.use {
bitmap = BitmapFactory.decodeStream(it)
}
bitmap
} catch (e: Exception) {
Log.e("getBitmapFromUri()", "Image not found.")
null
}
}
val bitmap = getBitmapFromUri(context, imageUri) //get uri String from database and convert it to uri -> uriString.toUri()
if (bitmap != null) {
imageView.setImageBitmap(bitmap)
}
Only ACTION_OPEN_DOCUMENT can access file uri permanently:
Android Retrieve Image by Intent Uri Failed: "has no access to content..."
Demo: https://www.youtube.com/watch?v=LFfWnt77au8

apache cordova set/get audio name/path from storage

I want to be able to save the name and path of a recorded audio file, then get these values next time I start my app. I believe I can save them like this, to local storage:
// after recording is done
function captureSuccess(mediaFile) {
addFilePath(mediaFile);
}
function addFilePath(mediaFile) {
localStorage.setItem(mediaFile.name, mediaFile.fullPath);
}
How can I get these values the next time I start the app? are there a better way to store these values?
In your way, when you will try to get the path of the audio file next time you can not know the key 'mediaFile.name'.
In my application, I created an object that contains the file's informations, then I stored it in localStorage using constant key like 'audio'. The following example illustrates how to do that:
var fileName = "audioFileName";
var filePath = "audioFilePath";
localStorage.myaudio = { name: fileName, path: filePath };
// to get file's informations in the next time
var audio = localStorage.myaudio;
var fileName = audio.name;
var filePath = audio.path;
If you want to store more than one file :
// get stored array in localStorage
audioFiles = localStorage.myaudio ? JSON.parse(localStorage.myaudio) : [];
// add new audio to array
var fileName = "audioFileName";
var filePath = "audioFilePath";
audioFiles.push({ name: fileName, path: filePath });
// store array in localStorage
localStorage.myaudio = JSON.stringify(audioFiles);

Android get image path from drawable as string

Is there any way that I can get the image path from drawable folder in android as String. I need this because I implement my own viewflow where I'm showing the images by using their path on sd card, but I want to show a default image when there is no image to show.
Any ideas how to do that?
These all are ways:
String imageUri = "drawable://" + R.drawable.image;
Other ways I tested
Uri path = Uri.parse("android.resource://com.segf4ult.test/" + R.drawable.icon);
Uri otherPath = Uri.parse("android.resource://com.segf4ult.test/drawable/icon");
String path = path.toString();
String path = otherPath .toString();
based on the some of above replies i improvised it a bit
create this method and call it by passing your resource
Reusable Method
public String getURLForResource (int resourceId) {
//use BuildConfig.APPLICATION_ID instead of R.class.getPackage().getName() if both are not same
return Uri.parse("android.resource://"+R.class.getPackage().getName()+"/" +resourceId).toString();
}
Sample call
getURLForResource(R.drawable.personIcon)
complete example of loading image
String imageUrl = getURLForResource(R.drawable.personIcon);
// Load image
Glide.with(patientProfileImageView.getContext())
.load(imageUrl)
.into(patientProfileImageView);
you can move the function getURLForResource to a Util file and make it static so it can be reused
If you are planning to get the image from its path, it's better to use Assets instead of trying to figure out the path of the drawable folder.
InputStream stream = getAssets().open("image.png");
Drawable d = Drawable.createFromStream(stream, null);
I think you cannot get it as String but you can get it as int by get resource id:
int resId = this.getResources().getIdentifier("imageNameHere", "drawable", this.getPackageName());
First check whether the file exists in SDCard. If the file doesnot exists in SDcard then you can set image using setImageResource() methodand passing default image from drawable folder
Sample Code
File imageFile = new File(absolutepathOfImage);//absolutepathOfImage is absolute path of image including its name
if(!imageFile.exists()){//file doesnot exist in SDCard
imageview.setImageResource(R.drawable.defaultImage);//set default image from drawable folder
}

Categories

Resources