I am firing an Intent for the camera to take a photo as follows:
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri myPhoto = Uri.fromFile(new File(Environment.getExternalStorageDirectory(),
"the_photo.jpg"));
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, myPhoto);
startActivityForResult(cameraIntent, INTENT_GET_PICTURE_FROM_CAMERA);
Now I want to get the thumbnail for this photo to show. If I store the "myPhoto" Uri, I can see exactly the file on the file system. However, to use the Media Store API, I need the Uri on the Content Uri form. Is there anyway to retrieve this from the intent the camera gives back and if not how can I convert my file system Uri to the android content Uri.
Note, I know I can just take the bitmap and decode it, downsampling as I go. This is not what I want. I can query all the thumbnails and I see it exists, so I want to get it from the system.
Thanks
I resolved it by performing a Media Scan on the item. From there I could get the content Uri (the extra complication of the handler in the code below is because I needed my result to be handled on the UI thread as it manipulated views):
private Uri mCameraFilePath;
private Context mContext;
...
private void handlePictureFromCamera(Intent data) {
MyMediaScannerClient client = new MyMediaScannerClient(
mCameraFilePath.getPath(), new Handler(), new OnMediaScanned() {
#Override
public void mediaScanned(Uri contentUri) {
//Here I have the content uri as its now in the media store
}
});
MediaScannerConnection connection = new MediaScannerConnection(mContext, client);
client.mConnection = connection;
connection.connect();
}
private interface OnMediaScanned {
public void mediaScanned(Uri contentUri);
}
/**
* This is used to scan a given file (image in this case) to ensure it is added
* to the media store thus making its content uri available. It returns the
* content form Uri by means of a callback on the supplied handler's thread.
*/
private class MyMediaScannerClient implements MediaScannerConnectionClient {
private final String mPath;
private MediaScannerConnection mConnection;
private final OnMediaScanned mFileScannedCallback;
private final Handler mHandler;
public MyMediaScannerClient(String path, Handler handler, OnMediaScanned fileScannedCallback) {
mPath = path;
mFileScannedCallback = fileScannedCallback;
mHandler = handler;
}
#Override
public void onMediaScannerConnected() {
mConnection.scanFile(mPath, null);
}
#Override
public void onScanCompleted(String path, final Uri uri) {
mConnection.disconnect();
mHandler.post(new Runnable() {
#Override
public void run() {
mFileScannedCallback.mediaScanned(uri);
}
});
}
}
AS i commented, i needed something similar (to get " content://media/external/images/xxx " uri format) and your solution worked pretty well. Thanks you!
But i found a shorter way to do it (maybe it doesn't work for you, but for someone else).
Using the same intent formulation as yours (first post), but with the "CAMERA_REQUEST"
inside onActivityResult
getContentResolver().notifyChange(mCapturedImageURI, null);
ContentResolver cr = getContentResolver();
Uri uriContent = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), photo.getAbsolutePath(), null, null));
and getting the 'uriContent' in 'content://media/external/images/xxx' format.
Related
I am trying to this for 3 days and whole I want to do is that send audio files which are on firebase storage. But before send it I have to download it. There is no problem about downloading but unfortunatelly I cant share it by using intent. Here is the code to downloading->
private void getFileUrl() {
final StorageReference refGulus = storage.getReference().child("gulus.mp3");
refGulus.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
downloadFiles(context, "Mobile", ".mp3",Environment.DIRECTORY_DOWNLOADS, uri);
}
});
}
public void downloadFiles(Context context, String fileName, String fileExtention, String destinationDirectory, Uri uri)
{
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(uri);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalFilesDir(context, destinationDirectory, fileName+fileExtention);
downloadManager.enqueue(request);
//Maybe the problem is in these 2 lines in below I dont know.
final File file1 = new File(Environment.getExternalStorageDirectory().getAbsolutePath(),""+fileName+fileExtention);
myUri = Uri.fromFile(file1); // (myUri is declarated as static in upper so no problem to use it to other classes)
}
And here is the DownloadBroadcastReceiver to understand the download is finished and ready to sharing ->
import static dem.ersin.idrisustaa.Adapters.FirebaseToRecyclerAdapter.myUri;
public class DownloadBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/*");
share.putExtra(Intent.EXTRA_STREAM, myUri);
context.startActivity(Intent.createChooser(share, "Share Sound File"));
}
}
And here is the error for Whatsapp and gmail:
File format is not supported
This error from vlc media player when I try to open it
file:///Android/data/dem.ersin.idrisustaa/files/Download/Mobile.mp3 location cannot be played
When I manually go to phone's storage and try to play the Mobile.mp3 it plays well. There is a problem about path in my opinion.
Where is the problem? What do you think?
I'm able to open Gallery in a specific folder and image; however, when I try to slide through the images that functionality does not seem to work. Since I'm opening the Gallery app I figured that Gallery should handle this functionality. Anyone have any ideas? Can't seem to figure out what to do.
Below is my code
public OpenGalleyToSpecificFolder( Context _context, String path, String fileName){
mContext = _context;
mMediaScannerConnection = null;
mPath = path;
mFileName = fileName;
}
public void OpenGallary( ){
File folder_name = new File(mPath);
allFiles = folder_name.listFiles();
for(int i = allFiles.length - 1; i > 0; --i){
if(allFiles[i].getName().equals(mFileName)){
index = i;
break;
}
}
if(mMediaScannerConnection == null)
mMediaScannerConnection = new MediaScannerConnection(mContext, this);
mMediaScannerConnection.connect();
}
#Override
public void onMediaScannerConnected() {
mMediaScannerConnection.scanFile(allFiles[index].getAbsolutePath(), null);
}
#Override
public void onScanCompleted(String path, Uri uri) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
mContext.startActivity(intent);
mMediaScannerConnection.disconnect();
}
There are thousands of Android device models. There will be hundreds of different "gallery apps" pre-installed across those models, let alone other apps that support ACTION_VIEW that users install themselves. What those apps do, in response to your Intent, is up to the developers of those apps, not you.
If you want a specific look-and-feel to browsing images, implement your own image-browsing UI in your app.
I have the following code that correctly attaches the image to the email and sends:
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
sharingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// Set tht type to image/* and add the extra text field for the message to send
sharingIntent.setType(Application2.instance().getResString(R.string.share_intent_type_text_image));
sharingIntent.putExtra(Intent.EXTRA_TEXT, String.format(Application2.instance().getResString(R.string.share_intent_body_question), question.question));
if (destFile != null)
{
Uri uri = Uri.fromFile(destFile);
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
((ActivityMain) getActivity()).startActivity(Intent.createChooser(sharingIntent, "Share via"));
}
R.string.share_intent_type_text_image is defined as "image/png"
destFile is an image grabbed from the external cache directory of the app, (((ActivityMain) getActivity()).getExternalCacheDir()
However, when I attempt to open the file in Gmail, a dialog appears that says: Info - No app can open this attachment for viewing. I've downloaded the file via my PC and the extension comes up as .File. I can open it with paint and other image viewers.
Anyone experience this before?
Considering the FileProvider problems, and also because I wanted to implement a max cache size for collected temp files, I went with a ContentProvider solution and it works a treat. Basically, you're allowed to use your internal cache without any problem but still provide third party apps with a URI they can use to reference your temporary files you want to share with them. Because you use your internal cache, there will be no unnecessary WRITE_EXTERNAL_STORAGE permission to ask for.
The added max cache size limit (that you can remove from the class by simply deleting everything from checkSize() to the end of the class, for instance, if you can make sure you delete all files directly after sharing, so they won't remain on the device) works by checking the cumulated max size upon each call and clearing up half the cache (deleting the oldest files) if necessary.
public class TemporaryFile extends ContentProvider {
private static final long MAX_SIZE = 512 * 1024;
// commented out on purpose so that you don't forget to rewrite it...
// public static final String AUTHORITY = "com.example.tempfile";
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
if (uriMatcher.match(uri) == 1) {
final String file = getContext().getCacheDir() + File.separator + uri.getLastPathSegment();
return ParcelFileDescriptor.open(new File(file), ParcelFileDescriptor.MODE_READ_ONLY);
}
else
throw new FileNotFoundException(uri.toString());
}
#Override
public int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
#Override
public int delete (Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
public static File getFile(Context context, String prefix, String extension) throws IOException {
checkSize(context);
File file = File.createTempFile(prefix, extension, context.getCacheDir());
file.setReadable(true);
file.deleteOnExit();
return file;
}
public static Uri getPublicUri(File file) {
return Uri.withAppendedPath(Uri.parse("content://" + AUTHORITY), file.getName());
}
public static void checkSize(Context context) throws IOException {
File dir = context.getCacheDir();
if (getDirSize(dir) > MAX_SIZE)
cleanDir(dir, MAX_SIZE / 2);
}
private static long getDirSize(File dir) {
long size = 0;
for (File file : dir.listFiles())
if (file.isFile())
size += file.length();
return size;
}
private static void cleanDir(File dir, long atLeast) {
long deleted = 0;
File[] files = dir.listFiles();
Arrays.sort(files, new Comparator<File>() {
public int compare(File f1, File f2) {
return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
}
});
for (File file : files) {
deleted += file.length();
file.delete();
if (deleted >= atLeast)
break;
}
}
}
Using it couldn't be simpler, just call
File file = TemporaryFile.getFile(this, "prefix", ".extension");
whenever you want to create a new file and
TemporaryFile.getPublicUri(file)
whenever you want to get a public Uri to the file, eg. to pass it to an intent as data or Intent.EXTRA_STREAM.
Being a provider, don't forget to add the necessary manifest entry, either:
<provider
android:name=".TemporaryFile"
android:authorities="com.example.tempfile"
android:exported="true"
tools:ignore="ExportedContentProvider" >
</provider>
This works but requires external storage and the relating permissions. When downloading an app, a dialog will show that the app is requesting to be able to read/write data which may turn users away. Use the FileProvider as Simon suggested in my initial post if that's a concern.
Useful links:
https://developer.android.com/reference/android/support/v4/content/FileProvider.html
I attempted to use the File Provider as Simon suggested in my initial post to no avail. I received a NullPointerException on the following line:
final ProviderInfo info = context.getPackageManager()
.resolveContentProvider(authority, PackageManager.GET_META_DATA);
I was unable to track the problem after following the guide at:
https://developer.android.com/reference/android/support/v4/content/FileProvider.html
as well as the other thread at:
How to use support FileProvider for sharing content to other apps?
At this point I realized there is no file type set for the images being used. I simply added .png to the files and the attachments work correctly in Gmail as well as the previous apps that already worked.
I provided the following code if anyone was curious how I shared an internal file. It's not complete and does not handle errors completely but it may be useful for someone as a start.
// Copy image file to external memory and send with the intent
File srcFile = getImage();
File destDir = new File(((ActivityMain) getActivity()).getExternalCacheDir(),
Application2.instance().getResString(R.string.temporary_external_image_path));
if(!destDir.exists())
{
destDir.mkdirs();
}
if(destDir != null && srcFile != null)
{
File destFile = new File(destDir, srcFile.getName());
if (!destFile.exists())
{
try
{
Application2.instance().copy(srcFile, destFile);
}
catch (IOException e)
{
if (BuildConfig.DEBUG) Log.e("Failed to copy file '" + srcFile.getName() + "'");
}
}
if (destFile != null)
{
Uri uri = Uri.fromFile(destFile);
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
((ActivityMain) getActivity()).startActivity(Intent.createChooser(sharingIntent, "Share via"));
}
}
Is there a good way to add new image resources(from SD card) to a gallery widget at runtime?
"new image resources"?
Image resources are a part of /res/drawable folder inside your .apk application package. You can not add "new" image resources during runtime.
Is there some other use case you had in mind?
Edited after posters explanation:
You have to add media files to Media Store in order to be seen by gallery widget. Use MediaScanner. I use this convenient wrapper in my code:
public class MediaScannerWrapper implements
MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mConnection;
private String mPath;
private String mMimeType;
// filePath - where to scan;
// mime type of media to scan i.e. "image/jpeg".
// use "*/*" for any media
public MediaScannerWrapper(Context ctx, String filePath, String mime){
mPath = filePath;
mMimeType = mime;
mConnection = new MediaScannerConnection(ctx, this);
}
// do the scanning
public void scan() {
mConnection.connect();
}
// start the scan when scanner is ready
public void onMediaScannerConnected() {
mConnection.scanFile(mPath, mMimeType);
Log.w("MediaScannerWrapper", "media file scanned: " + mPath);
}
public void onScanCompleted(String path, Uri uri) {
// when scan is completes, update media file tags
}
}
Then instantiate MediaScannerWrapper and start it with scan(). You could tweak it to handle more than one file at the time. Hint: pass List of File paths, and then loop around mConnection.scanFile.
Send broadcast to MediaStore Content Provider when you add a file
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(imageAdded)));
Working for devices before KitKat
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED,
Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
Also have a look at this
Working in Lolipop and should also solve kitkat issues.
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA,"file path");
values.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg");
mContext.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values);
Add Permission.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
For my application, I'd been using my own Camera class for taking images and my own database but soon enough I couldn't really keep up with changes and I decided to use the built in camera application in Android to do the job, but I can't seem to get it to save file. What am I missing here? The application seems to save the file but it's just 0 bytes. I looked up the source code of the Camera application and it's looking for the "output" in Extras to save the file. Any help would be greatly appreciated.
Public class CameraTest extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button cameraButton = (Button) findViewById(R.id.cameraButton);
cameraButton.setOnClickListener( new OnClickListener(){
public void onClick(View v ){
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, "title");
values.put(Images.Media.BUCKET_ID, "test");
values.put(Images.Media.DESCRIPTION, "test Image taken");
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra("output", uri.getPath());
startActivityForResult(intent,0);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode== 0 && resultCode == Activity.RESULT_OK){
((ImageView)findViewById(R.id.pictureView)).setImageURI(data.getData());
}
}
}
This worked with the following code, granted I was being a little dumb with the last one. I still think there's got to be a better way so that the original image is still saved somewhere. It still sends me the smaller 25% size image.
public class CameraTest extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button cameraButton = (Button) findViewById(R.id.cameraButton);
cameraButton.setOnClickListener( new OnClickListener(){
public void onClick(View v ){
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
startActivityForResult(intent,0);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode== 0 && resultCode == Activity.RESULT_OK) {
Bitmap x = (Bitmap) data.getExtras().get("data");
((ImageView)findViewById(R.id.pictureView)).setImageBitmap(x);
ContentValues values = new ContentValues();
values.put(Images.Media.TITLE, "title");
values.put(Images.Media.BUCKET_ID, "test");
values.put(Images.Media.DESCRIPTION, "test Image taken");
values.put(Images.Media.MIME_TYPE, "image/jpeg");
Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
OutputStream outstream;
try {
outstream = getContentResolver().openOutputStream(uri);
x.compress(Bitmap.CompressFormat.JPEG, 70, outstream);
outstream.close();
} catch (FileNotFoundException e) {
//
} catch (IOException e) {
//
}
}
}
Also, I do know that the cupcake release of Android should fix the small image size soon.
The below code will start the default camera and have the camera save the image to the specified uri. The key is to put the extra "MediaStore.EXTRA_OUTPUT" along with the desired uri.
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/Images/" + image_name + ".jpg");
Uri imageUri = Uri.fromFile(file);
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, 0);
In your first attempt, it could be that the Camera app crashes when it reads your "output" extra, since it expects it to be a Uri, while you provide a String. The Camera app seems to read the extra flag after capturing the photo. You should just provide uri, and not uri.getPath(). Then, since you already know the URI to the photo, it will not be returned in the onResult call. You need to remember the URI in a member variable.
In the second attempt you will get a scaled down (50%) bitmap back. It is primarily intended for views. I think the full sized bitmap is too large for the memory budget of the application. This may be the reason for the downscale.
FYI , found this on docs :
The caller may pass an extra EXTRA_OUTPUT to control where this image will be written. If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap object in the extra field. This is useful for applications that only need a small image. If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri value of EXTRA_OUTPUT.
located here :http://developer.android.com/reference/android/provider/MediaStore.html
so the app can save full size image for you , if you tell it where.
**Edit : This is not the case with HTC devices. HTC (not nexus) that uses htc sense ui have branched from android 1.5 and carry a bug that always save the image in low res. you can lunch activity for camera and use the share function from camera to use the full sized image.
I got the same Problem with the Emulator,
I tried it on a real Phone an it worked,
maybe it is because the Virtual Phone can't really take Picures.