I'm new to android developement and I'm supposed to use Java as the programming language. I have an app where I'm supposed to be able to capture images and the geographical location of the captured images and display these details. I am displaying the image in an imageView. I have a text file where I'm storing image links as well as the captured images. So, I basically have image links and captured images that are stored in an arraylist then to a text file.
Please feel free to ask for anything that I may have missed out in the question.
I tried using EXIFInterface method I found on a Stack Overflow response, I tried using Location provider but to no avail. Maybe where I'm placing the code is incorrect, as I said, I'm new to this. I tried watching YT videos and did some research online and I'm more confused than ever at this point. Another approach I tried using was capturing the current location of the device to an invisible textView then calling it to where the image name is being stored but this did not work either.
The EXIF method I tried:
`
try {
ExifInterface exifInterface = new ExifInterface(direct); //Direct is the filepath
Log.d("Latitude", exifInterface.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
Log.d("Longitude", exifInterface.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
} catch (IOException e) {
e.printStackTrace();
}
`
Location Provider method
#SuppressLint("MissingPermission")
private void showLocation() {
locationProvider.getLastLocation().addOnSuccessListener(this,
new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
if (location != null) {
hiddenLoc.setText("Current location is: Lat:" + location.getLatitude()
+ "Lon: " + location.getLongitude());
}
}
});
}
`
EXIF location is an optional interface- most images won't have one. In fact many (most?) camera apps have stopped using it by default to protect user privacy. You can try it, but don't expect it to be there.
Your location code- lastLocation will return null unless location was already up and running (generally because another app was using it). You'd need to request location updates, rather than rely on lastLocation. Please note that this gets the location of the phone now not the location when a photo was taken. So this only works if you run it when you take the photo (the exif data gets the location where the photo was taken, if its there at all).
Related
So I am doing an app that lets you take images (inside CameraActivity, using CameraX) and saves them to local storage. The images are later displayed in a RecyclerView, getting them by their URI.
My issue is that as soon as I open the fragment that contains the recyclerview with the images it gets very very slow. The images are small and don't necessarily need to be in good quality, so I thought of maybe compressing them or just saving them in lower quality right when they are taken to make the loading process faster. Is there any way I can do that right here at this point in the code?
This is some relevant code from CameraActivity:
imgView_cameraTrigger.setOnClickListener(v -> {
SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
File file = new File(getExternalFilesDir(Environment.DIRECTORY_DCIM), mDateFormat.format(new Date())+ ".jpg");
ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build(); // saves image file
imageCapture.takePicture(outputFileOptions, executor, new ImageCapture.OnImageSavedCallback () {
#Override
public void onImageSaved(#NonNull ImageCapture.OutputFileResults outputFileResults) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(CameraActivity.this, "image saved successfully", Toast.LENGTH_SHORT).show();
// Toast.makeText(CameraActivity2.this, "PATH: " + file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
Uri uri = Uri.fromFile(file);
ImageView preview = TrackFragment.getImageViewInTrackFragment();
preview.setImageURI(uri); // display taken image in imageview in track fragment
TrackFragment.setImgPreviewUri(uri); // pass uri to TrackFragment to use it there
CameraActivity.this.finish(); // exit camera activity
}
});
}
#Override
public void onError(#NonNull ImageCaptureException error) {
error.printStackTrace();
}
});
});
I thought of maybe compressing them or just saving them in lower quality right when they are taken to make the loading process faster
You probably can give resolution suggestions to CameraX to have it take a lower-resolution image, if that is what you mean. That would tie to code outside of your question.
However, a large part of your problem also lies here:
preview.setImageURI(uri); // display taken image in imageview in track fragment
The documentation for setImageURI() states:
This does Bitmap reading and decoding on the UI thread, which can cause a latency hiccup
In other words, using setImageURI() leads to poor performance and is rarely recommended.
Please use Glide, Picasso, or another image-loading library. Not only can they do the image loading on a background thread, but they can also automatically down-sample the image to fit your ImageView, improving loading speeds. They can also show a placeholder image while the image is being loaded, cache images in memory to reduce redundant image loads, etc.
FWIW, I demonstrate the use of Glide for image loading in this section of this free book, though changes in the US National Weather Service API may mean that the sample code itself does not work correctly now as it did when I published that final edition.
please help I could not add code, it is throwing error , I'm new.
ActivityResultLauncher<Intent> picActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
// your operation....
Uri pic = data.getData();
profile.setImageURI(pic);
}
}
}
It depends on the device's android version. If it's 10 or lower than that, then you can simply save the file path in the Sharedpreference so you can access it later and load the image from there (You can do it in android 10 with requestLegacyExternalStorage of course or you can go with the 2nd option which I provided below).
But, if you're writing the code for android 11 or higher, then there are only three standard ways to do it.
1. Using SAF (Storage Access Framework) :-
You can get the storage access permission of that perticular folder everytime you pick an image from there. But this is not the best option when your app is doing it multiple times. (what if it's photo editor app or social media or something like that?!)
2. Manage all files permission :-
You can go with the All files access permission but it's also too much for the small task and you also have to give clarification to google play when your app has that permission. So it's also the very last option.
3. Accessing from internal app directories - THE BEST WAY! :-
You can go with this option with almost every app!
All you have to do is just take read storage permission, access the file using file descriptor, write it to the internal app directory (it can be either external files directory or cache directory), then you'll have a lifetime access of the image. You can save the path to Sharedpreference and access it anytime.
If you want to save edited image to the gallery then it will also be easy because you already have both read and write permission to that image saved in internal app directory.
That's it. I know the answer is lengthy but it's worth it. :)
This is a problem I found exclusively on Xiaomi Redmi Note 4 device (MIUI Global 8.1, Marshmallow)
So I'm making an app that generates a bitmap and saves it to FOOD folder. I have successfully generated the image and saved it to FOOD folder.
However, some of the images don't show on Gallery App. Specifically Xiaomi's Gallery App and Google's Photos. Note that some images DO show on Gallery App.
My question are:
How to show the images in Gallery after saving the image?
What exactly is causing this problem?
This really confuses me since it works on other devices with different OS.
This is what I have tried so far:
I've tried using the Intent.ACTION_MEDIA_SCANNER_SCAN_FILE and MediaScannerConnection.scanFile.
try {
String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1);
MediaStore.Images.Media.insertImage(H5Environment.getContext().getContentResolver(), imagePath,
fileName, null);
} catch (Exception e) {
DanaLog.e(TAG, e);
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(new File(imagePath));
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
MediaScannerConnection.scanFile(this,
new String[]{imagePath},
new String[]{"image/png"},
new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String path, Uri uri) {
Log.d(TAG, "scan complete " + path);
}
});
I have also restarted the phone and it still doesn't show.
In Settings, I have enable Show hidden album but it doesn't show as well.
Current analysis:
I don't think there's a problem with the image itself because I can see it via File Manager.
The image path should also correct since some image is shown in the Gallery App.
Note:
I see a forum discussion on this and they suggest to 3rd party app such as Rescan SD Card!. But obviously, it was not the solution I was looking for.
try this for saveImage and notify Gallery
https://github.com/wuapnjie/StickerView/blob/master/sticker/src/main/java/com/xiaopo/flying/sticker/StickerUtils.java
Loading images with Picasso is seemingly so easy, until I hit this roadblock. Not sure why! I can load photos from contacts via PHOTO_URI if the contacts only have a thumbnail, or, if I instead ask for PHOTO_THUMBNAIL_URI specifically.
#Override
public void bindView(View view, Context context, Cursor cursor) {
ImageView icon = (ImageView)view.findViewById(R.id.ContactImage);
String photoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI));
if (photoUri == null) {
icon.setImageDrawable(null);
} else {
Picasso.with(context).load(photoUri).into(icon);
}
}
For what it's worth: if I use Picasso.with(context).load(photoUri).placeholder(R.drawable.placeholder).error(R.drawable.error).into(icon); then I see the placeholder image in the place of every contact who has a high res image. I never see an "error" picture. If I revert back to just using icon.setImageURI(Uri.parse(photoUri)); then I see the high res contact images again just fine. (But then I don't have a snazzy async caching picture loader!)
UPDATE: Thanks to #copolii and his answers below, the following now works flawlessly with Picasso 2.1.1:
#Override
public void bindView(View view, Context context, Cursor cursor) {
Long id = cursor.getLong(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
Uri contactUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
String photoUri = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI));
ImageView icon = (ImageView)view.findViewById(R.id.ContactImage);
if (photoUri == null) {
icon.setImageDrawable(null);
} else {
Picasso
.with(context)
.load(contactUri)
.into(icon);
}
}
This loads the higher-res photo, if there is one, and if not, shows the low-res photo, and if there is no photo set for a contact, it's set to a blank / null.
Have you tried using a contact uri?
That last boolean parameter in openContactPhotoInputStream promises to get you the high res photo if one is available.
Instead of using a photo uri use a contact uri or a contact lookup uri.
UPDATE
Since the question has been answered, I though I'd post the relevant details here:
A small test app is posted here (You need Android Studio): https://github.com/copolii/PicassoContactsTest
If you set both a placeholder and an error icon, the error icon is displayed for contacts who do not have a picture. I'd recommend setting the social face guy as your place-holder and no error icon. That way, your place-holder stays on if the contact has no picture.
If you do want to differentiate between the two, choose your error icon with the above in mind (i.e. don't use a big red OMFG error indicator).
--- Previous Content ---
Let me know if that helps.
I did the work for the contacts photo loading and unless I'm missing something, you should get the high resolution picture (API 14+) automatically:
if (SDK_INT < ICE_CREAM_SANDWICH) {
return openContactPhotoInputStream(contentResolver, uri);
} else {
return openContactPhotoInputStream(contentResolver, uri, true);
}
It seems that the openContactPhotoInputStream doesn't like the PHOTO_URI.
Android Docs: openContactPhotoInputStream
If the URIs are distinguishable I can easily add support for PHOTO_URI as well (I have to find out how to load it first though). I'm already determining if the given uri is a contact photo uri or a contact lookup uri (older android versions do not like lookup uris being fed into openContactPhotoInputStream so I have to dereference the lookup uri into a contact uri before passing it to openContactPhotoInputStream).
I hope this helps.
In my Android app I use Picasso to load images. This normally works perfectly well.
Today I tried loading a static image from the google maps api, but this doesn't seem to work. When I open the example link as provided on their info page, I get to see the static map image perfectly well. When I load it in my Android app using the line below, I get nothing at all.
Picasso.with(getContext()).load("http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=370x250&maptype=roadmap%20&markers=color:blue|label:S|40.702147,-74.015794&markers=color:green|label:G|40.711614,-74.012318%20&markers=color:red|color:red|label:C|40.718217,-73.998284&sensor=false").into(mapView);
I also tried to download the image and uploading it to my personal webspace, from which it loads perfectly well, but somehow, it doesn't seem to load directly from the direct google API url.
Does anybody know why this is so, and how I can solve it?
The only programmatic point-of-failure that comes to mind is in parsing the URI. Looking at the current Picasso code (https://github.com/square/picasso/blob/master/picasso/src/main/java/com/squareup/picasso/Picasso.java) I see the following:
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
return load(Uri.parse(path));
}
So I'd first debug
Uri.parse("http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=370x250&maptype=roadmap%20&markers=color:blue|label:S|40.702147,-74.015794&markers=color:green|label:G|40.711614,-74.012318%20&markers=color:red|color:red|label:C|40.718217,-73.998284&sensor=false")
and see what that Object looks like. Does it drop or confuse any of your parameters?
If that doesn't lead you anwhere, try downloading the file manually using a HttpClient [or similar]. Then at least you can fully debug the request/response.
Also, I know Google maps has some limits -- are you sure you haven't reached them?
replace http with https
replace | with %7C
add api key
The .loadMap() function has many declared variables. This is the heart of the whole process.
So what is required for the static maps API to give us an image is that we make an http request with a given url, for which an image response (URL) is received. Let us run through the meaning and utility of these variables. Yes, all of them have a completely different meaning!
The mapUrlInitial variable is always the same while making an API call. It has a query of center ( ?center ) which specifies that we want the location to be centered in the map.
The mapUrlProperties variable contains a string where you control the actual zooming of the image response you will get, the size ofthe image and the color of the marker which will point out our place.
The mapUrlMapType variable is a string where you can actually determine the marker size you want and the type of the map. We are using a roadtype map in the app.
Finally latLong is a string which concatenates the latitude and the longitude of the place we want to pinpoint!
We then concatenate all of these strings to form a feasible Url. The Url is then loaded as we have seen above, in the Picasso code. One thing we can notice is that an event object is always required for all of this to happen, because we are able to fetch the position details using the event object! Final Code:-
fun loadMap(event: Event): String{
//location handling
val mapUrlInitial = “https://maps.googleapis.com/maps/api/staticmap?center=”
val mapUrlProperties = “&zoom=12&size=1200×390&markers=color:red%7C”
val mapUrlMapType = “&markers=size:mid&maptype=roadmap”
val latLong: String = “” +event.latitude + “,” + event.longitude
return mapUrlInitial + latLong + mapUrlProperties + latLong + mapUrlMapType
}
//load image
Picasso.get()
.load(loadMap(event))
.placeholder(R.drawable.ic_map_black_24dp)
.into(rootView.image_map)