I have some images stored in getExternalFilesDir() and i am trying to show those images in the android gallery (cooliris).
Right now i have been doing this:
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(imgPath,"image/*");
startActivity(intent);
But nothing happens.
I have changed the setDataAndType to this:
intent.setDataAndType(Uri.fromFile(new File(imgPath)),"image/*");
This way it works, but it takes 5-10 seconds for the gallery to go from a black screen to showing my image.
Anyway to solve this or any better approach?
By implementing a file content provider you will be able to avoid this 5-10 second delay
import java.io.File;
import java.io.FileNotFoundException;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
public class FileContentProvider extends ContentProvider {
private static final String AUTHORITY = "content://com.yourprojectinfo.fileprovider";
public static Uri constructUri(String url) {
Uri uri = Uri.parse(url);
return uri.isAbsolute() ? uri : Uri.parse(AUTHORITY + url);
}
public static Uri constructUri(File file) {
Uri uri = Uri.parse(file.getAbsolutePath());
return uri.isAbsolute() ? uri : Uri.parse(AUTHORITY
+ file.getAbsolutePath());
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File file = new File(uri.getPath());
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file,
ParcelFileDescriptor.MODE_READ_ONLY);
return parcel;
}
#Override
public boolean onCreate() {
return true;
}
#Override
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException(
"Not supported by this provider");
}
#Override
public String getType(Uri uri) {
return "image/jpeg";
}
#Override
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException(
"Not supported by this provider");
}
#Override
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
throw new UnsupportedOperationException(
"Not supported by this provider");
}
#Override
public int update(Uri uri, ContentValues contentvalues, String s,
String[] as) {
throw new UnsupportedOperationException(
"Not supported by this provider");
}
}
Then you can call
Uri uri = FileContentProvider.constructUri(file);
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(uri,"image/*");
startActivity(intent);
This is a strange workaround but I think it has something to do with how android opens images using a URI. Their openFile(Uri uri, String mode) method is wrong / broken / can't resolve the URI properly.. I'm not really 100% sure though but I found this workaround to be effective.
don't forget to register to provider in the manifest
Related
If I use ContentProvider in my app am getting error like "unfortunately camera has stopped" after Result_ok.This is my code:
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
How to to solve this?I don't want to save image in sd card.
This my MyFileContentProvider class:
public class MyFileContentProvider extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse("content://com.example.user.studentadmission/");
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
static {
MIME_TYPES.put(".jpg", "image/jpeg");
MIME_TYPES.put(".jpeg", "image/jpeg");
}
#Override
public boolean onCreate() {
try {
File mFile = new File(getContext().getFilesDir(), "student.jpg");
if(!mFile.exists()) {
mFile.createNewFile();
}
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return (true);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
#Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), "student.jpg");
if (f.exists()) {
return (ParcelFileDescriptor.open(f,
ParcelFileDescriptor.MODE_READ_WRITE));
}
throw new FileNotFoundException(uri.getPath());
}
#Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
throw new RuntimeException("Operation not supported");
}
#Override
public Uri insert(Uri uri, ContentValues initialValues) {
throw new RuntimeException("Operation not supported");
}
#Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
#Override
public int delete(Uri uri, String where, String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
}
Few camera action works only on Kitkat, use condition for action:
Intent i = (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT)
? new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE)
: new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
If still crash occur, try avoiding ContentProvider just for testing. Let me know.
Give permission for open Camera.
Give run-time permission for open Camera.
I have an application which open pdf file with tap on app (default PDF in raw folder), openwith (myapp) and also from mail attachment. I converted the selected PDF file into inputstream and then to bytearray. Now I have a button and when user click on it need to send email through gmail app using intent. I need to add inputstream or bytearray as pdffile to the mail as attachment.I used the following code I can see the attachment with no bytes and received email donot have attachment. Struggling from more than 10 hours.. please help...
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("application/pdf");
sendIntent.setData(Uri.parse("sample#gmailcom"));
sendIntent.setClassName("com.google.android.gm", "com.google.android.gm.ComposeActivityGmail");
sendIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { "sample#gmail.com" });
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "testPDF");
sendIntent.putExtra(Intent.EXTRA_TEXT, "this is a PDF ");
File fileIn = new File(name, "myfile.pdf");
Toast.makeText(this, fileIn.getName(), Toast.LENGTH_LONG).show();
Uri fileuri = Uri.fromFile(fileIn);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(fileuri.toString()));
startActivity(sendIntent);
I tried many ways and finally found a solutions myself. Use file provider to attach the file to mail without storing it physically on the device. The code is follows and hope it helps someone else.
Add a class AttachFileProvider with below code
public class AttachFileProvider extends ContentProvider {
private static final String CLASS_NAME = "AttachFileProvider";
// The authority is the symbolic name for the provider class
public static final String AUTHORITY = "com.example.pDoc.pdocsigner.provider";
// UriMatcher used to match against incoming requests<br />
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Add a URI to the matcher which will match against the form
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// Check incoming Uri against the matcher
switch (uriMatcher.match(uri)) {
// If it returns 1 - then it matches the Uri defined in onCreate
case 1:
// The desired file name is specified by the last segment of the path
// Take this and build the path to the file
String fileLocation = getContext().getCacheDir() + File.separator + uri.getLastPathSegment();
// Create & return a ParcelFileDescriptor pointing to the file
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
// Otherwise unrecognised Uri
default:
//Log.v(LOG_TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
// //////////////////////////////////////////////////////////////
// Not supported / used / required for this example
// //////////////////////////////////////////////////////////////
#Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { return 0; }
#Override
public int delete(Uri uri, String s, String[] as) { return 0; }
#Override
public Uri insert(Uri uri, ContentValues contentvalues) { return null; }
#Override
public String getType(Uri uri) { return null; }
#Override
public Cursor query(Uri uri, String[] projection, String s, String[] as1, String s1) { return null; }
}
to attach the mail use the following code.
if (bytes.length > 0) {
try {
createCachedFile(this, attachmentFileName, bytes);
this.startActivity(getSendEmailIntent(this, "tomailid", "Test", "See attached", attachmentFileName));
} catch (IOException e) {
e.printStackTrace();
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "Gmail is not available on this device.", Toast.LENGTH_SHORT).show();
}
}
public void createCachedFile(Context context, String fileName, byte[] content) throws IOException {
File cacheFile = new File(context.getCacheDir() + File.separator + fileName);
cacheFile.createNewFile();
BufferedOutputStream bostream = new BufferedOutputStream(new FileOutputStream(cacheFile));
bostream.write(content);
bostream.flush();
bostream.close();
}
public static Intent getSendEmailIntent(Context context, String email, String subject, String body, String fileName) {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
//Explicitly only use Gmail to send
// emailIntent.setClassName("com.google.android.gm","com.google.android.gm.ComposeActivityGmail");
//emailIntent.setType("message/rfc822");
emailIntent.setType("vnd.android.cursor.item/email");
//Add the recipients
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { email });
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, body);
//Add the attachment to custom ContentProvider
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + AttachFileProvider.AUTHORITY + "/" + fileName));
return emailIntent;
}
You should make sure that you attachment file can be read by gmail app, so put it in the external sdcard storage area. Maybe you path has problem, change it and test.
Following MediaContentProvider (a simple image file provider) is working correctly with all types of apps, but not with my SMS (or better MMS) app.
I know, the SMS app is expecting a Cursor instead of a ParcelFileDescriptor, does this mean I have to save my image file to a database and retrieve it from there? Or is there a better solution for that?
public class MediaContentProvider extends ContentProvider
{
public static final String AUTHORITY = "MEDIA";
#Override
public int delete(Uri uri, String selection, String[] selectionArgs)
{
return 0;
}
#Override
public String getType(Uri uri)
{
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values)
{
return null;
}
#Override
public boolean onCreate()
{
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
return null;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
return 0;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
String fileName = uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(getContext().getFilesDir().getAbsolutePath() + "/" + fileName), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
}
}
EDIT
as suggested, here my FileProvider... Actually, it results in the same behaviour... As I said, I think I have to somehow provide a cursor for the SMS app...
public class ImageFileProvider extends FileProvider
{
public static final String AUTHORITY = "ImageFileProvider";
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
String fileName = uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(getContext().getFilesDir().getAbsolutePath() + "/" + fileName), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
}
}
It may be expecting you to support OpenableColumns in your query() method.
It may be needing you to grant it permission to access the data, as your ContentProvider should be appropriately secured.
It certainly is expecting you to return a real MIME type from getType(), rather than null.
I still recommend FileProvider, but if you want to roll something yourself, this sample app has the basics.
I am trying to create a button in my android app that allows the user to share an image using their choice of social media network. The image file is stored in the assets folder of the app.
My plan is to implement a custom ContentProvider to give external access to the image, then send a TYPE_SEND intent specifying the uri of the image within my content provider.
I have done this and it works for Google+ and GMail, but for other services it fails. The hardest part has been finding information on what I'm supposed to return from the query() method of my ContentProvider. Some apps specify a projection (e.g. Google+ asks for _id and _data), while some apps pass null as the projection. Even where the projection is specified, I've no idea what actual data (types) are expected in the columns. I can find no documentation on this.
I have also implemented the openAssetFile method of the ContentProvider and this gets called (twice by Google+!) but then inevitably the query method get called as well. Only the result of the query method seems to count.
Any ideas where I'm going wrong? What should I be returning from my query method?
Code below:
// my intent
Intent i = new Intent(android.content.Intent.ACTION_SEND);
i.setType("image/jpeg");
Uri uri = Uri.parse("content://com.me.provider/ic_launcher.jpg");
i.putExtra(Intent.EXTRA_STREAM, uri);
i.putExtra(android.content.Intent.EXTRA_TEXT, text);
startActivity(Intent.createChooser(i, "Share via"));
// my custom content provider
public class ImageProvider extends ContentProvider
{
private AssetManager _assetManager;
public static final Uri CONTENT_URI = Uri.parse("content://com.me.provider");
// not called
#Override
public int delete(Uri arg0, String arg1, String[] arg2)
{
return 0;
}
// not called
#Override
public String getType(Uri uri)
{
return "image/jpeg";
}
// not called
#Override
public Uri insert(Uri uri, ContentValues values)
{
return null;
}
#Override
public boolean onCreate()
{
_assetManager = getContext().getAssets();
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
{
MatrixCursor c = new MatrixCursor(new String[] { "_id", "_data" });
try
{
// just a guess!! works for g+ :/
c.addRow(new Object[] { "ic_launcher.jpg", _assetManager.openFd("ic_launcher.jpg") });
} catch (IOException e)
{
e.printStackTrace();
return null;
}
return c;
}
// not called
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
{
return 0;
}
// not called
#Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter)
{
return new String[] { "image/jpeg" };
}
// called by most apps
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException
{
try
{
AssetFileDescriptor afd = _assetManager.openFd("ic_launcher.jpg");
return afd;
} catch (IOException e)
{
throw new FileNotFoundException("No asset found: " + uri);
}
}
// not called
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException
{
return super.openFile(uri, mode);
}
}
Thanks, your question solved mine ;)
I was having the exactly inverse problem of yours: every service would work except g+.
I was returning null in the query method, that made g+ crash.
The only thing to actually expose my images was to implement openFile().
I have my images stored on the filesystem, not in the assets, but I suppose you
could get a ParcelFileDescriptor from your AssetFileDescriptor and return it.
My openFile() method looks like this:
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
String path = uri.getLastPathSegment();
if (path == null) {
throw new IllegalArgumentException("Not a Path");
}
File f = new File(getContext().getFilesDir() + File.separator + "solved" + path + ".jpg");
int iMode;
if ("r".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_ONLY;
} else if ("rw".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_WRITE;
} else if ("rwt".equals(mode)) {
iMode = ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_TRUNCATE;
} else {
throw new IllegalArgumentException("Invalid mode");
}
return ParcelFileDescriptor.open(f, iMode);
}
This works for every service I have installed with the ACTION_SEND intents except g+.
Using your query method makes it work for google plus, too.
I am using the following code to set an image from the assets folder.
Uri numBgUri = Uri.parse("file:///android_asset/background_numbers.png");
numBgImage.setImageURI(numBgUri);
The background_numbers.png file definitely exists in the assets root directory. I am getting a FileNotFoundException in the log: -
09-23 17:05:23.803: WARN/ImageView(23713): Unable to open content: file:///android_asset/background_numbers.png
Any ideas what I could be doing wrong?
Thanks!
I managed to get a viable workaround for this using my own custom ContentProvider (It's easier than you think!). Thanks to skink on Google Groups for the tip
This code should work, you just need to set a provider URL and register it as a provider in the manifest file
package sirocco.widgets;
import java.io.FileNotFoundException;
import java.io.IOException;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.net.Uri;
public class AssetContentProvider extends ContentProvider {
private AssetManager mAssetManager;
public static final Uri CONTENT_URI =
Uri.parse("content://your.provider.name");
#Override
public int delete(Uri arg0, String arg1, String[] arg2) {
return 0;
}
#Override
public String getType(Uri uri) {
return null;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
#Override
public boolean onCreate() {
mAssetManager = getContext().getAssets();
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
#Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
String path = uri.getPath().substring(1);
try {
AssetFileDescriptor afd = mAssetManager.openFd(path);
return afd;
} catch (IOException e) {
throw new FileNotFoundException("No asset found: " + uri);
}
}
}
I hope this helps any googlers out there with the same problem!
You need to place all images in you resources directory. For example :
/root/res/drawable/background_numbers.png
And you can access it via:
numBgImage.setImageResource(R.drawable.background_numbers);
I advice you to double check that:
ImageView activity that appears in your logcat belongs to the same apk that your code. asset files are only readable from within your application.
The assets folder is in the project directory root.
EDIT
It seems that it is not possible to feed an ImageView directly from an asset ressource. See this question for illustration.
As a workaround you should try to create a drawable from your asset file, then associate it with your Image view using ImageView.setImageDrawable:
Drawable d = Drawable.createFromStream(getAssets().open("background_numbers.png"), null);
if (null != d) numBgImage.setImageDrawable(d);
The first line of this code is an adaptation of this answer from #IgorKhomenko .