Building Preference Screen in SUSI Android

SUSI provides various preferences to the user in the settings to customize the app. This allows the user to configure the application according to his own choice. There are different preferences available such as to select the theme or the language for text to speech. Preference Setting Activity is an important part of an Android application. Here we will see how we can implement it in an Android app taking SUSI Android (https://github.com/fossasia/susi_android) as the example.

Firstly, we will proceed by adding the Gradle Dependency for the Setting Preferences

compile 'com.takisoft.fix:preference-v7:25.4.0.3'

Then to create the custom style for our setting preference screen we can set

@style/PreferenceFixTheme

as the base theme and can apply various other modifications and color over this. By default it has the usual Day and Night theme with NoActionBar extension.

Now to make different preferences we can use different classes as shown below:

SwitchPreferenceCompat: This gives us the Switch Preference which we can use to toggle between two different modes in the setting.

EditTextPreference: This preference allows the user to give its own choice of number or string in the settings which can be used for different actions.

For more details on this you can refer the this link.

Implementation in SUSI Android

In SUSI Android we have created an activity named activity_settings which holds the Preference Fragment for the setting.

<?xml version="1.0" encoding="utf-8"?>


<fragment

  xmlns:android="http://schemas.android.com/apk/res/android"

  xmlns:tools="http://schemas.android.com/tools"

  android:id="@+id/chat_pref"

  android:name="org.fossasia.susi.ai.activities.SettingsActivity$ChatSettingsFragment"

  android:layout_width="match_parent"

  android:layout_height="match_parent"

  tools:context="org.fossasia.susi.ai.activities.SettingsActivity"/>

The Preference Settings Fragment contains different Preference categories that are implemented to allow the user to have different customization option while using the app. The pref_settings.xml is as follows

<?xml version="1.0" encoding="utf-8"?>

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"

  xmlns:app="http://schemas.android.com/apk/res-auto"

  android:title="@string/settings_title">


  <PreferenceCategory

      android:title="@string/server_settings_title">

      <PreferenceScreen

          android:title="@string/server_pref"

          android:key="Server_Select"

          android:summary="@string/server_select_summary">

      </PreferenceScreen>

  </PreferenceCategory>


  <PreferenceCategory

      android:title="@string/settings_title">

      <com.takisoft.fix.support.v7.preference.SwitchPreferenceCompat

          android:id="@+id/enter_key_pref"

          android:defaultValue="true"

          android:key="@string/settings_enterPreference_key"

          android:summary="@string/settings_enterPreference_summary"

          android:title="@string/settings_enterPreference_label" />

  </PreferenceCategory>

All the logic related to Preferences and their action is written in SettingsActivity Java class. It listens for any change in the preference options and take actions accordingly in the following way.

public class SettingsActivity extends AppCompatActivity {


  private static final String TAG = "SettingsActivity";

  private static SharedPreferences prefs;


  @Override

  protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

      prefs = getSharedPreferences(Constant.THEME, MODE_PRIVATE);

      Log.d(TAG, "onCreate: " + (prefs.getString(Constant.THEME, DARK)));

      if(prefs.getString(Constant.THEME, "Light").equals("Dark")) {

          setTheme(R.style.PreferencesThemeDark);

      }

      else {

          setTheme(R.style.PreferencesThemeLight);

      }

      setContentView(R.layout.activity_settings);


  }

The class contains a ChatSettingFragment which extends the PreferenceFragmentCompat to give access to override functions such as onPreferenceClick. The code below shows the implementation of it.

public boolean onPreferenceClick(Preference preference) {

              Intent intent = new Intent();

              intent.setComponent( new ComponentName("com.android.settings","com.android.settings.Settings$TextToSpeechSettingsActivity" ));

              startActivity(intent);

              return true;

          }

      });


      rate=(Preference)getPreferenceManager().findPreference(Constant.RATE);

      rate.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {

          @Override

          public boolean onPreferenceClick(Preference preference) {

              startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=" + getContext().getPackageName())));

              return true;

          }

      });
}

For diving more into the code we can refer to the github repo of Susi Android (https://github.com/fossasia/susi_android).

Resources

Continue ReadingBuilding Preference Screen in SUSI Android

Upload Image to Imgur Anonymously Using Volley in Phimpme Android app

As Phimpme Android is an image app in which lets you share your image on multiple platform without installing that apps like Twitter, Facebook, Pinterest and Imgur. Imgur is the best place to share and enjoy the most awesome images on the Internet. Imgur provides APIs to  upload image from your account as well as anonymously. In this blog I am going to explain how I added the imgur upload feature in Phimpme Android app.

I have implemented upload to  Imgur anonymously. There is step by step guide to implementing it.

Step 1: Register your application on Imgur.

To register your application click here, For more detail read their documentation here Imgur API site.

Once you have registered your application, you will be provided with two keys, Client ID and Client Secret. Save them, we will need them later to upload an image and for = authentication purposes.

Step-2: Add a networking library in your Gradle, I have used volley.

Olley is a good networking library, works well for both API requests and File upload. To add volley in the project, add the following dependency

'com.android.volley:volley:1.0.0'

Step-3 Convert your image into the desired format to upload.

Imgur three kinds of images: A binary file, base64 data, or  URL of an image. (Up to 10MB).

In phimpme Android, we are uploading base64 image string.

So, we have to first convert our image to the bitmap and then convert the bitmap to base64. I recommend using an Image library to decode bitmap otherwise, there are chances of OutOfMemory exception thrown for large image files.

To convert bitmap to base64 string

public static String get64BaseImage (Bitmap bmp) {
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       bmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
       byte[] imageBytes = baos.toByteArray();
       return Base64.encodeToString(imageBytes, Base64.DEFAULT);
   }

Best practice to add such methods into theutility class. Also, you can apply the image compression in above if you want to reduce the image size, here number 100 represents, preserve the  100% quality of the image.

Step-4:  Now we need 2 things, add image string in body and add AUTHENTICATION key in headers of the request.

We have added two methods to do above mentioned tasks.

  1. To upload the image data to Imgur.
  2. To add a header in our network request.

The header is required for Imgur to authenticate the client who uploads the file and your authentication key is your  CLIENT-ID, which we have generated in step 1.

@Override
               protected Map<String, String> getParams() throws AuthFailureError {
                   Map<String, String> parameters = new HashMap<String, String>();
                   parameters.put("image", imageString);
                   if(caption!=null && !caption.isEmpty())
                       parameters.put("title",caption);
                   return parameters;
               }


Now add the headers to authenticate the client.The above code contains the body part with the key as “image” and value as the imageString data, which was the result of get64BaseImage () method.

   @Override
               public Map<String, String> getHeaders() throws AuthFailureError {
                   Map<String, String> headers = new HashMap<String, String>();
                   headers.put("Authorization",”Client-Id {client-id});
                   return headers;
               }

Override the getHeader method of Volley library and return a map which has a key named “Authorization” and value is client id of Imgur.

Now we are ready to upload an image on Imgur through Phimpme Android app.

The problem I faced:

Whenever I was trying to upload large size images, I was getting volleytimeout exception, by default connection timeout was not sufficient to upload large files, so I resolved this error by adding below line in the request policy.

           request.setRetryPolicy(new DefaultRetryPolicy( 50000, 5, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));

Now it works seamlessly with large files even on slow internet network and we are receiving the URL of the image in the response.

Resources :

Continue ReadingUpload Image to Imgur Anonymously Using Volley in Phimpme Android app

Persistence Layer in Open Event Organizer Android App

Open Event Organizer is an Event Managing Android App with the core features of Attendee Check In by QR Code Scan and Data Sync with the Open Event API Server. As an event can be large, so the app will be dealing with a large amount of a data. Hence to avoid repetitive network requests for fetching the data, the app maintains a local database containing all the required data and the database is synced with the server. Android provides android.database.sqlite package which contains the API needed to use the database on the Android. But it is really not a good practice to use the sqlite queries everywhere in the app. So there comes a persistence layer. A persistence layer works between the database and the business logic. Open Event Organizer uses Raizlabs’s DbFlow, an ORM based Android Database Library for the same. I will be talking about its implementation through the app in this blog.

First of all, you declare the base class of the database which is used to create the database by Android for the app. You declare all the base constants here. The class looks like:

@Database(
   name = OrgaDatabase.NAME,
   version = OrgaDatabase.VERSION,
   ...
)
public class OrgaDatabase {
   public static final String NAME = "orga_database";
   public static final int VERSION = 2;
   ...
}

OrgaDatabase.java
app/src/main/java/org/fossasia/openevent/app/data/db/configuration/OrgaDatabase.java

Initialise the database in the Application class using FlowManager provided by the library. Choose the Application class to do this to ensure that the library finds the generated code in the DbFlow.

FlowManager.init(
   new FlowConfig.Builder(context)
       .addDatabaseConfig(
           new DatabaseConfig.Builder(OrgaDatabase.class)
           ...
           .build()
       )
       .build());

OrgaApplication.java
app/src/main/java/org/fossasia/openevent/app/OrgaApplication.java

The database is created now. For tables creation, DbFlow uses model classes which must be annotated using the annotations provided by the library. The basic annotations are – @Table, @PrimaryKey, @Column, @ForeignKey etc.

For example, the Attendee class in the app looks like:

@Table(database = OrgaDatabase.class)
public class Attendee ... {

   @PrimaryKey
   public long id;

   @Column
   public boolean checkedIn;
   ...
   ...
   @ForeignKey(
       onDelete = ForeignKeyAction.CASCADE,
       onUpdate = ForeignKeyAction.CASCADE)
   public Order order;
   ...
}

Attendee.java
app/src/main/java/org/fossasia/openevent/app/data/models/Attendee.java

This will create a table named attendee with the columns and relationships annotated. Now comes the part of accessing data from the database. Open Event App uses RxJava’s support to the DbFlow library which enables async data accessing. The getItems method from DataBaseRepository looks like:

public <T> Observable<T> getItems(Class<T> typeClass, SQLOperator... conditions) {
   return RXSQLite.rx(SQLite.select()
       .from(typeClass)
       .where(conditions))
       .queryList()
       .flattenAsObservable(items -> items);
}

 

The method returns an observable emitting the items from the result. For data saving, the method looks like:

DatabaseDefinition database = FlowManager.getDatabase(OrgaDatabase.class);
FastStoreModelTransaction<T> transaction = FastStoreModelTransaction
   .insertBuilder(FlowManager.getModelAdapter(itemClass))
   .addAll(items)
   .build();
database.executeTransaction(transaction);

 

And for updating data, the method looks like:

ModelAdapter<T> modelAdapter = FlowManager.getModelAdapter(classType);
modelAdapter.update(item);

DatabaseRepository.java
app/src/main/java/org/fossasia/openevent/app/data/db/DatabaseRepository.java

DbFlow provides DirectModelNotifier which is used to get notified of the database change anywhere in the app. Open Event App uses PublishSubjects to send notifications on database change event. The implementation of the DatabaseChangeListener in the app looks like:

public class DatabaseChangeListener<T> ... {
   private PublishSubject<ModelChange<T>> publishSubject = PublishSubject.create();
   private DirectModelNotifier.ModelChangedListener<T> modelModelChangedListener;
   ...
   public void startListening() {
       modelModelChangedListener = new DirectModelNotifier.ModelChangedListener<T>() {
           @Override
           public void onTableChanged(@Nullable Class<?> aClass, @NonNull BaseModel.Action action) {
               // No action to be taken
           }
           @Override
           public void onModelChanged(@NonNull T model, @NonNull BaseModel.Action action) {
               publishSubject.onNext(new ModelChange<>(model, action));
           }
       };
       DirectModelNotifier.get().registerForModelChanges(classType, modelModelChangedListener);
   }
   ...
}

DatabaseChangeListener.java
app/src/main/java/org/fossasia/openevent/app/data/db/DatabaseChangeListener.java

The class is used in the app to get notified of the data change and to update the required local data fields using data from item emitted by the publishSubject of the class. This is used in the app where same data is accessed at more than one places. For example, There are two fragments – AttendeesFragment and AttendeeCheckInFragment from which an attendee’s check in status is toggled. So when the status is toggled from AttendeeCheckInFragment, the change must be updated in the AttendeesFragment’s attendees list. This is carried out using DatabaseChangeListener in the AttendeesPresenter which provides attendees list to the AttendeesFragment. And on the change in the attendee’s check in status, AttendeePresenter’s attendeeListener listens for the change and update the attendee in the list accordingly.

Links:
1. Raizlabs’s DbFlow , an ORM Android Database Library Github Repo Link
2. DbFlow documentation
3. Android database managing API android.database.sqlite

Continue ReadingPersistence Layer in Open Event Organizer Android App

Image Uploading in Open Event API Server

Open Event API Server manages image uploading in a very simple way. There are many APIs such as “Event API” in API Server provides you data pointer in request body to send the image URL. Since you can send only URLs here if you want to upload any image you can use our Image Uploading API. Now, this uploading API provides you a temporary URL of your uploaded file. This is not the permanent storage but the good thing is that developers do not have to do anything else. Just send this temporary URL to the different APIs like the event one and rest of the work is done by APIs.
API Endpoints which receives the image URLs have their simple mechanism.

  • Create a copy of an uploaded image
  • Create different sizes of the uploaded image
  • Save all images to preferred storage. The Super Admin can set this storage in admin preferences

To better understand this, consider this sample request object to create an event

{
  "data": {
    "attributes": {
      "name": "New Event",
      "starts-at": "2002-05-30T09:30:10+05:30",
      "ends-at": "2022-05-30T09:30:10+05:30",
      "email": "example@example.com",
      "timezone": "Asia/Kolkata",
      "original-image-url": "https://cdn.pixabay.com/photo/2013/11/23/16/25/birds-216412_1280.jpg"
    },
    "type": "event"
  }
}

I have provided one attribute as “original-image-url”, server will open the image and create different images of different sizes as

      "is-map-shown": false,
      "original-image-url": "http://example.com/media/events/3/original/eUpxSmdCMj/43c6d4d2-db2b-460b-b891-1ceeba792cab.jpg",
      "onsite-details": null,
      "organizer-name": null,
      "can-pay-by-stripe": false,
      "large-image-url": "http://example.com/media/events/3/large/WEV4YUJCeF/f819f1d2-29bf-4acc-9af5-8052b6ab65b3.jpg",
      "timezone": "Asia/Kolkata",
      "can-pay-onsite": false,
      "deleted-at": null,
      "ticket-url": null,
      "can-pay-by-paypal": false,
      "location-name": null,
      "is-sponsors-enabled": false,
      "is-sessions-speakers-enabled": false,
      "privacy": "public",
      "has-organizer-info": false,
      "state": "Draft",
      "latitude": null,
      "starts-at": "2002-05-30T04:00:10+00:00",
      "searchable-location-name": null,
      "is-ticketing-enabled": true,
      "can-pay-by-cheque": false,
      "description": "",
      "pentabarf-url": null,
      "xcal-url": null,
      "logo-url": null,
      "can-pay-by-bank": false,
      "is-tax-enabled": false,
      "ical-url": null,
      "name": "New Event",
      "icon-image-url": "http://example.com/media/events/3/icon/N01BcTRUN2/65f25497-a079-4515-8359-ce5212e9669f.jpg",
      "thumbnail-image-url": "http://example.com/media/events/3/thumbnail/U2ZpSU1IK2/4fa07a9a-ef72-45f8-993b-037b0ad6dd6e.jpg",

We can clearly see that server is generating three other images on permanent storage as well as creating the copy of original-image-url into permanent storage.
Since we already have our Storage class, all we need to do is to make the little bit changes in it due to the decoupling of the Open Event. Also, I had to work on these points below

  • Fix upload module, provide support to generate url of locally uploaded file based on static_domain defined in settings
  • Using PIL create a method to generate new image by converting first it to jpeg(lower size than png) and resize it according to the aspect ratio
  • Create a helper method to create different sizes
  • Store all images in preferred storage.
  • Update APIs to incorporate this feature, drop any URLs in image pointers except original_image_url

Support for generating locally uploaded file’s URL
Here I worked on adding support to check if any static_domain is set by a user and used the request.url as the fallback.

if get_settings()['static_domain']:
        return get_settings()['static_domain'] + \
            file_relative_path.replace('/static', '')
    url = urlparse(request.url)
    return url.scheme + '://' + url.host + file_relative_path

Using PIL create a method to create image

This method is created to create the image based on any size passed it to as a parameter. The important role of this is to convert the image into jpg and then resize it on the basis of size and aspect ratio provided.
Earlier, in Orga Server, we were directly using the “open” method to open Image files but since they are no longer needed to be on the local server, a user can provide the link to any direct image. To add this support, all we needed is to use StringIO to turn the read string into a file-like object

image_file = cStringIO.StringIO(urllib.urlopen(image_file).read())

Next, I have to work on clearing the temporary images from the cloud which was created using temporary APIs. I believe that will be a cakewalk for locally stored images since I already had this support in this method.

if remove_after_upload:
        os.remove(image_file)

Update APIs to incorporate this feature
Below is an example how this works in an API.

if data.get('original_image_url') and data['original_image_url'] != event.original_image_url:
            uploaded_images = create_save_image_sizes(data['original_image_url'], 'event', event.id)
            data['original_image_url'] = uploaded_images['original_image_url']
            data['large_image_url'] = uploaded_images['large_image_url']
            data['thumbnail_image_url'] = uploaded_images['thumbnail_image_url']
            data['icon_image_url'] = uploaded_images['icon_image_url']
        else:
            if data.get('large_image_url'):
                del data['large_image_url']
            if data.get('thumbnail_image_url'):
                del data['thumbnail_image_url']
            if data.get('icon_image_url'):
                del data['icon_image_url']

Here the method “create_save_image_sizes” provides the different URL of different images of different sizes and we clearly dropping any other images of different sizes is provided by the user.

General Suggestion
Sometimes when we work on such issues there are some of the things to take care of for example, if you checked the first snippet, I tried to ensure that you will get the URL although it is sure that static_domain will not be blank, because even if the user (admin) doesn’t fill that field then it will be filled by server hostname
A similar situation is the one where there is no record in Image Sizes table, may be server admin didn’t add one. In that case, it will use the standard sizes stored in the codebase to create different images of different sizes.

Resources:

Continue ReadingImage Uploading in Open Event API Server

Adding Text on an Image in Phimpme Android

As Phimpme Android is an image app which provides own custom camera, editing, and sharing images on multiple platforms, so we decided to introduce a new feature, where the user can write text on images.

Using this feature, a user can write text on images and share on multiple  platforms like Facebook, twitter, wordPress, drupal, pinterest, etc. In this post, I am going to explain how I implemented this feature.

Writing Text on image in Phimpme Android

How I implemented Text on Image in Phimpme

First, create an imageview and apply the original image bitmap or load with any image library by providing the local path of the image.

imageView.setImageBackgroundResources(imagePath);

Once an image is loaded we will ask a user to enter text in editext and the text will be displayed above the image. The code for this is:

EditText inputBox = (EditText) layout.findViewById(R.id.editText);
String textString = inputBox.getText().toString();

Now we have textString variable that stores the text we wanted to display. To set this text we need to create a textview on the image, which can be done as follows:

TextView textView = new TextView(context);
textView.setText(textString);

We allow the user to drag the text over image to his/her desired location. This is done using setOnTouchListener() as follows

TextView.setOnTouchListener(new View.OnTouchListener() {
    float lastX = 0, lastY = 0;
 
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case (MotionEvent.ACTION_DOWN):
                lastX = event.getX();
                lastY = event.getY();
 
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = event.getX() - lastX;
                float dy = event.getY() - lastY;
                float finalX = v.getX() + dx;
                float finalY = v.getY() + dy + v.getHeight();               v.setX(finalX;              
              v.setY(finalY);
              break;
              }
              return true;
              }
        });

Thus, we have set the text according to the user’s desired location.

Problem I faced

The text doesn’t appear to move from the desired position, instead, the text moves from some offset value. To solve this I have used two variable finalX and finaly. Now I am calculating the distance from the point to corner and adding the extra offset value to finalX and finalY. So this is how I achieve perfect image on text feature with point accuracy in Phimpme-Android.

Resources:

 

Continue ReadingAdding Text on an Image in Phimpme Android

Adding Unit Tests for Services in loklak search

In Loklak search, it can be tricky to write tests for services as these services are customizable and not fixed. Therefore, we need to test every query parameter of the URL. Moreover, we need to test if service is parsing data in a correct manner and returns only data of type ApiResponse.

In this blog here, we are going to see how to build different components for unit testing services. We will be going to test Search service in loklak search which makes Jsonp request to get the response from the loklak search.json API which are displayed as feeds on loklak search. We need to test if the service handles the response in a correct way and if the request parameters are exactly according to customization.

Service to test

Search service in loklak search is one of the most important component in the loklak search. SearchService is a class with a method fetchQuery() which takes parameter and sets up URL parameters for the search.json API of loklak. Now, it makes a JSONP request and maps the API response. The Method fetchQuery() can be called from other components with parameters query and lastRecord to get the response from the server based on a certain search query and the last record to implement pagination feature in loklak search. Now as the data is retrieved, a callback function is called to access the response returned by the API. Now, the response received from the server is parsed to JSON format data to extract data from the response easily.

@Injectable()
export class SearchService {
private static readonly apiUrl: URL = new URL(‘http://api.loklak.org/api/search.json’);
private static maximum_records_fetch = 20;
private static minified_results = true;
private static source = ‘all’;
private static fields = ‘created_at,screen_name,mentions,hashtags’;
private static limit = 10;
private static timezoneOffset: string = new Date().getTimezoneOffset().toString();constructor(
private jsonp: Jsonp
) { }// TODO: make the searchParams as configureable model rather than this approach.
public fetchQuery(query: string, lastRecord = 0): Observable<ApiResponse> {
const searchParams = new URLSearchParams();
searchParams.set(‘q’, query);
searchParams.set(‘callback’, ‘JSONP_CALLBACK’);
searchParams.set(‘minified’, SearchService.minified_results.toString());
searchParams.set(‘source’, SearchService.source);
searchParams.set(‘maximumRecords’, SearchService.maximum_records_fetch.toString());
searchParams.set(‘timezoneOffset’, SearchService.timezoneOffset);
searchParams.set(‘startRecord’, (lastRecord + 1).toString());
searchParams.set(‘fields’, SearchService.fields);
searchParams.set(‘limit’, SearchService.limit.toString());
return this.jsonp.get(SearchService.apiUrl.toString(), { search: searchParams })
.map(this.extractData)}private extractData(res: Response): ApiResponse {
try {
return <ApiResponse>res.json();
} catch (error) {
console.error(error);
}
}

Testing the service

  • Create a mock backend to assure that we are not making any Jsonp request. We need to use Mock Jsonp provider for this. This provider sets up MockBackend and wires up all the dependencies to override the Request Options used by the JSONP request.

const mockJsonpProvider = {
provide: Jsonp,
deps: [MockBackend, BaseRequestOptions],
useFactory: (backend: MockBackend, defaultOptions: BaseRequestOptions) => {
return new Jsonp(backend, defaultOptions);
}
};

 

  • Now, we need to configure the testing module to isolate service from other dependencies. With this, we can instantiate services manually. We have to use TestBed for unit testing and provide all necessary imports/providers for creating and testing services in the unit test.

describe(‘Service: Search’, () => {
let service: SearchService = null;
let backend: MockBackend = null;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
MockBackend,
BaseRequestOptions,
mockJsonpProvider,
SearchService
]
});
});

 

  • Now, we will inject Service (to be tested) and MockBackend into the Testing module. As all the dependencies are injected, we can now initiate the connections and start testing the service.

beforeEach(inject([SearchService, MockBackend], (searchService: SearchService, mockBackend: MockBackend) => {
service = searchService;
backend = mockBackend;
}));

 

  • We will be using it() block to mention about what property/feature we are going to test in the block. All the tests will be included in this block. One of the most important part is to induce callback function done which will close the connection as soon the testing is over.

it(‘should call the search api and return the search results’, (done)=>{
// test goes here
});

 

  • Now, we will create a connection to the MockBackend and subscribe to this connection. We need to configure ResponseOptions so that mock response is JSONified and returned when the request is made.  Now, the MockBackend is set up and we can proceed to make assertions and test the service.

const result = MockResponse;
backend.connections.subscribe((connection: MockConnection) => {
const options = new ResponseOptions({
body: JSON.stringify(result)
});
connection.mockRespond(new Response(options));

 

  • We can now add test by using expect() block to check if the assertion is true or false. We will now test:
    • Request method: We will be testing if the request method used by the connection created is GET.

expect(connection.request.method).toEqual(RequestMethod.Get);
    • Request Url: We will be testing if all the URL Search Parameters are correct and according to what we provide as a parameter to the method fetchQuery().

expect(connection.request.url).toEqual(
`http://api.loklak.org/api/search.json` +
`?q=${query}` +
`&callback=JSONP_CALLBACK` +
`&minified=true&source=all` +
`&maximumRecords=20&timezoneOffset=${timezoneOffset}` +
`&startRecord=${lastRecord + 1}` +
`&fields=created_at,screen_name,mentions,hashtags&limit=10`);
});
);

 

  • Response:  Now, we need to call the service to make a request to the backend and subscribe to the response returned. Next, we will make an assertion to check if the response returned and parsed by the service is equal the Mock Response that should be returned. At the end, we need to call the callback function done() to close the connection.

service
.fetchQuery(query, lastRecord)
.subscribe((res) => {
expect(res).toEqual(result);
done();
});
});

Reference

Continue ReadingAdding Unit Tests for Services in loklak search

Permission Manager in Open Event API Server

Open Event API Server uses different decorators to control permissions for different access levels as discussed here. Next challenging thing for permissions was reducing redundancy and ensuring permission decorators are independent of different API views. They should not look to the view for which they are checking the permission or some different logic for different views.

In API Server, we have different endpoints that leads to same Resource this way we maintain relationships between different entities but this leads to a problem where permission decorators has to work on different API endpoints that points to different or same resource and but to check a permission some attributes are required and one or more endpoints may not provide all attributes required to check a permission.

For instance, PATCH /session/id` request requires permissions of a Co-Organizer and permission decorator for this requires two things, user detail and event details. It is easy to fetch user_id from logged in user while it was challenging to get “event_id”. Therefore to solve this purpose I worked on a module named “permission_manager.py” situated at “app/api/helpers/permission_manager.py” in the codebase

Basic Idea of Permission Manager

Permission manager basically works to serve the required attributes/view_kwargs to permission decorators so that these decorators do not break

Its logic can be described as:

    1. It first sits in the middle of a request and permission decorator
    2. Evaluates the arguments passed to it and ensure the current method of the request (POST, GET, etc ) is the part of permission check or not.
    3. Uses two important things, fetch and fetch_as
      fetch => value of this argument is the URL parameter key which will be fetched from URL or the database ( if not present in URL )
      fetch_as => the value received from fetch will be sent to permission decorator by the name as the value of this option.
    4. If the fetch key is not there in URL, It uses third parameter model which is Model if the table from where this key can be fetched and then passes it to permission decorator
    5. Returns the requested view on passing access level and Forbidden error if fails

This way it ensures that if looks for the only specific type of requests allowing us to set different rules for different methods.

if 'methods' in kwargs:
        methods = kwargs['methods']

    if request.method not in methods:
        return view(*view_args, **view_kwargs)

Implementing Permission Manager

Implementing it was a simple thing,

  1. Firstly, registration of JSON API app is shifted from app/api/__init__.py to app/api/bootstrap.py so that this module can be imported anywhere
  2. Added permission manager to the app_v1 module
  3. Created permission_manager.py in app/api/helpers
  4. Added it’s usage in different APIs

An example Usage:

decorators = (api.has_permission('is_coorganizer', fetch='event_id', fetch_as="event_id", methods="POST",
                                     check=lambda a: a.get('event_id') or a.get('event_identifier')),)

Here we are checking if the request has the permission of a Co-Organizer and for this, we need to fetch event_id  from request URI. Since no model is provided here so it is required for event_id in URL this also ensures no other endpoints can leak the resource. Also here we are checking for only POST requests thus it will pass the GET requests as it is no checking.

What’s next in permission manager?

Permission has various scopes for improving, I’m still working on a module as part of permission manager which can be used directly in the middle of views and resources so that we can check for permission for specific requests in the middle of any process.

The ability to add logic so that we can leave the check on the basis of some logic may be adding some lambda attributes will work.

Resources

Continue ReadingPermission Manager in Open Event API Server

Auto-Refreshing Mode in loklak Media Wall

Auto-refreshing wall means that the request to the loklak server for the feeds must be sent after every few seconds and adding up new feeds in the media wall as soon as the response is received for a single session. For a nice implementation, it is also necessary to check if the new feeds are being received from the server and consequently, close the connection as soon as no feeds are received as to maintain session singularity.

In this blog post, I am explaining how I implemented the auto-refreshing mode for media wall using tools like ngrx/store and ngrx/effects.

Flow Chart

The flowchart below explains the workflow of how the actions, effects and service are linked to create a cycle of events for auto-refreshing mode. It also shows up how the response is handled as a dependency for the next request. Since effects play a major role for this behaviour, we can say it as the “Game of Effects”.

Working

  • Effect wallSearchAction$: Assuming the Query for media wall has changed and ACTION: WALL_SEARCH has been dispatched, we will start from this point of time. Looking into the flowchart, we can see as soon the action WALL_SEARCH is dispatched, a effect needs to be created to detect the action dispatched.This effect customizes the query and sets up various configurations for search service and calls the service. Depending on whether the response is received or not, it either dispatches WallSearchCompleteSuccessAction or WallSearchCompleteFailAction respectively. Moreover, this effect is responsible for changing the route/location of the application.

@Effect()
wallSearchAction$: Observable<Action>
= this.actions$
.ofType(wallAction.ActionTypes.WALL_SEARCH)
.debounceTime(400)
.map((action: wallAction.WallSearchAction) => action.payload)
.switchMap(query => {
const nextSearch$ = this.actions$.ofType(wallAction.ActionTypes.WALL_SEARCH).skip(1);
const searchServiceConfig: SearchServiceConfig = new SearchServiceConfig();if (query.filter.image) {
searchServiceConfig.addFilters([‘image’]);
} else {
searchServiceConfig.removeFilters([‘image’]);
}
if (query.filter.video) {
searchServiceConfig.addFilters([‘video’]);
} else {
searchServiceConfig.removeFilters([‘video’]);
}return this.apiSearchService.fetchQuery(query.queryString, searchServiceConfig)
.takeUntil(nextSearch$)
.map(response => {
const URIquery = encodeURIComponent(query.queryString);
this.location.go(`/wall?query=${URIquery}`);
return new apiAction.WallSearchCompleteSuccessAction(response);
})
.catch(() => of(new apiAction.WallSearchCompleteFailAction()));
  • Property lastResponseLength: Looking into the flow chart, we can see that after WallSearchCompleteSuccessAction is dispatched, we need to check for the number of feeds in the response. If the number of feeds in the response is more than 0, we can continue to make a new request to the server. On the other hand, if no feeds are received, we need to close the connection and stop requesting for more feeds. This check is implemented using lastResponseLength state property of the reducer which maintains the length of the entities for the last response received.

case apiAction.ActionTypes.WALL_SEARCH_COMPLETE_SUCCESS: {
const apiResponse = action.payload;return Object.assign({}, state, {
entities: apiResponse.statuses,
lastResponseLength: apiResponse.statuses.length
});
}

 

  • Effect nextWallSearchAction$: Now, we have all the information regarding if we should dispatch WALL_NEXT_PAGE_ACTION depending on the last response received. We need to implement an effect that detects WALL_SEARCH_COMPLETE_SUCCESS  keeping in mind that the next request should be made 10 seconds after the previous response is received. For this behaviour, we need to use debounceTime() which emits a value only after certain specified time period has passed. Here, debounce is set to 10000ms which is equal to 10 seconds. The effect also needs to dispatch the next action depending on the lastResponseLength state property of the reducer. It should dispatch WallNextPageAction if the entities length of the response is more than 0, otherwise, it should dispatch StopWallPaginationAction.

@Effect()
nextWallSearchAction$
= this.actions$
.ofType(apiAction.ActionTypes.WALL_SEARCH_COMPLETE_SUCCESS)
.debounceTime(10000)
.withLatestFrom(this.store$)
.map(([action, state]) => {
if (state.mediaWallResponse.lastResponseLength > 0) {
return new wallPaginationAction.WallNextPageAction();
}
else {
return new wallPaginationAction.StopWallPaginationAction();
}
});

 

  • Effect wallPagination$: Now, we need to have an effect that should detect WALL_NEXT_PAGE_ACTION and call the SearchService similar to wallSearchAction$ Effect. However, we need to keep a check on the last record of the entities from the previous response received. This can be done using lastRecord state property which maintains the last record of the entities.

@Effect()
wallPagination$: Observable<Action>
= this.actions$
.ofType(wallPaginationAction.ActionTypes.WALL_NEXT_PAGE)
.map((action: wallPaginationAction.WallNextPageAction) => action.payload)
.withLatestFrom(this.store$)
.map(([action, state]) => {
return {
query: state.mediaWallQuery.query,
lastRecord: state.mediaWallResponse.entities.length
};
})
.switchMap(queryObject => {
const nextSearch$ = this.actions$.ofType(wallAction.ActionTypes.WALL_SEARCH);this.searchServiceConfig.startRecord = queryObject.lastRecord + 1;
if (queryObject.query.filter.image) {
this.searchServiceConfig.addFilters([‘image’]);
} else {
this.searchServiceConfig.removeFilters([‘image’]);
}
if (queryObject.query.filter.video) {
this.searchServiceConfig.addFilters([‘video’]);
} else {
this.searchServiceConfig.removeFilters([‘video’]);
}return this.apiSearchService.fetchQuery(queryObject.query.queryString, this.searchServiceConfig)
.takeUntil(nextSearch$)
.map(response => {
return new wallPaginationAction.WallPaginationCompleteSuccessAction(response);
})
.catch(() => of(new wallPaginationAction.WallPaginationCompleteFailAction()));
});

 

  • Effect nextWallPageAction$: Similar to the nextWallSearchAction$ effect, we need to implement an effect that detects WALL_PAGINATION_SUCCESS_ACTION and depending on the lastResponseLength should either dispatch WallNextPageAction or StopWallPaginationAction after a certain specified debounceTime.

@Effect()
nextWallPageAction$
= this.actions$
.ofType(wallPaginationAction.ActionTypes.WALL_PAGINATION_COMPLETE_SUCCESS)
.debounceTime(10000)
.withLatestFrom(this.store$)
.map(([action, state]) => {
if (state.mediaWallResponse.lastResponseLength > 0) {
return new wallPaginationAction.WallNextPageAction();
}
else {
return new wallPaginationAction.StopWallPaginationAction();
}
});

 

Now the cycle is created and requests will be automatically made after every 10 seconds depending on the previous response. This cycle also closes the connection and stops making a pagination request for the particular query as soon as no feeds are received from the server.

Reference

Continue ReadingAuto-Refreshing Mode in loklak Media Wall

Testing Deploy Functions Using Sinon.JS in Yaydoc

In yaydoc, we deploy the generated documentation to the GitHub pages as well as Heroku. It is one of the important functions in the source code. I don’t want to break the build in future by any unnoticed change, so I decided to write a test case for deploy function. But the deploy function had lot dependencies like child processes, sockets, etc. Also it is not a pure function, so there is no return object to assert the value. Then I decided to stub for child process to check whether the correct script was passed or not. In order to write stub I decided to use sinon js framework because it can be used for writing stubs, mocks and spies. One of the advantages with sinon is that it’ll work with any testing framework.

sinon.stub(require("child_process"), "spawn").callsFake(function (fileName, args) {
  if (fileName !== "./ghpages_deploy.sh" ) {
    throw new Error(`invalid ${fileName} invoked`);
  }

  if (fileName === "./ghpages_deploy.sh") {
    let ghArgs = ["-e", "-i", "-n", "-o", "-r"];
    ghArgs.forEach(function (x)  {
      if (args.indexOf(x) < 0) {
        throw new Error(`${x} argument is not passed`);
      }
    })
  }
 
  let process = {
    on: function (listenerId, callback) {
      if (listenerId !== "exit") {
        throw new Error("listener id is not exit");
      }
    }
  }
  return process;
});

In sinon you can create s stub by passing the object in the first parameter and the method name in the second parameter to sinon’s stub method. After it returns an object, pass the function which you would like to replace with the “callFakes” function.

In above code, I wrote a simple stub which overwrites NodeJS child_process’s spawn method. So I passed the “child_process” module in the first parameter and “spawn” method name in the second parameter. You must check whether they are passing the correct deploy script and the correct parameter. So, I wrote a function which checks the condition and then pass the method to the callFakes method.

describe('deploy script', function() {
  it("gh-pages deploy", function() {
    deploy.deployPages(fakeSocket, {
      gitURL: "https://github.com/sch00lb0y/yaydoc.git",
      encryptedToken: crypter.encrypt("dummykey"),
      email: "admin@fossasia.org",
      uniqueId: "ajshdahsdh",
      username: "fossasia"
    });
  });
});

Finally test the deploy function by calling it. I use mocha as a testing framework. I have already written a blog on mocha. If you’re interested in mocha please check out this blog.

Resources:

Continue ReadingTesting Deploy Functions Using Sinon.JS in Yaydoc

Selecting Best persistent storage for Phimpme Android and how to use it

As we are progressing in our Phimpme Android app. I added account manager part which deals with connecting all other accounts to phimpme. Showing a list of connected accounts.

We need a persistent storage to store all the details such as username, full name, profile image url, access token (to access API). I researched on various Object Relation mapping (ORMs) such as:

  1. DBFlow: https://github.com/Raizlabs/DBFlow
  2. GreenDAO: https://github.com/greenrobot/greenDAO
  3. SugarORM: http://satyan.github.io/sugar/
  4. Requery: https://github.com/requery/requery

and other NoSQL databases such as Realm Database : https://github.com/realm/realm-java.

After reading a lot from some blogs on the benchmarking of these ORMs and database, I came to know that Realm database is quite better in terms of Speed of writing data and ease of use.

Steps to integrate Realm Database:

  • Installation of Realm database in android

Following these steps https://realm.io/docs/java/latest/#installation quickly setup realm in android. Add

classpath "io.realm:realm-gradle-plugin:3.3.2"

in Project level build.gradle file and Add

apply plugin: 'realm-android' 

in app level build.gradle, That’s it for using Realm

  • Generating required Realm models

Firstly, make sure what you need to store in your database. In case of phimpme, I first go through the account section and noted down what needs to be there.  Profile image URL, username, full name, account indicator image name. Below image illustrate this better.

This is the Realm Model class I made in Kotlin to store name, username and access token for accessing API.

open class AccountDatabase(
       @PrimaryKey var name: String = "",
       var username: String = "",
       var token: String = ""
) : RealmObject()

  • Writing data in database

In Account manager, I create a add account option from where a dialog appear with a list of accounts. Currently, Twitter is working, when onSuccess function invoke in AccountPickerFragment I start a twitter session and store values in database. Writing data in database:

// Begin realm transaction
realm.beginTransaction();

// Creating Realm object for AccountDatabase Class
account = realm.createObject(AccountDatabase.class,
       accountsList[0]);

account.setUsername(session.getUserName());
account.setToken(String.valueOf(session.getAuthToken()));
realm.commitTransaction();

Begin and commit block in necessary. There is one more way of doing this is using execute function in Realm

  • Use Separate Database Helper class for Database operations

It’s good to use a separate class for all the Database operations needed in the project. I created a DatabaseHelper Class and added a function to query the result needed. Query the database

public RealmResults<AccountDatabase> fetchAccountDetails(){
   return realm.where(AccountDatabase.class).findAll();
}

It give all of the results, stored in the database like below

  • Problems I faced with annotation processor while using Kotlin and Realm together

The Kotlin annotation processor not running due to the plugins wrong order. This issue https://github.com/realm/realm-java/pull/2568 helped me in solving that. I addded apply plugin: ‘kotlin-kapt’. In app gradle file and shift apply plugin: ‘realm-android’ In below the order.

Resources:

 

Continue ReadingSelecting Best persistent storage for Phimpme Android and how to use it