Integrating Google Calendar API in Open Event Web app

Open Event Web app allows organizers to add a feature which enables the users to add any session to their google calendar. The organizer is required to generate an API key and client ID on Google developers console and add the generated credentials to web app generator. The credentials are added to the generated application and every session is added with an Add to calendar button, which on click makes the request to add the corresponding session to the calendar.

Creating Client ID and API key

Enable the Google calendar API from Google developers console. Go to `Create Credentials` tab and generate an API key and client ID for your app. While creating the client ID, an input field is present which requires Authorised javascript origins, mention the domains where the generated application would be deployed.

Adding Client ID and API key to the generator

The Client ID and API key obtained from the developer console is added to the web app generator. The event generated uses these credentials to make a request to the server for adding any session to the calendar.

The added credentials are used to initialise the client in the procedure `initClient()` –

function initClient() {
 let id = document.getElementById('gcalendar-id').value;
 let key = document.getElementById('gcalendar-key').value;
 let CLIENT_ID = id;
 let API_KEY = key;
 let DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest"];
 let SCOPES = "https://www.googleapis.com/auth/calendar";

 gapi.client.init({
   apiKey: API_KEY,
   clientId: CLIENT_ID,
   discoveryDocs: DISCOVERY_DOCS,
   scope: SCOPES
 })
}

Adding session to the Google calendar

Every Google calendar enabled event is provided a button with every session, so that corresponding session can be added to the calendar. A procedure named `handleAuthClick` is called with the details of session being passed as parameter when the user clicks on the button. This function handles the authentication required for adding session to the calendar.

function handleAuthClick(title, location, calendarStart, calendarEnd, timezone, description) {
 let isSignedIn = gapi.auth2.getAuthInstance().isSignedIn.get();
 if (!isSignedIn) {
   gapi.auth2.getAuthInstance().signIn().then(function() {
     main.listUpcomingEvents(title, location, calendarStart, calendarEnd, timezone, description);
   });
 } else {
   main.listUpcomingEvents(title, location, calendarStart, calendarEnd, timezone, description);
 }
}

A function named `listUpcomingEvents` makes the request to insert the event object with details of the session to the calendar.

function listUpcomingEvents(title, location, calendarStart, calendarEnd, timezone, description) {
 let event = {
  ... // Event details  


  ... // Code for notifications
  ...
  ...
   'colorId': '5'
 };

 let request = gapi.client.calendar.events.insert({
   'calendarId': 'primary',
   'resource': event
 });
 request.execute(function(event) {
  // Success notification
 });
}

When the session with the corresponding data is added to calendar, an alert box notifying successful addition of session is shown up on the screen.

Resources

Continue Reading

Delete Image Permanently from Trashbin in Phimpme App

In the Phimpme Android application, users can perform various operations on the images including renaming an image, sharing images, deleting images from the storage etc. However, with the implementation of the Trash Bin feature in the app, the user is now provided with the option to restore back the deleted images. Whenever the delete operation is performed, the selected images are moved to the Trash Bin and the user has the option to either delete the photos permanently or restoring back the deleted photos by navigating to the Trash bin section. So in this post, I’d be discussing the implementation of permanently deleting image/images from the Trashbin.

Step 1

Firstly, we need to add permanent delete option in the popup menu provided in the itemview in the TrashView section. Every item in the Trashbin section displays a popup menu with two options-restore and delete permanently. The permanent delete option has been implemented in the itemview by adding the following lines of code.

<?xml version=“1.0” encoding=“utf-8”?>
<menu xmlns:android=“http://schemas.android.com/apk/res/android” xmlns:app=“http://schemas.android.com/apk/res-auto”>

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

Step 2

Now when the user opts to permanently delete any photo from the bin, a function deletePermanent would be invoked passing-in the trashbin object corresponding to the selected item as the parameter. Inside the deletePermanent method, a check is performed to determine whether the corresponding image file exists or not using the .exists method of the File class and if the result is true the file is deleted permanently using the .delete method of the File class. The method deletePermanent returns a boolean value depending on whether the image file is deleted permanently from the storage or not. The code snippets used to implement the deletePermanent method is provided below.

private boolean deletePermanent(TrashBinRealmModel trashBinRealmModel){
  boolean succ = false;
  String path = trashBinRealmModel.getTrashbinpath();
  File file = new File(Environment.getExternalStorageDirectory() + “/” + “.nomedia”);
  //File file = new File(Environment.getExternalStorageDirectory() + “/” + “TrashBin”);
  if(file.exists()){
      File file1 = new File(path);
      if(file1.exists()){
          succ = file1.delete();
      }
  }
  return succ;
}

Step 3

The function deletePermanent used implemented in the previous step returns a boolean value which will be used in this step. So if the deletePermanent method returns true indicating that the image has been deleted from the trashbin, first a method deleteFromRealm is invoked passing-in the path of the image in the Trashbin to delete the corresponding image’s record from the realm database. Thereafter, that particular trashbin item is removed from the ArrayList<TrashbinRealmModel> populating the TrashBin adapter to display the items in the TrashBin Activity and the adapter is notified of this change by the use of NotifyItemRemoved and NotifyItemRangeChanged methods of the RecyclerView adapter class passing-in the position of the item as parameter to the former and position along with the size of the updated list as parameters to the latter function. After the adapter is updated about the change in the dataset, the adapter re-populates the recyclerview thus displaying the remaining items in the trashbin section. The code snippets implementing the above-mentioned operations are provided below.

if(deletePermanent(trashBinRealmModel)){
  deleteFromRealm(trashItemsList.get(position).getTrashbinpath());
  trashItemsList.remove(position);
  notifyItemRemoved(position);
  notifyItemRangeChanged(position, trashItemsList.size());
}
private void deleteFromRealm(final String path){
  Realm realm = Realm.getDefaultInstance();
  realm.executeTransaction(new Realm.Transaction() {
      @Override public void execute(Realm realm) {
          RealmResults<TrashBinRealmModel> trashBinRealmModels = realm.where(TrashBinRealmModel.class).equalTo
                  (“trashbinpath”, path).findAll();
          trashBinRealmModels.deleteAllFromRealm();
      }
  });
}

This is how we have implemented the functionality to permanently delete an image from the Trashbin section 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. Realm database operations Android – https://www.androidhive.info/2016/05/android-working-with-realm-database-replacing-sqlite-core-data/
Continue Reading

Compressing Images in Resolution 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 compress an image. Two modes of compress operation have been implemented namely compress by size and compress by dimensions. In the previous blog post I discussed the implementation of compress by size operation. So in this blog post, I will be discussing how we achieved the functionality to compress any image by resolution.

Step 1

First, we need to add an option in the bottombar menu(in the SingleMediaActivity) to compress the image being viewed. The option to compress an image has been added by implementing the following lines of code in the  menu_bottom_view_pager.xml file.

<item
      android:id=“@+id/action_compress”
      android:orderInCategory=“2”
      app:showAsAction=“always”
      android:icon=“@drawable/compressicn”
      android:title=“Compress”/>

Step 2

Now on selecting the compress option, the user would be prompted to choose the compress mode i.e compress by size or compress by dimension. Once the user opts for compressing the image by resolution, a dialog containing the various resolutions options to choose from is to be displayed. Code snippets used to implement the dialog along with seekbar is displayed below.

<android.support.v7.widget.CardView
  xmlns:android=“http://schemas.android.com/apk/res/android”
  xmlns:app=“http://schemas.android.com/apk/vn.mbm.phimp.me”
  android:layout_width=“match_parent”
  android:layout_height=“wrap_content”
  app:cardCornerRadius=“2dp”
  android:id=“@+id/dialog_chose_provider_title”>
  <LinearLayout
      android:layout_width=“match_parent”
      android:layout_height=“wrap_content”
      android:orientation=“vertical”>

      <TextView
          android:id=“@+id/compress_title”
          android:layout_width=“match_parent”
          android:layout_height=“wrap_content”
          android:padding=“24dp”
          android:text=“@string/compress_by_dimension”
          android:textColor=“@color/md_dark_primary_text”
          android:textSize=“18sp”
          android:textStyle=“bold” />

      <ListView
          android:id=“@+id/listview”
          android:layout_width=“match_parent”
          android:layout_height=“321dp”
          android:layout_alignParentStart=“true”
          android:layout_centerVertical=“true”/>

  </LinearLayout>
</android.support.v7.widget.CardView>

A screenshot displaying the resolutions option dialog is provided below.

Step 3

Now in this final step as mentioned for the compress by size post, we would use the image compression library Compressor to obtain the compress by resolution functionality. After the user has specified the new resolution required, the Compressor class of the library is instantiated by passing-in the context as the parameter and some of its functions are invoked simaltaneously. The functions invoked are setMaxWidth(), setMaxHeight(),  setCompressFormat(), setDestinationDirectoryPath(), compressToFile().

setMaxWidth() – to set the width of the output image.

setMaxHeight() – to set the height of the output image.

setCompressFormat(Bitmap.CompressFormat) – to determine the format of the output compressed image.

setDestinationDirectoryPath(File) – to specify the path to which the compressed image is to be saved.

compressToFile(File) – to perform the compress operation, passing-in the file object of the corresponding image to be compressed.

The compressToFile() function performs the final compress operation and saves the compressed image to the specified path.

Code snippets to implement the above mentioned operations are given below

private void compressDim() {
  ListCompressAdapter lviewAdapter;
  ArrayList<String> compress_option= new ArrayList<String>();
  MediaDetailsMap<String,String> mediaDetailsMap = SingleMediaActivity.mediacompress.getMainDetails(this);
  //gives in the form like 1632×1224 (2.0 MP) , getting width and height of it
  String dim[]=mediaDetailsMap.get(“Resolution”).split(“x”);
  int  width= Integer.parseInt(dim[0].replaceAll(” “,“”));
  String ht[]=dim[1].split(” “);
  int height= Integer.parseInt(ht[0]);
  LayoutInflater inflater = getLayoutInflater();
  final View dialogLayout = inflater.inflate(R.layout.dialog_compresspixel, null);
  TextView title = (TextView) dialogLayout.findViewById(R.id.compress_title);
  title.setBackgroundColor(getPrimaryColor());
  //create options of compress in dimensions in multiple of 2
  int awidth=width;
  int aheight=height;
  ListView listView = (ListView)dialogLayout.findViewById(R.id.listview);
  while ((width%2==0)&&(height%2==0)) {
      compress_option.add(width + ” X “ + height);
      width=width/2;
      height=height/2;
  }

  lviewAdapter = new ListCompressAdapter(this, compress_option);
  listView.setAdapter(lviewAdapter);
  final int finalWidth = awidth;
  final int finalHeight = aheight;
  listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
      @Override
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
          if (position==0){
              cwidth[0] = finalWidth ;
              cheight[0] = finalHeight;}
          else{
              cwidth[0] = finalWidth /(position*2);
              cheight[0] = finalHeight /(position*2);}
          view.setBackgroundColor(R.color.md_light_blue_A400);
          new SaveCompressedImage().execute(“Resolution”);
          finish();
      }
  });
new Compressor(getApplicationContext())
      .setMaxWidth(cwidth[0])
      .setMaxHeight(cheight[0])
      .setCompressFormat(Bitmap.CompressFormat.JPEG)
      .setDestinationDirectoryPath( FileUtilsCompress.createFolders().getPath())
      .compressToFile(new File(saveFilePath));

This is how we have implemented the functionality to compress an image by size 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. Compressor Library – https://github.com/zetbaitsu/Compressor

 

Continue Reading

Compress Images Size 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 compress an image. Two modes of compress operation have been implemented namely compress by size and compress by dimensions. So in this blog post, I will be discussing how we achieved the functionality to compress any image by size.

Step 1

First, we need to add an option in the bottom bar menu(in the SingleMediaActivity) to compress the image being viewed. The option to compress an image has been added by implementing the following lines of code in the  menu_bottom_view_pager.xml file.

<item
      android:id=“@+id/action_compress”
      android:orderInCategory=“2”
      app:showAsAction=“always”
      android:icon=“@drawable/compressicn”
      android:title=“Compress”/>

Step 2

Now on selecting the compress option, the user would be prompted to choose the compress mode i.e compress by size or compress by dimension. Once the user has opted for the size mode a dialog with a seekbar to set the compress percentage is to be displayed. Code snippets used to implement the dialog along with seekbar is displayed below.

<RelativeLayout
  android:layout_width=“wrap_content”
  android:layout_height=“wrap_content”
  android:layout_marginTop=“8dp”>

  <SeekBar
      android:id=“@+id/seekBar”
      android:layout_width=“match_parent”
      android:layout_height=“wrap_content”
      android:layout_weight=“0.65”
      android:max=“90”
      android:progressBackgroundTint=“@color/colorPrimary”
      android:thumb=“@drawable/ic_location_on_black_24dp” />

  <TextView
      android:id=“@+id/textview2”
      android:layout_width=“wrap_content”
      android:layout_height=“wrap_content”
      android:layout_alignParentEnd=“true”
      android:layout_below=“@+id/seekBar”
      android:layout_marginBottom=“5dp”
      android:layout_marginEnd=“16dp”
      android:text=“95%”
      android:textAppearance=“@style/TextAppearance.AppCompat.Body1”
      android:textSize=“16sp” />
</RelativeLayout>

A screenshot displaying the dialog to set the compress percentage is provided below

Step 3

Here to obtain the compress functionality we have used an open-sourced custom image compression library Compressor. After the user has set the compress percentage, the Compressor class of the library is instantiated by passing-in the context as the parameter and some of its functions are invoked simultaneously. The functions invoked are setQuality(), setCompressFormat(), setDestinationDirectoryPath(), compressToFile().

setQuality(int) – to set the percentage compressed.

setCompressFormat(Bitmap.CompressFormat) – to determine the format of the output compressed image.

setDestinationDirectoryPath(File) – to specify the path to which the compressed image is to be saved.

compressToFile(File) – to perform the compress operation, passing-in the file object of the corresponding image to be compressed.

The compressToFile() function performs the final compress operation and saves the compressed image to the specified path.

Code snippets to implement the above-mentioned operations are given below

private void compressSize() {

  LayoutInflater inflater = getLayoutInflater();
  View dialogLayout = inflater.inflate(R.layout.dialog_compresssize, null);
  TextView title = (TextView) dialogLayout.findViewById(R.id.compress_title);
  title.setBackgroundColor(getPrimaryColor());
  SeekBar percentsize = (SeekBar) dialogLayout.findViewById(R.id.seekBar);
  percentsize.getThumb().setColorFilter(getAccentColor(), PorterDuff.Mode.SRC_IN);
  percentsize.setProgress(0);
  final TextView percent=(TextView)dialogLayout.findViewById(R.id.textview2);
  percentsize.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
      @Override
      public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
          //options of compress by size from 5% to 9;
          int progress1=95-progress;
          progress1=progress1-progress1%5;
          percent.setText(progress1+“%”);
          percentagecompress=progress1;}

      @Override
      public void onStartTrackingTouch(SeekBar seekBar) {
          //do nothing
      }

      @Override
      public void onStopTrackingTouch(SeekBar seekBar) {
          //do nothing
      }
  });}
new Compressor(getApplicationContext())
.setQuality(percentagecompress)
.setCompressFormat(Bitmap.CompressFormat.JPEG)
.setDestinationDirectoryPath(FileUtilsCompress.createFolders().getPath())
.compressToFile(new File(saveFilePath));

This is how we have implemented the functionality to compress an image by size in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository.

Resources

  1. Android Developer documentation –https://developer.android.com/reference/java/io/File
  2. Compressor Library – https://github.com/zetbaitsu/Compressor
  3. Seekbar Tutorial – https://abhiandroid.com/ui/seekbar
Continue Reading

Implementing Undo Operation in Phimpme Android application

In the Phimpme Android application, users can perform various operations on the images available including renaming an image, sharing images, deleting images from the storage etc. However, there might occur a scenario where the user accidentally or in a rush performs some action and would want to undo it the very next second, so in such cases the undo action feature comes in pretty handy. So in this blog post, I will be discussing the undo operation option implemented for the delete image feature in Phimpme Android app.

Step 1

Firstly, we need to add an “Undo” action button in the Snackbar that displays the success operation message. With this addition, the user would have time to undo an operation until the snackbar message is being displayed on the screen. The undo action button can be added to the Snackbar by the use of the setAction method of the Snackbar class. The code snippets used to implement the button is provided below

Snackbar snackbar = SnackBarHandler.showWithBottomMargin2(mDrawerLayout, String.valueOf(no) + ” “ + getString(R.string
              .trashbin_move_onefile),
      navigationView.getHeight
              (), Snackbar.LENGTH_SHORT);
snackbar.setAction(“UNDO”, new View.OnClickListener() {
  @Override
  public void onClick(View view) {
      getAlbum().moveAllMedia(getApplicationContext(), getAlbum().getPath(), media1);
      refreshListener.onRefresh();
  }
});
snackbar.show();

Step 2

When the user performs the delete operation, first the photos are moved to the trashbin and the user have the option to delete them from there. Now, if the user opts for undo operation, on clicking the undo button the function moveallMedia will be invoked passing-in context, the path of the current album and an ArrayList<Media> containing the deleted media on which the undo operation is to be performed upon, as parameters. Here the ArrayList<Media> passed as a parameter contains the trashbin paths of the images deleted by the previous action. A method storeDeletedFilesTemporarily has been used that returns the list containing the deleted images trashbin paths. The implementation of the method storeDeletedFilesTemporarily has been provided below.

private ArrayList<Media> storeDeletedFilesTemporarily(){
ArrayList<Media> deletedImages = new ArrayList<>();
if(!all_photos && !fav_photos && editMode){
for(Media m: getAlbum().getSelectedMedia()){
String name = m.getPath().substring(m.getPath().lastIndexOf(“/”) + 1);
deletedImages.add(new Media(Environment.getExternalStorageDirectory() + “/” + “.nomedia” + “/” + name));
}
} else if(all_photos && !fav_photos && editMode){
for(Media m: selectedMedias){
String name = m.getPath().substring(m.getPath().lastIndexOf(“/”) + 1);
deletedImages.add(new Media(Environment.getExternalStorageDirectory() + “/” + “.nomedia” + “/” + name));
}
}
return deletedImages;
}

Step 3

Now in the final step, I’d discuss the implementation of the moveAllMedia method that actually completes the undo operation. As I mentioned earlier that on performing the delete operation the images are moved to the bin folder(which is hidden from the other applications), so the moveAllMedia method here will again move the images from the trashbin folder to the current folder. With this operation deleted images would be stored back to their original folder and their trashbin record would be simultaneously deleted from the Realm database. The code implementation of the moveAllMedia method is provided below.

public int moveAllMedia(Context context, String targetDir, ArrayList<Media> albummedia){
 int n = 0;
 try
 {
    int index=-1;
    for(int i =0;i<albummedia.size(); i++)
    {
       String s = albummedia.get(i).getPath();
       int indexOfLastSlash = s.lastIndexOf(“/”);
       String fileName = s.substring(indexOfLastSlash + 1);

       if(!albummedia.get(i).getPath().equals(targetDir+“/”+fileName)){
          index=-1;
       }else{
          index=i;
          break;

       }
    }
    if(index!=-1)
    {
       n = –1;
    }else{
       for (int i = 0; i < albummedia.size(); i++) {

          if (moveMedia(context, albummedia.get(i).getPath(), targetDir)) {
             String from = albummedia.get(i).getPath();
             scanFile(context, new String[]{ from, StringUtils.getPhotoPathMoved(albummedia.get(i).getPath(),
                   targetDir) },
                   new MediaScannerConnection.OnScanCompletedListener() {
                      @Override
                      public void onScanCompleted(String s, Uri uri) {
                         Log.d(“scanFile”, “onScanCompleted: “ + s);
                      }
                   });
             media.remove(albummedia.get(i));
             n++;
          }
       }
       setCount(media.size());
    }
 } catch (Exception e) { e.printStackTrace(); }
 return n;

}

This is how we have implemented the functionality to perform Undo operation in the Phimpme Android application. To get the full source code, please refer to the Phimpme Android Github repository.

Resources

  1. Android Developer documentation –https://developer.android.com/reference/java/io/File
  2. MediaScanner Tutorial – https://stackoverflow.com/questions/9414955/trigger-mediascanner-on-specific-path-folder-how-to/25086535

 

Continue Reading

Option to Restore Deleted Images in Phimpme Android Application

In the Phimpme Android application, users can perform various operations on the images including renaming an image, sharing images, deleting images from the storage etc. However with the implementation of Trash Bin feature in the app, the user is now provided with the option to restore back the deleted images. Whenever the delete operation is performed, the selected images are moved to the Trash Bin and the user has the option to either delete the photos permanently or restoring back the deleted photos by navigating to the Trash bin section. So, in this post I’d be discussing the implementation of restore image/images functionality.

Step 1

Firstly, we need to add restore option in the popup menu provided in the itemview in the TrashView section. Every item in the Trashbin section displays a popup menu with two options-restore and delete permanently. The popup menu along with the two options have been implemented by adding the following lines of code.

<?xml version=“1.0” encoding=“utf-8”?>
<menu xmlns:android=“http://schemas.android.com/apk/res/android” xmlns:app=“http://schemas.android.com/apk/res-auto”>

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

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

Step 2

Now after the user opts to restore a particular image, the corresponding realm object for the image would be retrieved from the Realm database. This realm object would contain attributes namely trashbinpath, oldpath, datetime, timeperiod. Here the oldpath attribute contains the path where it  was stored in the device storage before the delete operation was performed on the image. Thereafter to restore the image to its old path, a move operation would be performed which would move the image from the TrashBin section to the album it was stored in before deletion. With this move operation, the image is restored to its old path and is visible in the respective album resulting in the removal/deletion of the image from the TrashBin section. The method implemented to perform the above mentioned operations is provided below.

private boolean restoreImage(TrashBinRealmModel trashBinRealmModel){
  String oldpath = trashBinRealmModel.getOldpath();
  boolean success = false;
  try {
      String from = trashBinRealmModel.getTrashbinpath();
      if (success = moveMedia(context, from, targetDir)) {
          scanFile(context, new String[]{ from, StringUtils.getPhotoPathMoved(pathofmed, targetDir) });
      }
  } catch (Exception e) { e.printStackTrace(); }
  return success;
}

Step 3

In the previous step, the move operation for the restore feature was performed by the restoreImage method which inturn uses method moveMedia() to get the job done. So in this step, I’d be discussing about the implementation of the moveMedia() method. Inside the moveMedia() method, moveFile method of ContentHelper class is invoked passing-in context, sourcefile, and the destination folder as the parameters. Thereafter a normal rename operation is performed by the use of the renameTo method to move the file to destination folder. Now if the rename operation is performed successfully, the file is moved to its old path whereas if the rename operation fails to get the job done, a copy operation is initiated to copy the file to the destination folder and the image  the is deleted from the trashbin section if the copy operation is successful. The code snippets used to implement the moveMedia and moveFile are provided below.

private boolean moveMedia(Context context, String source, String targetDir) {
 File from = new File(source);
 File to = new File(targetDir);
 return ContentHelper.moveFile(context, from, to);
}
public static boolean moveFile(Context context, @NonNull final File source, @NonNull final File targetDir) {
 // First try the normal rename.
 File target = new File(targetDir, source.getName());

 boolean success = source.renameTo(target);

 if (!success) {
    success = copyFile(context, source, targetDir);
    if (success) {
       success = deleteFile(context, source);
    }
 }
 //if (success) scanFile(context, new String[]{ source.getPath(), target.getPath() });
 return success;

}

This is how we have implemented the functionality to restore a deleted 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

Move Photos from one Path to Another in Phimpme Android Application

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

Step 1

First, we need to add an option in the overflow menu to move any no of images from one folder to another. The option to move an image has been added by implementing the following lines of code in the  menu_view_pager.xml file.

<item
  android:id=“@+id/action_move”
  app:showAsAction=“never”
  android:title=“Move”/>

Step 2

Now after the user opts for the move operation, a bottomSheetDialog containing all the albums/folders in the storage to choose for moving the photos into will be displayed. Once the user selects the required location to move the photos to, a method moveSelectedMedia would be invoked passing-in the context and the destination path as the parameters. Thereafter inside the method moveSelectedMedia, first a check would be performed to determine whether the selected photos(to move) are already present in the folder(path folder and destination folder both are same), if true the operation would be terminated as the photos are already present in the destination folder, and if false the selected photos would be moved to the destination folder one by one by the use of another method moveMedia. The code snippets used to implement the moveSelectedMedia is provided below.

public int moveSelectedMedia(Context context, String targetDir) {
 int n = 0;
 try
 {
    int index=-1;
    for(int i =0;i<selectedMedias.size();i++)
    {
       String s = selectedMedias.get(i).getPath();
       int indexOfLastSlash = s.lastIndexOf(“/”);
       String fileName = s.substring(indexOfLastSlash + 1);

       if(!selectedMedias.get(i).getPath().equals(targetDir+“/”+fileName)){
          index=-1;
       }else{
          index=i;
          break;

       }
    }
    if(index!=-1)
    {
       n = –1;
    }else{
       for (int i = 0; i < selectedMedias.size(); i++) {

          if (moveMedia(context, selectedMedias.get(i).getPath(), targetDir)) {
             String from = selectedMedias.get(i).getPath();
             scanFile(context, new String[]{ from, StringUtils.getPhotoPathMoved(selectedMedias.get(i).getPath(), targetDir) },
                   new MediaScannerConnection.OnScanCompletedListener() {
                      @Override
                      public void onScanCompleted(String s, Uri uri) {
                         Log.d(“scanFile”, “onScanCompleted: “ + s);
                      }
                   });
             media.remove(selectedMedias.get(i));
             n++;
          }
       }
       setCount(media.size());
    }
 } catch (Exception e) { e.printStackTrace(); }
 return n;
}

Step 3

In this step, I’d discuss the use and implementation of the moveMedia method mentioned in the earlier step. So the moveMedia is used to move a single photo to a particular destination, taking-in the context, the oldpath of the photo and the destination folder as parameters. Inside this method another method moveFile of the ContentHelper class is invoked passing-in the context, File object of the photo and File object of the destination folder as parameters. Now inside the moveFile method the actual move operation takes place. First the photo is tried to move by doing a normal rename operation by using the renameTo function of the File class. The success of the rename operation updates the path of the photo to the destination path and deletes the original path of the photo whereas if the normal rename operation fails, then the photo is moved by first performing a copy operation to copy the photo to the destination folder and then deleting the original photo. The code snippets used to implement the moveMedia and moveFile functions are provided below.

private boolean moveMedia(Context context, String source, String targetDir) {
 File from = new File(source);
 File to = new File(targetDir);
 return ContentHelper.moveFile(context, from, to);
}
public static boolean moveFile(Context context, @NonNull final File source, @NonNull final File targetDir) {
 // First try the normal rename.
 File target = new File(targetDir, source.getName());

 boolean success = source.renameTo(target);

 if (!success) {
    success = copyFile(context, source, targetDir);
    if (success) {
       success = deleteFile(context, source);
    }
 }

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

And at last, after the photos are moved successfully the media adapter is called with the updated photos list to display thus displaying the remaining photos in the folder.

This is how we have implemented the functionality to move images from one path to another 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

Implementing Trash Bin in Phimpme Android Application

In the Phimpme Android application, users can perform various operations/actions on the photos available, some of the important operations involve renaming an image, sharing an image, deleting image etc. However, when the user performs delete operation for image/images the images are permanently deleted from the storage and there is no option to restore/recover the deleted image. Now imagine a situation where the user accidentally or in a rush deletes photo/photos, and now there is no possible way for the user to recover those accidentally deleted photos, so in such circumstances, the Trash Bin feature could prove to be a breather for the user. So, in this blog post, I will be discussing the implementation of the Trash Bin feature.

Step 1

Firstly, we need to implement the functionality to move the images to bin folder whenever the delete operation is performed. For this, we’d programmatically create a folder naming it .nomedia, so that this folder is not picked up by any other similar application while scanning folders. Now when the user deletes images, a check would be performed first to determine whether the bin folder already exists or not and actions would be performed accordingly(a folder would be created if it doesn’t already exist and the selected photos for deletion would be moved to the bin folder or the deleted photos would be moved directly to the bin folder if its there). Code snippets used to implement the bin folder is provided below.

private boolean addToTrash(){
  int no = 0;
  boolean succ = false;
  File file = new File(Environment.getExternalStorageDirectory() + “/” + “.nomedia”);
  if(file.exists() && file.isDirectory()){
      if(!all_photos && !fav_photos && editMode){
          no = getAlbum().moveSelectedMedia(getApplicationContext(), file.getAbsolutePath());
      }else if(all_photos && !fav_photos && editMode){
          no = getAlbum().moveAllMedia(getApplicationContext(), file.getAbsolutePath(), selectedMedias);
      }else if(!editMode && !all_photos && !fav_photos){
          no = getAlbum().moveAllMedia(getApplicationContext(), file.getAbsolutePath(), getAlbum().getMedia());
      }
  else{
      if(file.mkdir()){
          if(!all_photos && !fav_photos && editMode){
              no = getAlbum().moveSelectedMedia(getApplicationContext(), file.getAbsolutePath());
          }else if(all_photos && !fav_photos && editMode){
              no = getAlbum().moveAllMedia(getApplicationContext(), file.getAbsolutePath(), selectedMedias);
          }else if(!editMode && !all_photos && !fav_photos){
              no = getAlbum().moveAllMedia(getApplicationContext(), file.getAbsolutePath(), getAlbum().getMedia());
          }
         // no = getAlbum().moveSelectedMedia(getApplicationContext(), file.getAbsolutePath());
       }
 // clearSelectedPhotos();
  return succ;
}

Step 2

Now if all the photos(selected by the user for deletion) are successfully moved to the bin folder, realm objects corresponding to those image files would be created and added to the realm database. The corresponding Realm object would consist of attributes namely oldpath, trashbinpath, time of delete and duration. The realm objects would be used at the time of implementing restore functionality for the trash bin images. The realm model class representing the Trash bin object is provided below.

public class TrashBinRealmModel extends RealmObject {

  @PrimaryKey
  private String trashbinpath;
  private String oldpath;
  private String datetime;
  private String timeperiod;

  public TrashBinRealmModel(){

  }

  public TrashBinRealmModel(String oldpath, String newpath, String datetime, String timeperiod){
      this.oldpath = oldpath;
      this.trashbinpath = newpath;
      this.datetime = datetime;
      this.timeperiod = timeperiod;
  }

  public void setTrashbinpath(String trashbinpath){
      this.trashbinpath = trashbinpath;
  }

  public String getTrashbinpath(){
      return trashbinpath;
  }
  public void setDatetime(String datetime){
      this.datetime = datetime;
  }

  public String getDatetime(){
      return datetime;
  }

  public void setOldpath(String oldpath){
      this.oldpath = oldpath;
  }

  public String getOldpath(){
      return oldpath;
  }

  public void setTimeperiod(String timeperiod){
      this.timeperiod = timeperiod;
  }

  public String getTimeperiod(){
      return timeperiod;
  }
}

The implementation for the function to add trash bin objects to Realm database is provided below.

for(int i = 0; i < media.size(); i++){
  int index = media.get(i).getPath().lastIndexOf(“/”);
  String name = media.get(i).getPath().substring(index + 1);
  realm.beginTransaction();
  String trashpath = trashbinpath + “/” + name;
  TrashBinRealmModel trashBinRealmModel =   realm.createObject(TrashBinRealmModel.class, trashpath);
  trashBinRealmModel.setOldpath(media.get(i).getPath());
  trashBinRealmModel.setDatetime(new SimpleDateFormat(“dd/MM/yyyy HH:mm:ss”).format(new Date()));
  trashBinRealmModel.setTimeperiod(“null”);
  realm.commitTransaction();
}

Step 3

Now we need to provide an option to the user to navigate to the Trash bin section to perform the delete(permanently) or restore option. So for the aforementioned action, we’d be adding an option in the navigation drawer menu titled “Trash Bin” clicking on which the Trash Bin section would be displayed to the user. The XML code changes committed to implementing the option has been provided below.

<LinearLayout xmlns:android=“http://schemas.android.com/apk/res/android”
            android:id=“@+id/ll_drawer_trashbin”
            android:layout_width=“match_parent”
            android:layout_height=“wrap_content”
            android:background=“@drawable/ripple”
            android:clickable=“true”
            android:orientation=“horizontal”>

  <com.mikepenz.iconics.view.IconicsImageView
      android:id=“@+id/Drawer_trash_Icon”
      android:layout_width=“@dimen/icon_width_height”
      android:layout_height=“@dimen/icon_width_height”
      android:layout_gravity=“center_vertical”
      android:layout_marginLeft=“@dimen/big_spacing”
      android:layout_marginRight=“@dimen/big_spacing”
      app:iiv_icon=“gmd-file-upload”/>

  <TextView
      android:id=“@+id/Drawer_trash_Item”
      android:layout_width=“wrap_content”
      android:layout_height=“wrap_content”
      android:paddingBottom=“16dp”
      android:paddingTop=“16dp”
      android:text=“Trash Bin”
      android:textColor=“@color/md_dark_background”
      android:textSize=“16sp”/>
</LinearLayout>

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. Realm for Android tutorial –https://www.androidhive.info/2016/05/android-working-with-realm-database-replacing-sqlite-core-data/

4. Hiding directories using .nomedia file –http://www.easycodeway.com/2016/08/hide-files-in-android-using-nomedia-file.html

Continue Reading

Option to pin albums in Phimpme Android Application

In the Phimpme Android application, users are provided with options to perform various operations on the albums available such as move, creating a zip file of the album, rename an album and many more. However, one another useful functionality that has been added to the Phimpme Android application is the option to pin albums. So in this post, I will be discussing the implementation of the pin to top functionality.

Step 1

First, we need to add an option in the overflow menu to pin the album which has been selected, to the top. The pin to top option can be added in the overflow menu by implementing the following lines of code in the menu_albums.xml file(This file contains the overflow menu options for albums).

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

Step 2

Now when the user selects the option to pin any album to the top, the user’s choice of the selected album is retrieved by the getSelectedAlbum method of Handling Albums class. Thereafter the togglePin method of class Album Settings is invoked, passing in the context as a parameter. In the togglePin method an instance of the CustomHelper class is obtained first, then its pinAlbum method is called passing in the album path and a variable pinned as the parameters. Here depending on the boolean value of the variable pinned, the selected album will be pinned or unpinned. The code snippets used to implement getSelectedAlbum and togglePin method are provided below.

public Album getSelectedAlbum(int index) {
return selectedAlbums.get(index);
}
public void togglePin(Context context) {
this.pinned = !pinned;
CustomAlbumsHelper h = CustomAlbumsHelper.getInstance(context);
h.pinAlbum(path, pinned);
}

The getSelectedAlbum method and the togglePin method are subsequently used by the following line of code.

getAlbums().getSelectedAlbum(0).settings.togglePin(getApplicationContext());

Step 3

In this step, I’d discuss the implementation of the pinAlbum method invoked inside the togglePin method. In the pinAlbum method, first a writable instance of album_settings SQLite database is obtained. The album_settings database maintains information about the albums in a table with the name of the table Albums and the columns representing info about the album such as path, excluded, pinned, sortorder etc. Next, the checkAndCreateAlbum method would be invoked passing in the writable instance of the database and path of the album as parameters. Now inside the checkAndCreateAlbum method, a check would be performed to determine whether a column representing info about the selected album is already present or not, if not a row is created for the selected album. The code snippet used to implement the checkAndCreateAlbum is provided below.

private void checkAndCreateAlbum(SQLiteDatabase db, String path) {

  Cursor cursor = db.query(TABLE_ALBUMS, null,  ALBUM_PATH+“=?”,
          new String[]{ path }, null, null, null);

  if (cursor.getCount() == 0) {
      ContentValues values = new ContentValues();
      values.put(ALBUM_PATH, path);
      values.put(ALBUM_SORTING_MODE, SortingMode.DATE.getValue());
      values.put(ALBUM_SORTING_ORDER, SortingOrder.DESCENDING.getValue());
      values.put(ALBUM_EXCLUDED, 0);
      db.insert(TABLE_ALBUMS, null, values);
  }

  cursor.close();
}

Now in the pinAlbum method through the writable instance of the album_settings database, the pinned information of the selected album is updated in the Albums table accordingly. The code snippets used to implement the pinAlbum method is provided below.

void pinAlbum(String path, boolean status) {
  SQLiteDatabase db = this.getWritableDatabase();
  checkAndCreateAlbum(db, path);
  ContentValues values = new ContentValues();
  values.put(ALBUM_PINNED, status ? 1 : 0);
  db.update(TABLE_ALBUMS, values, ALBUM_PATH+“=?”, new String[]{ path });
  db.close();
}

At last, the notifyDataSetChanged method of the AlbumsAdapter class would be called so as to display the pinned albums at the top irrespective of the sorting order.

A screenshot displaying the pinned albums at the top in albums view is provided below.

This is how we have implemented the functionality to pin an album to top 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/android/database/sqlite/SQLiteDatabase

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

3. Sqlite database tutorial for android –https://www.androidhive.info/2011/11/android-sqlite-database-tutorial/.

Continue Reading

Adding a Countdown to Orders Page in Open Event Frontend

This blog post will illustrate how you can add a countdown to orders page which on finishing expires the ticket in Open Event. In Open Event we allow some predefined time for users to fill in their details and once the time expires order gets expired and tickets get released. Users can order the tickets again if they want.

We start by adding a createdAt field to orders model so that we can keep track of remaining time. To calculate the time when the order should expire we add predefined time in which user should fill their details to createdAt time. In this way, we get the time when the order will expire.

So now to calculate the remaining time we just subtract the expiring time from current time. And then we render this data into the template. We define getRemainingTime property in our template and fetch the data for that property with help of javascript.

To see the template code visit this link.

The challenge here is to update the time remaining after every second. For this, we take the help of ember runloop. The run.later() function of ember runloop helps us to calculate the property after every second and set it. Code for setting the remaining time with the help of javascript is given below.

// app/components/forms/orders/order-form.js

getRemainingTime: computed('data', function() {
    let willExpireAt = this.get('data.createdAt').add(10, 'minutes');
    this.timer(willExpireAt, this.get('data.identifier'));
  }),

  timer(willExpireAt, orderIdentifier) {
    run.later(() => {
      let currentTime = moment();
      let diff = moment.duration(willExpireAt.diff(currentTime));
      this.set('getRemainingTime', moment.utc(diff.asMilliseconds()).format('mm:ss'));
      if (diff > 0) {
        this.timer(willExpireAt, orderIdentifier);
      } else {
        this.get('data').reload();
        this.get('router').transitionTo('orders.expired', orderIdentifier);
      }
    }, 1000);
  }

 

As given in the code. We pass expiring time and order’s model instance to the timer function. Timer function calculates the remaining time and sets it to getRemainingTime property of template. Timer function runs after every second with the help of run.later() function of ember runloop. To format the remaining time into MM:SS we take help of moment.js library and format the data accordingly.

Once the remaining time is less than zero (time expires) we reload the model and transition current route to expired route. We do not have to set order status as expired inside the FE. Server sets the order as expired after the predefined time. So we just reload the model from the server and we get the updated status of the order.

Resources:
Continue Reading
Close Menu
%d bloggers like this: