Implementing Custom Rotary Knobs and Circular Positioning in the Multimeter in the PSLab Android App

In my previous blog [2I have discussed about how to implement  normal rotary knob using an open source library, this blog will be about the new user interface (UI) of multimeter in the PSLab Android app, how a custom rotary knob is implemented in it and how how the text views are positioned circular in them.

Implementation of Custom Rotary Knob

In the PSLab device  the rotary knob is implemented using the BeppiMenozzi Knob library[1] as by doing this we don’t have to manually create the extra class for the knob and we don’t have to write the code from scratch.

Figure 1: A basic rotary knob

Figure 1 shows a basic knob implemented using the BeppiMenozzi library whereas figure 2 shows the implementation of a custom knob using the basic knob.

Figure 2: A custom Knob

Steps of making a Custom-Knob using a simple Knob

  1. Implement the the basic knob using the steps given in my previous knobs explained in my previous blogs.
  2. Download the images of the knob which has to be implemented.
android:layout_weight="1"
android:rotation="15"
app:kDefaultState="2"
app:kIndicatorWidth="@dimen/multimeter_length_0"
app:kKnobCenterColor="@color/colorPrimaryDark"
app:kKnobColor="@color/white"
app:kKnobDrawable="@drawable/knob"
  1. Using the above code amend the knob as per the requirement. The advantage of using the beppiMonzi library is that the knob is fully amenable  , we can even define the minimum and maximum angle and many more stuffs can be done using the library.

                 

 

 

 

 

Figure 3: Showing the implementation of other custom knobs

The above figure shows the example of custom knobs implemented using the simple knob and by following the steps.

Implementation Circular positioning

One of the other major issues while making the new UI of the multimeter is the positioning of text-view around the circular knob. The issue was made overcome by implementing a circular positioning constraints in the text-views.

Steps of implementing circular positioning

  1. Use the constraint layout version 1.1.0 or above as the previous versions do not support the circular positioning feature.
  2. Add the circular constraint individually to every text-view.
app:layout_constraintCircle="@id/knobs"
app:layout_constraintCircleAngle="105"
app:layout_constraintCircleRadius="@dimen/multimeter_knobcircle_radius_1"

The above code snippets shows the addition od circular constraints added to a text-view. Using these constraint it decides positions the views relative to another views at a particular angle which thus makes up circular positioning.

Thus, this is how we can implement circular positioning in the views.

Resources

  1. BeppiMenozzi Knob Library
    https://github.com/BeppiMenozzi/Knob
  2. Rotary knob Blog
    https://docs.google.com/document/d/1IU_lpdt4sHI4euM543bBlHwYpv8vwwjxoB76sW1i5HA/edit?usp=sharing
Continue Reading

Measuring capacitor in PSLab and its Bugs

In this blog I will discuss about how we have measured capacitance in Pocket Science Lab and the  issues in capacitance measurement which was finally solved.

Measuring capacitance in PSLab device

To measure capacitance we need to go to the multimeter instrument from the instrument section of the PSLab

Figure 1.  Showing Multimeter Tile

Capacitance in PSLab is measured by keeping the capacitor or the element of which capacitance is to be measured between the CAP and ground pin.

 Figure 2.  Showing CAP pins in PSLab

For measuring capacitance in PSLab we use a specific method in which we supply a constant current to the CAP pin and thus we charge the capacitor to the maximum level, the math involved in it is as follow:-

We know that

Q{charge stored} = C*V

Also

Q= I * time

Where

I=current (constant)

Thus the capacitance

C = Q / V

Therefore

C = I*time / V (measured). – (1)

Therefore we know the current supplied, we know the voltage measured and we have also set the time to charge the capacitor and thus we get the capacitance from equation (1).

Code implementation for measuring capacitance

This is the primary code for getting the data for measuring capacitance in which we pass the current range and the current time through which the data gets fetched from the device which is then further processed in another function in which we finally get the capacitance.

public double[] getCapacitance(int currentRange, int chargeTime) { // time in uSec
        try {
            mPacketHandler.sendByte(mCommandsProto.COMMON);
            mPacketHandler.sendByte(mCommandsProto.GET_CAPACITANCE);
            mPacketHandler.sendByte(currentRange);
            mPacketHandler.sendInt(chargeTime);
            Thread.sleep((long)(chargeTime * 1e-6 + .02));
            int VCode;
            do VCode = mPacketHandler.getVoltageSummation();
            while (VCode == -1);
            double v = 3.3 * VCode / 4095;
            mPacketHandler.getAcknowledgement();
            double chargeCurrent = this.currents[currentRange];
            double c = 0;
            if (v != 0)
                c = (chargeCurrent * chargeTime * 1e-6 / v - this.SOCKET_CAPACITANCE) / this.currentScalars[currentRange];
            return new double[] {
                v,
                c
            };
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return null;

In the above function we can clearly see how we send the bytes in the device by the sendByte function through which various functions are sending current, setting voltage, setting current range etc are done in the device and then we can see how the voltage measured is taken using the getVoltageSummition method (of packet Handler class) , how we get the current and finally using them in equation (1) we get the capacitance of the element.

The following implementation is taken from the PSLab desktop app where the same method is used to measure capacitance.

Bugs in measuring capacitance

The capacitance measurement although was working in the desktop app but had bugs in the android app. It could never read the correct value also everytime gave a null value for capacitance.

Figure 3.  Showing null value for capacitance  PSLab

Solving the Bug [2]

After a deep research in the inside  the code of the capacitance measurement it was found that the error was caused while fetching the incorrect data from the device and processing it. The device gives a negative one value when there is any error in capacitance measurement  and that was being processed, thus the error was solved by introducing a do while loop as shown

 do VCode = mPacketHandler.getVoltageSummation();
 while (VCode == -1);

And thus now only the correct data fetched is processed further and thus the bug was solved after which the capacitance measurement was correct.

Resources

Continue Reading

Channel Communications Error of PSLab

The Pocket Science Lab multimeter has got three channels namely CH1,CH2 and CH3 with different ranges for measuring the voltages, but there was a channel communication error occuring at CH1 and CH2 pins of PSLab. This blogs will give a brief description of the channel communication error and how was it solved by me.

In the previous blog I have discussed about the channel communication of PSLab and how it works. In this blog i will be discussing about the channel communication error which was occuring while using CH1 and CH2 pins of PSLab android.

Communication between PSLab device and Android App

As discussed in the previous blog the communication between the PSLab and the android occurs with the help of the USBManger class of android.

One of the major function which makes it possible is the bulk transfer function of the android

amtWritten = mConnection.bulkTransfer(mWriteEndpoint, src, written, writeLength, timeoutMillis);

As shown in the above code, there is a timeout that some time required for this function to be executed, and otherwise this function will return a negative value which will mean that the communication is not successful.

Voltage Measuring Functions

The main function which gets called while pressing the button for measuring voltage is the getVoltage function which simultaneously calls the volmeterAutoRange function as well as the getAverage voltage function. The voltageAutoRnge function also calls the getAverage function inside of it.

public double getVoltage(String channelName, Integer sample) {
    this.voltmeterAutoRange(channelName);
    double Voltage = this.getAverageVoltage(channelName, sample);
    if (channelName.equals("CH3")) {
        return Voltage;
    } else {
        return 2 * Voltage;
    }
}

Calling both these functions simultaneously results in calling of the bulktranfer method

VoltmeterAutoRange function:-

private double voltmeterAutoRange(String channelName) {
    if (this.analogInputSources.get(channelName).gainPGA == 0)
        return 0;
    this.setGain(channelName, 0, true);
    double V = this.getAverageVoltage(channelName, null);
    return this.autoSelectRange(channelName, V);
}

The getAverage voltage function calls the getRawableVoltage function which thus calls the USBManger class functions of read and write, thus calling the bulkTranfer function.

Thus as the bulk transfer function is called simultaneously it caused problem in communication.

Solving the issue

The communication related  issues were finally solved when these bugs were spotted, the solution to this issue is that the voltageAutoRange function’s return value was never used in the codes and was thus not required.[2]The voltageAutoRange function was calling the getAverageVoltage function just to get a return value. Thus I formatted the function and now it looks like this-

private void voltmeterAutoRange(String channelName) {
    if (this.analogInputSources.get(channelName).gainPGA != 0) {
        this.setGain(channelName, 0, true);
    }
}

And thus finally the issue was solved and all things were working fine in channel communication.

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

Working with Shared Preferences in PSLab App

This blog demonstrates how to work with Shared Preferences in an Android app. The blog includes a detailed explanation about what are the methods available to store data in Android and where to use Shared Preferences (a type of data storage method) to save extra memory usage and work efficiently. After the detailed explanation is a step by step guide on how to use Shared Preferences in any Android app by taking an example of one used in PSLab Android app under PR #1236

What are methods available in Android for data storage ?

Android provides a variety of methods to store data some of which are

  1. Shared Preferences
  2. Internal Storage
  3. External Storage
  4. SQLite Database

A very brief description of the above four data storage method would be

  • Shared Preference – Used to store key-data pair for a given app
  • Internal Storage – Used to store any type of data such as pictures, videos, etc. which can be used only within the app
  • External Storage – Used to store any type of data such as audio, video, etc. which can be shared between different apps or different systems
  • SQLite database – It is also a type of Internal Storage method but with a different programming language in use which is SQL

Where to use different data storage methods?

Following are some of the  distinct cases where the above-mentioned data storing methods can be differentiated

  • Shared Preference – Shared Preference should be used when a very small amount of data i.e. key-value pair data is to be stored. An example of it would be storing the state of a widget when an app is closed and restoring the state when the app is opened again.
  • Internal Storage – Internal storage should be used while storing data such as text files, audio, video, photographs, etc. but occupying a very less device memory space. So, internal storage should be used when a limited amount of data needs to be stored for app execution.
  • External Storage – External storage should be used when data to be stored is very large and as a result, Internal storage can’t be used. External Storage can also write data on external memories like SD Card, etc.

How to use Shared Preferences in an Android app?

Following is a step by step guide on how Shared Preferences were used in PSLab Android app

  • First, declare a variable using final and static keyword so as to make its value permanent because it will be used to differentiate current activity/fragment data from other activity/fragment data in a common folder of Shared Preference.
private static final String FRAG_CONFIG = "LuxMeterConfig";
  • Now, we can make Shared Preferences for current activity/fragment by using the code:

When in Activity:

final SharedPreferences settings = getSharedPreferences(FRAG_CONFIG, Context.MODE_PRIVATE);

When in fragment:

final SharedPreferences settings = getActivity().getSharedPreferences(FRAG_CONFIG, Context.MODE_PRIVATE);

Here Context.MODE_PRIVATE is a context through which we define our Shared Preference i.e. for the current context it means that the above made Shared Preference can only be used inside the current activity/fragment. A detailed description of other modes available can be found in [1].

  • Now, Shared Preference for current activity/fragment is ready for use and so now, we can add as many numbers of the key-value pair as we want by using the following code
settings.getInt("HighValue", 2000);

Here, “HighValue” is the key whereas 2000 is the value. The above method is used to give a default value when a pair is created.

  • Now to edit the value of any before-made pair, we can use the Editor method available in the Shared Preference class to edit the default value.
SharedPreferences.Editor editor = settings.edit();
editor.putInt("HighValue", 5000);
editor.apply();

Here, the editor is an instance of edit() method available in Shared Preference class. After changing the default value of the key, we can use apply() method to apply the changes to the default key-value pair.

Where to find the Shared Preference folder on the target device?

To find the Shared Preference folder for any Android application, do the following steps:

  • Connect the target device (device on which app is installed) to the system running Android Studio.
  • Now click on the “Device File Explorer” button in Android Studio as shown in figure 1.

Figure 1. Device File Explorer button in Android Studio

  • Now after clicking the button, a list of folders would pop up as shown in figure 2.

Figure 2. Screenshot of Android Studio showing list of folders on the device

  • Now follow the given path, and you can see the desired folder
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml

So, in this way, the Shared Preferences can be used for data storage in any Android application.

Resources

  1. https://www.androidauthority.com/how-to-store-data-locally-in-android-app-717190/ – Documentation on different modes available to define the context of Shared Preference
  2. https://stackoverflow.com/questions/6146106/where-are-shared-preferences-stored – StackOverflow Q/A for where the Shared Preferences are stored on the target device
Continue Reading

Implementing Progress Bar in PSLab App

This blog is a step by step guide on how to make a functional progress bar in an Android app by taking an example of the progress bar implemented in PSLab Android application. The example is based on my work done in PSLab Android repository under PR #1077 and so it will only demonstrate making a simple progress bar (both linear and circular) and not the one showing progress in percentage too.

How progress bar was implemented in the PSLab app?

Both horizontal and circular progress bar is available in the Material Design Library provided by Google in Android Studio. So, no extra dependencies are needed.

Just drag and drop the progress bar of whichever shape necessary i.e. circular or horizontal, directly on the screen available in the Design tab as shown in Figure 1.

Figure 1. Design tab in Android Studio

There are two ways to use the progress bar available in the Material Design Library.

  • To show a loading screen
  • To show the progress of a process

Loading Screen

Loading Screens are used when the time that will be taken by the process is not known. So, an indeterminate circular progress bar is used to show that some process is going on and so the user needs to wait.

Layout

A circular progress bar is used for this process and so drag and drop the circular progress bar as shown in figure 1. Now set the position, height, and width of the progress bar in the layout as necessary. To set the color of the progress bar, use attribute android:indeterminateTint

Backend

To implement this type of functionality, use the setVisibility function to show the progress bar while some process is taking place in the backend and immediately remove it as soon as the result is ready to be displayed. To make the progress bar visible use progressBar.setVisibility(View.VISIBLE) and to make it invisible use progressBar.setVisibility(View.GONE)

Showing Progress

This is a very common type of process and is used by most of the apps. A horizontal progress bar is used to show the actual progress of the process taking place in the backend on a scale of 0-100 (the scale may vary) where 0 means the process hasn’t started and 100 means the result is now ready to be displayed.

Layout

Horizontal Progress bars are used for this type of usage. So, drag and drop the horizontal progress bar as shown in figure 1. Now set the position, height, and width of the progress bar in the layout as necessary. Different styles of the progress bar can be found in the documentation [1].

Backend

Initially, set the progress of the bar to 0 as no process is taking place by using method setProgress(). Now as soon as the process starts, to increase the progress by a fixed value, use progressBar.incrementProgressBy() method and to set the progress directly, use progressBar.setProgress() method.

So in this way, a progress bar can be implemented in an Android application. Other features like adding custom designs and animations can be done by making the necessary shapes and animations respectively and using the functions available in the documentation [1].

Resources

  1. https://developer.android.com/reference/android/widget/ProgressBar – Documentation of Progress Bar

 

Continue Reading
Close Menu