Charges Layer in Open Event Server

The Open Event Server enables organizers to manage events from concerts to conferences and meetups. It offers features for events with several tracks and venues. This blog post explains how the charge layer has been implemented in the Open Event Server in order to charge the user for tickets of an event.

Schema

We currently support payments via Stripe and Paypal. As a result the schema for Charges layer consists of fields for providing the token for stripe or paypal. It also contains a read only id field.

class ChargeSchema(Schema):
    """
    ChargeSchema
    """

    class Meta:
        """
        Meta class for ChargeSchema
        """
        type_ = 'charge'
        inflect = dasherize
        self_view = 'v1.charge_list'
        self_view_kwargs = {'id': '<id>'}

    id = fields.Str(dump_only=True)
    stripe = fields.Str(allow_none=True)
    paypal = fields.Str(allow_none=True)

Resource

The ChargeList resource only supports POST requests since there is no need for other type of requests. We simply declare the schema, supported methods and the data layer. We also check for required permissions by declaring the decorators.

class ChargeList(ResourceList):
    """
    ChargeList ResourceList for ChargesLayer class
    """
    methods = ['POST', ]
    schema = ChargeSchema

    data_layer = {
        'class': ChargesLayer,
        'session': db.session
    }

    decorators = (jwt_required,)

Layer

The data layer contains a single method create_object which does all the heavy lifting of charging the user according to the payment medium and the related order. It first loads the related order from the database using the identifier.

We first check if the order contains one or more paid tickets or not. If not, then ConflictException is raised since it doesn’t make sense to charge a user without any paid ticket in the order. Next, it checks the payment mode of the order. If the payment mode is Stripe then it checks if the stripe_token is provided with the request or not. If not, an UnprocessableEntity exception is raised otherwise relevant methods are called in order to charge the user accordingly. A similar procedure is followed for payments via Paypal. Below is the full code for reference.

class ChargesLayer(BaseDataLayer):

    def create_object(self, data, view_kwargs):
        """
        create_object method for the Charges layer
        charge the user using paypal or stripe
        :param data:
        :param view_kwargs:
        :return:
        """
        order = Order.query.filter_by(id=view_kwargs['id']).first()
        if not order:
            raise ObjectNotFound({'parameter': 'id'},
                                 "Order with id: {} not found".format(view_kwargs['id']))
        elif order.status == 'cancelled' or order.status == 'expired':
            raise ConflictException({'parameter': 'id'},
                                    "You cannot charge payments on a cancelled or expired order")
        elif (not order.amount) or order.amount == 0:
            raise ConflictException({'parameter': 'id'},
                                    "You cannot charge payments on a free order")

        # charge through stripe
        if order.payment_mode == 'stripe':
            if not data.get('stripe'):
                raise UnprocessableEntity({'source': ''}, "stripe token is missing")
            success, response = TicketingManager.charge_stripe_order_payment(order, data['stripe'])
            if not success:
                raise UnprocessableEntity({'source': 'stripe_token_id'}, response)

        # charge through paypal
        elif order.payment_mode == 'paypal':
            if not data.get('paypal'):
                raise UnprocessableEntity({'source': ''}, "paypal token is missing")
            success, response = TicketingManager.charge_paypal_order_payment(order, data['paypal'])
            if not success:
                raise UnprocessableEntity({'source': 'paypal'}, response)
        return order

The charge_stripe_order_payment and charge_paypal_order_payment are helper methods defined to abstract away the complications of the procedure from the layer.

References

Continue ReadingCharges Layer in Open Event Server

Connecting to a Raspberry Pi through a SSH connection Wirelessly

The tech stack of the SUSI.AI smart speaker project is mainly Python/Bash scripts. Every smart speaker has an essential feature that allows the user’s mobile device to connect and give instructions to the speaker wirelessly. To make this connection possible, we are trying to implement this using an SSH connection.

Why SSH?

SSH(a.k.a Secure Shell) is a cryptographic connection which allows secure transfer of data even over an unsecured connection.SSH connection even allows TCP as well as X11 forwarding which are an added bonus.

Step 1: Initial Setup

  • Both the raspberry Pi with raspbian installed and the mobile device should be on a same wireless network
  • One should have an SSH viewer like JuiceSSH(Android) and iTerminal(IOS) installed on their mobile devices
  • Now we must enable SSH on our raspberry Pi

Step 2: Enabling SSH on Raspberry PI

  • To enable SSH on your Pi , follow the steps mentioned below:
Menu > Preferences > Raspberry Pi Configuration.

Choose the interfaces tab and enable SSH

Step 3:Setting Up the client

 

  • Login to your raspberry pi as the root user (pi by default)
  • Type the following command to know the broadcasting ip address
pi@raspberrypi:hostname -I

 

  • Now , open the client on your mobile device and add the configurations

By default the username of the system is ‘pi’ and the password is ‘raspberry’

Step 4: Changing the default SSH password

Since the default password of every RaspberryPi is the same. So , the pi can be accessed by any device that has access to the local network which is not a secure way of accessing the device

  • In the SSH window type ‘passwd’
  • Type the current password
  • Type the new password
  • Re-enter the new password

Now you will be able to login to your raspberry through an SSH connection

Resources


Tags

Fossasia, gsoc, gsoc’18, susi, susi.ai, hardware,susi_linux

Continue ReadingConnecting to a Raspberry Pi through a SSH connection Wirelessly

Producing Waveforms using Wave Generator module in the PSLab Android App

This blog will demonstrate how to produce different waveforms using the Wave Generator module in the PSLab android app and view them on the Oscilloscope.

The Wave Generator in PSLab android app is simple to use and has a UI which is similar to physical commodity wave generators. It is capable of producing different waveforms like sine, sawtooth and square wave.

Apparatus Required

Before getting started with the wave generator we require the following items:

  1. PSLab device
  2. An android phone with PSLab app installed in it.
  3. USB cable (Mini B)
  4. OTG(On the Go) wire
  5. Some connecting wires having pins at both ends

Understanding the Wave Generator Pins

Figure 1 shows the pin diagram of the PSLab device

Let me briefly explain the use of the pins that are going to be used in the Wave generator module:

S1 and S2 pins

The PSLab device contains two pins (S1, S2) which are capable of producing two independent analog waveforms (sine,  sawtooth) having different frequencies and phase offset. The frequency range is from 10Hz to 5Khz.

SQR1, SQR2, SQR3 and SQR4 pin

The SQR1 pin is used for producing the square waveform and all the SQ pins can be used together to produce four different PWM signal having the same frequency. These PWM signal can have a different duty cycle and phase.

CH1, CH2 and CH3 pin

The CH pins are used by the oscilloscope in the  PSLab android app to monitor waveform signals produced by the wave generator pins. They can be used together to simultaneously monitor multiple waveforms.

Setting up the Device

We need to connect the PSLab device with the mobile phone as shown in Figure 2 which can be done by following steps:

  1. Connect a micro USB(Mini B) to the PSLab device.
  2. Connect the other end of the micro USB cable to the OTG.
  3. Connect the OTG to the phone.
Figure 2 shows the connection of the PSLab device with the smartphone

Producing Waveforms

Now, once the device has been properly connected to the device (which is shown at the top right corner of the app), then in the instruments page scroll down to the Wave Generator card and click on it to open the WaveGenerator activity.

Figure 3 shows the instruments containing card view to all the instruments and icon to show device status

Here you will see a screen like shown in Figure 4 containing two monitors and a controlling panel with lots of buttons. Here the Waveform panel is used to control the S1 and S2 pins whose properties are shown on the left monitor screen and the Digital panel is used to control the SQR pins whose properties are shown on the right monitor screen.

Figure 4 shows the UI of the Wave Generator Activity

For sine/sawtooth wave:

Connect the S1 pin to the CH1 pin using a connecting wire, then in the Waveform panel select the Wave1 button, choose the type of waveform(either sine or sawtooth), then click on the Freq button to change the frequency of the wave, then use the Seek bar or the up/down arrow buttons to change the value of frequency and then press the set button to set the frequency for the S1 pin as shown below:

Figure 5 The GIF shows the setting of the properties of the W1 pin in the UI

Now, click the view button at bottom right corner, this will directly open the Oscilloscope provided by the PSLab android app .

Once the oscilloscope is open, check the CH1 pin from the panel in the bottom and we can see the sine wave in the monitor shown by the screen in Figure 6 and Figure 7

Figure 6 shows the screenshot of oscilloscope showing the sine wave
Figure 7 shows the screenshot of the oscilloscope showing sawtooth wave

Similarly, if you want to see two sine waves connect the S1 pin to the CH1 and connect the S2 pin to the CH2 channel , choose the wave-type for both pin, set the frequencies for both of the waves, here you can also set the phase difference between the two waves, for setting phase difference first click on Wave2 button it will enable the phase button, then click on the Phase button and set the value of phase with the help of the Seek bar.

For Square Wave

Connect the CH1 pin to the SQ1 pin, after making the connection head over to the Digital panel in the Wave Generator, ensure that the mode is selected to square, now click on the Freq button in the digital panel and set the frequency of the square wave with the help of Seek bar, then click on the Duty button and set the value of duty cycle for the square wave as shown below:

Figure 8 The GIF shows the setting of properties for producing square wave from SQ1 pin

Now, once the square wave has been set click on the view button, the oscilloscope will open then select the CH1 pin and you can see the square wave on the monitor as shown by the screen in Figure 9.

Figure 9 shows the screenshot of the square wave as shown in the oscilloscope

Thus we have produced different waveforms using PSLab wave generator module.

Resources

PSLab device pin diagram  – https://github.com/fossasia/pslab-artwork/blob/master/Sticker/pslabdesign.png

Youtube Video Screencast for Wave Generator – https://www.youtube.com/watch?v=NC2T5kElWbE&t=1s

Continue ReadingProducing Waveforms using Wave Generator module in the PSLab Android App

Generating a Stripe token in the Open Event Android App

To implement the payment functionality in the Open Event Android App using credit cards we are using Stripe. Let’s see how this is being done.

We are taking the sensitive information about the user like the card details and sending it to Stripe’s servers which will return a token encrypting users information which we will use to send it to the Open Event Server to complete the payment.

We need to add the library in the build.gradle file in the dependency block.

//Stripe
implementation 'com.stripe:stripe-android:6.1.2'

 

Next we add Stripe’s Card Input Widget in our layout file. This widget is used to input card details like the card number, expiry date and CVC. It automatically validates the card details. It has a minimum width of 320 px. By default we are setting it’s visibility to gone that is it won’t be visible unless the user selects the payment option.

<com.stripe.android.view.CardInputWidget
android:id="@+id/cardInputWidget"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />

 

The visibility of the input field for card details is determined here. We want to show the Input widget only when the user selects Stripe as the mode of payment.

override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
selectedPaymentOption = paymentOptions[p2]
if (selectedPaymentOption == "Stripe")
rootView.cardInputWidget.visibility = View.VISIBLE
else
rootView.cardInputWidget.visibility = View.GONE
}

 

Next we store the user’s card details in a variable. If any of the details be it card number, expiry date or CVC isn’t correct, the value of the variable becomes null and then we show a message to the user.

val cardDetails: Card? = cardInputWidget.card

if (cardDetails == null)
Toast.makeText(context, "Invalid card data", Toast.LENGTH_LONG).show()

 

This is the most important part where we receive the Stripe token. We are sending a request in a background thread to the Stripe servers using the Stripe API_KEY. In the onSuccess method we receive the token if everything went successfully while in the onError method we display the errors to the user.

cardDetails?.let {
context?.let { contextIt ->
Stripe(contextIt).createToken(
it,
API_KEY,
object : TokenCallback {
override fun onSuccess(token: Token) {
//Send this token to server
Toast.makeText(context, "Token received from Stripe", Toast.LENGTH_LONG).show()
}

override fun onError(error: Exception) {
Toast.makeText(context, error.localizedMessage.toString(), Toast.LENGTH_LONG).show()
}
})
}
}

 

Resources

  1. Stripe Android documentation: https://stripe.com/docs/mobile/android  
  2. Stripe Documentation: https://stripe.com/docs/quickstart
  3. Stripe Android Github: https://github.com/stripe/stripe-android
Continue ReadingGenerating a Stripe token in the Open Event Android App

Stepper Motors Experiment with PSLab

PSLab device is capable of building up a complete science lab almost anywhere. While the privilege is mostly taken by high school students and teachers to perform scientific experiments, electronic hobbyists can greatly be influenced from the device. One of the usages is to test and debug sensors and other electronic components before actually using them in their projects. This blog will explain how steppers motors can be used with PSLab.

A stepper motor is an electromechanical device which converts electrical power into mechanical power. Also it is a brushless, synchronous electric motor that can divide a full rotation into an expansive number of steps. The stepper motor uses the theory of operation for magnets to make the motor shaft turn a precise distance when a pulse of electricity is provided. Stepper motors are similar to switched reluctance motors. [1]


Figure 1: Showing the working of a stepper motor [4]                                                      

Figure 1 shows the animation of a simplified stepper motor. Unlike a brushless DC motor which rotates continuously when a fixed DC voltage is applied to it, a step motor rotates in discrete step angles as shown in the above figure.

How Stepper Motors Work?

  • Stepper Motor works on the principle of electromagnetism.
  • Stepper motors consist of a permanent magnetic rotating shaft, called the     rotor, and electromagnets on the stationary portion that surrounds     the motor, called the stator.
  • Figure 1 illustrates one complete rotation of a stepper motor. At position 1, we can see that the rotor is beginning at the upper     electromagnet, which is currently active (has voltage applied to it).
  • To move the rotor clockwise (CW), the upper electromagnet is deactivated and the right electromagnet is activated, causing the rotor to move 90 degrees CW, aligning itself with the active magnet.
  • This process is repeated in the same manner at the south and west     electromagnets until we once again reach the starting position.

           Figure  (2): Showing different stages of stepper motors’ working cycle [3]

What are the most common reasons to choose stepper motors over other types? [2]

  1. Positioning     Since steppers move in precise repeatable steps, they excel in applications requiring precise positioning such as 3D printers, CNC, Camera platforms and X,Y Plotters. Some disk drives also use stepper motors to position the read/write head.
  2. Speed Control – Precise increments of movement also allow for excellent control of rotational speed for process automation and robotics.
  3. Low Speed Torque – Normal DC motors don’t have very much torque at low speeds. A Stepper motor has maximum torque at low     speeds, so they are a good choice for applications requiring low speed with high precision.

Applications of Stepper Motors [2]

  1. Industrial Machines – Stepper motors are used in automotive gauges and machine tooling automated production equipments.
  2. Office Equipments – Stepper motors are incorporated inside PC based scanning equipment, data storage tape drives, optical disk drive head driving mechanism, printers, bar-code printers, scanners
  3. Medical – Stepper motors are used inside medical scanners, samplers, and also found inside digital dental photography, fluid pumps, respirators and blood analysis machinery.
  4. Consumer Electronics – Stepper motors in cameras for automatic digital camera focus and zoom functions.

       

Figure  (3) :Figure showing stepper motors being used in robo -arms [5]

Implementation of Stepper Motor in PSLab

Figure  (4) :A screenshot of Stepper Motor Experiment using PSLab Android App.

  • In the PSLab the stepper motor experiment is implemented to tell the user what a stepper motor is  and how to use it.
  • There is one field to enter the number of steps i.e the breaks in one one rotation which the stepper motor will have.
  • Using PSLab device experiment “Stepper Motor”, a user can acquire any number of steps just by entering the step value in the text box.
  • The following code is implemented for executing the function.
private void setSteps() {
         int stepCount = Integer.parseInt(steps.getText().toString());
         if (stepCount > 0) {
               stepForward(stepCount);
         } else {
               stepBackward(stepCount);
         }
}
  • The other two buttons are designed for choosing the direction in which the motor  will rotate.
  • The following code is for the backward function.
private void stepBackward(final int steps) {
    java.lang.Runnable runnable = new java.lang.Runnable() {
        @java.lang.Override
        public void run() {
            scienceLab.stepBackward(steps, 100);
        }
    };
    new java.lang.Thread(runnable).start();
}
  • Thus when the stepper motor  is connected to the PSLab device and the android application experiment is made to run, the stepper motor will rotate accordingly.

Resources

  1. https://learn.adafruit.com/all-about-stepper-motors/what-is-a-stepper-motor
  2. https://www.elprocus.com/stepper-motor-types-advantages-applications/
  3. https://www.imagesco.com/articles/picstepper/02.html
  4. https://en.wikipedia.org/wiki/Stepper_motor
  5. https://www.instructables.com/id/Robot-Arm-MK2-Plus-Stepper-Motor-Used/
Continue ReadingStepper Motors Experiment with PSLab

Implementing Access Codes in Open Event Frontend

The Open-Event-Frontend allows the event organiser to create access codes for his or her event.  Access codes can be used to password protect hidden tickets reserved for sponsors, members of the press and media. This blog post explains how we have integrated access codes creation in the frontend utilising the various features of Ember JS and Semantic UI.

Create Access code component

We will be creating a separate component for creating access code. To create it we will use the following command:

ember g component forms/events/view/create-access-code

This will create the following files:

  1. components/forms/events/view/create-access-code.js
  2. templates/components/forms/events/view/create-access-code.hbs
  3. tests/integration/components/forms/events/view/create-access-code-test.js

Create-access-code.hbs

This file includes the handlebar syntax to design the front end of the access code component. The whole template is nested inside the Semantic UI’s form class. Some of the helpers used are as follows:

  • Ember Input Helper: It has been used extensively throughout the template in order to take input from the event organizer. For e.g.:
{{input type=‘text’ name=‘access_code’ value=data.code}}
  • Semantic Radio Button: The semantic radio button has been used in order to allow the organizer to select the state of the access-code. He/She can choose if the access-code is active or inactive.

<div class="grouped inline fields">
    <label class="required">{{t 'Status'}}</label>
    <div class="field">
      {{ui-radio current=data.isActive name='status' label='Active' value='true' onChange=(action (mut data.isActive))}}
    </div>
    <div class="field">
      {{ui-radio name='status' label='Inactive' value='false' current=data.isActive onChange=(action (mut data.isActive))}}
    </div>
  </div>
  • Date Time Picker: The organizer can set the validity of the access code as well. We have used date-picker and time-picker components which were already created in the project. They have been used in the following way:

<div class="fields">
        <div class="wide field {{if device.isMobile 'sixteen' 'five'}}">
          <label>{{t 'Valid from'}}</label>
          {{widgets/forms/date-picker id='start_date' value=data.validFromDate rangePosition='start'}}
          <div class="ui hidden divider"></div>
          {{widgets/forms/time-picker id='start_time' value=data.validFromTime rangePosition='start'}}
        </div>
        <div class="wide field {{if device.isMobile 'sixteen' 'five'}}">
          <label>{{t 'Expires on'}}</label>
          {{widgets/forms/date-picker id='end_date' value=data.validTillDate rangePosition='end'}}
          <div class="ui hidden divider"></div>
          {{widgets/forms/time-picker id='end_time' value=data.validTillTime rangePosition='end'}}
        </div>
      </div>

Create-access-code.js

We use this file as the core of the component and handle the following use cases:

  1. Validation of the input. We show warning if something is wrong.
  2. Actions used by the various elements of the templates.
  3. Providing the link for the access-code.
  4. Saving the access-code.

accessCode : '',
  accessUrl  : computed('data.code', function() {
    const params = this.get('router._router.currentState.routerJsState.params');
    this.set('data.accessUrl', location.origin + this.get('router').urlFor('public', params['events.view'].event_id, { queryParams: { access_code: this.get('data.code') } })) ;
    return this.get('data.accessUrl');
  }),
  actions: {
    toggleAllSelection(allTicketTypesChecked) {
      this.set('allTicketTypesChecked', allTicketTypesChecked);
      if (allTicketTypesChecked) {
        this.set('data.tickets', this.get('data.event.tickets').slice());
      }
    },
    updateTicketSelections(newSelection) {
      if (newSelection.length === this.get('data.event.tickets').length) {
        this.set('allTicketTypesChecked', true);
      } else {
        this.set('allTicketTypesChecked', false);
      }
    },
    submit(data) {
      this.onValid(() => {
        data.save()
          .then(() => {
            this.get('notify').success(this.get('l10n').t('Access code has been successfully created.'));
            this.get('router').transitionTo('events.view.tickets.access-codes');
          })
          .catch(() => {
            this.get('notify').error(this.get('l10n').t('An unexpected error has occurred. Access code cannot be created.'));
          });
      });
    }
  }

Create-access-code-test.js

We can specify the tests in order to test the compatibility of the component here. For now, we will just write a simple test which checks if the component is rendered or not.

import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';

module('Unit | Controller | events/view/tickets/access codes/create', function(hooks) {
  setupTest(hooks);


  test('it exists', function(assert) {
    let controller = this.owner.lookup('controller:events/view/tickets/access-codes/create');
    assert.ok(controller);
  });
});

Now that we are done, setting up our component, we just need to add it in our application. We can achieve that using the following:

{{forms/events/view/create-access-code data=model}}

The model passed to the component is fetched from the create-access-code.js file.

References

Continue ReadingImplementing Access Codes in Open Event Frontend

Implementing Custom Date and Time Picker with 2-way Data Binding Support

The Data binding library is one of the most popular libraries among the android developers. We have been using it in the Open Event Organiser Android app for building interactive UI’s for some time now. The Open Event Organiser Android App is the Event management app for organizers using the Open Event Platform. This blog explains how we implemented our own custom Date and Time picker with 2-way data binding support using the Data binding framework.

Why custom picker ?

One specific requirement in the app is to have a button, clicking on that button should open a DatePicker which would allow the user to select the date. A similar behaviour was required to allow the user to select the time as well. In order to handle this requirement we were using Binding Adapters on Button. For eg. the following Binding Adapter allowed us to define a property date on a button and set an Observable String as it’s value. We implemented a similar Binding Adapter for selecting time as well.

@BindingAdapter("date")
public static void bindDate(Button button, ObservableField<String> date) {
    String format = DateUtils.FORMAT_DATE_COMPLETE;

    bindTemporal(button, date, format, zonedDateTime ->
        new DatePickerDialog(button.getContext(), (picker, year, month, dayOfMonth) ->
                setPickedDate(
                    LocalDateTime.of(LocalDate.of(year, month + 1, dayOfMonth), zonedDateTime.toLocalTime()),
                    button, format, date),
                zonedDateTime.getYear(), zonedDateTime.getMonthValue() - 1, zonedDateTime.getDayOfMonth()));
  }

It calls the bindTemporal method which takes in a function along with the button, date and the format and does two things. First, it sets the value of the date as the text of the button. Secondly, it attaches a click listener to the button and applies the function passed in as the argument when clicked. Below is the bindTemporal method for reference:

private static void bindTemporal(Button button, ObservableField<String> date, String format, Function<ZonedDateTime, AlertDialog> dialogProvider) {
        if (date == null)
            return;

        String isoDate = date.get();
        button.setText(DateUtils.formatDateWithDefault(format, isoDate));

        button.setOnClickListener(view -> {
            ZonedDateTime zonedDateTime = ZonedDateTime.now();
            try {
                zonedDateTime = DateUtils.getDate(isoDate);
            } catch (DateTimeParseException pe) {
                Timber.e(pe);
            }
            dialogProvider.apply(zonedDateTime).show();
        });
    }

It was working pretty well until recently when we started getting deprecation warnings about using Observable fields as a parameter of Binding Adapter. Below is the full warning:

Warning:Use of ObservableField and primitive cousins directly as method parameters is deprecated and support will be removed soon. Use the contents as parameters instead in method org.fossasia.openevent.app.common.app.binding.DateBindings.bindDate

The only possible way that we could think of was to pass in regular String in place of Observable String. Now if we pass in a regular String object then the application won’t be reactive. Hence we decided to implement our own custom view to resolve this problem.

Custom Date and Time Picker

We decided to create an Abstract DateTimePicker class which will hold all the common code of our custom Date and Time pickers. It is highly recommended that you go through this awesome blog post first before reading any further. We won’t be going through the details already explained in the post.

Following are the important features of this Abstract class:

  1. It extends the AppCompatButton class.
  2. It stores an ObservableString named value and an OnDateTimeChangedListener as it’s field. We will discuss the change listener later in the article.
  3. It implements the three mandatory constructors and calls it’s super method. It also calls the init method which sets the current date and time as the default.
  4. It has a bindTemporal method which is the same as we discussed earlier.
  5. It has a setPickedDate method which sets the selected date/time as the text for the button so that users can see the selected date/time on the button itself. Moreover it notifies the change listener about the change in date if attached.
  6. It has an abstract method called setValue. It will be implemented in the sub classes and used to set the date or time value for the field named value.

You can check the full implementation here.

The OnDateTimeChangedListener which we mentioned above is an extremely simple interface. It defines a simple method onDateChanged which takes in the selected date as the argument.

public interface OnDateTimeChangedListener {
    void onDateChanged(ObservableString newDate);
}

Let’s have a look at the implementation of the DatePicker class. The key features of this class are:

  1. It extends the AbstractDateTimePicker class and implements the necessary constructors calling the corresponding super constructor.
  2. It implements the method setValue which sets the date or time passed in to the field value. It also calls the bindTemporal method of the super class.

public class DatePicker extends AbstractDateTimePicker {
    public DatePicker(Context context) {
        super(context);
    }

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

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

  public void setValue(String value) {
        ObservableString observableValue = getValue();
        if (observableValue.get() == null || !TextUtils.equals(observableValue.get(), value)) {
            observableValue.set(value);
            String format = DateUtils.FORMAT_DATE_COMPLETE;

            bindTemporal(value, format, zonedDateTime ->
                new DatePickerDialog(this.getContext(), (picker, year, month, dayOfMonth) ->
                    setPickedDate(
                        LocalDateTime.of(LocalDate.of(year, month + 1, dayOfMonth), zonedDateTime.toLocalTime()), format),
                    zonedDateTime.getYear(), zonedDateTime.getMonthValue() - 1, zonedDateTime.getDayOfMonth()));
        }
    }
}

Next we discuss the BindingAdapter and the InverseBindingAdapter for the custom DatePicker which allows the data binding framework to set the action to be performed when date changes and get the date from the view respectively.

@BindingAdapter(value = "valueAttrChanged")
public static void setDateChangeListener(DatePicker datePicker, final InverseBindingListener listener) {
        if (listener != null) {
            datePicker.setOnDateChangedListener(newDate -> listener.onChange());
        }
    }

@InverseBindingAdapter(attribute = "value")
public static String getRealValue(DatePicker datePicker) {
    return datePicker.getValue().get();
}

Now in order to use our view, we can simply define it in the layout file as shown below:

<org.fossasia.openevent.app.ui.views.DatePicker
                    style="?attr/borderlessButtonStyle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="@color/purple_500"
                    app:value="@={ date }" />

The key thing to notice is the use of @= instead of @ which denotes two way data binding.    

Conclusion

The Android Data binding framework is extremely powerful and flexible at the same time. We can use it for our custom requirements as shown in this article.

References

 

 

 

 

 

Continue ReadingImplementing Custom Date and Time Picker with 2-way Data Binding Support

Implementing the discrete Seekbar for Wave Generator

The Wave Generator instrument in PSLab Android app allows us to produce waveforms having different values of properties like frequency, duty, phase etc.

The range of these properties allowed by PSLab Device are :

Table showing the range of properties that can be set for waves by PSLab device
Wave Property Range
Min Max Step Size
Frequency 10 Hz 5000 Hz 1 Hz
Phase 360°
Duty 10% 100% 10%

We can set these values using the up/down arrow buttons provided by the wave generator but the problem is that the range of values is very high and least counts are small so it is convenient to set the values using only the up and down arrow buttons.

Therefore we need something that could allow us to directly set any value of our choice while keeping the UI interactive.

The solution to this problem – “Discrete Seekbar”. It contains a slider having points at equal intervals and whose length represents the range of the values and a head that slides over the slider and is used to select a specific value from a range of values.

I have included the discrete Seekbar in Wave Generator by using a third-party library if you want to add Seekbar directly you can do that by directly using the default Seekbar widget provided by Android SDK and setting the following attribute in as shown below.

android:theme = “@style/Widget.AppCompat.SeekBar.Discrete”

Refer to this post[2] for implementing Seekbar directly without an external library.

The reason I chose this library is that:-

  • It offers various implementation of different types of Seekbar like discrete and continuous.
  • Implementation of Seekbar is simpler and it offers various customizations like thumb color, track color, tick text etc.  

In following steps I will implement the discrete Seekbar:

Step 1 Adding the dependency

For this project, I will be using an external library “IndicatorSeekbarLibrary” by Warkiz[1], for adding the dependency we need to include the following code in our build.gradle file.

dependencies{
implementation 'com.github.warkiz.widget:indicatorseekbar:2.0.9'
}

Step 2 Including the Seekbar in layout

For this step, we need to add the Seekbar widget using <com.warkiz.widget.IndicatorSeekBar> XML tag in our wave generator layout file to include the Seekbar in our layout as shown in the code below:

<com.warkiz.widget.IndicatorSeekBar
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:isb_max="5000"
    app:isb_min="0"
    app:isb_ticks_count="5"
    app:isb_thumb_color="@color/color_green"
    app:isb_thumb_size="20dp"
    app:isb_track_background_color="@color/color_gray"
    app:isb_track_background_size="2dp"
    app:isb_track_progress_color="@color/color_blue"
    app:isb_track_progress_size="4dp" />

Some important attributes used above:

app:isb_max : defines the max value that can be achieved by the Seekbar.

app:isb_min :  defines the min value that can be achieved by the Seekbar

app:isb_ticks_count: no. of ticks(interval) that has to be shown on the slider

We can see different components of Seekbar like track, indicator, thumb, tick of SeekBar in the following diagram[2].

Figure 1 depicts the different attributes of the slider
(Source – https://github.com/warkiz/IndicatorSeekBar/blob/master/README.md)

Step 3 Attaching the listener to the Seekbar in Java file

In this step we need to attach the listener to the Seekbar to record changes in the Seekbar made by the user, for this we will create a new listener with the help of onSeekBarChangeListener interface and attach it with the Seekbar as shown in following code

IndicatorSeekBar seekbar = (IndicatorSeekBar) findViewbyId(R.id.seekbar);

seekBar.setOnSeekChangeListener(new OnSeekChangeListener() {
            @Override
            public void onSeeking(SeekParams seekParams) {
                /* called when the user is sliding the thumb */
            }

            @Override
            public void onStartTrackingTouch(IndicatorSeekBar seekBar) {
                /* called when the sliding of thumb is started */
            }

            @Override
            public void onStopTrackingTouch(IndicatorSeekBar seekBar) {
                /* called when the sliding of thumb stops */
            }
        });

After following all the above steps, I  implemented the Seekbar shown in Figure 2 below in my wave generator and now it becomes really easy to set different values of properties for without having to continually press the up/down button.

Figure 2 shows the Seekbar included in wave generator beside up/down arrow button

Resources

  1. warkiz/IndicatorSeekBar library  – Github Repo of the Indicator SeekBar library
  2. http://nileshsenta.blogspot.com/2016/10/discrete-seekbar-without-third-party.html – Blog by Nilesh Shenta on how to implement discrete without third party library

 

Continue ReadingImplementing the discrete Seekbar for Wave Generator

Adding product flavors to SUSI Android app

Product flavors are required by production level apps to provide different versions of their app available to people. Each of the flavor has separate functionality depending on the type of flavors chosen. Most common flavors are full and demo flavors, from their name only we can understand that these flavors contain full version and demo version of their apps. Since SUSI.AI android app has to be released on FDroid several libraries that were present in the current app were not  acceptable by them as it only allows apps that completely use only open source software to develop and therefore there was a need to make the app compatible with both the app stores (Play Store and FDroid) product flavors were added in the app.

Proprietary software in the app

There are several libraries which are not acceptable on FDroid but are present in the app, a couple of them being :

  1. Crashlytics using fabric
  2. LinkPreview library

These two libraries were found which are not acceptable in FDroid. Although the link preview library was not proprietary software, but fdroid has guidelines which only allows that recognised and trusted libraries from maven repository is accepted.

Further, youtube api recently integrated in the app is also not acceptable by FDroid as it is under google and categorised as a proprietary software.

Adding product flavors

The flavor names chosen to be made were “fdroid” and “playStore”, fdroid here specifying the build variant for the app that would contain the libraries and code acceptable by fdroid and playStore flavor to contain the sources and libraries for the regular version of the app which can contain proprietary software.

The flavors were added as shown below in the module level build.gradle file :

flavorDimensions “default”

productFlavors {
  fdroid {
      dimension “default”
      applicationIdSuffix “.fdroid”
      versionNameSuffix “-FDroid”
  }

  playStore {
      dimension “default”
      versionNameSuffix “-PlayStore”
  }
}

the flavors are added under the block of code by the name productFlavors, and each flavor needs to have some properties to be set. The applicationId of the app can be changed or can be kept same, in our case for the fdroid flavor the applicationIdSuffix was added which adds “.fdroid” at the end of the applicationId for the fdroid version.

versionNameSuffix property was set in both the flavors which adds “-FDroid” to end of the versionname for the fdroid variant and “-PlayStore” to the end of the versionname for the playstore variant.

Using the product flavors we can generate multiple apks , as in our case after the build flavors were added there were four apks built. These can be seen as  :

Choosing any of these apks we can select the variant that is to be worked upon. The debug apks here are those used for developing purposes.

Conclusion

Adding flavors allows SUSI.AI android app to keep separate code and resources for different variants. Any code that is different to both the variants is added separately after creating the directories named “fdroid” and “playStore” in the src folder.

This was added after switching to the Project view in Android Studio and then moving to the app folder and then inside the src folder make a directories named fdroid and playStore.

References

  1. Configure Build Variants: https://developer.android.com/studio/build/
  2. Android Product Flavors: https://medium.com/@iammert/android-product-flavors-1ef276b2bbc1

Handling multiple java sources and flavors using flavors on Gradle: https://medium.com/@thiagolopessilva/the-handling-multiple-java-source-and-resources-using-flavors-on-gradle-18a4b581285b

Continue ReadingAdding product flavors to SUSI Android app

Make a cumulative API to return skills based on standard metrics in SUSI.AI

In this blog post, we are going to discuss on how to make a cumulative API which returns skills based on different standard metrics. It was implemented to combine various API calls, that were made from various clients, thereby reducing the number of API calls made. It lead to an optimization in the page load time of various clients and also helped to reduce the 503 errors that were received, due to very frequent API hits. The API endpoint for it is https://api.susi.ai/cms/getSkillMetricsData.json.

It accepts 5 optional parameters –

  • model – It represents the model for which the skill are fetched. The default value is set to General.
  • group – It represents the group(category) for which the skill are fetched. The default value is set to Knowledge.
  • language – It represents the language for which the skill are fetched. The default value is set to en.
  • duration – It represents the duration based on which skills are fetched by standard metrics like usage.
  • count – It represents the number of skills to be returned on a particular metric.

The minimalUserRole is set to ANONYMOUS for this API, as the data is required for home-page and doesn’t need the user to be logged-in.

Going through the API development

  • The parameters are first extracted via the call object that is passed to the main function. The  parameters are then stored in variables. If any parameter is absent, then it is set to the default value.
  • There is a check if the count param exists. If exists, then it is checked if it is of valid data-type. If no, an exception is thrown. And if count param doesn’t exist, it is set to default of 10.
  • This code snippet discusses the above two points –
@Override
public ServiceResponse serviceImpl(Query call, HttpServletResponse response, Authorization rights, final JsonObjectWithDefault permissions) throws APIException {

        String model_name = call.get("model", "general");
        File model = new File(DAO.model_watch_dir, model_name);
        String group_name = call.get("group", "All");
        String language_name = call.get("language", "en");
        int duration = call.get("duration", 0);
        JSONArray jsonArray = new JSONArray();
        JSONObject json = new JSONObject(true);
        JSONObject skillObject = new JSONObject();
        String countString = call.get("count", null);
        Integer count = null;

        if(countString != null) {
            if(Integer.parseInt(countString) < 0) {
                throw new APIException(422, "Invalid count value. It should be positive.");
            } else {
                try {
                    count = Integer.parseInt(countString);
                } catch(NumberFormatException ex) {
                    throw new APIException(422, "Invalid count value.");
                }
            }
        } else {
            count = 10;
        }
.
.
.

 

  • Then the skills are fetched based on the group name and then stored in a JSONArray named jsonArray. This array basically contains the metadata objects of each skill.
  • Now, we need to sort and filter these skills based on standard metrics like – Rating, Feedback Count, Usage Count, Latest skills.
  • The implementation for getting the skills based on the creation time is as follows. Rest, all the metrics were also implemented in the same fashion.

JSONObject skillMetrics = new JSONObject();
List<JSONObject> jsonValues = new ArrayList<JSONObject>();

// temporary list to extract objects from skillObject
for (int i = 0; i < jsonArray.length(); i++) {
    jsonValues.add(jsonArray.getJSONObject(i));
}

// Get skills based on creation date - Returns latest skills
Collections.sort(jsonValues, new Comparator<JSONObject>() {
    private static final String KEY_NAME = "creationTime";
    @Override
    public int compare(JSONObject a, JSONObject b) {
        String valA = new String();
        String valB = new String();
        int result = 0;

        try {
            valA = a.get(KEY_NAME).toString();
            valB = b.get(KEY_NAME).toString();
            result = valB.compareToIgnoreCase(valA);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return result;
    }
});

JSONArray creationDateData = getSlicedArray(jsonValues, count);
skillMetrics.put("latest", creationDateData);

.
.
.

 

  • The above code snippet deals with sorting the skills based on the creation time, that helps us to fetch the latest skills. The latest skills are stored on the skillMetrics object with the key name latest.
  • Similarly, the skills based on metrics like rating, feedback count and usage are stored with key names rating, feedback & usage respectively.

From the above snippet, we can also see a call to the function getSlicedArray. It takes a list of skills and count as input paramters. It returns the first ‘count’ skills from the list depending on the count value. The implementation of it is as follows –

private JSONArray getSlicedArray(List<JSONObject> jsonValues, Integer count)
{
    JSONArray slicedArray = new JSONArray();
    for (int i = 0; i < jsonValues.size(); i++) {
        if(count == 0) {
            break;
        } else {
            count --;
        }
        slicedArray.put(jsonValues.get(i));
    }
    return slicedArray;
}

 

  • The response object is then sent with 6 (six) key values mainly, apart from the session object. They are
    • accepted –  true – It tells that the skills have been fetched.
    • message – “Success: Fetched skill data based on metrics”
    • model –  It is the model that is sent on request params or the default value.
    • group –  It is the group that is sent on request params or the default value.
    • language – It is the language that is sent on request params or the default value.
    • metrics –  It is the main object of relevance for this API, which contains 4 child keys with values as an array of Skill Metadata objects.

{
  "accepted": true,
  "model": "general",
  "group": "All",
  "language": "en",
  "metrics": {
    "feedback": [
    {skill_1}, {skill_2}, ...
    ],
    "usage": [
    {skill_1}, {skill_2}, ...
    ],
    "rating": [
    {skill_1}, {skill_2}, ...
    ],
    "latest": [
    {skill_1}, {skill_2}, ...
    ]
  },
  "message": "Success: Fetched skill data based on metrics",
  "session": {
   ....
  }
}

 

I hope the development of creating the aforesaid API and the purpose of it is clear and proved to be helpful for your understanding.

References

Continue ReadingMake a cumulative API to return skills based on standard metrics in SUSI.AI