androidbitmapgoogle-glassfileobserver

Google Glass File Observer Not Working Properly


The goal of my program is (at least in this stage) to get a photo from glass and then display it on the screen (I am using an immersion).

My class (extends Activity of course) has the following:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_picture_analysis);

    Intent getTheImageIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(getTheImageIntent, 25);
}

Which launches the camera. After the user takes and accepts a photo, it calls my public void onActivityResult method

public void onActivityResult(int requestCode, int resultCode, Intent data) {



    // A bunch of debug logging that I inserted
    Log.d("glass", "got a pic!!");
    Log.d("glass", String.valueOf(resultCode));
    Log.d("glass", String.valueOf(resultCode==RESULT_OK));


    if (data.getExtras() != null && requestCode == 25) {
        Log.d("glass", "in the if");

        //Create an instance of bundle and get the returned data
        Bundle extras = data.getExtras();

        final String pathToImage = (String) extras.get("picture_file_path");

        for (String key : extras.keySet()) {
            Object value = extras.get(key);
            Log.d("glass", String.format("%s %s (%s)", key,
                    value.toString(), value.getClass().getName()));
        }




        FileObserver observer = new FileObserver(pathToImage) {

            @Override
            public void onEvent(int event, String path) {

                Log.d("glass", "we observed an event");

                if (event == FileObserver.CLOSE_WRITE || event==FileObserver.CLOSE_NOWRITE) {
                    Bitmap thePicUnscaled = BitmapFactory.decodeFile(pathToImage);

                    Log.d("glass", "observer found a close_write event");


                    Bitmap thePic = Bitmap.createScaledBitmap(thePicUnscaled,
                            getResources().getDisplayMetrics().widthPixels,
                            getResources().getDisplayMetrics().heightPixels,
                            true);

                    ImageView imgView = (ImageView) findViewById(R.id.thePictureView);
                    imgView.setImageBitmap(thePic);
                }
            }
        };

        observer.startWatching();
        Log.d("glass","observer started watching");


    } else {
        Log.d("glass", "There were no extras passed with intent in onActivityResult");
    }

}

Now my problem is that although I have called the file observer, it never actually observes a change. Even though I have the Log.d("glass", "we observed an event"); that should log every time there is an event, it never logs. My logcat (filtered for my tags) looks like:

05-16 14:27:31.007    4976-4976/com.nkhosla.glasstest.app D/dalvikvm﹕ Late-enabling CheckJNI
05-16 14:27:31.007    4976-4976/com.nkhosla.glasstest.app D/dalvikvm﹕ Try to disable coredump for pid 4976
05-16 14:27:31.007    4976-4976/com.nkhosla.glasstest.app D/dalvikvm﹕ Process 4976 nice name: com.nkhosla.glasstest.app
05-16 14:27:31.007    4976-4976/com.nkhosla.glasstest.app D/dalvikvm﹕ Extra Options: not specified
05-16 14:27:32.031    4976-4976/com.nkhosla.glasstest.app D/OpenGLRenderer﹕ Enabling debug mode 0
05-16 14:27:35.625    4976-4976/com.nkhosla.glasstest.app D/glass﹕ got a pic!!
05-16 14:27:35.625    4976-4976/com.nkhosla.glasstest.app D/glass﹕ -1
05-16 14:27:35.625    4976-4976/com.nkhosla.glasstest.app D/glass﹕ true
05-16 14:27:35.625    4976-4976/com.nkhosla.glasstest.app D/glass﹕ in the if
05-16 14:27:35.625    4976-4976/com.nkhosla.glasstest.app D/glass﹕ thumbnail_file_path /storage/emulated/0/thumbnail_cache/t_thumb_20140516_142732_930.jpg (java.lang.String)
05-16 14:27:35.625    4976-4976/com.nkhosla.glasstest.app D/glass﹕ picture_file_path /storage/emulated/0/DCIM/Camera/20140516_142732_930.jpg (java.lang.String)
05-16 14:27:35.640    4976-4976/com.nkhosla.glasstest.app D/glass﹕ observer started watching

I have tried to just access the file there, right after the observer starts watching, but I get a NullPointerException if I try to access the bitmap created using Bitmap thePicUnscaled = BitmapFactory.decodeFile(pathToImage);

I have left the file observer sit an watch for at least 5 or so seconds, which is an eternity in flash write speeds. I have no clue what my problem is.

For reference, my layout.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.nkhosla.glasstest.app.PictureAnalysis">


    <ImageView
        android:layout_height="fill_parent"
        android:layout_width="fill_parent"
        android:id="@+id/thePictureView"
        android:maxHeight="300dp"
        android:maxWidth="640dp"
        android:scaleType="centerCrop"
        android:adjustViewBounds="true"/>

</RelativeLayout>

Solution

  • If the only thing you're interested in when using the Camera Intent is a scaled down version of the image, you should use the CameraManager#EXTRA_THUMBNAIL_FILE_PATH instead of the full picture for those reasons:

    1. It is available right away: the full image may take multiple seconds to be written due to some post processing
    2. It is already scaled-down to the Preview size

    If you need the full image, the FileObserver should observe the parent directory of the file, not the file itself. Here is the code snippet we provided in our Developer's Guide:

    private static final int TAKE_PICTURE_REQUEST = 1;
    
    private void takePicture() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(intent, TAKE_PICTURE_REQUEST);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == TAKE_PICTURE_REQUEST && resultCode == RESULT_OK) {
            String picturePath = data.getStringExtra(
                    CameraManager.EXTRA_PICTURE_FILE_PATH);
            processPictureWhenReady(picturePath);
        }
    
        super.onActivityResult(requestCode, resultCode, data);
    }
    
    private void processPictureWhenReady(final String picturePath) {
        final File pictureFile = new File(picturePath);
    
        if (pictureFile.exists()) {
            // The picture is ready; process it.
        } else {
            // The file does not exist yet. Before starting the file observer, you
            // can update your UI to let the user know that the application is
            // waiting for the picture (for example, by displaying the thumbnail
            // image and a progress indicator).
    
            final File parentDirectory = pictureFile.getParentFile();
            FileObserver observer = new FileObserver(parentDirectory.getPath(),
                    FileObserver.CLOSE_WRITE | FileObserver.MOVED_TO) {
                // Protect against additional pending events after CLOSE_WRITE
                // or MOVED_TO is handled.
                private boolean isFileWritten;
    
                @Override
                public void onEvent(int event, String path) {
                    if (!isFileWritten) {
                        // For safety, make sure that the file that was created in
                        // the directory is actually the one that we're expecting.
                        File affectedFile = new File(parentDirectory, path);
                        isFileWritten = affectedFile.equals(pictureFile);
    
                        if (isFileWritten) {
                            stopWatching();
    
                            // Now that the file is ready, recursively call
                            // processPictureWhenReady again (on the UI thread).
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    processPictureWhenReady(picturePath);
                                }
                            });
                        }
                    }
                }
            };
            observer.startWatching();
        }
    }