0

I have implemented a custom gallery that give to the user the possibility to choose many images in real time, and pass them to a new activity where the ImageViews are created pragmatically for each image passed. The user can add many times the images freely. What i noticed is that if the user select every time a new image that isn't already selected, all works correctly. But if the user select an image that is already been passed to the next activity, the app crash and i receive a java.lang.StringIndexOutOfBounds error. I need to understand what is causing this problem. Now i add my code, but i can edit or add more if you need to understand better.

The custom gallery:

public void onResume(){
    super.onResume();
    Intent previousIntent= getIntent(); //create a new intent for retrieve the photos path from the PhotoManagement activity (this need to be done for not lost the already selected images)
    Bundle customGalleryIntentBundle = getIntent().getExtras();
    if( customGalleryIntentBundle != null ){
        photosPath = (String) customGalleryIntentBundle.get("Selected Images"); //get the selected images from the previous activity
        photosPath = photosPath.replace("null", "");
        Log.i("CUSTOM GALLERY PHOTOS PATH",""+photosPath);
    }

    final String[] columns = { MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID };
    final String orderBy = MediaStore.Images.Media._ID;
    Cursor imagecursor = managedQuery( //create the cursor for navigate inside the database device and retrieve informations about the media contents like photo, video ecc...
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null,
            null, orderBy);
    int image_column_index = imagecursor.getColumnIndex(MediaStore.Images.Media._ID); //get the current column index
    this.count = imagecursor.getCount(); //count the external content uri of the cursor
    this.thumbnails = new Bitmap[this.count]; //create an array of bitmap using the size retrieved from the count variable
    this.arrPath = new String[this.count]; //create the array that contains the list of the path of the media (photos in this case)
    this.thumbnailsselection = new boolean[this.count]; //initialize the array of boolean to save informations about the selection of the thumbnails
    for (int i = 0; i < this.count; i++) {
        imagecursor.moveToPosition(i); //let's start from the first position and move one by one until the end
        int id = imagecursor.getInt(image_column_index);
        int dataColumnIndex = imagecursor.getColumnIndex(MediaStore.Images.Media.DATA);
        thumbnails[i] = MediaStore.Images.Thumbnails.getThumbnail( //take the thumbnails of the images and store it inside the array using the MICRO format for avoid high memory usage
                getApplicationContext().getContentResolver(), id,
                MediaStore.Images.Thumbnails.MICRO_KIND, null);
        arrPath[i]= imagecursor.getString(dataColumnIndex);
    }
    GridView imagegrid = (GridView) findViewById(R.id.PhoneImageGrid); //assign the GridView to the equivalent element in the xml file
    imageAdapter = new ImageAdapter(); //create a new imageAdapter. It is used how source for all the items in the gridView
    imagegrid.setAdapter(imageAdapter);
    imagecursor.close(); //close the image cursor when we arrive to the end of the columns to analize

    final Button selectBtn = (Button) findViewById(R.id.selectBtn); //assign the button to the equivalent in the xml layout
    selectBtn.setOnClickListener(new OnClickListener() { //create a listener for reveal when it is pressed

        public void onClick(View v) {
            // TODO Auto-generated method stub
            final int len = thumbnailsselection.length;
            int cnt = 0; //initialize a count for check the number of selected photos
            String selectImages = "";
            for (int i =0; i<len; i++)
            {
                if (thumbnailsselection[i]){
                    cnt++;
                    selectImages = selectImages + arrPath[i] + "|"; //store the correct path of the images in the variable (we can use this string for find the images path in others activity)         
                    Log.i("CUSTOM GALLERY SELECT IMAGES",""+selectImages);
                }
            }
            //if no one image is selected show a message to the user
            if (cnt == 0){
                Toast.makeText(getApplicationContext(),
                        "Please select at least one image",
                        Toast.LENGTH_LONG).show();
            } else {
                //If at least one image is selected we create a new activity where the images are displayed and the user can manage them
                Intent photoManagementIntent = new Intent(
                        getApplicationContext(),
                        PhotoManagement.class //select the class for the next activity
                        );
                photoManagementIntent.putExtra("Selected Images",""+selectImages+photosPath); //add an extra parameter that contains the path of the selected images. In this mode we can read it in the next activity
                startActivity(photoManagementIntent); //start the new activity
            }
        }
    });
}

This is the class where i create the ImageViews pragmatically for show the images passed from the custom gallery:

protected void onResume(){
    super.onResume();
        Intent previousIntent= getIntent(); //create a new intent for retrieve informations from the previous activity
        Bundle photoManagementIntentBundle = getIntent().getExtras();
        if(photoManagementIntentBundle != null) //if the new intent is different from null
        {
            photosPath = (String) photoManagementIntentBundle.get("Selected Images"); //get the "selected images" string created in the previous activity
            photosPath = photosPath.replace("null", "");
            final String photosPathCopy = photosPath;
            int count = photosPath.length() - photosPath.replace("|", "").length(); //count contains the number of images selected

            //SCROLL VIEW
            ScrollView scrollView = new ScrollView(this); //create a new scrollView
            scrollView.setBackground(getResources().getDrawable(R.drawable.background)); //give the background gradient
            scrollView.setLayoutParams(new ScrollView.LayoutParams(ScrollView.LayoutParams.MATCH_PARENT, //set the main params about the dynamic size of the scrollView
                                                         ScrollView.LayoutParams.MATCH_PARENT));
            scrollView.setPadding(0, 20, 0, 0);

            //LINEAR LAYOUT
            LinearLayout linearLayout = new LinearLayout(this); //create a new linearLayout
            linearLayout.setOrientation(LinearLayout.VERTICAL); //set the layout orientation
            linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT));

            for(int i=0; i<count; i++) {
                int indexOf = photosPath.indexOf('|'); //take the number of characters from | char
                String correctPath = photosPath.substring(0, indexOf); //this take the correct path of the photo
                photosPath = photosPath.replace(correctPath+"|", ""); //replace the old path with nothing. In this mode at every loop we have a new path until the end.
                Log.i("PHOTO MANAGEMENT PHOTOS PATH INSIDE LOOP",""+photosPath);
                //RELATIVE LAYOUT
                RelativeLayout relativeLayout = new RelativeLayout(this); //create a new relative layout
                relativeLayout.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, //set main params about the width and height
                        RelativeLayout.LayoutParams.FILL_PARENT));
                relativeLayout.setBackgroundColor(getResources().getColor(R.color.grayColor)); //set background color
                LinearLayout.LayoutParams relativeParams = new LinearLayout.LayoutParams(
                        new LinearLayout.LayoutParams(
                                LinearLayout.LayoutParams.MATCH_PARENT,
                                LinearLayout.LayoutParams.WRAP_CONTENT));
                relativeParams.setMargins(20, 20, 20, 0);
                relativeLayout.setLayoutParams(relativeParams); //set declared params about layout to the relativeLayout
                relativeLayout.requestLayout();
                relativeLayout.setOnClickListener(new View.OnClickListener(){ //create a listener about the layout. When a user press a point inside the relative layout a new activity should be created
                     @Override
                     public void onClick(View v){
                         Intent photoDetailsActivity = new Intent(
                                getApplicationContext(),
                                PhotoDetails.class //assign the class for create a new intent
                            );
                        photoDetailsActivity.putExtra("Selected Images",""+photosPathCopy);
                        startActivity(photoDetailsActivity); //let's start the new activity
                     }
                 });

                //IMAGE VIEW
                ImageView selectedPhoto = new ImageView(this); //create a new imageView
                selectedPhoto.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT)); //set width and height params about the selected photo zone
                try {
                    File standardImage = new  File(correctPath); //create a new file with the original path of the images
                    BitmapFactory.Options o = new BitmapFactory.Options(); //Enable the Bitmap factory options
                    o.inJustDecodeBounds = true; //allow the caller to query the bitmap without having to allocate the memory for its pixels
                    BitmapFactory.decodeStream(new FileInputStream(standardImage),null,o); //decode the image using the bitmap factory options

                    final int REQUIRED_SIZE=70; //The new size we want to scale to (save big quantity of memory)
                    int scale=1; //we need to find the correct scale value. It should be the power of 2.
                    while(o.outWidth/scale/2>=REQUIRED_SIZE && o.outHeight/scale/2>=REQUIRED_SIZE)
                        scale*=2;
                    BitmapFactory.Options o2 = new BitmapFactory.Options();  
                    o2.inSampleSize=scale; //decode with inSampleSize
                    selectedPhoto.setImageBitmap(BitmapFactory.decodeStream(new FileInputStream(standardImage), null, o2)); //assign the scaled image to the gridView
                } catch (FileNotFoundException e) {
                    Log.i("Error","File not found");
                }
                selectedPhoto.getLayoutParams().height = 90; //use a fixed size for each thumnail
                selectedPhoto.getLayoutParams().width = 90;

                //TEXT VIEWS
                TextView numberCopies = new TextView(this); //create new TextView
                numberCopies.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
                numberCopies.setGravity(Gravity.CENTER); //set position to the center in confront to the parent
                numberCopies.setPadding(25, 25, 25, 25);
                numberCopies.setTextColor(getResources().getColor(R.color.blackColor));
                numberCopies.setText("2 copies "); //this need to be dynamic
                RelativeLayout.LayoutParams layoutParamsNumberCopies = (RelativeLayout.LayoutParams) numberCopies.getLayoutParams();
                layoutParamsNumberCopies.addRule(RelativeLayout.CENTER_HORIZONTAL); //add a rule to the layout params. We put his position at the horizontal center of the relative layout
                numberCopies.setLayoutParams(layoutParamsNumberCopies); //set the layout rules to the textView

                TextView priceCopies = new TextView(this);
                priceCopies.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));
                priceCopies.setGravity(Gravity.CENTER);
                numberCopies.setPadding(25, 25, 25, 25);
                priceCopies.setTextColor(getResources().getColor(R.color.redColor));

                RelativeLayout.LayoutParams layoutParamsPriceCopies = (RelativeLayout.LayoutParams) priceCopies.getLayoutParams();
                layoutParamsPriceCopies.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                layoutParamsPriceCopies.addRule(RelativeLayout.CENTER_VERTICAL);
                priceCopies.setLayoutParams(layoutParamsPriceCopies);

                relativeLayout.addView(selectedPhoto);
                relativeLayout.addView(numberCopies);
                relativeLayout.addView(priceCopies);
                linearLayout.addView(relativeLayout);
            }               
            scrollView.addView(linearLayout);
            setContentView(scrollView);
            linearLayout.addView(relativeLayoutOpenButton); //add the button to the view
        }
}

This is the error that i'm receiving when i try to add an image that has been already added to the next activity:

10-04 09:15:20.019: E/AndroidRuntime(1715): FATAL EXCEPTION: main
10-04 09:15:20.019: E/AndroidRuntime(1715): java.lang.RuntimeException: Unable to resume activity {com.example.dilandprints2/com.example.dilandprints2.PhotoManagement}: java.lang.StringIndexOutOfBoundsException: length=0; regionStart=0; regionLength=-1
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2742)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2771)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2235)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.access$600(ActivityThread.java:141)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.os.Handler.dispatchMessage(Handler.java:99)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.os.Looper.loop(Looper.java:137)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.main(ActivityThread.java:5041)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.reflect.Method.invokeNative(Native Method)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.reflect.Method.invoke(Method.java:511)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at dalvik.system.NativeStart.main(Native Method)
10-04 09:15:20.019: E/AndroidRuntime(1715): Caused by: java.lang.StringIndexOutOfBoundsException: length=0; regionStart=0; regionLength=-1
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.String.startEndAndLength(String.java:583)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at java.lang.String.substring(String.java:1464)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at com.example.dilandprints2.PhotoManagement.onResume(PhotoManagement.java:93)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1185)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.Activity.performResume(Activity.java:5182)
10-04 09:15:20.019: E/AndroidRuntime(1715):     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2732)
10-04 09:15:20.019: E/AndroidRuntime(1715):     ... 12 more

Any idea or suggestion please?

ssantos
  • 16,001
  • 7
  • 50
  • 70
Hieicker
  • 595
  • 1
  • 11
  • 29

2 Answers2

3

This line

int indexOf = photosPath.indexOf('|');

is returning -1 (no | character found on photosPath), so you're calling

photosPath.substring(0, -1);

which throws the IndexOutOfBoundsException. You're doing pretty much operations with photosPath, follow the trace step by step to check if it gets the initial value you're expecting, and when it loses it.

ssantos
  • 16,001
  • 7
  • 50
  • 70
  • Great it helped :) If the answer was useful, please consider accepting so that user users can easily spot the right answer. – ssantos Oct 04 '13 at 10:19
0

Try this, change this line:

 photosPath = (String) customGalleryIntentBundle.get("Selected Images"); 

to this one:

 photosPath = (String) customGalleryIntentBundle.getString("Selected_Images"); 

Do the same for this:

 photosPath = (String) photoManagementIntentBundle.get("Selected Images");

to this:

 photosPath = (String) photoManagementIntentBundle.getString("Selected_Images");

Every place that you have this key: "Selected Images" you have to change it to "Selected_Images", also you have to get a String and no t just a regular get.

Dyna
  • 2,304
  • 1
  • 14
  • 25