4

Should I be using something other than the MediaScannerConnection.scanFile method to refresh the gallery?

After saving a new jpg I run media scanner to refresh the gallery app like so

        MediaScannerConnection.scanFile(this,
                new String[] { fullname }, null,
                new MediaScannerConnection.OnScanCompletedListener() {
            public void onScanCompleted(String path, Uri uri) {
                Log.d("ExternalStorage", "@@@@ Scanned " + path + ":");
                Log.d("ExternalStorage", "@@@@ -> uri=" + uri);
            }
       });

The output of the log shows the following correct output

@@@@ Scanned /data/data/com.mypackage/files/skit_106_01.jpg:

@@@@ -> uri=content://media/external/images/media/95

The gallery app shows no media available

This code has been working perfectly for some time now. It was only when I created an Android avd against version 4.4.2 for further testing that the problem has surfaced.

The code I have seems to be the recommended way of refreshing the gallery app according to Androids documentation so maybe this issue is related to the way I am saving the file, the code for which is as follows.

UPDATE

The code checks for external storage availability and will write to external storage and if external storage is not available it will write the file to internal storage.

    private void doSave(String fname, boolean doShare) {
        fname = "skit_"+mCurrentSkitId +
                "_"+mSkitManager.getCurrentFrameCount(
                        mCurrentSkitId)+1;
        Log.d(TAG, "@@@@ doSave fName = " + fname + " Current skit id = " + mCurrentSkitId);
        CharSequence text = getResources().getString(R.string.saved_as)
                + " " + fname;
        try {
            Bitmap b = mMainView.getSaveBitmap();
            if (b == null) {
                text = getResources().getString(R.string.save_fail_1);
                ;
                Toast.makeText(this, text, Toast.LENGTH_LONG).show();
                return;
            }
            fname = FileUtils.replaceInvalidFileNameChars(fname);
            String value = fname;
            File folder = FileUtils.getWritableFolder(this);
            /*
             * String folder =
             * Environment.getExternalStorageDirectory().toString() +
             * "/Pictures"; try { folder =
             * Environment.getExternalStoragePublicDirectory
             * (Environment.DIRECTORY_PICTURES).toString(); } catch
             * (NoSuchFieldError e) {
             * 
             * }
             */
            String ext = ".jpg";
            if (mPrefs.getString("format", "JPG").equals("PNG"))
                ext = ".png";
            String fullname = folder.getAbsolutePath() + File.separator + value
                    + ext;
            Map<String, String> hm = new HashMap<String, String>();
            hm.put("filename", fullname);
            FileOutputStream fos;
            if (folder == getFilesDir())
                fos = openFileOutput(value + ext, Context.MODE_WORLD_WRITEABLE);
            else {
                File f2 = new File(fullname);
                fos = new FileOutputStream(f2);
            }
            b.compress(CompressFormat.JPEG, 95, fos);
            fos.close();
            String[] str = new String[1];
            str[0] = fullname;
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.FROYO) {
                MediaScannerConnection.scanFile(this,
                        new String[] { fullname }, null,
                        new MediaScannerConnection.OnScanCompletedListener() {
                    public void onScanCompleted(String path, Uri uri) {
                        Log.d("ExternalStorage", "@@@@ Scanned " + path + ":");
                        Log.d("ExternalStorage", "@@@@ -> uri=" + uri);
                    }
               });
            }
            text = text + value + ext + " "
                    + getResources().getString(R.string.saved_end);
            ;
            mLastSaveName = value;
            setDetailTitle();
            mSkitManager.createFrame(mCurrentSkitId, fullname);
        } catch (Exception e) {
            Map<String, String> hm = new HashMap<String, String>();
            hm.put("text", e.toString());
            e.printStackTrace();
            text = getResources().getString(R.string.save_fail_2)
                    + e.toString();
        } catch (Error e) {
            Map<String, String> hm = new HashMap<String, String>();
            hm.put("text", e.toString());
            e.printStackTrace();
            text = getResources().getString(R.string.save_fail_2)
                    + e.toString();
        }
        Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
    }

The code that does the check for external storage availability looks like this

public static File getWritableFolder(Context context) {
    File folder = context.getFilesDir();
    if (externalStorageAvailable()) {
        try {
            folder = Environment
                    .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
            if (!folder.exists() || !folder.canWrite()) {
                folder = Environment
                        .getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
            }
            if (!folder.exists() || !folder.canWrite()) {
                folder = Environment.getExternalStorageDirectory();
            }
        } catch (Exception e) {
            folder = Environment.getExternalStorageDirectory();
        } catch (Error e) {
            folder = Environment.getExternalStorageDirectory();
        }
        if (!folder.exists() || !folder.canWrite()) {
            folder = context.getFilesDir();
        }
    }
    return folder;
}

private static boolean externalStorageAvailable() {
    boolean mExternalStorageAvailable;
    boolean mExternalStorageWriteable;
    String state = Environment.getExternalStorageState();

    if (state.equals(Environment.MEDIA_MOUNTED)) {
        // We can read and write the media
        mExternalStorageAvailable = mExternalStorageWriteable = true;
    } else if (state.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
        // We can only read the media
        mExternalStorageAvailable = true;
        mExternalStorageWriteable = false;
    } else {
        // Something else is wrong. It may be one of many other states, but
        // all we need
        // to know is we can neither read nor write
        mExternalStorageAvailable = mExternalStorageWriteable = false;
    }
    return mExternalStorageAvailable && mExternalStorageWriteable;
}

If anyone is able to pick holes in any of the above that might help to solve this issue then that would be great.

jamesc
  • 12,423
  • 15
  • 74
  • 113
  • 1
    "Should I be using something other than the MediaScannerConnection.scanFile method to refresh the gallery?" -- yes, but only for files on external storage. You are attempting to scan files on internal storage. "It was only when I created an Android avd against version 4.4.2 for further testing that the problem has surfaced." -- you had security problems all along, using the now-deprecated `MODE_WORLD_WRITEABLE`. You might see if the `Uri` actually works, to determine where things are breaking down. – CommonsWare Apr 16 '14 at 20:51
  • @CommonsWare Thank you for your input. It appears that the uri does not work. I would be greatful for any suggestions. – jamesc Apr 17 '14 at 03:18
  • @CommonsWare regarding the point about MODE_WORLD_WRITEABLE. Are you suggesting the documentation is wrong http://developer.android.com/guide/topics/data/data-storage.html I'm left confused as to what I should be doing – jamesc Apr 17 '14 at 04:57
  • 1
    "I would be greatful for any suggestions" -- try storing your images on external storage. "Are you suggesting the documentation is wrong" -- if you want the file to be world-writeable, put the file on external storage. You may also wish to read [the documentation for `MODE_WORLD_WRITEABLE`](https://developer.android.com/reference/android/content/Context.html#MODE_WORLD_WRITEABLE) where it shows that this has been deprecated since Android 4.2 and is not recommended. – CommonsWare Apr 17 '14 at 11:20
  • @CommonsWare I first try to store on external storage and if not available I store on internal storage. So I need a solution that works with both. – jamesc Apr 17 '14 at 21:35
  • The solution is to not index the files on internal storage. External storage will be available the *vast* majority of the time on Android 3.0+ devices (and many Android 2.3 devices), where external storage is part of the on-board flash memory. – CommonsWare Apr 17 '14 at 21:37
  • @CommonsWare The images must be visible to the gallery app whether they are on internal or external storage. Is this possible. Unclear by your use of the term "index" The 4.4.2 avd I set up seems reluctant to recognise that the SD card I set up is available so my code is saving to internal storage. The fact that you state the vast majority of devices will have external storage leads me to an understanding of why I have only just come across this problem. Thank's for the clarification on MODE_WORLD_WRITABLE. – jamesc Apr 17 '14 at 21:45
  • "Is this possible" -- that is up to Google. – CommonsWare Apr 17 '14 at 21:50
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/50925/discussion-between-jamesw-and-commonsware) – jamesc Apr 17 '14 at 22:19

1 Answers1

4

i was having mixed results with MediaScannerConnection so i used the sendBroadcast method instead. I do not know if the sendBroadcast method is not standard/should not be used but it works for me.

public void galleryAddPic(File currentPhotoPath) {  
    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    Uri contentUri = Uri.fromFile(currentPhotoPath);
    mediaScanIntent.setData(contentUri);
    this.sendBroadcast(mediaScanIntent);
}

Also regarding the MediaScannerConnection : https://stackoverflow.com/a/4825615/1497188

Community
  • 1
  • 1
Android2390
  • 1,150
  • 12
  • 21
  • Thanks for the reply but I think, as @CommonsWare has suggested this may be down to the uri and the way I am saving the images in conjunction with Android api 19. Your solution did not work – jamesc Apr 17 '14 at 05:04