Uploaded Images History in Phimpme Android

In Phimpme Android one core feature is of sharing images to many different platforms. After sharing we usually wants to look in the our past records, where we uploaded what pictures? Which image we uploaded? What time it was? So I added a feature to view the upload history of images. User can go to the Upload history tab, present in the navigation drawer of the app. From there he can browse the repository.

How I added history feature in Phimpme

  • Store the data when User initiate an upload

To get which data uploading is in progress. I am storing its name, date, time and image path. When user approve to upload image from Sharing Activity.

Created a database model

public class UploadHistoryRealmModel extends RealmObject{

   String name;
   String pathname;
   String datetime;

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public String getPathname() {
       return pathname;
   }

   public void setPathname(String pathname) {
       this.pathname = pathname;
   }

   public String getDatetime() {
       return datetime;
   }

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

This is the realm model for storing the name, date, time and image path.

Saving in database

UploadHistoryRealmModel uploadHistory;
uploadHistory = realm.createObject(UploadHistoryRealmModel.class);
uploadHistory.setName(sharableAccountsList.get(position).toString());
uploadHistory.setPathname(saveFilePath);
uploadHistory.setDatetime(new SimpleDateFormat("dd/MM/yyyy HH:mm:ss").format(new Date()));
realm.commitTransaction();

Creating realm object and setting the details in begin and commit Transaction block

  • Added upload history entry in Navigation Drawer

    <LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:id="@+id/ll_drawer_uploadhistory"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="@drawable/ripple"
       android:clickable="true"
       android:orientation="horizontal">
    
       <com.mikepenz.iconics.view.IconicsImageView
           android:id="@+id/Drawer_Upload_Icon"
           android:layout_width="@dimen/icon_width_height"
           android:layout_height="@dimen/icon_width_height"
           app:iiv_icon="gmd-file-upload"/>
    
       <TextView
           android:id="@+id/Drawer_Upload_Item"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@string/upload_history"
           android:textColor="@color/md_dark_background"
           android:textSize="16sp"/>
    </LinearLayout>

It consist of an ImageView and TextView in a horizontal oriented Linear Layout

  • Showing history in Upload History Activity

Added recyclerview in layout.

<android.support.v7.widget.RecyclerView
   android:id="@+id/upload_history_recycler_view"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_below="@id/toolbar">
</android.support.v7.widget.RecyclerView>

Query the database and updated the adapter of Upload History

uploadResults = realm.where(UploadHistoryRealmModel.class);
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this);
uploadHistoryRecyclerView.setLayoutManager(layoutManager);
uploadHistoryRecyclerView.setAdapter(uploadHistoryAdapter);

Added the adapter for recycler view and created an Item using Constraint layout.

Resources

Continue ReadingUploaded Images History in Phimpme Android

Implementing the tickets API at the ‘tickets/add-order’ route

In Open Event Frontend, we have the ‘tickets/add-order’ route for a specific event which facilitates us to add the order based on the tickets that we create at the time of creation of event. The tickets are listed at the ‘tickets/add-order’ route where we can select the tickets required for example, ‘free’, ‘paid’, the payment type and proceed to the ticket buyer’s info page.

This is how we achieved implementing the API:
We use table to show the data to the user, the columns of which are Ticket Type, Price, Quantity, Item Total something like:

So, the workflow to achieve this is as follows:

  • Query the tickets for current event.
  • Have a controller to calculate the ‘Grand Total’ of the individual tickets that the user wants to buy.
  • Show the tickets in our table.

Querying the tickets: Since we are using ember data, we query tickets by the following query in our model method of route.

  model() {
    return this.modelFor('events.view').query('tickets', {});
  }

Thus, the above query shows that we get the current event by actually querying the model for route ‘events.view’ which returns the current event and then query the tickets model so that we get the tickets associated with the current event.

Since there is no UI table support for ember data, we are using a custom table for all the tables in Open Event Frontend and pass the data to it. To render the data in tables, we follow the following approach.
In our controller, we have a columns property as:

columns: [
    {
      propertyName : 'name',
      title        : 'Ticket Type'
    },
    {
      propertyName   : 'price',
      title          : 'Price(US$)',
      disableSorting : true
    },
    {
      propertyName : '',
      title        : 'Quantity',
      template     : 'components/ui-table/cell/cell-input-number'
    },
    {
      propertyName : 'itemTotal',
      title        : 'Item Total'
    }
  ]

The propertyName maps the property of the objects returned from the server i.e in our case, the ‘tickets’. Thus, we pass this skeleton of columns and data from the model to our component so as to render the table in view.

  {{events/events-table columns=columns data=model
    useNumericPagination=true
    showGlobalFilter=true
    showPageSize=true
  }

Also, as seen from the image shown earlier in this blog post, we can see that we also need to calculate the ‘Grand Total’ of the total purchase. Thus, we have a computed property in controller to do this:

  total: computed('model.@each.itemTotal', function() {
    let sum = 0.0;
    this.get('model').forEach(ticket => {
      sum += ticket.get('itemTotal');
    });
    return sum;
  }),

We iterate over the each ‘itemTotal’ in the model and keep on adding it so that the total purchase gets added accordingly. Lastly we show the Grand Total to the user as seen in the image shown earlier in the blog.
Thus, the user can select the tickets and proceed towards the checkout.

Resources:
Ember data official guide
Blog on ember data by Andy Crum

Continue ReadingImplementing the tickets API at the ‘tickets/add-order’ route

Shortcuts in the Phimpme Android Application

The Phimpme Android application comes with a great functionality of capturing moments, editing them, and sharing it with the world using various social media or cloud platforms integrated into the application. Sometimes, the user may want to directly go to a particular section of the application without having to go to the home activity all the time. We have solved this issue using the App shortcuts option which is provided on Android versions greater than 7.0 Nougat. When the user long clicks on the icon of the application, it provides us with multiple options to choose which activity or section we would like to go to as depicted in the screenshot below.

In this post, I will be explaining how we have achieved this functionality in the Phimpme Android application.

Step 1

In the Phimpme app, we have three main sections named Camera, Gallery and Accounts and we have added these three options in the app shortcut menu. To do this, first of all, we need to add a meta data to the main activity in the AndroidManifest.xml file. To do this, search for the activity which contains “android.intent.action.MAIN” and add the following line of code in the activity tag.

<meta-data android:name="android.app.shortcuts"
  android:resource="@xml/shortcuts" />

Step 2

Now after we have configured the manifest file, we need to create shortcuts.xml file in the resources folder which will contain the information about our app shortcuts along with the intent action to go to a particular activity.

After creating the xml file, add the following line of code.

<shortcuts xmlns:tools="http://schemas.android.com/tools"
  xmlns:android="http://schemas.android.com/apk/res/android">

Now after this we have to create a shortcut tag and define the id of our shortcut and should add the icon to it which will be displayed to the user along with the shortcut text as depicted in the screenshot above. This can be done using the following line of code.

android:shortcutId="camera"
android:enabled="true"
android:icon="@drawable/ic_camera_alt_black_24dp"
android:shortcutShortLabel="@string/camera_short"
android:shortcutLongLabel="@string/camera_long"
android:shortcutDisabledMessage="@string/camera_short"

Now after this, we have to define the action which we want to perform when the user clicks on a particular app shortcut. To do this, create an intent tag and add the following lines of code in it.

<intent
  android:action="android.media.action.IMAGE_CAPTURE_SECURE"
  android:targetPackage="org.fossasia.phimpme"
  android:targetClass="org.fossasia.phimpme.opencamera.Camera.CameraActivity" />
<categories android:name="android.intent.category.DEFAULT" />

The above code sets the action type as Image capture and defines the package name of the application. The above code is used to open the Camera activity of the Phimpme Application from the shortcut menu. In the similar fashion as described above, we have implemented the option to open up the Gallery and Accounts section of our application by just modifying the action name of the shortcut.

This is how we have implemented the App shortcut functionality in the Phimpme app. To get the full source code of the Shortcuts.xml file, please refer to the Phimpme Android repository.

Resources

  1. Android Developer’s Guide – App shortcuts – https://developer.android.com/guide/topics/ui/shortcuts.html
  2. GitHub – Google Sample Project to depict the usage of app shortcuts – https://github.com/googlesamples/android-AppShortcuts
  3. Blog – Using app shortcuts in Android 7.0 – http://www.brevitysoftware.com/blog/how-to-use-app-shortcuts-in-android-7-1-nougat/
  4. GitHub – Phimpme Android repository – https://github.com/fossasia/phimpme-android/
Continue ReadingShortcuts in the Phimpme Android Application

Showing Pull Request Build Status in Yaydoc

Yaydoc is integrated to various open source projects in FOSSASIA.  We have to make sure that the contributors PR should not break the build. So, I decided to check whether the PR is breaking the build or not. Then, I would notify the status of the build using GitHub status API.

exports.registerHook = function (data, accessToken) {
  return new Promise(function(resolve, reject) {
    var hookurl = 'http://' + process.env.HOSTNAME + '/ci/webhook';
    if (data.sub === true) {
      hookurl += `?sub=true`;
    }
    request({
      url: `https://api.github.com/repos/${data.name}/hooks`,
      headers: {
        'User-Agent': 'Yaydoc',
        'Authorization': 'token ' + crypter.decrypt(accessToken)
      },
      method: 'POST',
      json: {
        name: "web",
        active: true,
        events: [
          "push",
          "pull_request"
        ],
        config: {
          url: hookurl,
          content_type: "json"
        }
      }
    }, function(error, response, body) {
      if (response.statusCode !== 201) {
        console.log(response.statusCode + ': ' + response.statusMessage);
        resolve({status: false, body:body});
      } else {
        resolve({status: true, body: body});
      }
    });
  });
};

I’ll register the webhook, when user registers the repository to yaydoc for push and pull request event. Push event will be for building documentation and hosting the documentation to the GitHub pages. Pull_request event would be for checking the build of the pull request.

github.createStatus(commitId, req.body.repository.full_name, "pending", "Yaydoc is checking your build", repositoryData.accessToken, function(error, data) {
                    if (!error) {
                      var user = req.body.pull_request.head.label.split(":")[0];
                      var targetBranch = req.body.pull_request.head.label.split(":")[1];
                      var gitURL = `https://github.com/${user}/${req.body.repository.name}.git`;
                      var data = {
                        email: "admin@fossasia.org",
                        gitUrl: gitURL,
                        docTheme: "",
                        debug: true,
                        docPath: "",
                        buildStatus: true,
                        targetBranch: targetBranch
                      };
                      generator.executeScript({}, data, function(error, generatedData) {
                        var status, description;
                        if(error) {
                          status = "failure";
                          description = error.message;
                        } else {
                          status = "success";
                          description = generatedData.message;
                        }
                        github.createStatus(commitId, req.body.repository.full_name, status, description, repositoryData.accessToken, function(error, data) {
                          if (error) {
                            console.log(error);
                          } else {
                            console.log(data);
                          }
                       });
                 });
              }
        });

When anyone opens a new PR, GitHub will send  a request to yaydoc webhook. Then, I’ll send the status to GitHub saying that “Yaydoc is checking your build” with status `pending`. After, that I’ll documentation will be generated.Then, I’ll check the exit code. If the exit code is zero,  I’ll send the status `success` otherwise I’ll send `error` status.
Resources:

Continue ReadingShowing Pull Request Build Status in Yaydoc

Using RecyclerView Instead Of ViewPager For Gallery

Phimpme is an Image app that provide camera, editing ,sharing options and a gallery section. The Gallery section allows us to view large number of images that are locally available in the users device. Generally developers used viewpager to swipe the horizontal images although we are also using viewPager but the problem is it is taking more time to load large size images and that disturb the user smooth experience. After so much research I came to new solution. So in this post, I will be explaining how to use recyclerview to view gallery images instead of viewPager.

Let’s get started

Make sure you have Recyclerview support in your dependencies in build.gradle. As recyclerView required an adapter and viewHolder to set data in recyclerView. So I will be explaining about adapter.

ViewHolder for RecyclerView

public static class ViewHolder extends RecyclerView.ViewHolder {
  ImageView imageView;
  LinearLayout linearLayout;

  public ViewHolder(View itemView) {
      super(itemView);
      imageView = new ImageView(context);
      linearLayout = (LinearLayout) itemView.findViewById(R.id.layout);
      WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
      Display display = wm.getDefaultDisplay();
      Point size = new Point();
      display.getSize(size);
      int width = size.x;
      int height = size.y;
      LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
              width, height);
      imageView.setLayoutParams(params);
      imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
      linearLayout.addView(imageView);
  }
}

Right now the imageView is adjusting according to device screen size so that it will be compatible with all devices.

I am passing the width and height in LayoutParams to parent of imageview i.e in our case linearlayout is parentView.

Adapter for RecyclerView

public ImageAdapter(ArrayList<Media> media) {
  this.media = media;
}

@Override
public ImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.unit_image_pager, null, false);

  return new ViewHolder(view);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
  Glide.with(getContext())
          .load(media.get(position).getUri())
          .diskCacheStrategy(DiskCacheStrategy.SOURCE)
          .thumbnail(0.5f)
          .into(holder.imageView);
  holder.imageView.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
          basicCallBack.callBack(0,null);
      }
  });
}

MediaList is an arrray of media that contains the list of images with URI that will help to load images. I am using Glide to load images you can use any library to load images. Adapter helps to load data in recyclerView.

Now set viewPager where you require to scroll images horizontally

@Nullable @BindView(R.id.photos_pager)
RecyclerView mRecylerPager;
mRecylerPager.setLayoutManager(linearLayoutManager);
mRecylerPager.setHasFixedSize(true);
mRecylerPager.setLongClickable(true);

Our recycler view is ready now the most important part is to set things onPageChangeListner. For example : In Phimpme we are getting path of current position image to show in image description so to update the value we are writing that codde in onPageChangeListner and to update the toolbar.

mViewPager.setOnPageChangeListener(new PagerRecyclerView.OnPageChangeListener() {
  @Override
  public void onPageChanged(int oldPosition, int position) {
      getAlbum().setCurrentPhotoIndex(position);
      toolbar.setTitle((position + 1) + " " + getString(R.string.of) + " " + size_all);
      invalidateOptionsMenu();
      pathForDescription = getAlbum().getMedia().get(position).getPath();
  }
});

To scroll to the given position we require to set the position to recyclerView and it can be done by the following code

mViewPager.scrollToPosition(getCurrentPsotion());

This is how I implemented the recyclerView instead of ViewPager to load gallery images faster as compare to ViewPager.

RecyclerView in Phimpme to load gallery Images

Resources:     

 

Continue ReadingUsing RecyclerView Instead Of ViewPager For Gallery

Electrical Experiments with PSLab

PSLab has the capability to perform a variety of experiments. The PSLab Android App and the PSLab Desktop App have built-in support for over 70 experiments which are commonly performed by students. In addition to that, it can be used in other experiments conveniently. This blog post is in continuation with the previous two posts regarding performing experiments (links in the reference) and this blog deals with another category of experiments that can be performed using PSLab.

The blog lists experiments which mainly involve the basic circuit elements like resistors, capacitors and inductors. These experiments involve the study of R-C, L-R, L-C and L-C-R circuits. These circuits have properties which make them important in real life applications and this blog attempts to give a rough picture of their importance.

Ohm’s Law, Capacitive Reactance and Inductive Reactance

These experiments involve the study of each of the basic circuit element individually. The current and voltage characteristics of each of the elements is studied.

The definitions of the above are:

Ohm’s Law – This is a law familiar to most. It relates the voltage and current of a purely resistive circuit stating that the voltage and current are proportional to each other and their ratio is a constant called the resistance. In this case, the current and voltage are in the same phase.

Capacitive Reactance – Across a capacitor in an AC circuit, the current and voltage are not in the same phase and the current leads the voltage. For a purely capacitive circuit, this difference is 90o.

Inductive Reactance –  Across an inductor in an AC circuit, the current and voltage are not in the same phase and the current lags behind the voltage. For a purely inductive circuit, this difference is 90o.

The reactance is given for capacitor and inductor is given by 1/wC and wL respectively, where C & L are the values of capacitance and inductance respectively and w is the frequency of the AC signal.

The circuit for the setup is shown below. We need to observe the plot of the input waveform and the plot of the voltage across individual elements to observe the phase shift.

  1. Connect CH1 & GND across the input terminals and CH2 & GND across the terminals of any of the elements.
  2. An external signal can be used or can be generated using the PSLab. Use the PSLab to generate a sinusoidal signal of frequency 1000 Hz. by connecting the ends of PV1 in the circuit.
  3. Observe the waveforms. In case of the resistor, there should be no observable phase lag between the two. In case of the capacitor and inductor, there will be an observable phase difference of 90o.
  4. For the capacitive and inductive circuits, just replace the resistor in the above circuit with capacitor/inductor.

RC Circuits

Drawing their names from their respective calculus functions, the integrator produces a voltage output proportional to the product (multiplication) of the input voltage and time; and the differentiator (not to be confused with differential) produces a voltage output proportional to the input voltage’s rate of change.

RC Integrator circuit

For constructing the RC integrator circuit, connect the circuit as shown in the diagram.

  • Construction of the integrator circuit is fairly simple once the differentiator circuit is done.
  • Interchange the positions of the capacitor and resistor in the above circuit and the circuit for the integrator is complete.
  • Observe the output waveform. Plot both the CH1 and CH2 data simultaneously to compare the waveforms.

RC Differentiator circuit

For constructing the RC differentiator circuit, connect the circuit as shown in the diagram.

  • The values of resistance and capacitance used here are 10k ohm and 0.01uF.
  • Connect the CH1 and GND pins of the board with the input side marked as Vi. Ensure that GND is connected to the GND of the circuit.
  • Similarly, connect CH2 and GND with the corresponding ends of the output side marked as Vo.
  • PSLab can also be used for supplying the input to the circuit. Connect the ends of W1 and GND across Vi. W1 can be used to generate a square wave of 10V peak to peak voltage with a frequency of 500 Hz.
  • Observe the output waveform. Plot both the CH1 and CH2 data simultaneously to compare the waveforms.

RL Circuits

RL Integrator Circuit.

For constructing the RL integrator circuit, connect the circuit as shown in the diagram.

  • Construction of the integrator circuit is fairly simple once the differentiator circuit is done.
  • Interchange the positions of the inductor and resistor in the above circuit and the circuit for the integrator is complete.
  • Observe the output waveform. Plot both the CH1 and CH2 data simultaneously to compare the waveforms.

RL Differentiator Circuit

For constructing the RL differentiator circuit, connect the circuit as shown in the diagram.

  • The values of resistance and inductance used here are 470 ohm and 10mH.
  • Connect the CH1 and GND pins of the board with the input side marked as Vi. Ensure that GND is connected to the GND of the circuit.
  • Similarly, connect CH2 and GND with the corresponding ends of the output side marked as Vo.
  • PSLab can also be used for supplying the input to the circuit. Connect the ends of W1 and GND across Vi. W1 can be used to generate a square wave of 2V peak to peak voltage with a frequency of 5000 Hz.
  • Observe the output waveform. Plot both the CH1 and CH2 data simultaneously to compare the waveforms.

Frequency Response

Frequency Response of an electric or electronics circuit allows us to see exactly how the output gain (known as the magnitude response) and the phase (known as the phase response) changes at a particular single frequency, or over a whole range of different frequencies from 0Hz, (d.c.) to many thousands of megahertz, (MHz) depending upon the design characteristics of the circuit.

Frequency response of a circuit can be studied using different tools like Bode plots, phase plots etc. However, this blog would limit to using simple RC and RL circuits as they can be easily visualised using an oscilloscope.

RC Circuits

  • For observing the frequency response of RC circuits, the circuit can be constructed as shown below.
  • The values of resistance and capacitance used here are 10k ohm and 0.01uF.
  • Connect the CH1 and GND pins of the board with the input side marked as Vi. Ensure that GND is connected to the GND of the circuit.
  • Similarly, connect CH2 and GND with the corresponding ends of the output side marked as Vo.
  • PSLab can also be used for supplying the input to the circuit. Connect the ends of W1 and GND across Vi. W1 can be used to generate a square wave of 10V peak to peak voltage with a frequencies ranging from 100 Hz to 5000 Hz.
  • Switch to the X-Y mode of the oscilloscope and observe the waveform formed.

RL Circuits

  • For observing the frequency response of RL circuits, the circuit can be constructed as shown below.
  • The values of resistance and inductance used here are 470 ohm and 10mH.
  • Connect the CH1 and GND pins of the board with the input side marked as Vi. Ensure that GND is connected to the GND of the circuit.
  • Similarly, connect CH2 and GND with the corresponding ends of the output side marked as Vo.
  • Note: PSLab in this case cannot be used as an AC source as the maximum frequency of waveforms produced by PSLab is limited to 5kHz. However, this experiment would also need frequencies much higher than 5 Hz i.e upto 50 kHz. So, a dedicated function generator is needed. However, the oscilloscope would work just fine.
  • Switch to the X-Y mode of the oscilloscope and observe the waveform formed.

References:

  1. The previous blog on experiments using PSLab focusing in electronics https://blog.fossasia.org/electronics-experiments-with-pslab/
  2. The previous blog on experiments using PSLab involving some general experiments https://blog.fossasia.org/fascinating-experiments-with-pslab/
  3. Read more about differentiators and integrators and their uses https://www.allaboutcircuits.com/textbook/semiconductors/chpt-8/differentiator-integrator-circuits/
Continue ReadingElectrical Experiments with PSLab

Implementing Skill Listing in SUSI Android App using Nested RecyclerViews

SUSI Skills are rules that are defined in SUSI Skill Data repo which are basically the responses SUSI gives to the user queries. When a user queries something from the SUSI Android app, a query to SUSI Server is made which further fetches response from SUSI Skill Data and gives the response to the app. Similarly, when we need to list all skills, an API call is made to server to list all skills. The server then checks the SUSI Skill Data repo for the skills and then return all the required information to the app. Then the app displays all the information about the skill to user. User then can view details of each skill and then interact on the chat interface to use that skill. This process is similar to what SUSI Skill CMS does. The CMS is a skill wiki like interface to view all skills and then edit them. Though the app can not be currently used to edit the skills but it can be used to view them and try them on the chat interface.

API Information

For listing SUSI Skill groups, we have to call on  /cms/getGroups.json

This will give you all groups in SUSI model in which skills are present. Current response:

{
  "session": {"identity": {
    "type": "host",
    "name": "14.139.194.24",
    "anonymous": true
  }},
  "accepted": true,
  "groups": [
    "Small Talk",
    "Entertainment",
    "Problem Solving",
    "Knowledge",
    "Assistants",
    "Shopping"
  ],
  "message": "Success: Fetched group list"
}

So, the groups object gives all the groups in which SUSI Skills are located.

Next comes, fetching of skills. For that the endpoint is /cms/getGroups.json?group=GROUP_NAME

Since we want all skills to be fetched, we call this api for every group. So, for example we will be calling http://api.susi.ai/cms/getSkillList.json?group=Entertainment for getting all skills in group “Entertainment”. Similarly for other groups as well.

Sample response of skill:

{
  "accepted": true,
  "model": "general",
  "group": "Shopping",
  "language": "en",
  "skills": {"amazon_shopping": {
    "image": "images/amazon_shopping.png",
    "author_url": "https://github.com/meriki",
    "examples": ["Buy a dress"],
    "developer_privacy_policy": null,
    "author": "Y S Ramya",
    "skill_name": "Shop At Amazon",
    "dynamic_content": true,
    "terms_of_use": null,
    "descriptions": "Searches items on Amazon.com for shopping",
    "skill_rating": null
  }},
  "message": "Success: Fetched skill list",
  "session": {"identity": {
    "type": "host",
    "name": "14.139.194.24",
    "anonymous": true
  }}
}

It gives all details about skills:

  1. image
  2. author_url
  3. examples
  4. developer_privacy_policy
  5. author
  6. skill_name
  7. dynamic_content
  8. terms_of_use
  9. descriptions
  10. skill_rating

Implementation in SUSI Android App

Skill Listing UI of Google Assistant

Skill Listing UI of SUSI SKill CMS

Skill Listing UI of SUSI Android App

The UI of skill listing in SUSI Android App is the mixture of UI of Skill listing in Google Assistant ap and SUSI Skill CMS. It displays skills in a beautiful manner with horizontal recyclerview nested in vertical recyclerview.

So, for implementing horizontal recyclerview inside vertical recyclerview, you need two viewholders and two adapters (one each for a recyclerview).

Let’s see the implementation.

1. First task is to fetch the information of groups in which skills are located. This line calls method in SkillListModel which then makes an API call to fetch groups.

skillListingModel.fetchGroups(this)

2. When the API call is succeeded, the below mentioned method is called which then calls a  skillListingModel.fetchSkills(groups[0], this) which fetches the skills located in group[0] group.

override fun onGroupFetchSuccess(response: Response<ListGroupsResponse>) {
   if (response.isSuccessful && response.body() != null) {
       groupsCount = response.body().groups.size
       groups = response.body().groups
       skillListingModel.fetchSkills(groups[0], this)
   } else {
       skillListingView?.visibilityProgressBar(false)
       skillListingView?.displayErrorDialog()
   }
}

3. When API call for fetching skills in group[0] succeeds, the count value is increased and then skills in group[1] are fetched and so on.

override fun onSkillFetchSuccess(response: Response<ListSkillsResponse>, group: String) {
   if (response.isSuccessful && response.body() != null) {
       skills.add(Pair(group, response.body().skillMap))
       count++
       if(count == groupsCount) {
           skillListingView?.visibilityProgressBar(false)
           skillListingView?.updateAdapter(skills)
       } else {
           skillListingModel.fetchSkills(groups[count], this)
       }
   } else {
       skillListingView?.visibilityProgressBar(false)
       skillListingView?.displayErrorDialog()
   }
}

4. When skills in all groups are fetched, the data in adapter is updated using skillGroupAdapter.notifyDataSetChanged()

override fun updateAdapter(skills: ArrayList<Pair<String, Map<String, SkillData>>>) {
   this.skills.clear()
   this.skills.addAll(skills)
   skillGroupAdapter.notifyDataSetChanged()
}

5. The data is set to the layout in two adapters made earlier. The following is the code to set the group name and adapter to horizontal recyclerview. This is the GroupAdapter to set data to row item in vertical recyclerview.

override fun onBindViewHolder(holder: GroupViewHolder?, position: Int) {
   if(skills[position].first != null)
       holder?.groupName?.text = skills[position].first
   holder?.skillList?.setHasFixedSize(true)
   val mLayoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
   holder?.skillList?.layoutManager = mLayoutManager
   holder?.skillList?.adapter = SkillListAdapter(context, skills[position])
}

6. Similarly, the data of each individual element in the horizontal recyclerview is set in the skillAdapter. The data set are title, examples, description and image. We have used Picasso library to load images from the URL.

override fun onBindViewHolder(holder: SkillViewHolder?, position: Int) {
   val skillData = skillDetails.second.values.toTypedArray()[position]

   if(skillData.skillName == null || skillData.skillName.isEmpty()){
       holder?.skillPreviewTitle?.text = context.getString(R.string.no_skill_name)
   } else {
       holder?.skillPreviewTitle?.text = skillData.skillName
   }

   if( skillData.descriptions == null || skillData.descriptions.isEmpty()){
       holder?.skillPreviewDescription?.text = context.getString(R.string.no_skill_description)
   } else {
       holder?.skillPreviewDescription?.text = skillData.descriptions
   }

   if(skillData.examples == null || skillData.examples.isEmpty())
       holder?.skillPreviewExample?.text = StringBuilder("\"").append("\"")
   else
       holder?.skillPreviewExample?.text = StringBuilder("\"").append(skillData.examples[0]).append("\"")

   if(skillData.image == null || skillData.image.isEmpty()){
       holder?.previewImageView?.setImageResource(R.drawable.ic_susi)
   } else {
       Picasso.with(context.applicationContext).load(StringBuilder(imageLink)
               .append(skillDetails.first.replace(" ","%20")).append("/en/").append(skillData.image).toString())
               .fit().centerCrop()
               .into(holder?.previewImageView)
   }
}

Summary

So, this blog talked about how the Skill Listing feature in SUSI Android App is implemented. This included how a network call is made, logic for making different network calls, making a nested horizontal recyclerview inside vertical recyclerview, etc. So, If you are looking forward to contribute to SUSI Android App, this can help you a little. But if not so, this may also help you in understanding and how you can implement nested recyclerviews similar to Google Play Store.

References

  1. To know about servlets https://en.wikipedia.org/wiki/Java_servlet
  2. To see how to implement one https://www.javatpoint.com/servlet-tutorial
  3. To see how to make network calls in android using Retrofit https://guides.codepath.com/android/Consuming-APIs-with-Retrofit
  4. To see how to implement Horizontal recyclerView inside Vertical recyclerView http://android-pratap.blogspot.in/2015/12/horizontal-recyclerview-in-vertical.html
  5. To see how to implement custom RecyclerView Adapter https://www.survivingwithandroid.com/2016/09/android-recyclerview-tutorial.html
Continue ReadingImplementing Skill Listing in SUSI Android App using Nested RecyclerViews

Enabling Google App Signing for Android Project

Signing key management of Android Apps is a hectic procedure and can grow out of hand rather quickly for large organizations with several independent projects. We, at FOSSASIA also had to face similar difficulties in management of individual keys by project maintainers and wanted to gather all these Android Projects under singular key management platform:

To handle the complexities and security aspect of the process, this year Google announced App Signing optional program where Google takes your existing key’s encrypted file and stores it on their servers and asks you to create a new upload key which will be used to sign further updates of the app. It takes the certificates of your new upload key and maps it to the managed private key. Now, whenever there is a new upload of the app, it’s signing certificate is matched with the upload key certificate and after verification, the app is signed by the original private key on the server itself and delivered to the user. The advantage comes where you lose your key, its password or it is compromised. Before App Signing program, if your key got lost, you had to launch your app under a new package name, losing your existing user base. With Google managing your key, if you lose your upload key, then the account owner can request Google to reassign a new upload key as the private key is secure on their servers.

There is no difference in the delivered app from the previous one as it is still finally signed by the original private key as it was before, except that Google also optimizes the app by splitting it into multiple APKs according to hardware, demographic and other factors, resulting in a much smaller app! This blog will take you through the steps in how to enable the program for existing and new apps. A bit of a warning though, for security reasons, opting in the program is permanent and once you do it, it is not possible to back out, so think it through before committing.

For existing apps:

First you need to go to the particular app’s detail section and then into Release Management > App Releases. There you would see the Get Started button for App Signing.

The account owner must first agree to its terms and conditions and once it’s done, a page like this will be presented with information about app signing infrastructure at top.

So, as per the instructions, download the PEPK jar file to encrypt your private key. For this process, you need to have your existing private key and its alias and password. It is fine if you don’t know the key password but store password is needed to generate the encrypted file. Then execute this command in the terminal as written in Step 2 of your Play console:

java -jar pepk.jar –keystore={{keystore_path}} –alias={{alias}} –output={{encrypted_file_output_path}} –encryptionkey=eb10fe8f7c7c9df715022017b00c6471f8ba8170b13049a11e6c09ffe3056a104a3bbe4ac5a955f4ba4fe93fc8cef27558a3eb9d2a529a2092761fb833b656cd48b9de6a

You will have to change the bold text inside curly braces to the correct keystore path, alias and the output file path you want respectively.

Note: The encryption key has been same for me for 3 different Play Store accounts, but might be different for you. So please confirm in Play console first

When you execute the command, it will ask you for the keystore password, and once you enter it, the encrypted file will be generated on the path you specified. You can upload it using the button on console.

After this, you’ll need to generate a new upload key. You can do this using several methods listed here, but for demonstration we’ll be using command line to do so:

keytool -genkey -v -keystore {{keystore_path}} -alias {{alias_name}} -keyalg RSA -keysize 2048 -validity 10000

The command will ask you a couple of questions related to the passwords and signing information and then the key will be generated. This will be your public key and be used for further signing of your apps. So keep it and the password secure and handy (even if it is expendable now).

After this step, you need to create a PEM upload certificate for this key, and in order to do so, execute this command:

keytool -export -rfc -keystore {{keystore_path}} -alias {{alias_name}} -file {{upload_certificate.pem}}

After this is executed, it’ll ask you the keystore password, and once you enter it, the PEM file will be generated and you will have to upload it to the Play console.

If everything goes right, your Play console will look something like this:

 

Click enrol and you’re done! Now you can go to App Signing section of the Release Management console and see your app signing and new upload key certificates

 

You can use the SHA1 hash to confirm the keys as to which one corresponds to private and upload if ever in confusion.

For new apps:

For new apps, the process is like a walk in park. You just need to enable the App Signing, and you’ll get an option to continue, opt-out or re-use existing key.

 

If you re-use existing key, the process is finished then and there and an existing key is deployed as the upload key for this app. But if you choose to Continue, then App Signing will be enabled and Google will use an arbitrary key as private key for the app and the first app you upload will get its key registered as the upload key

 

This is the screenshot of the App Signing console when there is no first app uploaded and you can see that it still has an app signing certificate of a key which you did not upload or have access to.

If you want to know more about app signing program, check out these links:

Continue ReadingEnabling Google App Signing for Android Project

Introducing Stream Servlet in loklak Server

A major part of my GSoC proposal was adding stream API to loklak server. In a previous blog post, I discussed the addition of Mosquitto as a message broker for MQTT streaming. After testing this service for a few days and some minor improvements, I was in a position to expose the stream to outside users using a simple API.

In this blog post, I will be discussing the addition of /api/stream.json endpoint to loklak server.

HTTP Server-Sent Events

Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is standardized as part of HTML5 by the W3C.

Wikipedia

This API is supported by all major browsers except Microsoft Edge. For loklak, the plan was to use this event system to send messages, as they arrive, to the connected users. Apart from browser support, EventSource API can also be used with many other technologies too.

Jetty Eventsource Plugin

For Java, we can use Jetty’s EventSource plugin to send events to clients. It is similar to other Jetty servlets when it comes to processing the arguments, handling requests, etc. But it provides a simple interface to send events as they occur to connected users.

Adding Dependency

To use this plugin, we can add the following line to Gradle dependencies –

compile group: 'org.eclipse.jetty', name: 'jetty-eventsource-servlet', version: '1.0.0'

[SOURCE]

The Event Source

An EventSource is the object which is required for EventSourceServlet to send events. All the logics for emitting events needs to be defined in the related class. To link a servlet with an EventSource, we need to override the newEventSource method –

public class StreamServlet extends EventSourceServlet {
    @Override
    protected EventSource newEventSource(HttpServletRequest request) {
        String channel = request.getParameter("channel");
        if (channel == null) {
            return null;
        }
        if (channel.isEmpty()) {
            return null;
        }
        return new MqttEventSource(channel);
    }
}

[SOURCE]

If no channel is provided, the EventSource object will be null and the request will be rejected. Here, the MqttEventSource would be used to handle the stream of Tweets as they arrive from the Mosquitto message broker.

Cross Site Requests

Since the requests to this endpoint can’t be of JSONP type, it is necessary to allow cross site requests on this endpoint. This can be done by overriding the doGet method of the servlet –

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
     response.setHeader("Access-Control-Allow-Origin", "*");
    super.doGet(request, response);
}

[SOURCE]

Adding MQTT Subscriber

When a request for events arrives, the constructor to MqttEventSource is called. At this stage, we need to connect to the stream from Mosquitto for the channel. To achieve this, we can set the class as MqttCallback using appropriate client configurations –

public class MqttEventSource implements MqttCallback {
    ...
    MqttEventSource(String channel) {
        this.channel = channel;
    }
    ...
    this.mqttClient = new MqttClient(address, "loklak_server_subscriber");
    this.mqttClient.connect();
    this.mqttClient.setCallback(this);
    this.mqttClient.subscribe(this.channel);
    ...
}

[SOURCE]

By setting the callback to this, we can override the messageArrived method to handle the arrival of a new message on the channel. Just to mention, the client library used here is Eclipse Paho.

Connecting MQTT Stream to SSE Stream

Now that we have subscribed to the channel we wish to send events from, we can use the Emitter to send events from our EventSource by implementing it –

public class MqttEventSource implements EventSource, MqttCallback {
    private Emitter emitter;


    @Override
    public void onOpen(Emitter emitter) throws IOException {
        this.emitter = emitter;
        ...
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        this.emitter.data(message.toString());
    }
}

[SOURCE]

Closing Stream on Disconnecting from User

When a client disconnects from the stream, it doesn’t makes sense to stay connected to the server. We can use the onClose method to disconnect the subscriber from the MQTT broker –

@Override
public void onClose() {
    try {
        this.mqttClient.close();
        this.mqttClient.disconnect();
    } catch (MqttException e) {
        // Log some warning 
    }
}

[SOURCE]

Conclusion

In this blog post, I discussed connecting the MQTT stream to SSE stream using Jetty’s EventSource plugin. Once in place, this event system would save us from making too many requests to collect and visualize data. The possibilities of applications of such feature are huge.

This feature can be seen in action at the World Mood Tracker app.

The changes were introduced in pull request loklak/loklak_server#1474 by @singhpratyush (me).

Resources

Continue ReadingIntroducing Stream Servlet in loklak Server

Displaying Proper Notification While Image is Being Uploaded in Phimpme

In this blog, I will explain how to display App Icon and appropriate text messages in the notification bar while the image is being uploaded on the various social media platform in Phimpme Android application.

Displaying Application icon in the Notification Bar

Whenever Phimpme application uses the notification the application icon should be present during the progress bar is showing and after the image has been uploaded to the social network website.

In the notification bar there are two types of the icon that can be set:

  • Small icon
  • Large Icon

In the small icon, we are putting the upload sign to tell the users that the image is being uploaded.

In the large icon, I am putting the logo of the Phimpme application. This way when the user exits the application while there is an upload process going on, he or she will know that the upload process is from the Phimpme application.

To set the app icon in the Notification bar:

.setLargeIcon(BitmapFactory.decodeResource(ActivitySwitchHelper.getContext().getResources(),
       R.mipmap.ic_launcher))

To set the small icon in the Notification bar:

.setSmallIcon(R.drawable.ic_cloud_upload_black_24dp)

Displaying appropriate account name while uploading the Image

While uploading an Image the notification bar should show the appropriate Account Name in which the account is being uploaded. For example, if the image is being uploaded on Google Plus then the Notification should display “Uploading the image on Google Plus”.

For this, we need to modify the NotificationHandler.make() function and make it accept String resource as a parameter. We can then modify setContentTitle() function to display the appropriate message.

public static void make(@StringRes int title){

//Display Notification code over here

}

setContentTitle() function in Phimpme to display the appropriate function:

mBuilder.setContentTitle(ActivitySwitchHelper.getContext().getString(R.string.upload_progress) + " " + ActivitySwitchHelper.getContext().getResources().getString(title))

Notification make() function after the changes:

public static void make(@StringRes int title){
   mNotifyManager = (NotificationManager) ActivitySwitchHelper.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
   mBuilder = new NotificationCompat.Builder(ActivitySwitchHelper.getContext());
   mBuilder.setContentTitle(ActivitySwitchHelper.getContext().getString(R.string.upload_progress) + " " + ActivitySwitchHelper.getContext().getResources().getString(title))
           .setLargeIcon(BitmapFactory.decodeResource(ActivitySwitchHelper.getContext().getResources(),
                   R.mipmap.ic_launcher))
           .setContentText(ActivitySwitchHelper.getContext().getString(R.string.progress))
           .setSmallIcon(R.drawable.ic_cloud_upload_black_24dp)
           .setOngoing(true);
   mBuilder.setProgress(0, 0, true);
   // Issues the notification
   mNotifyManager.notify(id, mBuilder.build());
}

Conclusion

Notification makes any application more interactive and show live updates even when the application is in use. By following this method users can be aware of the upload functionality and in which account the image is beign uploaded.

Github

Resources

 

Continue ReadingDisplaying Proper Notification While Image is Being Uploaded in Phimpme