Enabling Live Streaming of Sessions in the Open Event Android App

The Open Event Android App had no option for viewing session videos if the links for them were provided by the event organiser. This post walks through how viewing of session videos was made possible through use of implicit intents which launched the Youtube app if the video link was a Youtube link and what all possible cases were taken into account so as to provide the user to view the video of a session as described below:

What is the current status of the app?

The current SessionDetailActivity.java containing the detail of the sessions of the event looks like this:

Adding Live Streaming option in the app

A live streaming feature is added in the app which enables the user to view the session. Now there were certain edge cases which needed to be handled:

  • No video links provided in the sessions json file

In that case the SessionDetailActivity.java looked the way it looks above as there is no way we can enable the live streaming option as the live video links aren’t provided by the event organiser.

  • The video links are provided but are not Youtube links

In that case the SessionDetailActivity.java looks like this, where the card view has a grey background showing that the user will be directed to the required link on a click.

  • The video links are Youtube links

In that case the Youtube thumbnail is fetched through Picasso and is made as the card view background. The SessionDetailActivity.java looks like this in such a case:

First the link is checked whether it is a Youtube link or not as illustrated in the code snippet below:

if(!Utils.isEmpty(video_link)) {
    playButton.setVisibility(View.VISIBLE);

    if(video_link.contains(ConstantStrings.YOUTUBE)) {

        Picasso.with(this)
               .load(youtubeLink)
               .into(youtubeThumbnail);
    
        youtubeThumbnail.setVisibility(View.VISIBLE);
    }

}

After the link is guaranteed as a Youtube link the play icon click is handled. On clicking the play icon, the user is directed to a Youtube app for the viewing of a session through an implicit intent using this piece of code:

playButton.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {

        startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(video_link)));

    }

});

Thus the user is given an option to view the live streaming of a session, if the video links are provided by the event organiser.

Related Links:

Continue ReadingEnabling Live Streaming of Sessions in the Open Event Android App

Save Server Response to File Using Python in Open Event Android App Generator

The Open Event Android project helps event organizers to generate Apps (apk format) using Open Event App generator for their events/conferences by providing API endpoint or zip generated using Open Event server.

The Open Event Android project has an assets folder which contains sample JSON files for the event, which are used to load the data when there is no Internet connection. When an organizer generates an app with the help of an API endpoint, then all the assets, e.g. images, are removed and because of it generated don’t have sample JSON files. So if the device is offline then it will not load the data from the assets (issue #1606). The app should contain sample event JSON files and should be able to load the data without the Internet connection.

One solution to this problem is to fetch all the event data (JSON files) from the server and save it to the assets folder while generating the app using Open Event App generator. So in this blog post, I explain how to make a simple request using Python and save the response in a file.

Making a simple request

To fetch the data we need to make a request to the server and the server will return the response for that request. Here’s step to make a simple request,

Import Requests module using,

import requests

The requests module has get, post, put, delete, head, options to make a request. Example:

requests.get(self.api_link + '/' + end_point)

Here all method returns requests.Response object. We can get all the information we need from this object.

So we can make response object like this,

response = requests.get(self.api_link + '/' + end_point)

Saving response in a file

For saving response in a file we need to open/create a file for writing.

Create or open file

The open() method is used for opening file. It takes two arguments one is file name and second in access mode which determines the mode in which the file has to be opened, i.e., read, write, append, etc.

file = open(path/to/the/file, "w+")

Here “w+” overwrites the existing file if the file exists. If the file does not exist, it creates a new file for reading and writing.

Write and close file

Now write the content of the response using the write() method of file object then close the file object using the close()  method. The close() method flushes any unwritten information and closes the file object.

file.write(response.text)
file.close()

Here response.text gives the content of the response in Unicode format.

Conclusion

The requests.Response object contains much more data like encoding, status_code, headers etc. For more info about request and response refer additional resources given below.

Additional Resources

Continue ReadingSave Server Response to File Using Python in Open Event Android App Generator

JSON Deserialization Using Jackson in Open Event Android App

The Open Event project uses JSON format for transferring event information like tracks, sessions, microlocations and other. The event exported in the zip format from the Open Event server also contains the data in JSON format. The Open Event Android application uses this JSON data. Before we use this data in the app, we have to parse the data to get Java objects that can be used for populating views. Deserialization is the process of converting JSON data to Java objects. In this post I explain how to deserialize JSON data using Jackson.

1. Add dependency

In order to use Jackson in your app add following dependencies in your app module’s build.gradle file.

dependencies {
	compile 'com.fasterxml.jackson.core:jackson-core:2.8.9'
	compile 'com.fasterxml.jackson.core:jackson-annotations:2.8.9'
	compile 'com.fasterxml.jackson.core:jackson-databind:2.8.9'
}

2.  Define entity model of data

In the Open Event Android we have so many models like event, session, track, microlocation, speaker etc. Here i am only defining track model because of it’s simplicity and less complexity.

public class Track {

	private int id;
	private String name;
	private String description;
	private String color;
	@JsonProperty("font-color")       
	private String fontColor;
    
	//getters and setters
}

Here if the property name is same as json attribute key then no need to add JsonProperty annotation like we have done for id, name color property. But if property name is different from json attribute key then it is necessary to add JsonProperty annotation.

3.  Create sample JSON data

Let’s create sample JSON format data we want to deserialize.

{
        "id": 273,
        "name": "Android",
        "description": "Sample track",
        "color": "#94868c",
        "font-color": "#000000"
}

4.  Deserialize using ObjectMapper

ObjectMapper is Jackson serializer/deserializer. ObjectMapper’s readValue() method is used for simple deserialization. It takes two parameters one is JSON data we want to deserialize and second is Model entity class. Create an ObjectMapper object and initialize it.

ObjectMapper objectMapper = new ObjectMapper();

Now create a Model entity object and initialize it with deserialized data from ObjectMapper’s readValue() method.

Track track = objectMapper.readValue(json, Track.class);

So we have converted JSON data into the Java object.

Jackson is very powerful library for JSON serialization and deserialization. To learn more about Jackson features follow the links given below.

Continue ReadingJSON Deserialization Using Jackson in Open Event Android App

Cache Thumbnails and Images Using Picasso in Open Event Android

In the event based Android projects like Open Event Android, we have speakers and sponsors. And these projects needs to display image of the speakers and sponsors because it affects project a lot. So instead of every time fetching image from the server it is good to store small images(thumbnails) in the cache and load images even if device is offline. It also reduces data usage.

Picasso is mostly used image loading library for Android. It automatically handles ImageView recycling and download cancellation in an adapter, complex image transformations with minimal memory use, memory and disk caching.

But one problem is Picasso caches images for only one session by default. I mean if you close the app then all by default cached image will be removed.  If you are offline then Picasso will not load cached images because of it. It will make network calls every time you open the app.

In this post I explain how to manually cache images using Picasso so that images load even if the device is offline. It will make a network call only once for a particular image and will cache image in memory.

We will use okhttp3 library for OkHttpClient.

1. Add dependency

In order to use Picasso in your app add following dependencies in your app module’s build.gradle file.

dependencies {
        compile 'com.squareup.okhttp3:okhttp:3.8.1'
        compile 'com.squareup.picasso:picasso:2.5.2'
}

2. Make static Picasso object

Make static Picasso object in the Application class so that we can use it directly from the other activity.

public static Picasso picassoWithCache;

3. Initialize cache

Create a File object with path as app specific cache and use this object to create a Cache object.

File httpCacheDirectory = new File(getCacheDir(), "picasso-cache");
Cache cache = new Cache(httpCacheDirectory, 15 * 1024 * 1024);

Here it will create a Cache object with 15MB. getCacheDir() method returns the absolute path to the application specific cache directory on the filesystem.

OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder().cache(cache);

4. Initialize Picasso with cache

Now initialize picassoWithCache object using Picass.Builder(). Set downloader for picasso by adding  new OkHttp3Downloader object.

picassoWithCache = new Picasso.Builder(this).downloader(new OkHttp3Downloader(okHttpClientBuilder.build())).build();

5. Use picassoWithCache object

As it is a static object you can directly use it from any activity. All the images loaded using this picassoWithCache instance will be cached in memory.

Application.picassoWithCache().load(thumbnail/image url);

 

To know more how i solved this issue in Open Event Project visit this link. To learn more about Picasso features follow the links given below.

Continue ReadingCache Thumbnails and Images Using Picasso in Open Event Android

Iteration through the Android File System in the phimpme project

Android uses the single file system structure which has a single root. The task involved creating a custom folder chooser to whitelist folders while displaying images in the gallery in the Phimpme Photo App. The challenge arose in iterating over the files in the most efficient way. The best possible way to represent the file structure is in the form of tree data structure as given below.

Current Alternative

Currently, the MediaStore class contains metadata for all available media on both internal and external storage devices. Since it only returns a list of a particular media file format, it refrains the developer from customizing the structure in his way.

Implementation

Create a public class which represents the file tree. Since each subtree of the tree could itself be represented as file tree itself, therefore the parent of a node will be a FileTree object itself. Therefore declare a list of FileTree objects as children of the node, a FileTree object as the parent of the particular node, node’s own File object along with string values filepath and display name associated with it.

public class FileTree {
 public final String filepath;
 public final String displayName;
 public final List<FileTree> childFileTreeList = new ArrayList<>();
 public final FileTree parent;
 public boolean hasMedia = false;

 public FileTree(String filepath, String displayName, FileTree parent) {
    this.filepath = filepath;
    this.displayName = displayName;
    this.parent = parent;
 }
}

For iterating through the file system, we create a recursive function which is called on the root of the Android file system. If the particular file is a directory, with the help of Depth First traversal algorithm, the directory is traversed. Else, the file is added to the list of the file. The below code snippet is the recursive function.

public static void walkDir(FileTree fileTree, List<File> files) {
  File listFile[] = fileTree.file.listFiles();

  if (listFile != null) {
      for (File file : listFile) {
          if (file.isDirectory()) {
              FileTree childFileTree = new FileTree(file, file.getName(), fileTree);
              fileTree.childFileTreeList.add(childFileTree);
              walkDir(childFileTree, files);
          }
          else {
                  files.add(file);

          }
      }
  }
}

Conclusion

The android file system was used to whitelist folders so that the images of the folders could neither be uploaded nor edited.

For the complete guide to whitelisting folders, navigate here

Continue ReadingIteration through the Android File System in the phimpme project

Creating Multiple Device Compatible Layouts in PSLab Android

The developer’s goal is that PSLab Android App as an app should run smoothly on all the variety of Android devices out in the market. There are two aspects of it – the app should be able to support maximum number of Android versions possible which is related to the core software part and the other being the app should be able to generate the same user experience on all sizes of screens. This post focuses on the later.

There are a whole range of android devices available in the market right from 4 inch mobile phones to 12 inch tablets and the range in the screen sizes is quite large. So, the challenge in front of app designers is to make the app compatible with the maximum  number of devices without doing any specific tweaks related to a particular resolution range. Android has its mechanism of scaling the app as per the screen size and it does a good job almost all the time, however, still there are cases where android fails to scale up or scale down the app leading to distorted layout of the app.

This blog discusses some of the tricks that needs to be kept in mind while designing layouts that work independent of screen sizes.

Avoid using absolute dimensions

It is one of the most common things to keep in mind before starting any UI design. Use of absolute dimensions like px, inch etc. must be avoided every time as they are fixed in size and don’t scale up or scale down while screen sizes are changed. Instead relative dimensions like dp should be used which depend on the resolution and scale up or scale down. ( It’s a fair assumption that bigger screens will have better resolution compared to the smaller ones although exceptions do exist) .

Ensure the use of correct layout/View group

Since, android provides a variety of layouts like Linearlayout, Constrainedlayout, Relativelayout, Tablelayout and view groups like ScrollView, RecyclerView, ListView etc. it is often confusing to know which layout/viewgroup should be used. The following list gives a rough idea of when to use a particular layout or view group.

  • Linearlayout – Mostly used for simple designs when the elements are stacked in ordered horizontal/vertical fashion and it needs explicit declaration of orientation.
  • Relativelayout – Mostly used when the elements need to defined relative to the parent or the neighbouring elements. Since, the elements are relative, there is no need to define the orientation.
  • Constraintlayout – It has all the features of Relativelayout and in addition a feature of adding constraints to the child elements or neighbouring elements.
  • Tablelayout – Tablelayout is helpful to when all the views/widgets are arranged in an ordered fashion.

All the above layouts can be used interchangeably most of the times, however, certain cases make some more favourable than others like when than views/ widgets are not present in an organised manner, it is better to stick to Linearlayout or Relativelayout.

  • ListView – Used when the views/ widgets in a screen are repeated, so using a listview ensures that the volume of the code is reduced and all the repetitive views are identical in nature.
  • RecyclerView – More of an improved version of ListView. It is recommended to use this view over ListView. Additionally this view group supports features like swipe to refresh.
  • ScrollView – Used when the UI screen cannot fit within the given screen space. ScrollView supports one direct child layout. So, to implement a scrollview, all the views must be under a particular layout and then masked by scrollview.

Choosing the correct layout or view group would help to create a better UI.

Use of layout_weight

Ensuring the layout width assigned in XML file covers the entire width on the screen. For ensuring this, one possible solution is to use layout_weight instead of layout_width.

Example –

<TextView
   android:id="@+id/tv_control_read9"
   android:layout_width="0dp"
   android:layout_weight="1"
   android:layout_height="30dp"
   android:layout_marginTop="10dp"
/>

 

In order to use layout_weight, layout_width must be set to 0 else it would interfere with the width and as layout_width is a compulsory parameter it cannot be omitted. Layout weight can be any number and the space is allocated to each view in proportion to the weights assigned. Since it does not involve numerical dimensions, the distribution would be uniform for all types of screens. The result is clearly evident here. The same UI in different screen sizes is displayed below.

blog_post_8_2

Fig: Screenshot taken on a 6” phone and on a 4” phone. Although the screen area of 4” phone is 44% that of the 6” phone, the UIs are identically the same.

Create different layout directories for different resolutions

  • Creating different layouts for different screen sizes ensures that the limitations of smaller screen sizes are taken care of and the advantages offered by bigger screen sizes are put to the best use.
  • The Android documentation here mentions the conventions to be followed while designing.
  • Although over the years, android has become better at auto-adjusting layouts for different screen sizes. However, if the no. of views and widgets are high, auto-adjusting does not work well as in case of PSLab and it is better to create different sets of layouts.
  • As evident from the picture of the 8” tablet, although the auto-adjusted layout is manageable, the layout looks stretched and does not utilize the entire screen space, so it a better UI can be made by creating a dedicated layout directory for bigger screens.

Additional resources

 

Continue ReadingCreating Multiple Device Compatible Layouts in PSLab Android

Using Sensors with PSLab Android App

The PSLab Android App as of now supports quite a few sensors. Sensors are an essential part of many science experiments and therefore PSLab has a feature to support plug & play sensors. The list of sensors supported by PSLab can be found here.

  • AD7718 – 24-bit 10-channel Low voltage Low power Sigma Delta ADC
  • AD9833 – Low Power Programmable Waveform generator
  • ADS1115 – Low Power 16 bit ADC
  • BH1750 – Light Intensity sensor
  • BMP180 – Digital Pressure Sensor
  • HMC5883L – 3-axis digital magnetometer
  • MF522 – RFID Reader
  • MLX90614 – Infrared thermometer
  • MPU6050 – Accelerometer & gyroscope
  • MPU925x – Accelerometer & gyroscope
  • SHT21 – Humidity sensor
  • SSD1306 – Control for LED matrix
  • Sx1276 – Low Power Long range Transceiver
  • TSL2561 – Digital Luminosity Sensor

All the sensors except Sx1276 communicate using the I2C protocol whereas the Sx1276 uses the SPI protocol for communication. There is a dedicated set of ports on the PSLab board for the communication under the label I2C with the ports named 3.3V, GND, SCL & SDA.

blog_post_7_1

Fig; PSLab board sketch

Any I2C sensor has ports named 3.3V/VCC, GND, SCL, SDA at least along with some other ports in some sensors. The connections are as follows:

  1. 3.3V on PSLab – 3.3V/VCC on sensor
  2. GND on PSLab – GND on sensor
  3. SCL on PSLab – SCL on sensor
  4. SDA on PSLab – SDA on sensor

The diagram here shows the connections

For using the sensors with the Android App, there is a dedicated I2C library written in communication in Java for the communication. Each sensor has its own specific set of functionalities and therefore has its own library file. However, all these sensors share some common features like each one of them has a getRaw method which fetches the raw sensor data. For getting the data from a sensor, the sensor is initially connected to the PSLab board.

The following piece of code is responsible for detecting any devices that are connected to the PSLab board through the I2C bus. Each sensor has it’s own unique address and can be identified using it. So, the AutoScan function returns the addresses of all the connected sensors and the sensors can be uniquely identified using those addresses.

public ArrayList<Integer> scan(Integer frequency) throws IOException {
	if (frequency == null) frequency = 100000;
	config(frequency);
	ArrayList<Integer> addresses = new ArrayList<>();
	for (int i = 0; i < 128; i++) {
		int x = start(i, 0);
		if ((x & 1) == 0) {
			addresses.add(i);
		}
		stop();
	}
	return addresses;
}

 

As per the addresses fetched, the sensor library corresponding to that particular sensor can be imported and the getRaw method can be called. The getRaw method will return the raw sensor data. For example here is the getRaw method of ADS1115.

public int[] getRaw() throws IOException, InterruptedException {
	String chan = typeSelection.get(channel);
	if (channel.contains("UNI"))
		return new int[]{(int) readADCSingleEnded(Integer.parseInt(chan))};
	else if (channel.contains("DIF"))
		return new int[]{readADCDifferential(chan)};
	return new int[0];
}

Here the raw data is returned in the form of voltages in mV.

Similarly, the other sensors return some values like luminosity sensor TSL2561 returns values of luminosity in Lux, the accelerometer & gyroscope MPU6050 returns the angles of the 3-axes.

In order to initiate the process of getting raw data from the sensor in Sensor Activity, the object for the sensor is created and the method of getRaw is called. The following is the implementation for ADS1115. The rest of the sensors also have an implementation similar to this. There are try-catch statements in the code to handle some of the exceptions thrown during process of method calls.

ADS1115 ADS1115 = null;
try {
	ADS1115 = new ADS1115(i2c);
} catch (IOException | InterruptedException e) {
	e.printStackTrace();
}

int[] dataADS1115 = null;
String datadispADS1115 = null;
try {
	if (ADS1115 != null) {
		dataADS1115 = ADS1115.getRaw();
	}
} catch (IOException | InterruptedException e) {
	e.printStackTrace();
}

if (dataADS1115 != null) {
	for(int i = 0; i < dataADS1115.length; i++)
		datadispADS1115 += String.valueOf(dataADS1115[i]);
	}

tvSensorGetRaw.setText(datadispADS1115);

 

Additional Resources

  1. Sensor implementation in PSLab Python repository – https://github.com/fossasia/pslab-python/tree/development/PSL/SENSORS
  2. Using the sensors with Arduino in case you have worked with Arduino before – The basic connections are same as PSLab http://www.instructables.com/id/Arduino-MPU-6050-Getting-It-to-Work/
Continue ReadingUsing Sensors with PSLab Android App

Creating Custom Components in the PSLab Android App

PSLab Android App supports a lot of features and each of these features need components & views for their implementation. A typical UI of PSLab is shown in the figure below. Considering the number of views & components used in the figure, implementation of each view & component separately would lead to a huge volume of repetitive and inefficient code. As it is evident that the EditText and two buttons beside it keep repeating a lot, it is wiser to create a single custom component consisting of an EditText and two buttons. This not only leads to efficient code but also results in a drastic reduction of the volume of code.

Android has a feature which allows creating components. For almost all the cases, the pre-defined views in Android serve our purpose of creating the UIs. However, sometimes there is a need to create custom components to reduce code volume and improve quality. Custom components are used when a particular set of component needed by us is not present in the Android view collection or when a pattern of components is frequently repeated or when we need to reduce the code complexity.

The above set can be replaced by defining a custom component which includes an edittext and two buttons and then treating it like just any other component. To get started with creating a custom component, the steps are the following:

Create a layout for the custom component to be designed

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="horizontal" android:layout_width="match_parent"
   android:layout_height="match_parent">

   <Button
       android:id="@+id/button_control_plus"
       android:layout_width="0dp"
       android:layout_weight="0.5"
       android:layout_height="20dp"
       android:background="@drawable/button_minus" />

   <EditText
       android:id="@+id/edittext_control"
       android:layout_width="0dp"
       android:layout_weight="2"
       android:layout_height="24dp"
       android:layout_marginTop="@dimen/control_margin_small"
       android:inputType="numberDecimal"
       android:padding="@dimen/control_edittext_padding"
       android:background="@drawable/control_edittext" />

   <Button
       android:id="@+id/button_control_minus"
       android:layout_width="0dp"
       android:layout_weight="0.5"
       android:layout_height="20dp"
       android:background="@drawable/button_plus" />
</LinearLayout>

The layout file edittext_control.xml is created with three views and each one of them has been assigned an ID along with all the other relevant parameters.

Incorporate the newly created custom layout in the Activity/Fragment layout file

<org.fossasia.pslab.others.Edittextwidget
       android:id="@+id/etwidget_control_advanced1"
       android:layout_height="wrap_content"
       android:layout_width="0dp"
       android:layout_weight="2"
       android:layout_marginLeft="@dimen/control_margin_small"
       android:layout_marginStart="@dimen/control_margin_small"
/>

The custom layout can be added the activity/fragment layout just like any other view and can be assigned properties similarly.

Create the activity file for the custom layout

public class Edittextwidget extends LinearLayout{

   private EditText editText;
   private Button button1;
   private Button button2;
   private double leastCount;
   private double maxima;
   private double minima;

 
   public Edittextwidget(Context context, AttributeSet attrs, int defStyle) {
       super(context, attrs, defStyle);
       applyAttrs(attrs);
   }

   public Edittextwidget(Context context, AttributeSet attrs) {
       super(context, attrs);
       applyAttrs(attrs);
   }

   public Edittextwidget(Context context) {
       super(context);
   }

  public void init(Context context, final double leastCount, final double minima, final double maxima) {
       View.inflate(context, R.layout.edittext_control, this);
       editText = (EditText) findViewById(R.id.edittext_control);
       button1 = (Button) findViewById(R.id.button_control_plus);
       button2 = (Button) findViewById(R.id.button_control_minus);

       button1.setOnClickListener(new OnClickListener() {
           @Override
           public void onClick(View v) {
               Double data = Double.valueOf(editText.getText().toString());
               data = data - leastCount;
               data = data > maxima ? maxima : data;
               data = data < minima ? minima : data;
               editText.setText(String.valueOf(data));
           }
       });

       button2.setOnClickListener(new OnClickListener() {
           @Override
           public void onClick(View v) {
               Double data = Double.valueOf(editText.getText().toString());
               data = data + leastCount;
               data = data > maxima ? maxima : data;
               data = data < minima ? minima : data;
               editText.setText(String.valueOf(data));
           }
       });
   }

   private void applyAttrs(AttributeSet attrs) {
       TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Edittextwidget);
       final int N = a.getIndexCount();
       for (int i = 0; i < N; ++i) {
           int attr = a.getIndex(i);
           switch (attr) {
               case R.styleable.Edittextwidget_leastcount:
                   this.leastCount = a.getFloat(attr, 1.0f);
                   break;
               case R.styleable.Edittextwidget_maxima:
                   this.maxima = a.getFloat(attr, 1.0f);
                   break;
               case R.styleable.Edittextwidget_minima:
                   this.minima = a.getFloat(attr, 1.0f);
           }
       }
       a.recycle();
   }
}

In the activity file Editextwidget.java, the views of the custom layout are defined and functionalities are assigned to them. For example, here there are two buttons which work as increment/decrement buttons and an edittext which takes numeric input. The buttons are initiated just like the way they are done in other activity/fragment using OnClickListener.

Define the attributes for the custom layout

<declare-styleable name="Edittextwidget">
     <attr name="leastcount" format="float" />
     <attr name="maxima" format="float" />
     <attr name="minima" format="float" />
</declare-styleable>

The attributes for the custom layout are defined in the attrs.xml file. Each attribute is assigned a name and a format which can be int, float, double, string etc.

Finally call the methods of the custom layout from the desired activity/fragment

Edittextwidget etwidgetControlAdvanced1 = (Edittextwidget)view.findViewById(R.id.etwidget_control_advanced1);

etwidgetControlAdvanced1.init(getContext(), 1.0, 10.0, 5000.0);

The init method of Edittextwidget.java is called while passing the relevant parameters like context, least count, maxima and minima.

Additional Resources on Custom Components

  1. Official Android Guide on Custom components – https://developer.android.com/guide/topics/ui/custom-components.html
  2. Simple example of creating a custom component to get started – https://www.tutorialspoint.com/android/android_custom_components.htm
Continue ReadingCreating Custom Components in the PSLab Android App

Trigger Controls in Oscilloscope in PSLab

PSLab Desktop App has a feature of oscilloscope. Modern day oscilloscopes found in laboratories support a lot of advanced features and addition of trigger controls in oscilloscope was one such attempt in adding an advanced feature in the oscilloscope. As the current implementation of trigger is not robust enough, this feature would help in better stabilisation of waveforms.

Captured waveforms often face the problem of distortion and trigger helps to solve this problem. Trigger in oscilloscope is an essential feature for signal characterisation.  as it synchronises the horizontal sweep of the oscilloscope to the proper point of the signal. The trigger control enables users to stabilise repetitive waveforms as well as capture single-shot waveforms. By repeatedly displaying similar portion of the input signal, the trigger makes repetitive waveform look static. In order to visualise how an oscilloscope looks with or without a trigger see the following figures below.

blog_post_5_1

blog_post_5_2

Fig 1: (a) Without trigger  (b) With trigger

The Fig:1(a) is the actual waveform received by the oscilloscope and it can be easily noticed that interpreting it is confusing due to the overlapping of multiple waveforms together. So, in Fig:1(b) the trigger control stabilises the waveforms and captures just one waveform.

In general the commonly used trigger modes in laboratory oscilloscopes are:-

  • Auto – This trigger mode allows the oscilloscope to acquire a waveform even when it does not detect a trigger condition. If no trigger condition occurs while the oscilloscope waits for a specific period (as determined by the time-base setting), it will force itself to trigger.
  • Normal – The Normal mode allows the oscilloscope to acquire a waveform only when it is triggered. If no trigger occurs, the oscilloscope will not acquire a new waveform, and the previous waveform, if any, will remain on the display.
  • Single – The Single mode allows the oscilloscope to acquire one waveform each time you press the RUN button, and the trigger condition is detected.
  • Scan – The Scan mode continuously sweeps waveform from left to right.

Implementing Trigger function in PSLab

PSLab has a built in basic functionality of trigger control in the configure_trigger method in sciencelab.py. The method gets called when trigger is enabled in the GUI. The trigger is activated when the incoming wave reaches a certain voltage threshold and the PSLab also provides an option of either selecting the rising or falling edge for trigger. Trigger is especially useful in experiments handling waves like sine waves, square wave etc. where trigger helps to get a clear picture.

In order to initiate trigger in the PSLab desktop app, the configure_trigger method in sciencelab.py is called. The configure_trigger method takes some parameters for input but they are optional. If values are not specified the default values are assumed.

def configure_trigger(self, chan, name, voltage, resolution=10, **kwargs):
        
  prescaler = kwargs.get('prescaler', 0)
        try:
            self.H.__sendByte__(CP.ADC)
            self.H.__sendByte__(CP.CONFIGURE_TRIGGER)
            self.H.__sendByte__(
                (prescaler << 4) | (1 << chan))  
            if resolution == 12:
                level = self.analogInputSources[name].voltToCode12(voltage)
                level = np.clip(level, 0, 4095)
            else:
                level = self.analogInputSources[name].voltToCode10(voltage)
                level = np.clip(level, 0, 1023)

            if level > (2 ** resolution - 1):
                level = (2 ** resolution - 1)
            elif level < 0:
                level = 0

            self.H.__sendInt__(int(level))  # Trigger
            self.H.__get_ack__()
        
        except Exception as ex:
  	    self.raiseException(ex, "Communication Error , Function : " + inspect.currentframe().f_code.co_name)

The method takes the following parameters in the method call

  • chan – Channel . 0, 1,2,3. corresponding to the channels being recorded by the capture routine(not the analog inputs).
  • name – The name of the channel. ‘CH1’… ‘V+’.
  • voltage – The voltage level that should trigger the capture sequence(in Volts).

The similar feature will also be used in oscilloscope in the Android app with the code corresponding to this method  in ScienceLab written in Java.

Additional Resources

  1. Read more about Trigger here – http://www.radio-electronics.com/info/t_and_m/oscilloscope/oscilloscope-trigger.php
  2. Learn more about trigger modes in oscilloscopes – https://www.picotech.com/library/oscilloscopes/advanced-digital-triggers
  3. PSLab Python repository to know the underlying code – https://github.com/fossasia/pslab-python

 

Continue ReadingTrigger Controls in Oscilloscope in PSLab

How to add Markers in Map Fragment of Open Event Android App

The Open Event Android project helps event organizers to generate Apps (apk format) for their events/conferences by providing API endpoint or zip generated using Open Event server. In the  Open Event Android App, we have a map for showing all locations of sessions. In this map users should be able to see different locations with multiple markers. In this post I explain how to add multiple markers in the Google map fragment and set the bound in the map so that all markers are visible on one screen with specified padding.

Create Map Fragment

The first step to do is to create a simple xml file and add a fragment element in it. Then create MapsFragment.java file and find fragment element added in the xml file using findFragmentById() method.

SupportMapFragment supportMapFragment = ((SupportMapFragment)
           getChildFragmentManager().findFragmentById(R.id.map));

1. Create fragment_map.xml file

In this file add FrameLayout as a top level element and add fragment element inside FrameLayout with the name “com.google.android.gms.maps.SupportMapFragment”.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">

       <fragment
           android:id="@+id/map"
           android:name="com.google.android.gms.maps.SupportMapFragment"
           android:layout_width="match_parent"
           android:layout_height="match_parent" />

</FrameLayout>

2. Create MapsFragment.java

MapsFragment.java extends SupportMapFragement and implements LocationListener, OnMapReadyCallback. Make instance of GoogleMap object. In onViewCreated method inflate fragment_map.xml file using inflater. Now find the fragment element added in the xml file using findFragmentById() method and assign it to SupportMapFragement instance.

public class MapsFragment extends SupportMapFragment implements LocationListener, OnMapReadyCallback {

    private GoogleMap mMap;

    @Override
    public void onViewCreated(View view, @Nullable Bundle     savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_map, container, false);
        SupportMapFragment supportMapFragment = ((SupportMapFragment)
        getChildFragmentManager().findFragmentById(R.id.map));
supportMapFragment.getMapAsync(this);

        return view;
    }

    @Override
    public void onMapReady(GoogleMap map) {  }
    ...
}

Create list of locations

Location object has three variables name, latitude & longitude. Create a list of locations for which we want to add markers to the map.

public class Location {
    private String name;
    private float latitude;
    private float longitude;
}

List<Microlocation> mLocations= new ArrayList<>();

 

Add location objects in mLocations in onViewCreated() method

mLocations.add(location);

 

You can add multiple locations using for loop or fetching from the database.

Add markers

Add following code in onMapReady(GoogleMap map) method. onMapReady(GoogleMap map) is called when map is ready to be used. setMapToolbarEnabled(true) used to show toolbar for marker. If the toolbar is enabled users will see a bar with various context-dependent actions, including ‘open this map in the Google Maps app’ and ‘find directions to the highlighted marker in the Google Maps app’.

if(map != null){
    mMap = map;
    mMap.getUiSettings().setMapToolbarEnabled(true);
}
showEventLocationOnMap();

 

Create showEventLocationOnMap() method and add following code

private void showLocationsOnMap(){

   float latitude;
   float longitude;
   Marker marker;

   //Add markers for all locations
   for (Location location : mLocations) {

       latitude = location.getLatitude();
       longitude = location.getLongitude();
       latlang = new LatLng(latitude, longitude);

       marker = mMap.addMarker(new MarkerOptions()
                .position(latlang)
                .title(location.getName()));
   }
}

 

So what we are doing here?

For each location object in mLocations list, we are creating LatLng(latitude, longitude)  object and adding it to the map using

marker = mMap.addMarker(new MarkerOptions().position(latlang).title(location.getName()));

Setting bound

We want to set the zoom level so that all markers are visible. To do this, we can use LatLngBounds.Builder. Add the position of the marker in the builder object using include method while adding the marker in the map.

LatLngBounds.Builder builder = new LatLngBounds.Builder();
builder.include(marker.getPosition());

 

Then make the LatLngBounds object from builder.

LatLngBounds bounds = builder.build();
CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, dpToPx(40));
mMap.moveCamera(cameraUpdate);

private int dpToPx(int dp) {
    return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
}

 

dpTopx converts dp to px so that it set the same padding in all devices.

Conclusion

Markers in map give very nice user experience for an event or a conference because they show the relative position of places and we now offer this feature in Open Event Android.

Additional resources:

Continue ReadingHow to add Markers in Map Fragment of Open Event Android App