Option to Rename an Image in Phimpme Android Application

In the Phimpme Android application, users can perform various operations on images such as editing an image, sharing an image, moving the image to another folder, printing a pdf version of the image and many more. However, another important functionality that has been implemented is the option to rename an image. So in this blog post, I will be discussing how we achieved the functionality to rename an image.

Step 1

First, we need to add an option in the overflow menu to rename the image being viewed. The option to rename an image has been added by implementing the following lines of code in the  menu_view_pager.xml file.

<item
  android:id=“@+id/rename_photo”
  app:showAsAction=“never”
  android:title=“@string/Rename”/>

Step 2

Now after the user chooses the option to rename any image from the overflow menu, an alert dialog with an edittext would be displayed to the user with the old name and provide the user to add a new name for the photo. The dialog box with edittext has been implemented with the following lines of XML code.

<android.support.v7.widget.CardView
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  app:cardCornerRadius=“2dp”
  android:id=“@+id/description_dialog_card”>
  <ScrollView
      android:layout_width=“match_parent”
      android:layout_height=“match_parent”>
      <LinearLayout
          android:layout_width=“match_parent”
          android:layout_height=“wrap_content”
          android:orientation=“vertical”>
          <TextView
              android:id=“@+id/description_dialog_title”
              android:layout_width=“match_parent”
              android:textColor=“@color/md_dark_primary_text”
              android:layout_height=“wrap_content”
              android:background=“@color/md_dark_appbar”
              android:padding=“24dp”
              android:text=“@string/type_description”
              android:textSize=“18sp”
              android:textStyle=“bold” />
          <LinearLayout
              android:id=“@+id/rl_description_dialog”
              android:layout_width=“match_parent”
              android:layout_height=“wrap_content”
              android:orientation=“horizontal”
              android:padding=“15dp”>
              <EditText
                  android:id=“@+id/description_edittxt”
                  android:layout_width=“fill_parent”
                  android:layout_height=“wrap_content”
                  android:padding=“15dp”
                  android:hint=“@string/description_hint”
                  android:textColorHint=“@color/grey”
                  android:layout_margin=“20dp”
                  android:gravity=“top|left”
                  android:inputType=“textCapSentences|textMultiLine”
                  android:maxLength=“2000”
                  android:maxLines=“4”
                  android:selectAllOnFocus=“true”/>
              <ImageButton
                  android:layout_width=“@dimen/mic_image”
                  android:layout_height=“@dimen/mic_image”
                  android:layout_alignRight=“@+id/description_edittxt”
                  app2:srcCompat=“@drawable/ic_mic_black”
                  android:layout_gravity=“center”
                  android:background=“@color/transparent”
                  android:paddingEnd=“10dp”
                  android:paddingTop=“12dp”
                  android:id=“@+id/voice_input”/>
          </LinearLayout>
      </LinearLayout>
  </ScrollView>
</android.support.v7.widget.CardView>

The screenshot displaying the dialog with edittext is provided below.

Step 3

Now after retrieving the new name entered by the user for the photo we would extract the extension of the old name which can be .jpg, .png etc. Thereafter we’d need to create a new File object passing in the new path of the image(the path folder remains the same only the image name gets changed to a new one) as the constructor parameter. Now using the renameTo method of the File class the old image file can be renamed. However, with this rename operation the image reference would not be automatically updated in the MediaStore database. So we’d need to delete the old file path from the Android system MediaStore database which keeps a URI reference to all the media files present on the device. At last, we’d need to invoke the MediaScanner class to scan all the media files so that the new file path of the renamed image is scanned and is picked up by the MediaStore database. This can be done with the help of an action intent to initiate the media scan action. The code changes implemented to perform the above-mentioned operation is given below.

public void onClick(View dialog) {
      if (editTextNewName.length() != 0) {
          int index = file.getPath().lastIndexOf(“/”);
          String path = file.getPath().substring(0, index);
          File newname = new File(path + “/” + editTextNewName.getText().toString() + “.” +
                  imageextension);
          if(file.renameTo(newname)){
              ContentResolver resolver = getApplicationContext().getContentResolver();
              resolver.delete(
                      MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.DATA +
                              “=?”, new String[] { file.getAbsolutePath() });
              Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
              intent.setData(Uri.fromFile(newname));
              getApplicationContext().sendBroadcast(intent);
          }
          if(!allPhotoMode){
              int a = getAlbum().getCurrentMediaIndex();
              getAlbum().getMedia(a).setPath(newname.getPath());
          }else {
              listAll.get(current_image_pos).setPath(newname.getPath());
          }
          renameDialog.dismiss();
          SnackBarHandler.showWithBottomMargin(parentView, getString(R.string.rename_succes), navigationView
                  .getHeight());
      } else {
          SnackBarHandler.showWithBottomMargin(parentView, getString(R.string.insert_something),
                  navigationView.getHeight());
          editTextNewName.requestFocus();
      }
  }
});

This is how we have implemented the functionality to rename an image in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

Resources

1.Android Developer documentation-https://developer.android.com/reference/java/io/File

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Renaming a file in java – http://stacktips.com/tutorials/java/how-to-delete-and-rename-a-file-in-java

 

Continue Reading

Option to delete albums from storage in Phimpme Android Application

In the Phimpme Android application, users can perform various operations on the albums available such as creating a zip file of the album, hiding an album and many more. However, one another useful functionality that has been added to the Phimpme Android application is the option to delete unwanted albums permanently from the storage. So in this post, I will be discussing how we implemented the functionality to delete albums.

Step 1

First, we need to provide an option in the overflow menu to delete album/albums when the user long-selects album/albums or the user is viewing photos inside a particular album. The option in the overflow menu can be added by integrating following lines of code in the menu_albums.xml file(this file contains the XML code for overflow menu options provided for albums).

 <item
android:id=”@+id/delete_action”
android:title=”@string/delete”
app:showAsAction=”ifRoom” />

Step 2

Now when the user opts to delete some selected albums, we need to retrieve user’s choice of albums to delete and store it in an ArrayList<Album> to keep track of the albums and for further use. We would here also keep track of all the albums displayed currently to the user through another ArrayList<Album> named displayAlbums. Thereafter deleteSelectedAlbums method of class HandlingAlbums will be invoked passing in the activity context as the parameter. Now in this method we’d iterate through the ArrayList<Album>(ArrayList used to store user’s choice of albums to delete) and in turn invoke another method deleteAlbum with each iteration, passing in the album and context as the parameters. The deleteAlbum method would return a boolean value that will indicate whether or not the particular album was successfully deleted. If the boolean value returned is true, indicating that the particular album and its contents have been permanently deleted from the device storage, then we would delete that particular album object from the displayAlbums list and the AlbumsAdapter would be notified to display the remaining set of albums by passing in the modified displayAlbums list. The code snippet used to implement the method deleteSelectedAlbums and deleteAlbum is provided below.

public boolean deleteSelectedAlbums(Context context) {
boolean success = true;

for (Album selectedAlbum : selectedAlbums) {
  int index = dispAlbums.indexOf(selectedAlbum);
  if(deleteAlbum(selectedAlbum, context))
    dispAlbums.remove(index);
  else success = false;
}
return success;
}
public boolean deleteAlbum(Album album, Context context) {
return ContentHelper.deleteFilesInFolder(context, new File(album.getPath()));
}

Step 3

In the previous step, we were invoking a function deleteAlbum which would return a boolean variable denoting whether the album was deleted permanently or not. Therefore in this step, I’d be discussing the working of the deleteAlbum method. So the deleteAlbum method was being invoked passing in the album to delete and the context as the parameters. This method, in turn invokes the deleteFilesInFolder method of the ContentHelper class passing in the context and a File object created from the album path(to be deleted). This method returns a boolean variable indicating the success or failure of the operation and this variable is in turn returned by the deleteAlbum method. The code representing the function invoke has been provided above. The code snippet representing the deleteFilesInFolder method is provided below.

public static boolean deleteFilesInFolder(Context context, @NonNull final File folder) {
 boolean totalSuccess = true;

 String[] children = folder.list();
 if (children != null) {
    for (String child : children) {
       File file = new File(folder, child);
       if (!file.isDirectory()) {
          boolean success = deleteFile(context, file);
          if (!success) {
             Log.w(TAG, “Failed to delete file” + child);
             totalSuccess = false;
          }
       }
    }
 }
 return totalSuccess;
}

Now inside the deleteFilesInFolder method first all the contents of the file are stored in a String[] array child. Thereafter by iterating through the child array, every time a method deleteFile is invoked passing in an item from the child array(an image path) and the context as parameters, which returns a boolean variable indicating if the particular image content is deleted or not.

Now depending on which Android version the device is running, there are two methods used to delete the image file permanently from the storage.

If the device is running on version LOLLIPOP and above, the Android Storage Access Framework has been used to delete the image file, and if the device is running version KITKAT, the MediaStore content provider is used to delete the file from device’s storage. The code snippet implementing the deleteFile method is provided below.

public static boolean deleteFile(Context context, @NonNull final File file) {
 boolean success = file.delete();

 // Try with Storage Access Framework.
 if (!success && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    DocumentFile document = getDocumentFile(context, file, false, false);
    success = document != null && document.delete();
 }

 // Try the Kitkat workaround.
 if (!success && Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
    ContentResolver resolver = context.getContentResolver();

    try {
       Uri uri = null;//MediaStoreUtil.getUriFromFile(file.getAbsolutePath());
       if (uri != null) {
          resolver.delete(uri, null, null);
       }
       success = !file.exists();
    }
    catch (Exception e) {
       Log.e(TAG, “Error when deleting file “ + file.getAbsolutePath(), e);
       return false;
    }
 }

 if(success) scanFile(context, new String[]{ file.getPath() });
 return success;
}

This is how we achieved the functionality to delete album/albums from the storage in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

Resources

1.Android Developer Guide – https://developer.android.com/guide/topics/providers/document-provider#delete

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.MediaStore class – https://developer.android.com/reference/android/provider/MediaStore

4.Deleting files via Android media content provider – https://stackoverflow.com/questions/10925196/deleting-files-via-a-contentresolver-as-opposed-to-deleting-them-via-file-del

Continue Reading

Displaying all the images from storage at once in Phimpme Android Application

In the Phimpme Android application, the images are displayed in the albums in which they are indexed in the device’s storage. However, there is also an “All photos” section in the application where all the images present in the device’s storage are displayed at once irrespective of the folder they’re indexed in. So in this post, I will be discussing how we achieved the “All Photos”  functionality.

Android framework provides developers with a media content provider class called MediaStore. It contains metadata for all available media on both internal and external storage devices. With the help of particular methods we can obtain metadata for all the images stored in the device’s storage.

Step 1

So First we need to get the Uri from MediaStore content provider pointing to the media(here media refers to the photos stored on the device). This can be done by the following snippet of code.

Uri uri = android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

Step 2

Now retrieving a cursor object containing the data column for the image media is required to be performed. The data column will contain the path to the particular image files on the disk. This can be done by querying the MediaColumns table of the MediaStore class, which can be performed by the use of the content resolver query method. The mentioned functionality can be achieved by the following lines of code.

String[] projection = {MediaStore.MediaColumns.DATA};
Cursor cursor = activity.getContentResolver().query(uri, projection, null, null, null);

Step 3

In the final step, we would retrieve the path of all the images by iterating through the cursor object obtained in the previous step and keep adding those paths to an ArrayList<String>. Creating Media objects passing in the image path and concurrently adding those Media objects to an ArrayList<Media> to be done thereafter. Finally, the dataset(ArrayList<Media> in this case) containing Media objects for all the images is required to be passed to the Media Adapter in order to display all the photos in the UI. The code snippets used for the final steps are provided below.

while (cursor.moveToNext()) {
  absolutePathOfImage = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA));
  listOfAllImages.add(absolutePathOfImage);
}
ArrayList<Media> list = new ArrayList<>();

for (String path : listOfAllImages) {
  list.add(new Media(new File(path)));
}
return list;

This is how we achieved the functionality to display all the images from the storage on a single screen(at once) in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository listed in the resource section below.

The screenshot for displaying the “All photos” section is provided below.

Resources

1.Android Developer Guide – https://developer.android.com/reference/android/provider/MediaStore

2.Github-Phimpme Android Repository – https://github.com/fossasia/phimpme-android/

3.Displaying all the images from gallery in android – https://deepshikhapuri.wordpress.com/2017/03/20/get-all-images-from-gallery-in-android-programmatically/

Continue Reading
Close Menu