I've been trying and trying to get this to work. I've gone through many examples with no luck. The problem is that the image is not attached when I try to share it, for example, using an email client. I managed to get this to work when using external storage but internal storage suits my needs better.
I click a button and then the image is saved to internal storage and right after that it is shared but there's no image.
This is from the Activity class:
int width = size.x;
int height = size.y;
Bitmap shareBmp = Bitmap.createBitmap(screenBmp, 0, 0, width, height);
ContextWrapper wrapper = new ContextWrapper(getApplicationContext());
File directory = wrapper.getDir("images", Context.MODE_PRIVATE);
File filePath = new File(directory, "share.png");
FileOutputStream fos;
try
{
fos = new FileOutputStream(filePath);
shareBmp.compress(Bitmap.CompressFormat.PNG, 90, fos);
fos.close();
}
catch (Exception aE){}
Uri uri = Uri.parse("content://com.example.Test/share.png");
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setType("image/png");
startActivity(Intent.createChooser(intent, "Share Image"));
Here's the ImageProvider class:
public class ImageProvider extends ContentProvider
{
#Override
public ParcelFileDescriptor openFile(Uri aUri, String aMode) throwsFileNotFoundException
{
File file = new File(getContext().getFilesDir(), aUri.getPath());
if (file.exists())
{
return (ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY));
}
throw new FileNotFoundException(aUri.getPath());
}
#Override
public boolean onCreate()
{
return false;
}
#Override
public int delete(Uri aUri, String aSelection, String[] aSelectionArgs)
{
return 0;
}
#Override
public String getType(Uri aUri)
{
return null;
}
#Override
public Uri insert(Uri aUri, ContentValues aValues)
{
return null;
}
#Override
public Cursor query(Uri aUri, String[] aProjection, String aSelection, String[] aSelectionArgs, String aSortOrder)
{
return null;
}
#Override
public int update(Uri aUri, ContentValues aValues, String aSelection, String[] aSelectionArgs)
{
return 0;
}
}
This is from the manifest:
<provider
android:name=".ImageProvider"
android:authorities="com.example.Test"
android:exported="true"/>
Only your app can see/read/write files in it's app specific -private- storage. So you cannot ask other app to share from it as they cannot even 'see' those files there.
Related
I want to allow users of my Android app to export the SQLite database file for content they create. My current solution copies the file to private storage (/data/data/com.package.name/files/Content.db), then creates a URI for this file and opens the Share dialog. This is working, allowing me to export the database file using Dropbox, for example. Here is the code I'm using, partially adapted from https://stackoverflow.com/a/2661882 -
private void exportContent() {
copyContentToPrivateStorage();
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("application/octet-stream");
Uri uri = new FileProvider().getDatabaseURI(this);
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Backup via:"));
}
private void copyContentToPrivateStorage() {
// From https://stackoverflow.com/a/2661882
try {
File data = Environment.getDataDirectory();
File sd = getFilesDir();
if (sd.canWrite()) {
String currentDBPath = "//data//com.package.name//databases//Content.db";
String backupDBPath = "Content.db";
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, backupDBPath);
if (currentDB.exists()) {
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
}
} catch (Exception e) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show();
}
}
public class FileProvider extends android.support.v4.content.FileProvider {
public Uri getDatabaseURI(Context c) {
File exportFile = new File(c.getFilesDir(), "Content.db");
Uri uri = getUriForFile(c, "com.package.name.fileprovider", exportFile);
c.grantUriPermission("*", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
return uri;
}
}
It seems like I should be able to directly create a URI from the existing database path, instead of doing an intermediate copy. Is there a way to do this?
I could keep doing the intermediate copy, but I believe it would be bad practice to leave the second copy of the database in the data directory longer than necessary. Is there a way to clean it up and delete it after the chosen app has finished using the URI to share the file?
I solved this on my own. I'm documenting it here, per Neil's request.
This is where I launch the export/backup from my activity:
public class MyActivity {
private void exportUserContent() {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("application/octet-stream");
Uri uri = new FileProvider().getDatabaseURI(this);
intent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Backup via:"));
}
}
The FileProvider:
public class FileProvider extends android.support.v4.content.FileProvider {
public Uri getDatabaseURI(Context c) {
// https://developer.android.com/reference/android/support/v4/content/FileProvider.html
// old approach that worked until 2020-ish
// File data = Environment.getDataDirectory();
// String dbName = "UserContent.db";
// String currentDBPath = "//data//com.url.myapp//databases//" + dbName;
// File exportFile = new File(data, currentDBPath);
File exportFile = c.getDatabasePath(dbName); // new approach
return getFileUri(c, exportFile);
}
public Uri getFileUri(Context c, File f){
return getUriForFile(c, "com.url.myapp.fileprovider", f);
}
}
Inside AndroidManifest.xml:
<manifest ...>
<application ...>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.url.myapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
Inside \app\src\main\res\xml\filepaths.xml
(I think the first entry is the relevant one, but I'll include the whole file for completeness):
<paths>
<files-path
path="../databases/"
name="mydatabases"/>
<files-path
path=""
name="migrations"/>
<external-path
path=""
name="external"/>
</paths>
Share the SQLite database using content providers. This tutorial can guide you more on SQLite database and content provider: Android SQLite DB and Content Provider
Here's how I solved this with a custom ContentProvider:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.provider.OpenableColumns;
import java.io.File;
import java.io.FileNotFoundException;
/**
* ContentProvider to share SQLite database
*
* Credit: https://android.googlesource.com/platform/frameworks/support/+/android-support-lib-19.1.0/v4/java/android/support/v4/content/FileProvider.java
*/
public class MyProvider extends ContentProvider {
private final File file = new File("/data/data/com.example.provider/databases", "mydatabase.db");
#Override
public boolean onCreate() {
return true;
}
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if (projection == null) {
projection = new String[] { OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE };
}
String[] cols = new String[projection.length];
Object[] values = new Object[projection.length];
int i = 0;
for (String col : projection) {
if (OpenableColumns.DISPLAY_NAME.equals(col)) {
cols[i] = OpenableColumns.DISPLAY_NAME;
values[i++] = file.getName();
} else if (OpenableColumns.SIZE.equals(col)) {
cols[i] = OpenableColumns.SIZE;
values[i++] = file.length();
}
}
cols = copyOf(cols, i);
values = copyOf(values, i);
final MatrixCursor cursor = new MatrixCursor(cols, 1);
cursor.addRow(values);
return cursor;
}
#Override
public String getType(Uri uri) {
return "application/octet-stream";
}
#Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("No external inserts");
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("No external updates");
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
private static String[] copyOf(String[] original, int newLength) {
final String[] result = new String[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
private static Object[] copyOf(Object[] original, int newLength) {
final Object[] result = new Object[newLength];
System.arraycopy(original, 0, result, 0, newLength);
return result;
}
}
Then in the manifest:
<provider
android:name="com.example.appname.MyProvider"
android:authorities="com.example.provider">
</provider>
I have this snippet, which is creating image from view. File can be seen in file manager and accessible through code, but default android gallery app is not showing them.
Bitmap returnedBitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(returnedBitmap);
view.draw(canvas);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
returnedBitmap.compress(Bitmap.CompressFormat.PNG, 100, bytes);
Date now = new Date();
String path = Environment.getExternalStorageDirectory() + File.separator + "Download" + File.separator +now.getTime()+".png";
File f = new File(path);
try {
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
fo.close();
} catch (Exception e) {
}
You need to add the file to the gallery. Try this code from the developer's website:
private void galleryAddPic(String path) {
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(path);//your file path
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}
Note make sure that you are not saving in the private memory of the app.
My experience is that to use MediaScanner is better, you can get the URI of the content stored in media database.
static final class PicScanner implements MediaScannerConnectionClient {
#SuppressWarnings("unused")
private static PicScanner mInstance;
private String mFilename;
private String mMimetype;
private MediaScannerConnection mConn;
public static void scan(Context ctx, File file, String mimetype) {
mInstance = new PicScanner (ctx, file, mimetype);
}
private PicScanner (Context ctx, File file, String mimetype) {
this.mFilename = file.getAbsolutePath();
mConn = new MediaScannerConnection(ctx, this);
mConn.connect();
}
#Override
public void onMediaScannerConnected() {
mConn.scanFile(mFilename, mMimetype);
}
#Override
public void onScanCompleted(String path, Uri uri) {
mConn.disconnect();
mInstance = null;
//notifyNewPicSavedLocally(path, uri);
}
}
Usage example:
PicScanner.scan(mContext, picFile, "image/jpeg"); //picFile should be access-able for other process
I'm developing an Android app that is a gallery of images in which the images are downloaded from internet for display on the screen of smathphone. Images are displayed one at a time and the application has a button to share the image that is displayed.
Following the directions I've found in some StackOverflow post which indicated that the right way to share an image was using a ContentProvider I have implemented the following code that works to share the images of certain applications (eg Twitter, Gmail ...) but does not work for others (Facebook, Yahoo, MMS ...).
Then I show the code used hoping you can help me get the correct implementation to share images in all applications.
Initially I capture the button press to share:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_share) {
// I get the image being displayed on the screen
View root = getView();
ImageView imageView = (ImageView) root.findViewById(R.id.image);
Drawable imageToShareDrawable = imageView.getDrawable();
if (imageToShareDrawable instanceof BitmapDrawable) {
// I convert the image to Bitmap
Bitmap imageToShare = ((BitmapDrawable) imageToShareDrawable).getBitmap();
// Name of de image extracted from a bean property
String fileName = quote.getImage();
// I keep the image in the folder "files" of internal storage application
TempInternalStorage.createCachedFile(fileName, imageToShare, getActivity().getApplicationContext());
// I start the Activity to select the application to share the image after the intent Built with the method "getDefaultShareIntent"
startActivity(getDefaultShareIntent(fileName));
} else {
Toast.makeText(getActivity().getApplicationContext(), "Please wait, the quote is being downloaded", Toast.LENGTH_SHORT).show();
}
}
return true;
}
The method for saving the image to the internal storage of the application is as follows:
public static void createCachedFile(String fileName, Bitmap image, Context context) {
try {
File file = new File(context.getFilesDir(), fileName);
if (!file.exists()) {
FileOutputStream fos = new FileOutputStream(file);
image.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
}
} catch (Exception e) {
Log.e("saveTempFile()", "**** Error");
}
}
The method that constructs the Intent to share it:
private Intent getDefaultShareIntent(String fileName) {
final Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Test text");
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + File.separator + "img" + File.separator + fileName));
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
return shareIntent;
}
Finally ContentProvider code is as follows:
public class CachedFileProvider extends ContentProvider {
private static final String CLASS_NAME = "CachedFileProvider";
public static final String AUTHORITY = "com.example.appname.cachefileprovider";
private UriMatcher uriMatcher;
#Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "img/*", 1);
return true;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
String LOG_TAG = CLASS_NAME + " - openFile";
Log.i(LOG_TAG, "Called with uri: '" + uri + "'." + uri.getLastPathSegment());
switch (uriMatcher.match(uri)) {
case 1:
String fileLocation = getContext().getFilesDir() + File.separator + uri.getLastPathSegment();
ParcelFileDescriptor image = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return image;
default:
Log.i(LOG_TAG, "Unsupported uri: '" + uri + "'.");
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
#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) {
MatrixCursor c = null;
Log.i(">>>> projection", java.util.Arrays.toString(projection));
String fileLocation = getContext().getFilesDir() + File.separator + uri.getLastPathSegment();
File file = new File(fileLocation);
long time = System.currentTimeMillis();
c = new MatrixCursor(new String[] { "_id", "_data", "orientation", "mime_type", "datetaken", "_display_name" });
c.addRow(new Object[] { 0, file, 0, "image/jpeg", time, uri.getLastPathSegment() });
return c;
}
#Override
public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
return null;
}
}
I have found that when the image is sharing some applications only call the method "query" (these are where the code does not work and I can not share the image) while there are others that also call the method "query" also call the method "openFile" and these do work and I can share the image.
I hope you can help me, thank you very much in advance.
As #Sun Ning-s comment noted some "share target apps" can handle URI-s starting with "content://.." which you have implemented.
Other apps handle file uri-s starting with "file://...." so you have to implement a 2nd share menue "share as file"
private Intent getFileShareIntent(String fullPathTofile) {
final Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + fullPathTofile));
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
return shareIntent;
}
You can use the android app intentintercept to find out what other "share source apps" provide.
I´m trying to export an image from my application to other applications like twitter, facebook or other apps accepting it images.
I'm doing the following:
private void exportImage()
{
storeBitmapOnDisk(this.bitmap);
Intent i = new Intent(Intent.ACTION_SEND);
i.addCategory(Intent.CATEGORY_LAUNCHER);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
File imgFile = context.getFileStreamPath("export.png");
Uri uri = Uri.fromFile(imgFile);
i.putExtra(Intent.EXTRA_STREAM, uri);
i.setType("image/png");
i.setComponent(componentName);
context.startActivity(i);
}
private void storeBitmapOnDisk(Bitmap bitmap)
{
try
{
FileOutputStream outStream = context.openFileOutput("export.png",
Context.MODE_WORLD_READABLE);
bitmap.compress(Bitmap.CompressFormat.PNG, 50, outStream);
outStream.close();
}
catch (IOException e)
{
Log.d(TAG, e.getMessage());
}
}
This is not working because I'm writing internal storage with is not accessible by other applications. As I don't want to use external storage (SD card) I think what I need is a ContentProvider but all examples I saw is about custom ContentPovider are using a SQlite database to store the data. I can't figure out how I could store a bitmap in internal storage and make it available through a ContentProvider to other applications without storing my bitmap in a database. MatrixCursor seems not to be adapted too...
SOLUTION I create a custom Content Provider and set the path of the file in the the content provider as the Uri EXTRA_STREAM extra parameter of the intent:
First my provider class where I only needed to override openFile which is not clear in the doc...
public class ExportContentProvider extends ContentProvider
{
public static Uri CONTENT_URI = Uri
.parse("content://com.path.to.my.provider");
#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 false;
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
{
int imode = 0;
if (mode.contains("w"))
imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
if (mode.contains("r"))
imode |= ParcelFileDescriptor.MODE_READ_ONLY;
if (mode.contains("+"))
imode |= ParcelFileDescriptor.MODE_APPEND;
try
{
return ParcelFileDescriptor.open(new File(uri.getEncodedPath()), imode);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return null;
}
#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;
}
}
And then my export function:
private void exportImage()
{
storeBitmapOnDisk();
Intent i = new Intent(Intent.ACTION_SEND);
i.addCategory(Intent.CATEGORY_LAUNCHER);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
i.putExtra(Intent.EXTRA_STREAM, Uri.withAppendedPath(
ExportContentProvider.CONTENT_URI, context.getFileStreamPath(
"export.png").getAbsolutePath()));
i.setType("image/png");
i.setComponent(componentName);
if (exportFileObserver == null)
{
this.bitmapPath = context.getFileStreamPath("export.png")
.getAbsolutePath();
exportFileObserver = new ExportFileObserver(this.bitmapPath);
exportFileObserver.startWatching();
}
listener.launchExportActivity(Dms.ACT_IMAGE_EXPORT, i);
}
Nevertheless there are 2 problems remaining:
- When trying to delete my temporary export.png file on my internal memory using result of the activity I received the onResult when the activity is launch...
- It s not working for mail app in my 2.3 emulator. I'm getting "File to large" message...
Somenone tested that ?
I'm using following code to open a gallery inside of my app
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, FIND_RESULT);
Is it possible to limit a list of images to only show images taken by camera? Viewing Gallery on my 2.1 system, images are grouped so there has to be a parameter that defines to which folder it belongs.
Checking the MediaStore.Images.ImageColumns I did not a find any column that would define such thing.
Could I be wrong? Because if I could create a query to filter by folder and create my own gallery view, then my problem would be solved.
You just need to implement MediaScannerConnectionClient in your activity and after that you have to give the exact path of one of the file inside that folder name here as SCAN_PATH and it will scan all the files containing in that folder and open it inside built in gallery. So just give the name of you folder and you will get all the files inside including video. If you want to open only images change FILE_TYPE="image/*"
public class SlideShow extends Activity implements MediaScannerConnectionClient {
public String[] allFiles;
private String SCAN_PATH ;
private static final String FILE_TYPE = "*/*";
private MediaScannerConnection conn;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
File folder = new File("/sdcard/yourfoldername/");
allFiles = folder.list();
SCAN_PATH=Environment.getExternalStorageDirectory().toString()+"/yourfoldername/"+allFiles[0];
Button scanBtn = (Button) findViewById(R.id.scanBtn);
scanBtn.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
startScan();
}
});
}
private void startScan()
{
if(conn!=null)
{
conn.disconnect();
}
conn = new MediaScannerConnection(this, this);
conn.connect();
}
public void onMediaScannerConnected()
{
conn.scanFile(SCAN_PATH, FILE_TYPE);
}
public void onScanCompleted(String path, Uri uri)
{
try
{
if (uri != null)
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
}
}
finally
{
conn.disconnect();
conn = null;
}
}
}
None of the above answers are correct, including the one marked as correct.
Here's the actual correct solution:
The secret is finding the bucket/album your folder is represented as. Buckets show up after a successful MediaScan so be sure any images/videos you want to show are first scanned as demonstrated multiple times above.
Let's assume I have an indexed folder in /sdcard/myapp/myappsmediafolder:
String bucketId = "";
final String[] projection = new String[] {"DISTINCT " + MediaStore.Images.Media.BUCKET_DISPLAY_NAME + ", " + MediaStore.Images.Media.BUCKET_ID};
final Cursor cur = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);
while (cur != null && cur.moveToNext()) {
final String bucketName = cur.getString((cur.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME)));
if (bucketName.equals("myappsmediafolder")) {
bucketId = cur.getString((cur.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_ID)));
break;
}
}
Now that we have the bucketId for our album we can open it with a simple intent.
Filters Video files:
Uri mediaUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
Filters Image files:
Uri mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
...
if (bucketId.length() > 0) {
mediaUri = mediaUri.buildUpon()
.authority("media")
.appendQueryParameter("bucketId", bucketId)
.build();
}
Intent intent = new Intent(Intent.ACTION_VIEW, mediaUri);
startActivity(intent);
I can verify this works with the built-in Gallery app. Mileage may vary with other apps such as Google Photos.
I have yet to figure out how not to filter images/video, even though within Gallery you can select a specific Album with no filter.
I figured this out by looking at the AOSP source to the gallery app.
I don't have enough reputation to upvote or comment on his answer but ShellDude's answer allows you to put a directory URI in the gallery intent. So when the gallery app is opened it displays all of the images instead of 1.
For me, scanning my files like the answers above did not work. Querying the MediaStore.Images.Media.EXTERNAL_CONTENT_URI only worked after inserting new rows into the MediaStore.Images.Media.DATA table with the ContentResolver:
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, image.getPath());
values.put(MediaStore.Images.Media.MIME_TYPE,"image/jpeg");
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
Here is a simplified one
private MediaScannerConnection conn;
private void notifySystemWithImage(final File imageFile) {
conn = new MediaScannerConnection(this, new MediaScannerConnectionClient() {
#Override
public void onScanCompleted(String path, Uri uri) {
try {
if (uri != null) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/*");
startActivity(intent);
}
} finally {
conn.disconnect();
conn = null;
}
}
#Override
public void onMediaScannerConnected() {
conn.scanFile(imageFile.getAbsolutePath(), "*/*");
}
});
conn.connect();
}
For those who this still give activity not found exception:
You need to specify directory of your inner application folder. Not user default root if images and everything.
public class SlideShow extends Activity implements MediaScannerConnectionClient {
public String[] allFiles;
private String SCAN_PATH ;
private static final String FILE_TYPE = "*/*";
private MediaScannerConnection conn;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
File folder = new File(HistoryActivity.this.getExternalFilesDir(null)+"/a/");
allFiles = folder.list();
SCAN_PATH= HistoryActivity.this.getExternalFilesDir(null)+"/a/"+allFiles[0];
Button scanBtn = (Button) findViewById(R.id.scanBtn);
scanBtn.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
startScan();
}
});
}
private void startScan()
{
if(conn!=null)
{
conn.disconnect();
}
conn = new MediaScannerConnection(this, this);
conn.connect();
}
public void onMediaScannerConnected()
{
conn.scanFile(SCAN_PATH, FILE_TYPE);
}
public void onScanCompleted(String path, Uri uri)
{
try
{
if (uri != null)
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
}
}
finally
{
conn.disconnect();
conn = null;
}
}
}
works... but kitkat show only one photo. I managed to fix it for earlier versions with (updating gallery, when storing image):
public void savePhoto(Bitmap bmp)
{
File imageFileFolder = new File(context.getExternalFilesDir(null)+"/a/") ;
imageFileFolder.mkdir();
FileOutputStream out = null;
Calendar c = Calendar.getInstance();
String date = fromInt(c.get(Calendar.MONTH))
+ fromInt(c.get(Calendar.DAY_OF_MONTH))
+ fromInt(c.get(Calendar.YEAR))
+ fromInt(c.get(Calendar.HOUR_OF_DAY))
+ fromInt(c.get(Calendar.MINUTE))
+ fromInt(c.get(Calendar.SECOND));
File imageFileName = new File(imageFileFolder, date.toString() + ".jpg");
try
{
out = new FileOutputStream(imageFileName);
bmp.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
scanPhoto(imageFileName.toString());
out = null;
} catch (Exception e)
{
e.printStackTrace();
}
}
public String fromInt(int val)
{
return String.valueOf(val);
}
public void scanPhoto(final String imageFileName)
{
msConn = new MediaScannerConnection(context,new MediaScannerConnection.MediaScannerConnectionClient()
{
public void onMediaScannerConnected()
{
msConn.scanFile(imageFileName, null);
Log.i("msClient obj in Photo Utility", "connection established");
}
public void onScanCompleted(String path, Uri uri)
{
msConn.disconnect();
Log.i("msClient obj in Photo Utility","scan completed");
}
});
msConn.connect();
}