3

I have not found anything online that addresses this particular issue. I have an activity that takes a photo or video using the Android camera app. It then returns from the camera with the image or video, and shows it in a view. Both my video and image, capture and show, work fine when I am in the same configuration. But if I start the camera from my app in say, portrait, then when I am in the camera app I decide to change it to landscape, then once I come back from the camera app, my image shows a blank ImageView. It works fine if I stay in the same configuration. Like when I start in portrait, and use the camera app also in portrait, then my ImageView shows the image upon returning. Since I cannot write code for the camera app, not sure how to handle this.

However, my video has no trouble if I change orientation during use of the camera video. It displays fine when it comes back into my app, no matter which orientation I am in in either the app or the camera app.

But why does the image have problems? I've tried to save state in onSaveInstanceState and onRestoreInstanceState, but this does not affect anything. I get null values for all my variables in those methods, when I log them (for video too). I think because it might have more to do with my Intent data? But that's just a guess. I looked over the Intent and see nothing missing. The image intent is a little different from the video intent, so maybe I'm missing something.

I do have 2 different xml files for the portrait and landscape layout, but they are identical and in the right folders. Android auto-chooses them when I do a configuration change.

If anyone has tips, thanks very much.

UPDATE:

I tried to add a line of code in my manifest: android:configChanges="keyboardHidden|orientation|screenSize"

and I overrode onConfigurationChanged(), but now my image comes back as a black screen. Both layout files are in my layout folder.

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        setContentView(R.layout.activity_make_photo_video_land);
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        setContentView(R.layout.activity_make_photo_video);
    }
}

UPDATE 2:

I undid everything I tried in UPDATE 1. Since that gave me a black screen and other unfixable bugs.

This time, I have added a configuration selector in onCreate to manually change the view:

// Checks the orientation of the screen
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            setContentView(R.layout.activity_make_photo_video_land);
            mImageView = (ImageView) findViewById(R.id.taken_photo_land);
        } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            setContentView(R.layout.activity_make_photo_video);
            mImageView = (ImageView) findViewById(R.id.taken_photo);
        }

After more logging, I have realized when I change the configuration in the camera app, the following methods execute in this order (listed from 1st activity start):

  1. onCreate()

  2. onSavedInstanceState() - I leave my app to go to the camera

  3. onCreate() - I return from app with a photo, a log on mImageBitmap is null.

  4. onRestoreInstanceState()

  5. inActivityResult() - then it processes the photo that is captured from camera.

The only thing I persist in onSavedInstanceState() is the mCurrentPhotoPath, which is needed to run the setPic() method in inActivityResult().

In onSavedInstanceState(): outState.putString("FILE_PATH", mCurrentPhotoPath);

In onCreate():

if (savedInstanceState != null) {
            mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");
   }

This processes fine, and a log on the bitmap value is not null. So why doesn't the image post still??

MakePhotoVideo.java

package org.azurespot.makecute;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

import org.azurespot.R;
import org.azurespot.cutecollection.CuteCollection;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;

public class MakePhotoVideo extends ActionBarActivity {

    private static final int ACTION_TAKE_PHOTO = 1;
    private static final int ACTION_TAKE_VIDEO = 2;
    private static final String BITMAP_STORAGE_KEY = "viewbitmap";
    private static final String IMAGEVIEW_VISIBILITY_STORAGE_KEY = "imageviewvisibility";
    private ImageView mImageView;
    private Bitmap mImageBitmap;

    private static final String VIDEO_STORAGE_KEY = "viewvideo";
    private static final String VIDEOVIEW_VISIBILITY_STORAGE_KEY = "videoviewvisibility";
    private VideoView mVideoView;
    private Uri mVideoUri;
    private File fileVideo;

    private String mCurrentPhotoPath;
    String videoPath;
    private int position = 0;

    private static final String JPEG_FILE_PREFIX = "IMG_";
    private static final String JPEG_FILE_SUFFIX = ".jpg";

    private PhotoStorageDirFactory mPhotoStorageDirFactory = null;



    /* Photo album for this application */
    private String getAlbumName() {
        return getString(R.string.album_name);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_make_photo_video);

        mImageView = (ImageView) findViewById(R.id.taken_photo);
        mVideoView = (VideoView) findViewById(R.id.video_view);
        mVideoView.setVisibility(View.INVISIBLE);
        mImageView.setSaveEnabled(true);

        Button photoBtn = (Button) findViewById(R.id.click);
        setBtnListenerOrDisable(photoBtn, mTakePicOnClickListener, MediaStore.ACTION_IMAGE_CAPTURE);

        Button videoBtn = (Button) findViewById(R.id.record_video);
        setBtnListenerOrDisable(videoBtn,mTakeVidOnClickListener, MediaStore.ACTION_VIDEO_CAPTURE);

        mPhotoStorageDirFactory = new BasePhotoDirFactory();

        // Shows the up carat near app icon in ActionBar
        getSupportActionBar().setDisplayUseLogoEnabled(false);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    }


    public void viewCollection(View v){

        // finishes/restarts the activity so the unsaved video does not corrupt
        Intent intent = getIntent();
        finish();
        startActivity(intent);

        // goes to Cute Collection activity
        Intent i = new Intent(this, CuteCollection.class);
        startActivity(i);
    }

    private File getAlbumDir() {
        File storageDir = null;

        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {

            storageDir = mPhotoStorageDirFactory.getAlbumStorageDir(getAlbumName());

            if (storageDir != null) {
                if (! storageDir.mkdirs()) {
                    if (! storageDir.exists()){
                        Log.d("Camera", "failed to create directory");
                        return null;
                    }
                }
            }

        } else {
            Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
        }

        return storageDir;
    }

    private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
        File albumF = getAlbumDir();
        File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);
        return imageF;
    }

    private File setUpPhotoFile() throws IOException {

        File f = createImageFile();
        mCurrentPhotoPath = f.getAbsolutePath();

        return f;
    }

    private void setPic() {

        /* There isn't enough memory to open up more than a couple camera photos */
        /* So pre-scale the target bitmap into which the file is decoded */

        /* Get the size of the ImageView */
        int targetW = mImageView.getWidth();
        int targetH = mImageView.getHeight();

        /* Get the size of the image */
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;

        /* Figure out which way needs to be reduced less */
        int scaleFactor = 1;
        if ((targetW > 0) || (targetH > 0)) {
            scaleFactor = Math.min(photoW/targetW, photoH/targetH);
        }

        /* Set bitmap options to scale the image decode target */
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;

        /* Decode the JPEG file into a Bitmap */
        mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);


        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            mImageBitmap = rotateBitmap(mImageBitmap, 90);
        }

        savePhoto(mImageBitmap);

        /* Associate the Bitmap to the ImageView, make sure the VideoView
         * is cleared to replace with ImageView */
        mImageView.setImageBitmap(mImageBitmap);

        Log.d("TAG", "Value of mImageBitmap inside setPic(): " + mImageBitmap);
        mVideoUri = null;
        mImageView.setVisibility(View.VISIBLE);
        mVideoView.setVisibility(View.INVISIBLE);

    }

    // save your photo to SD card
    private void savePhoto(final Bitmap bitmapPhoto){
        // set OnClickListener to save the photo
        mImageView.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                boolean success = false;

                File photoDir = new File(Environment.getExternalStoragePublicDirectory
                        (Environment.DIRECTORY_PICTURES) + "/Cute Photos");
                photoDir.mkdirs();
                Random generator = new Random();
                int n = 10000;
                n = generator.nextInt(n);
                String photoName = "Photo"+ n +".jpg";
                File filePhoto = new File (photoDir, photoName);
//                if (filePhoto.exists ()) filePhoto.delete ();
                try {
                    FileOutputStream out = new FileOutputStream(filePhoto);
                    bitmapPhoto.compress(Bitmap.CompressFormat.JPEG, 100, out);
                    out.flush();
                    out.close();
                    success = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (success) {
                    Toast toast = Toast.makeText(getApplicationContext(), "Cute photo saved!",
                            Toast.LENGTH_LONG);
                    LinearLayout toastLayout = (LinearLayout) toast.getView();
                    toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
                    TextView toastTV = (TextView) toastLayout.getChildAt(0);
                    toastTV.setTextSize(30);
                    toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
                    toast.show();
                } else {
                    Toast.makeText(getApplicationContext(),
                            "Error during image saving", Toast.LENGTH_SHORT).show();
                }

            }
        });
    }


    // save your video to SD card
    protected void saveVideo(final Uri uriVideo){

        // click the video to save it
        mVideoView.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {

                boolean success = false;

                if(event.getAction() == MotionEvent.ACTION_UP) {

                    try {
                        // make the directory
                        File vidDir = new File(android.os.Environment.getExternalStoragePublicDirectory
                                (Environment.DIRECTORY_MOVIES) + File.separator + "Cute Videos");
                        vidDir.mkdirs();

                        // create unique identifier
                        Random generator = new Random();
                        int n = 100;
                        n = generator.nextInt(n);
                        // create file name
                        String videoName = "Video" + n + ".mp4";

                        fileVideo = new File(vidDir.getAbsolutePath(), videoName);

                        videoPath = fileVideo.getAbsolutePath();

                        Log.d("TAG", "Value of videoPath:" + videoPath);

                        fileVideo.setWritable(true, false);

                        OutputStream out = new FileOutputStream(fileVideo);
                        InputStream in = getContentResolver().openInputStream(uriVideo);

                        byte buffer[] = new byte[1024];
                        int length = 0;
                        while ((length = in.read(buffer)) > 0) {
                            out.write(buffer, 0, length);
                        }

                        out.close();
                        in.close();

                        success = true;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    if (success) {
                        Toast toast = Toast.makeText(getApplicationContext(), "Cute video saved!",
                                Toast.LENGTH_SHORT);
                        LinearLayout toastLayout = (LinearLayout) toast.getView();
                        toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
                        TextView toastTV = (TextView) toastLayout.getChildAt(0);
                        toastTV.setTextSize(30);
                        toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
                        toast.show();
                    } else {
                        Toast.makeText(getApplicationContext(),
                                "Error during video saving", Toast.LENGTH_SHORT).show();
                    }
                }

                return true;
            }
        });
    }


    public Bitmap rotateBitmap(Bitmap source, int angle)
    {
        Matrix matrix = new Matrix();
        matrix.set(matrix);
        matrix.setRotate(angle);
        return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
                source.getHeight(), matrix, false);
    }

    private void galleryAddPic() {
        Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
        File f = new File(mCurrentPhotoPath);
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);
        this.sendBroadcast(mediaScanIntent);
    }

    private void dispatchTakePictureIntent(int actionCode) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        switch(actionCode) {
            case ACTION_TAKE_PHOTO:
                File f;

                try {
                    f = setUpPhotoFile();
                    mCurrentPhotoPath = f.getAbsolutePath();
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
                } catch (IOException e) {
                    e.printStackTrace();
                    f = null;
                    mCurrentPhotoPath = null;
                }
                break;

            default:
                break;
        } // switch

        startActivityForResult(takePictureIntent, actionCode);
    }

    // Captures video from Android camera component
    protected void dispatchTakeVideoIntent() {
        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
            // set the video image quality to high
            takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
            startActivityForResult(takeVideoIntent, ACTION_TAKE_VIDEO);
        }
    }

    private void handleCameraPhoto() {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);

        Log.d("TAG", "Value of mCurrentPhotoPath: " + mCurrentPhotoPath);


        if (mCurrentPhotoPath != null) {
            setPic();
            galleryAddPic();
            mCurrentPhotoPath = null;
        }

    }
    // Post recorded video into VideoView
    private Uri handleCameraVideo(Intent intent) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);

        mVideoUri = intent.getData();
        mVideoView.setVideoURI(mVideoUri);
//        mImageBitmap = null;
        mVideoView.setVisibility(View.VISIBLE);
        mImageView.setVisibility(View.INVISIBLE);
        mVideoView.start();
        // saves video to file
        saveVideo(mVideoUri);

        return mVideoUri;

    }

    // click listener for the Android Camera button (not my app's button)
    Button.OnClickListener mTakePicOnClickListener =
            new Button.OnClickListener() {
                @Override
                public void onClick(View v) {
//                    mImageBitmap = null;
                    dispatchTakePictureIntent(ACTION_TAKE_PHOTO);
                    // releases the orientation lock
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                }
            };
    Button.OnClickListener mTakeVidOnClickListener =
            new Button.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dispatchTakeVideoIntent();
                    // releases the orientation lock
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                }
            };


    // Intent data is how the photo and video transfer into their views
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case ACTION_TAKE_PHOTO: {
                if (resultCode == RESULT_OK) {
                    handleCameraPhoto();
                } else {
                    Log.d("TAG", "Result of photo not work.");
                }
                break;
            } // ACTION_TAKE_PHOTO

            case ACTION_TAKE_VIDEO: {
                if (resultCode == RESULT_OK) {
                    handleCameraVideo(data);
                }
                break;
            } // ACTION_TAKE_VIDEO
        } // switch
    }


    // Some lifecycle callbacks so that the image can survive orientation change
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putParcelable(BITMAP_STORAGE_KEY, mImageBitmap);
        outState.putParcelable(VIDEO_STORAGE_KEY, mVideoUri);
        outState.putBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY, (mImageBitmap != null) );
        outState.putBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY, (mVideoUri != null) );
        outState.putString("FILE_PATH", mCurrentPhotoPath);


        if (mVideoUri != null) {
            // use onSaveInstanceState in order to store the video or photo
            outState.putInt("PositionVideo", mVideoView.getCurrentPosition());
            // playback position for orientation change
            mVideoView.pause();
        }

        // super should be last in this method
        super.onSaveInstanceState(outState);

    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {


        mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");

        mImageView.setImageBitmap(mImageBitmap);
        mImageView.setVisibility(
                savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
                        ImageView.VISIBLE : ImageView.INVISIBLE
        );


        mVideoUri = savedInstanceState.getParcelable(VIDEO_STORAGE_KEY);


        mVideoView.setVideoURI(mVideoUri);
        mVideoView.setVisibility(
                savedInstanceState.getBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY) ?
                        ImageView.VISIBLE : ImageView.INVISIBLE
        );


        if (mVideoUri != null) {
            // for video, restores position it was playing
            position = savedInstanceState.getInt("PositionVideo");
            mVideoView.seekTo(position);
        }


        super.onRestoreInstanceState(savedInstanceState);


    }

    /**
     * Indicates whether the specified action can be used as an intent. This
     * method queries the package manager for installed packages that can
     * respond to an intent with the specified action. If no suitable package is
     * found, this method returns false.
     * http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
     *
     * @param context The application's environment.
     * @param action The Intent action to check for availability.
     *
     * @return True if an Intent with the specified action can be sent and
     *         responded to, false otherwise.
     */
    public static boolean isIntentAvailable(Context context, String action) {
        final PackageManager packageManager = context.getPackageManager();
        final Intent intent = new Intent(action);
        List<ResolveInfo> list =
                packageManager.queryIntentActivities(intent,
                        PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }

    private void setBtnListenerOrDisable(Button btn, Button.OnClickListener onClickListener,
                                                                        String intentName) {
        if (isIntentAvailable(this, intentName)) {
            btn.setOnClickListener(onClickListener);

        } else {
            btn.setClickable(false);
        }
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        // Makes the UP caret go back to the previous fragment MakeCuteFragment
        switch (item.getItemId()) {
            case android.R.id.home:
                android.app.FragmentManager fm= getFragmentManager();
                fm.popBackStack();
                finish();

                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

}
Azurespot
  • 3,066
  • 3
  • 45
  • 73

4 Answers4

1

After all of my trial and error, I finally found the problem. My 2nd update was very close, the only fault was in the

mImageView.setVisibility(
           savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
                    ImageView.VISIBLE : ImageView.INVISIBLE
    );

method. I had this in onCreate, only executed if it passed the null check, but what I forgot is that the image bitmap does come back null when the configuration changes in the camera app, so the visibility will always be set to invisible. The IMAGEVIEW_VISIBILITY_STORAGE_KEY was set up to be true if the bitmap is not null, and false if it is null. So once I got rid of this, my other saving state code worked really well. Below is the final working code.

MakePhotoVideo.java

package org.azurespot.makecute;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

import org.azurespot.R;
import org.azurespot.cutecollection.CuteCollection;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Random;

public class MakePhotoVideo extends ActionBarActivity {

    private static final int ACTION_TAKE_PHOTO = 1;
    private static final int ACTION_TAKE_VIDEO = 2;
    private static final String BITMAP_STORAGE_KEY = "viewbitmap";
    private static final String IMAGEVIEW_VISIBILITY_STORAGE_KEY = "imageviewvisibility";
    private ImageView mImageView;
    private Bitmap mImageBitmap;

    private static final String VIDEO_STORAGE_KEY = "viewvideo";
    private static final String VIDEOVIEW_VISIBILITY_STORAGE_KEY = "videoviewvisibility";
    private VideoView mVideoView;
    private Uri mVideoUri;
    private File fileVideo;

    private String mCurrentPhotoPath;
    String videoPath;
    private int position = 0;
    int targetH;
    int targetW;

    private static final String JPEG_FILE_PREFIX = "IMG_";
    private static final String JPEG_FILE_SUFFIX = ".jpg";

    private PhotoStorageDirFactory mPhotoStorageDirFactory = null;


    /* Photo album for this application */
    private String getAlbumName() {
        return getString(R.string.album_name);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_make_photo_video);

        mImageView = (ImageView) findViewById(R.id.taken_photo);
        mVideoView = (VideoView) findViewById(R.id.video_view);
        mImageView.setVisibility(View.VISIBLE);
        mVideoView.setVisibility(View.INVISIBLE);
        mImageView.setSaveEnabled(true);

        Button photoBtn = (Button) findViewById(R.id.click);
        setBtnListenerOrDisable(photoBtn, mTakePicOnClickListener, MediaStore.ACTION_IMAGE_CAPTURE);

        Button videoBtn = (Button) findViewById(R.id.record_video);
        setBtnListenerOrDisable(videoBtn, mTakeVidOnClickListener, MediaStore.ACTION_VIDEO_CAPTURE);

        mPhotoStorageDirFactory = new BasePhotoDirFactory();

        // Shows the up carat near app icon in ActionBar
        getSupportActionBar().setDisplayUseLogoEnabled(false);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

    }


    public void viewCollection(View v){

        // finishes/restarts the activity so the unsaved video does not corrupt
        Intent intent = getIntent();
        finish();
        startActivity(intent);

        // goes to Cute Collection activity
        Intent i = new Intent(this, CuteCollection.class);
        startActivity(i);
    }

    private File getAlbumDir() {
        File storageDir = null;

        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {

            storageDir = mPhotoStorageDirFactory.getAlbumStorageDir(getAlbumName());

            if (storageDir != null) {
                if (! storageDir.mkdirs()) {
                    if (! storageDir.exists()){
                        Log.d("Camera", "failed to create directory");
                        return null;
                    }
                }
            }

        } else {
            Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
        }

        return storageDir;
    }

    private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = JPEG_FILE_PREFIX + timeStamp + "_";
        File albumF = getAlbumDir();
        File imageF = File.createTempFile(imageFileName, JPEG_FILE_SUFFIX, albumF);
        return imageF;
    }

    private File setUpPhotoFile() throws IOException {

        File f = createImageFile();
        mCurrentPhotoPath = f.getAbsolutePath();

        return f;
    }


    private void setPic() {

        mImageView.setVisibility(View.VISIBLE);
        mVideoView.setVisibility(View.INVISIBLE);

        /* There isn't enough memory to open up more than a couple camera photos */
        /* So pre-scale the target bitmap into which the file is decoded */

        /* Get the size of the image */
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;

        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE){
            targetH = 570;
            targetW = 960;
        } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){
            targetH = 960;
            targetW = 570;
        }

        BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;

        /* Figure out which way needs to be reduced less */
        int scaleFactor = Math.min(photoW/targetW, photoH/targetH);

        /* Set bitmap options to scale the image decode target */
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inBitmap = mImageBitmap;
        bmOptions.inPurgeable = true;


        /* Decode the JPEG file into a Bitmap */
        mImageBitmap = BitmapFactory.decodeFile(mCurrentPhotoPath, bmOptions);


        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            mImageBitmap = rotateBitmap(mImageBitmap, 90);
        }

        savePhoto(mImageBitmap);

        /* Associate the Bitmap to the ImageView, make sure the VideoView
         * is cleared to replace with ImageView */
        mImageView.setImageBitmap(mImageBitmap);
        mVideoUri = null;
    }

    // save your photo to SD card
    private void savePhoto(final Bitmap bitmapPhoto){
        // set OnClickListener to save the photo
        mImageView.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                boolean success = false;

                File photoDir = new File(Environment.getExternalStoragePublicDirectory
                        (Environment.DIRECTORY_PICTURES) + "/Cute Photos");
                photoDir.mkdirs();
                Random generator = new Random();
                int n = 10000;
                n = generator.nextInt(n);
                String photoName = "Photo"+ n +".jpg";
                File filePhoto = new File (photoDir, photoName);

                try {
                    FileOutputStream out = new FileOutputStream(filePhoto);
                    bitmapPhoto.compress(Bitmap.CompressFormat.JPEG, 100, out);
                    out.flush();
                    out.close();
                    success = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (success) {
                    Toast toast = Toast.makeText(getApplicationContext(), "Cute photo saved!",
                            Toast.LENGTH_LONG);
                    LinearLayout toastLayout = (LinearLayout) toast.getView();
                    toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
                    TextView toastTV = (TextView) toastLayout.getChildAt(0);
                    toastTV.setTextSize(30);
                    toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
                    toast.show();
                } else {
                    Toast.makeText(getApplicationContext(),
                            "Error during image saving", Toast.LENGTH_SHORT).show();
                }

            }
        });
    }


    // save your video to SD card
    protected void saveVideo(final Uri uriVideo){

        // click the video to save it
        mVideoView.setOnTouchListener(new View.OnTouchListener() {

            public boolean onTouch(View v, MotionEvent event) {

                boolean success = false;

                if(event.getAction() == MotionEvent.ACTION_UP) {

                    try {
                        // make the directory
                        File vidDir = new File(android.os.Environment.getExternalStoragePublicDirectory
                                (Environment.DIRECTORY_MOVIES) + File.separator + "Cute Videos");
                        vidDir.mkdirs();

                        // create unique identifier
                        Random generator = new Random();
                        int n = 100;
                        n = generator.nextInt(n);
                        // create file name
                        String videoName = "Video" + n + ".mp4";

                        fileVideo = new File(vidDir.getAbsolutePath(), videoName);

                        videoPath = fileVideo.getAbsolutePath();

                        Log.d("TAG", "Value of videoPath:" + videoPath);

                        fileVideo.setWritable(true, false);

                        OutputStream out = new FileOutputStream(fileVideo);
                        InputStream in = getContentResolver().openInputStream(uriVideo);

                        byte buffer[] = new byte[1024];
                        int length = 0;
                        while ((length = in.read(buffer)) > 0) {
                            out.write(buffer, 0, length);
                        }

                        out.close();
                        in.close();

                        success = true;

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    if (success) {
                        Toast toast = Toast.makeText(getApplicationContext(), "Cute video saved!",
                                Toast.LENGTH_SHORT);
                        LinearLayout toastLayout = (LinearLayout) toast.getView();
                        toastLayout.setBackgroundColor(getResources().getColor(R.color.toast_color));
                        TextView toastTV = (TextView) toastLayout.getChildAt(0);
                        toastTV.setTextSize(30);
                        toast.setGravity(Gravity.CENTER_VERTICAL, 0, 80);
                        toast.show();
                    } else {
                        Toast.makeText(getApplicationContext(),
                                "Error during video saving", Toast.LENGTH_SHORT).show();
                    }
                }

                return true;
            }
        });
    }


    public Bitmap rotateBitmap(Bitmap source, int angle)
    {
        Matrix matrix = new Matrix();
        matrix.set(matrix);
        matrix.setRotate(angle);
        return Bitmap.createBitmap(source, 0, 0, source.getWidth(),
                source.getHeight(), matrix, false);
    }

    private void galleryAddPic() {
        Intent mediaScanIntent = new Intent("android.intent.action.MEDIA_SCANNER_SCAN_FILE");
        File f = new File(mCurrentPhotoPath);
        Uri contentUri = Uri.fromFile(f);
        mediaScanIntent.setData(contentUri);
        this.sendBroadcast(mediaScanIntent);
    }

    private void dispatchTakePictureIntent(int actionCode) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        switch(actionCode) {
            case ACTION_TAKE_PHOTO:
                File f;

                try {
                    f = setUpPhotoFile();
                    Log.d("TAG", "Value of f in picture intent: " + f);
                    mCurrentPhotoPath = f.getAbsolutePath();
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
                } catch (IOException e) {
                    e.printStackTrace();
                    f = null;
                    mCurrentPhotoPath = null;
                }
                break;

            default:
                break;
        } // switch

        startActivityForResult(takePictureIntent, actionCode);
    }

    // Captures video from Android camera component
    protected void dispatchTakeVideoIntent() {
        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
            // set the video image quality to high
            takeVideoIntent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
            startActivityForResult(takeVideoIntent, ACTION_TAKE_VIDEO);
        }
    }

    private void handleCameraPhoto() {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);

        if (mCurrentPhotoPath != null) {
            setPic();
            galleryAddPic();
            mCurrentPhotoPath = null;
        }

    }
    // Post recorded video into VideoView
    private Uri handleCameraVideo(Intent intent) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);

        mVideoUri = intent.getData();
        mVideoView.setVideoURI(mVideoUri);
        mImageBitmap = null;
        mVideoView.setVisibility(View.VISIBLE);
        mImageView.setVisibility(View.INVISIBLE);
        mVideoView.start();
        // saves video to file
        saveVideo(mVideoUri);

        return mVideoUri;

    }

    // click listener for the Android Camera button (not my app's button)
    Button.OnClickListener mTakePicOnClickListener =
            new Button.OnClickListener() {
                @Override
                public void onClick(View v) {
//                    mImageBitmap = null;
                    dispatchTakePictureIntent(ACTION_TAKE_PHOTO);
                    // releases the orientation lock
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                }
            };
    Button.OnClickListener mTakeVidOnClickListener =
            new Button.OnClickListener() {
                @Override
                public void onClick(View v) {
                    dispatchTakeVideoIntent();
                    // releases the orientation lock
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                }
            };

    // Intent data is how the photo and video transfer into their views
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case ACTION_TAKE_PHOTO: {
                if (resultCode == RESULT_OK) {
                    handleCameraPhoto();
                }
                break;
            }
            case ACTION_TAKE_VIDEO: {
                if (resultCode == RESULT_OK) {
                    handleCameraVideo(data);
                }
                break;
            }
        }
    }


    // Some lifecycle callbacks so that the image can survive orientation change
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putParcelable(BITMAP_STORAGE_KEY, mImageBitmap);
        outState.putParcelable(VIDEO_STORAGE_KEY, mVideoUri);

        outState.putBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY, (mImageBitmap != null) );
        outState.putBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY, (mVideoUri != null) );

        outState.putString("FILE_PATH", mCurrentPhotoPath);

        if (mVideoUri != null) {
            // use onSaveInstanceState in order to store the video or photo
            outState.putInt("PositionVideo", mVideoView.getCurrentPosition());
            // playback position for orientation change
            mVideoView.pause();
        }

        // super should be last in this method
        super.onSaveInstanceState(outState);

    }

    // this is called after onCreate (when returning from camera app),
    // so br careful what you put here
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");

        mVideoUri = savedInstanceState.getParcelable(VIDEO_STORAGE_KEY);
        mVideoView.setVideoURI(mVideoUri);
        mVideoView.setVisibility(
                savedInstanceState.getBoolean(VIDEOVIEW_VISIBILITY_STORAGE_KEY) ?
                        ImageView.VISIBLE : ImageView.INVISIBLE
        );

        if (mVideoUri != null) {
            // for video, restores position it was playing
            position = savedInstanceState.getInt("PositionVideo");
            mVideoView.seekTo(position);
        }

        super.onRestoreInstanceState(savedInstanceState);
    }

    /**
     * Indicates whether the specified action can be used as an intent. This
     * method queries the package manager for installed packages that can
     * respond to an intent with the specified action. If no suitable package is
     * found, this method returns false.
     * http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
     *
     * @param context The application's environment.
     * @param action The Intent action to check for availability.
     *
     * @return True if an Intent with the specified action can be sent and
     *         responded to, false otherwise.
     */
    public static boolean isIntentAvailable(Context context, String action) {
        final PackageManager packageManager = context.getPackageManager();
        final Intent intent = new Intent(action);
        List<ResolveInfo> list =
                packageManager.queryIntentActivities(intent,
                        PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
    }

    private void setBtnListenerOrDisable(Button btn, Button.OnClickListener onClickListener,
                                                                        String intentName) {
        if (isIntentAvailable(this, intentName)) {
            btn.setOnClickListener(onClickListener);

        } else {
            btn.setClickable(false);
        }
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        // Makes the UP caret go back to the previous fragment MakeCuteFragment
        switch (item.getItemId()) {
            case android.R.id.home:
                android.app.FragmentManager fm= getFragmentManager();
                fm.popBackStack();
                finish();

                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

}
Azurespot
  • 3,066
  • 3
  • 45
  • 73
0

Hi We have the same problem in our one of the product, We apply the below solution it's work fine, actually our client has the Samsung device. Let's open your android manifest file see the MakePhotoVideo activity. see below what you have to add inside your activity tag.

<activity android:name=".MakePhotoVideo "
              android:label="@string/app_name"     android:configChanges="keyboardHidden|orientation">

Actually there are lots of use case related to camera intent, especially Samsung device has majority of problem due to of the customization the native application, Apply this attribute to your activity tag. Let me know if you have any problem. thank you.

Bhavdip Sagar
  • 1,951
  • 15
  • 27
  • Thanks, I did try that before, and it does persist the photo, but then I cannot choose a different layout for each configuration. So it only will choose the portrait layout for both portrait and landscape. Did you find a way around that issue? Btw, I had to add`screenSize` to that code, otherwise I was still getting blank. But, the layout is still an issue. – Azurespot Apr 05 '15 at 04:58
  • To be more specific, since Android won't let you choose manually a layout from the `layout-land` folder, there was no way to use a landscape layout if needed. Only if you don't have that code in the manifest, will it auto-choose the landscape layout (if there is one). – Azurespot Apr 05 '15 at 05:01
  • Yes correct if you specified the flag in android manifest it will auto select your layout. otherwise you have options programmatically handle the orientation. – Bhavdip Sagar Apr 05 '15 at 05:06
  • Any idea how to do that? I tried to find a way to choose the layout in the `layout-land`, but I could not do it. The 2 `xml` files are identical, same name, just that one is in the `layout` folder and one in the `layout-land`. I don't know how to programmatically choose the one for landscape when needed. – Azurespot Apr 05 '15 at 05:11
  • Hi about change the layout of activity when orientation change, you can load your define xml file inside you code do you know this? – Bhavdip Sagar Apr 05 '15 at 05:11
  • I thought that was possible, but I tried to find an example how to do it, but couldn't find one. – Azurespot Apr 05 '15 at 05:12
  • I mean do not allow the android to handle your auto configuration change. you have to handle the orientation change and you can decide which layout you want to load at time of landscape or portrait mode. – Bhavdip Sagar Apr 05 '15 at 05:13
  • see this might be help http://stackoverflow.com/questions/5726657/how-to-detect-orientation-change-in-layout-in-android – Bhavdip Sagar Apr 05 '15 at 05:15
  • But how do I get the layout from my resources? This `setContentView(R.layout-land.activity_make_photo_video);` does not work. I tried before this way, but I can't use `layout-land` it gives a red error. – Azurespot Apr 05 '15 at 05:19
  • What you have to do is you have keep two xml file for example one for landscape another for portrait mode 1) activity_make_photo_land 2)activity_make_photo_port. now inside you code load appropriate xml file. When configuration change function called. – Bhavdip Sagar Apr 05 '15 at 05:23
  • I tried to give them a different name, and one is in the `layout` and the other in the `layout-land` and when I try to get the one in `layout-land` then the photo taken comes back with a black screen. If I try to put both files in the `layout` folder, Android forces both files to be portrait, even though I designed the landscape one different, it will show a cut-off layout view. Not sure what other option there is. :( – Azurespot Apr 05 '15 at 05:27
  • Okay, if I create the landscape layout from the `layout-land` folder, then move it to the `layout` folder, it keeps the landscape, but then using it still gives me a black screen in `ImageView` after photo is taken and returns back to my app. – Azurespot Apr 05 '15 at 05:40
  • The `VideoView` is now black screen too on configuration change within the camera app. If I do same configuration in both app and camera, then they both work. I think this does not work because when it comes back from the camera app, there is technically no `configuration change`. The method to handle changes only works if you change the configuration **within** the app. But I don't know how to handle it otherwise. – Azurespot Apr 05 '15 at 05:44
  • Yes with in the app you can detect the your application orientation. – Bhavdip Sagar Apr 05 '15 at 05:46
  • I just get a black screen with this code. If you know of a fix let me know, thanks for the help. – Azurespot Apr 05 '15 at 05:55
0

Whatever happens in the camera app while it is fulfilling your intent, causes the system to destroy your activity because it needs more memory.

You need to override onSaveInstanceState(Bundle savedInstanceState) and save the ImageView state, and maybe other values, too. See an answer to Saving Activity state in Android for details.

You should be careful restoring app state when the activity is created to answer its startActivityForResult(), as explained here.

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Thanks Alex, I tried all that though. We established (below) that it was not working because it is technically not a configuration change. The app itself does not think it is changing, only when it comes back from the camera app it is different. The in-app (my app) configuration changes do work when I write code in those methods, but not for the camera app config changes. I know this because I did a log and those methods were not even called in my journey from app to camera back to app. Only if I change configuration within-app are they called. – Azurespot Apr 05 '15 at 06:48
  • 1
    You are right, there is no configuration change from the point of view of your activity. It is destroyed by the system while stopped. The trick is that onActivityResult and onCreate, onStart, onResume get called in a different order in such circumstance. – Alex Cohn Apr 05 '15 at 07:18
  • Oh... so you are saying, instead of restoring the variables in `onRestoreInstanceState` to do so in `onCreate` since that is called first, before others... I will try this. Thanks for pointing that out! I will be in touch... – Azurespot Apr 06 '15 at 01:16
  • Hi Alex, I updated my question with my new observations and changes. But still not getting `ImageView` to show anything when I change config in the camera app. Any idea why? I did not persist the `ImageView` itself, since it gets recreated when coming back from the camera, plus I heard views automatically persist. I just don't see why it won't work. – Azurespot Apr 06 '15 at 02:54
  • So, `setPic()` returns a black image? – Alex Cohn Apr 06 '15 at 16:42
  • No, not black just blank. Although I have persisted the file path, which seems like that is all that's needed, because with the file path, `setPic()` is able to turn it into a bitmap, according to the logs. Yet it doesn't show. So something is still missing. :( – Azurespot Apr 06 '15 at 20:52
  • Sorry, Alex, I updated my post. I actually had to undo all changes I made in UPDATE 1, since those were the ones that gave a black screen. But now with my current configuration, I still get a blank `ImageView`, only if I change the orientation while in the camera app. Keeping the same orientation keeps the view just fine. I know something is being lost, since it restarts the activity with a new config., I just can't figure out what. I thought it was the file path, but saving/retrieving that did not help. – Azurespot Apr 07 '15 at 01:56
  • It may be something that happens in `onResume()` – Alex Cohn Apr 07 '15 at 06:12
0

try to recieve the bitmap in onSaveInstanceState():

mCurrentPhotoPath = savedInstanceState.getString("FILE_PATH");

mImageBitmap = savedInstanceState.getParceable("BITMAP_STORAGE_KEY");

    mImageView.setImageBitmap(mImageBitmap);
    mImageView.setVisibility(
           savedInstanceState.getBoolean(IMAGEVIEW_VISIBILITY_STORAGE_KEY) ?
                    ImageView.VISIBLE : ImageView.INVISIBLE
    );
San Ti
  • 1
  • Hi, I'm not sure if I understand... I can't receive the bitmap in `onSaveInstanceState()` because the bitmap has not been created yet. It gets created in `onActivityResult()` when user comes back from camera app and that is after `onSaveInstanceState()` is called. I tried already too to add the bitmap to `onSaveInstanceState()` , but got a null when logged, because it is not created before leaving my app. Until that point, only the file path is saved and used, which I did persist in `onSaveInstanceState()`, retrieving it in `onCreate`. But still nothing... – Azurespot Apr 07 '15 at 01:47