Creating Activity for Visualizing Recorded Sensor Data from List Items

In previous blog Using RealmRecyclerView Adapter to Show Recorded Sensor Experiments[2], I  have created a DataLoggerActivity in PSLab Android app containing RecyclerView showing a list having all the recorded experiments where every list item shows the date, time and the sensor instrument used for recording the data, but there arises below questions:-

  • What if the user wants to see the actual recorded data in form of a graph?
  • How the user can see the location recorded along with the data on the map?
  • How can the user export that data?

There is no way I could show all of that information just on a list item so I created another activity called “SensorGraphViewActivity” the layout of that activity is shown in the figure below:

Figure 1 shows the layout of the Activity as produced in Android editor

The layout contains three views:-

  1. At the top there is graph view which I created using Android MP chart which will show the recorded data plotted on it forming the exact same curve that was created while recording it, this way it is useful to visualize the data and also there is also a play button on the top which simulates the data as it was being plotted on the graph in real time.
  2. In the middle, there is a Card view containing two rows which will simply show the date and time of recording.
  3. At the bottom, there is a Map view which shows the location of the user which would be fetched when the user recorded the data.

This is the gist of the layout file created for the activity.

But now the question arises:-

How to show the data in the activity for the item that the user wanted?

For that, I implemented click listener on every list item by simply adding it inside the onBindViewHolder() method

@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
   SensorLogged temp = getItem(position);
   holder.sensor.setText(temp.getSensor());
   Date date = new Date(temp.getDateTimeStart());
   holder.dateTime.setText(String.valueOf(sdf.format(date)));
   holder.cardView.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View view) {
                    ...
       });
}

and inside the click listener I performed following three steps:-

  1. First I stored the position of the item clicked inside a variable.

    int positionVar = holder.getAdapterPosition();
  2. Then I used that position from the variable to fetch related data from the Realm database by simply using getItem() method which returns the SensorLogged[1] RealmObject at that position as I used a special type of RecyclerView Adapter called as RealmRecyclerViewAdapter[2].

    int positionVar = holder.getAdapterPosition();
  3. Then I created an Intent to open the SensorGraphViewActivity and passed the related data (i.e., sensortype, foreignkey, latitude, longitude, timezone, date, time) from SensorLogged[1] object to activity in form of extras.
    Intent intent = new Intent(context, SensorGraphViewActivity.class);
    intent.putExtra(SensorGraphViewActivity.TYPE_SENSOR, item.getSensor());
    intent.putExtra(SensorGraphViewActivity.DATA_FOREIGN_KEY, item.getUniqueRef());
    intent.putExtra(SensorGraphViewActivity.DATE_TIME_START,item.getDateTimeStart());
    intent.putExtra(SensorGraphViewActivity.DATE_TIME_END,item.getDateTimeEnd());
    intent.putExtra(SensorGraphViewActivity.TIME_ZONE,item.getTimeZone());
    intent.putExtra(SensorGraphViewActivity.LATITUDE,item.getLatitude());
    intent.putExtra(SensorGraphViewActivity.LONGITUDE,item.getLongitude());
    
    context.startActivity(intent);
    

And, in the SensorGraphViewActivity, I used getIntent() method to fetch all those extra data in the form of Bundle.
For showing the data in the graph I used the foreign key fetched from the intent and queried all the LuxData[1] RealmObject containing that foreignkey in the form of RealmResult<LuxData>[2] ArrayList and used that list to populate the graph.

Long foreignKey = intent.getLongExtra(DATA_FOREIGN_KEY, -1);
Realm realm = Realm.getDefaultInstance();
entries = new ArrayList<>();
RealmResults<LuxData> results = realm.where(LuxData.class).equalTo(DATA_FOREIGN_KEY, foreignKey).findAll();
for (LuxData item : results) {
   entries.add(new Entry((float) item.getTimeElapsed(), item.getLux()));
}

For the map, I fetched the latitude and longitude again from the intent and used the coordinates to show the location on the open street view map.

Thread thread = new Thread(new Runnable() {
   @Override
   public void run() {
       IMapController mapController = map.getController();
       mapController.setZoom((double) 9);
       GeoPoint startPoint = new GeoPoint(latitude, latitude);
       mapController.setCenter(startPoint);
   }
});

For map purposes, of course, I used a separate thread as it is a heavy and time-consuming process and it could lead the app to lag for a long time which could hamper the User Experience.

Thus after the data being plotted on the map and coordinated being plotted on the map, we can see the layout of the activity as shown in Figure 2.

Figure 2 shows the layout of the activity after being populated with data.

I also created the export button in the toolbar that will use the CSVLogger[3] class implemented inside the PSLab android app to export the data in the form of CSV file and save it in the external storage directory.

Resources

  1. Storing Recorded Sensor Data in Realm Database – My blog where I created the Realm Model classes to store recorded data.
  2. Using RealmRecyclerView Adapter to Show Recorded Sensor Experiments – My previous blog where I created the RecyclerView.
  3. Saving Sensor Data in CSV format – Blog by Padmal storing the data in CSV format.

Continue ReadingCreating Activity for Visualizing Recorded Sensor Data from List Items

How to pass data between fragments of an Activity in Android app

This blog demonstrates how to pass values of a variable between two fragments of a single activity. The blog will mainly include the demonstration of passing values between fragments while using BottomSheet Navigation as done in PSLab Android application.

This blog contains the work done by me in the Lux Meter instrument of the PSLab Android app of passing data from LuxMeterConfiguration fragment to LuxMeterData fragment as shown in the featured image to set the high limit for the pointer and to set the update period of the Lux Sensor. The blog will solve the difficult task of communication between two fragments of a single activity. For passing data between multiple fragments of different activities, refer to [1].

How to pass data between fragments?

In this blog, I will pass data from Fragment 2 to Fragment 1 only. But vice versa or passing data from both the fragments can also be made using the same given approach.

  • First, make a static method in Fragment 1 which can set the parameters i.e. the value of the variables as soon as the fragment is inflated as follow
public static void setParameters(int one, int two, int three) {
        Fragment1.firstValue = one;
        Fragment1.secondValue = two;
        Fragment1.thirdValue = three;
    }
  • Now, there is one point to mark that Fragment 1 will be inflated only when Fragment 2 gets destroyed. Else, other than default inflation of Fragment 1, there is no way Fragment 1 can be inflated after navigating to Fragment 2.
  • So, override the OnDestroy() method of Fragment 2 and use the setParameters() method to set the value of variables from Fragment 2 to be used in Fragment 1.
@Override
    public void onDestroyView() {
        super.onDestroyView();
        highValue = getValueFromText(highLimit, 0, highLimitMax);
        updatePeriodValue = getValueFromText(updatePeriod, updatePeriodMin, updatePeriodMax + 100);
        Fragment1.setParameters(selectedSensor, highValue, updatePeriodValue);
    }

Here, the highValue, updatePeriodValue and selectedSensor are the variables being used in the Lux Meter fragment in PSLab Android app. But they can be replaced by the necessary variables as per the app.

So, in this way, we can pass data between the fragments of the same Activity in an Android application. Above demonstration can be extended in passing values between multiple fragments of the same Activity by creating different methods in different fragments.

Resources

  1. Blog on how to pass data between fragments of different/same activities: https://www.journaldev.com/14207/android-passing-data-between-fragments

Continue ReadingHow to pass data between fragments of an Activity in Android app

Prevent Android Activity from Operating while using Bottom Sheet in PSLab App

This blog demonstrates how to prevent the Android Activity in the background from operating while the Bottom Sheet is up in the foreground. The demonstration will be purely from the work I have done under PR #1355 in PSLab Android repository.

Why prevent the Activity from operating?

When using Bottom Sheet in Android, it is preferable to dim the screen behind the Bottom Sheet to provide a good user experience. But the dimming of the screen is itself an indication that the screen won’t work. Also, if the Bottom Sheet is open and while sliding it, if, by mistake, any button in the background of the bottom sheet gets pressed, then if the function related to that button starts executing then it can create a bad user experience.

For example, in PSLab Android app, in Accelerometer instrument, there are record/pause and delete buttons in the toolbar as shown in figure 1. Now, if the bottom sheet is opened and while closing it if the delete button is by mistake pressed by the user, then whole recorded data gets deleted. Thus, it’s a good practice to prevent the background Activity from operating while Bottom Sheet is opened.

Figure 1. Accelerometer Instrument in PSLab Android app

How to prevent the Activity from operating?

In this demonstration, I will use the method followed by PSLab Android app in creating a Bottom Sheet and making the background dim using a View widget. A step by step guide on how to make a Bottom Sheet as in PSLab Android app can be found in [1] and [2].

Strategy

The strategy used in solving this problem is setting an OnClickListener to the View that is used to dim the background and close the Bottom Sheet (if open) and hide the View as soon as the method is called. The View is again made visible when an upward slide gesture is made to open the Bottom Sheet.

Follow the below steps to get the desired results:

  • First, in OnCreate() method, set the OnTouchListener to the view.
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
                              if(bottomSheetBehavior.getState()==BottomSheetBehavior.STATE_EXPANDED)
                    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
tvShadow.setVisibility(View.GONE);
      }
});
  • Now, override the OnSlide() method of the GestureDetector class and add the following code to it.
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
    Float value = (float) MathUtils.map((double) slideOffset, 0.0, 1.0, 0.0, 0.8);
    view.setVisibility(View.VISIBLE);
    view.setAlpha(value);
   }

So, now test the Bottom Sheet and you will find that the Bottom Sheet will get closed as soon as the click is made outside it if it is opened. The demonstration of the working of the above code is shown in figure 2.

Figure 2. Demonstration of preventing the background Activity from operating while Bottom Sheet is up

Resources

  1. http://thetechnocafe.com/make-bottom-sheet-android/: Blog on how to make a Bottom Sheet in Android

Continue ReadingPrevent Android Activity from Operating while using Bottom Sheet in PSLab App

How to use Mobile Sensors as Instruments in PSLab Android App

This blog demonstrates how to use built-in mobile sensors in an Android application. This blog will mainly feature my work done in PSLab Android repository of making a Compass and Accelerometer instrument using built-in mobile sensors.

How to access built-in mobile sensors?

Android provides an abstract class called SensorManager which is able to communicate with the hardware i.e. here the sensors in the mobile. But the SensorManager can’t provide continuous data fetched by the sensor. For this, Android provides an interface known as SensorEventListener which receives notifications from SensorManager whenever there is a new sensor data.

How to implement the functionality of sensors in Android app?

Following is a step by step process on how to add support for different sensors in an Android app

  • First, make a new class which extends SensorEventListener and override the default methods.
public class SensorActivity extends Activity implements SensorEventListener {

     public SensorActivity() {
        // Default Constructor      
     }

     @Override
     public void onAccuracyChanged(Sensor sensor, int accuracy) {
     }

     @Override
     public void onSensorChanged(SensorEvent event) {
     }
 }

Here, the SensorActivity() is the default constructor of the class and the onAccuracyChanged() and onSensorChanged() methods will be explained soon.

  • Now declare the SensorManager and use the sensor needed in the app.
private final SensorManager mSensorManager;
private final Sensor mAccelerometer;

     public SensorActivity() {
         mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
         mAccelerometer =        mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
     }

Here, I have used Sensor.TYPE_ACCELEROMETER to use the built-in Accelerometer in the device. Some of the other options available are:

  1. TYPE_LIGHT – To measure ambient light
  2. TYPE_MAGNETOMETER – To measure magnetic field along different axis
  3. TYPE_GYROSCOPE – To measure movements (sudden changes) in any particular direction

The list of all available sensors in Android can be found in [1].

  • It is necessary to disable the sensors especially when the activity is paused. Failing to do so can drain the battery in just a few hours.

NOTE: The system will not disable sensors automatically when the screen turns off.

So, to save the battery and make the app efficient, we can use the registerListener method to notify the SensorManager to start fetching data from sensor and unregisterListener to notify it to stop.

@Override
protected void onResume() {
         super.onResume();
         mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
     }

@Override
     protected void onPause() {
         super.onPause();
         mSensorManager.unregisterListener(this);
     }


The onResume() method activates when the app is resumed from a paused state and the onPause() method is called when the app is paused i.e. some other app draws over the current app.

  • Now coming back to onAccuracyChanged() and onSensorChanged() methods, the onAccuracyChanged() method is used to set the accuracy of a sensor. For example, while using GeoLocation sensor, sometimes the position of the mobile isn’t very accurate and so we can define the accuracy level in this method so that the fetched data is used for calculations only if it is in the provided range. And the onSensorChanged() method is the main method where all the data is processed as soon as the new data is notified.

To get the latest value from the sensor, we can use

@Override
public void onSensorChanged(SensorEvent event) {
   data = Float.valueOf(event.values[0]);
   unRegisterListener();
}

Here, the event is an instance of the SensorEvent class which provides the updated data fetched from the sensor. Event.values is used to get the values for any of the three axis including the bias in their values. Following is the list of the index for which we can get a necessary value

values[0] = x_uncalib without bias compensation
values[1] = y_uncalib without bias compensation
values[2] = z_uncalib without bias compensation
values[3] = estimated x_bias
values[4] = estimated y_bias 
values[5] = estimated z_bias

So, in this way, we can add support for any built-in mobile sensor in our Android application.

Resources

Continue ReadingHow to use Mobile Sensors as Instruments in PSLab Android App

How to Add Icons or Menus into the PSLab Android App Toolbar

This blog demonstrates how to add different icons like help, play, pause, etc. and/or menu bar in the toolbar of an Android app along with setting their visibilities on the toolbar i.e. to display the icons only when space is available else to add them in the menu. The topic will be mainly explained by taking the example of menus and icons added to the PSLab app.

How to add a menu in a toolbar?

Following are the steps to add a menu or an icon in the toolbar widget of the Android app

  • First, add toolbar widget to the main layout file as follows
<android.support.v7.widget.Toolbar
   android:id="@+id/compass_toolbar"
   android:layout_width="match_parent"
   android:layout_height="?attr/actionBarSize"
   android:background="?attr/colorPrimary"
   app:popupTheme="@style/AppTheme.PopupOverlay"
   app:title="@string/compass" />

Here, popupTheme is the theme that activates when inflating the toolbar. Usually, it is kept similar to the default theme of the toolbar.

  • Now as the toolbar is ready, we can make the menu that needs to be inflated on the toolbar. For making a menu, make a folder named menu in the resources folder. Now, add a menu resource file in it by giving a proper name and then add the following 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/compass_help_icon"
       android:icon="@drawable/compass_help_icon"
       android:title="@string/show_axis_help"
       app:showAsAction="always" />
</menu>

A detailed explanation of the above code is as follows:

  1. The <menu>…</menu> covers all the items in the menu. There can be sub-menu and also sub-sub-menu too. To make a sub-menu, use <menu>…</menu> inside the main menu.
  2. The <item> tag inside the menu defines a specific item to be included in the menu. The icon attribute of an item is used to show the icon on the toolbar. The title attribute of an item is used to show the text inside the menu if space isn’t available to show the icon on the toolbar. The showAsAction attribute is used to define the method of an item i.e. how the item should be visible to the user. Following are some of the values that showAsAction attribute can take:
    • always – It is used to show the icon of the item on the toolbar everytime
    • never – It is used to show the item as a text in the menu everytime the activity is opened
    • ifRoom – It is used to show the icon on the toolbar if there is enough space else the item is included in the menu

NOTE: Always give IDs to menu items as they are used to distinctly identify the item in the java code.

Figure 1. Example of menu and icons in toolbar in PSLab app

As shown in figure 1, the first two icons have always value in their showAsAction attribute whereas other items have never values in their showAsAction attribute.

  • Now the layout and the menu are ready to be inflated from the Java code. First, the toolbar needs to be set up from the Java code. So find the toolbar with its id and then write the following line in the code.
setSupportActionBar(mToolbar);
  • Now the toolbar is ready and so the menu can be inflated on it. So, override the following method to inflate the menu
@Override
public boolean onCreateOptionsMenu(Menu menu) {
   MenuInflater inflater = getMenuInflater();
   inflater.inflate(R.menu.activity_compass_help_menu, menu);
   return true;
}

Here, the getMenuInflater() method is used to inflate the menu on the toolbar.

  • Now override the onCreateOptionsMenu() method to do the predefined task of selecting the icon or the item from the menu.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
   switch (item.getItemId()) {
       case R.id.compass_help_icon:
           // Do something
           break;
       default:
           break;
   }
   return true;
}

So, in this way a menu can be made so that the number of items delivered to the user can be increased by using the minimum space possible.

Resources

  1. https://developer.android.com/guide/topics/ui/menus – Android Developers guide on how to make a menu in Android
Continue ReadingHow to Add Icons or Menus into the PSLab Android App Toolbar

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 ReadingImplementing Custom Rotary Knobs and Circular Positioning in the Multimeter in the PSLab Android App

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 ReadingMeasuring capacitor in PSLab and its Bugs

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 ReadingWorking with Shared Preferences in PSLab App

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 ReadingImplementing Progress Bar in PSLab App

Making custom dialog boxes in PSLab app

This blog covers the topic of how to create custom dialog boxes in an Android app by taking an example of how it was implemented in PSLab Android application. The custom dialog box that will be illustrated in this blog will have a layout similar to that from my PR feat: added a guide for Logic Analyzer Instrument using Alert Dialog Box in PSLab Android repository. But all the main functionalities that can be added to a custom dialog will be illustrated in this blog.

How custom dialog was implemented in the PSLab app?

  • The first step is to make a layout for the custom dialog box. The layout of custom dialog should be such that it shouldn’t occupy the whole screen as the idea here is to overlay the dialog over the activity and not replace the whole activity with the dialog layout. The layout can be implemented by using the following attributes :
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <RelativeLayout
       android:layout_width="match_parent"
       android:layout_height="wrap_content">

       <TextView
           android:id="@+id/custom_dialog_text"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"/>

       <ImageView
           android:id="@+id/custom_dialog_schematic"
           android:layout_width="wrap_content"
           android:layout_height="@dimen/schematic_height" />

       <TextView
           android:id="@+id/description_text"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />

       <CheckBox
           android:id="@+id/toggle_show_again"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content" />

       <RelativeLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_below="@id/toggle_show_again">

           <Button
               android:id="@+id/dismiss_button"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content" />

       </RelativeLayout>
   </RelativeLayout>
</ScrollView>

The result of the above layout after adding proper margins and padding is as shown in figure 1.

Figure 1. The output of the layout made for custom dialog

  • Now as the layout is ready, we can focus on adding Java code to inflate the layout over a particular activity. Make a function in the activity or the fragment in which the custom dialog is to be inflated. Add the following code (if the layout is same as that in PSLab app else modify the code as per the layout) in the function to display the custom dialog box.
public void howToConnectDialog(String title, String intro, int iconID, String desc) {
   try {
       final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
       LayoutInflater inflater = getLayoutInflater();
       View dialogView = inflater.inflate(R.layout.custom_dialog_box, null);

 // Find and set all the attributes used in the layout and then create the dialog as shown
       
       builder.setView(dialogView);
       builder.setTitle(title);
       final AlertDialog dialog = builder.create();
       ok_button.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
                dialog.dismiss();
           }
       });
       dialog.show();
   } catch (Exception e) {
       e.printStackTrace();
   }
}

Here, the LayoutInflater inflates the custom dialog layout in the Alertdialog builder. The Alertdialog builder is used to hold and set values for all the views present in the inflated layout. The builder then creates a final custom dialog when builder.create() method is called. Finally, the dialog is shown by calling method dialog.show() and is dismissed by calling method dialog.dismiss().

  • Now the dialog box is ready and it will be inflated when the above method is called. But the “Don’t show again” checkbox currently has no functionality and so the dialog box will pop up although “Don’t show again” checkbox is ticked mark by the user. For adding this functionality, we will need to use Shared Preferences to save the state of the dialog box. For using Shared Preferences, first, add the following lines of code in the above method
final SharedPreferences settings = getActivity().getSharedPreferences(PREFS_NAME, 0);
Boolean skipMessage = settings.getBoolean("skipMessage", false);

The PREFS_NAME is the key to distinctly identify the state of the given dialog box from the stored state of many dialog boxes (if any). A suggested key name is “customDialogPreference”. The skipMessage is a boolean used here to check the state if the dialog box should be inflated or not.

Now format the onClickListener of the OK button with the following code :

Boolean checkBoxResult = false;
if (dontShowAgain.isChecked())
   checkBoxResult = true;
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("skipMessage", checkBoxResult);
editor.apply();
dialog.dismiss();

The following code checks the state of “Don’t show again” dialog and then inflates the custom dialog if applicable. If the checkbox is ticked mark then on pressing the OK button the dialog won’t be inflated again.

Resources

  1. https://developer.android.com/guide/topics/ui/dialogs – Android Documentation on Dialogs (Great Resource to make a custom dialog)
Continue ReadingMaking custom dialog boxes in PSLab app